メモリ効率の良い方法で同じリストを指す複数のキーを持つPython辞書


9

このコードで説明できるこの固有の要件があります。これは機能するコードですが、メモリ効率は良くありません。

data = [[
        "A 5408599",
        "B 8126880",
        "A 2003529",
    ],
    [
        "C 9925336",
        "C 3705674",
        "A 823678571",
        "C 3205170186",
    ],
    [
        "C 9772980",
        "B 8960327",
        "C 4185139021",
        "D 1226285245",
        "C 2523866271",
        "D 2940954504",
        "D 5083193",
    ]]

temp_dict = {
    item: index for index, sublist in enumerate(data)
        for item in sublist
}

print(data[temp_dict["A 2003529"]])

out: ['A 5408599', 'B 8126880', 'A 2003529']

簡単に言うと、サブリストの各項目をインデックス可能にして、サブリストを返す必要があります。

上記の方法は機能しますが、データが大きい場合、大量のメモリを消費します。メモリとCPUに優しい方法はありますか?データはJSONファイルとして保存されます。

編集 私は可能な最大のユースケースシナリオ(1000サブリスト、各サブリストに100アイテム、100万クエリ)の回答を試しましたが、結果は次のとおりです(10回の実行の平均)。

Method,    Time (seconds),    Extra Memory used
my,        0.637              40 Mb
deceze,    0.63               40 Mb
James,     0.78               200 kb
Pant,      > 300              0 kb
mcsoini,   forever            0 kb

{item: sublist for sublist in data for item in sublist}少し効率的で直接的かもしれません…?!
だます

はい。私のサンプルの場合。私の実際のシナリオでは、サブリストには数百のアイテムと数千のそのようなサブリストがあります。コードのユーザーはメモリが少ない(<2GB)ため、他の重いアプリを実行しているときに、スクリプトが遅いと不平を言っています。
Rahul、

どの問題を正確に解決しようとしていますか?おそらく、最初の文字でインデックスを作成し、いくつかの候補リストを反復処理して、ハッシュテーブルの衝突解決アルゴリズムのような正確な値を見つけるハイブリッドアプローチが機能する可能性があります。
だます

効率的な方法では、yield()などのジェネレータを使用してください。
Saisiva A

ありがとう。「ハッシュテーブルの衝突解決」の意味を学びます。
Rahul、

回答:


2

ディクショナリの生成にかかる時間/メモリと、オンザフライ方式でデータ全体をスキャンするのにかかる時間との間のトレードオフの領域にいます。

低メモリ方式が必要な場合は、各サブリストで値を検索する関数を使用できます。ジェネレーターを使用すると、ユーザーは初期結果をより速く取得できますが、大きなデータセットの場合、これは戻りの間隔が遅くなります。

data = [[
        "A 5408599",
        "B 8126880",
        "A 2003529",
    ],
    [
        "C 9925336",
        "C 3705674",
        "A 823678571",
        "C 3205170186",
    ],
    [
        "C 9772980",
        "B 8960327",
        "C 4185139021",
        "D 1226285245",
        "C 2523866271",
        "D 2940954504",
        "D 5083193",
    ]]


def find_list_by_value(v, data):
    for sublist in data:
        if v in sublist:
            yield sublist

for s in find_list_by_value("C 9772980", data):
    print(s)

コメントで述べたように、最初の文字または最初の2または3文字だけに基づいてハッシュテーブルを作成することから始めるのがよいでしょう。これにより、サブリストの候補リストを作成し、それらをスキャンして、値がサブリストにあるかどうかを確認できます。

from collections import defaultdict

def get_key(v, size=3):
    return v[:size]

def get_keys(sublist, size=3):
    return set(get_key(v, size) for v in sublist)

def find_list_by_hash(v, data, hash_table, size=3):
    key = get_key(v, size)
    candidate_indices = hash_table.get(key, set())
    for ix in candidates:
        if v in data[ix]:
            yield data[ix]

# generate the small hash table
quick_hash = defaultdict(set)
for i, sublist in enumerate(data):
    for k in get_keys(sublist, 3):
        quick_hash[k].add(i)

# lookup a value by the small hash
for s in find_list_by_hash("C 9772980", data, quick_hash, 3):
    print(s)

このコードでquick_hashは、データ構造全体をスキャンしているため、ビルドに時間がかかります。ただし、メモリフットプリントははるかに小さくなります。パフォーマンスを調整するための主なパラメータはsizeです。サイズが小さいほどメモリフットプリントは小さくなりfind_list_by_hashますが、候補プールが大きくなるため、実行時に時間がかかります。いくつかのテストを行ってsize、データに適切なものを確認することができます。すべての値が少なくともと同じであることに注意してくださいsize


そして、私はpythonとプログラミングを知っていると思いました。ありがとう。学ぶことはたくさんあります。
Rahul、

2

あなたはこのようなことを試すことができます:

list(filter(lambda x: any(["C 9772980" in x]),data))

マッピング構造を作成する必要はありません。


ありがとう、男。これが速いかどうかを確認する必要があります。
Rahul、

1
計算する理解力がないため、最初ははるかに速くなりますが、各要素を見つけるために、このメソッドはデータ全体を再スキャンするため、使用時にはるかに遅くなります。
エドゥ

もちろん、これで問題が解決するかどうかお知らせください。
Bhushan Pant

@EdouardThiel:私も同じように感じています。私の実際の使用は、開始ケースよりも多くのユースケースがあります。
Rahul、

@EdouardThiel正しい。しかし、正確なユースケースについてはわかりません。
Bhushan Pant

2

パンダを使ってこれを試してください

import pandas as pd
df=pd.DataFrame(data)
rows = df.shape[0]
for row in range(rows):
    print[[row]]    #Do something with your data

これはシンプルなソリューションに見えますが、データが大きくなっても、効率的に処理されます


あなたのサイズを確認してくださいdf:与えられたサンプルデータのリストdata(> x12)とdict temp_dict(〜x2)よりかなり大きいです-メモリ効率が良いとは言えません
MrFuppes

@MrFuppesこの場合、パンダは文字列を物理的にコピーしないため、この引数は有効ではないと思います
mcsoini

@mcsoini、私は私のコメントが少し表面的であることを認めます- pandas組み込みのPython機能よりもこの問題をより効率的に処理するかどうかを判断するには、より詳細な分析が必要になります。
MrFuppes

@MrFuppes:同意します。を使用しpandasて実行できる場合に使用する理由stdlib。派手に見えるからといって?
Rahul、

1
しかし、あなたは私がどのようにデータフレームをクエリするかを提供しませんでした。あなたの解決策が私の問題をどのように解決するか教えてください。私はパンダ用に@mcsoiniの解決策を試しましたが、100万回のクエリに永久にかかっています。理由はわかりません。さまざまな方法の結果については、更新された質問を参照してください。
Rahul、

0

これが大量のデータに対してどのように動作するかは完全にはわかりませんが、次のような方法で試すことができます。

import pandas as pd
df = pd.DataFrame(data).T
df.loc[:, (df == 'A 2003529').any(axis=0)]
Out[39]: 
           0
0  A 5408599
1  B 8126880
2  A 2003529
3       None
4       None
5       None
6       None

編集:偽の大規模データを使用した簡単なテストに基づくと、時間の面で有益ではないようです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.