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}可能会更有效,更直接...?!
— deceze

是。我的样品盒 在我的实际情况中,子列表包含100项商品和数千个此类子列表。该代码的用户的内存较小(<2gb),因此在运行其他繁重的应用程序时,他们会抱怨您的脚本运行缓慢。
— 拉胡尔

您到底想解决什么问题?也许可以使用一种混合方法,在该方法中,您可以按第一个字母索引,然后遍历几个候选列表以找到您的确切值,就像哈希表冲突解决算法一样。
— deceze

对于有效的方式,请使用诸如yield()之类的生成器。
— Saisiva A

谢谢。我将学习“哈希表冲突解决”的含义。
— 拉胡尔

Answers:


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和编程。谢谢。有很多东西要学。
— 拉胡尔

2

您可以尝试如下操作:

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

无需建立映射结构。


谢谢你,兄弟。我将不得不检查这是否更快。
— 拉胡尔

1
因为没有计算能力,所以开始时会快很多,但是使用起来会慢很多,因为对于每个要查找的元素,此方法都会重新扫描整个数据。
— 爱德华·蒂尔

当然,让我知道这是否适合您。
— Bhushan Pant

@EdouardThiel:我也有同感。我的实际使用情况是比开始情况更多的用例。
— 拉胡尔

@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)和字典temp_dict(
— 〜x2)

@MrFuppes我认为此参数无效,因为在这种情况下,pandas不会物理复制字符串
— mcsoini

@mcsoini,我承认我的评论有点肤浅-需要进行更详细的分析以确定是否pandas比内置python功能更有效地处理此问题。
— MrFuppes

@MrFuppes:我同意。pandas如果可以使用,为什么要使用stdlib。只是因为看起来不错?
— 拉胡尔

1
但是您没有提供如何查询数据框。您能告诉我您的解决方案将如何解决我的问题。我尝试了@mcsoini的熊猫解决方案,但它永远需要100万条查询。我不知道为什么 请参阅我更新的问题,以了解各种方法的结果。
— 拉胡尔

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

编辑:基于对一些伪造的大规模数据的快速测试,在时间上似乎无益。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.