在 python 中是否有增加字典的有效替代方法?
Is there an efficient alternative for growing dictionary in python?
我正在阅读 xml 格式的大型文本语料库,并将某些单词出现的次数存储在字典中,其中键是三个元素的元组 {('a','b','c'):1}
。该字典的大小不断增长,同时其值得到更新。
在将字典写入 hdf 文件之前,我需要一直将字典保存在内存中 ~25GB。
我试图找到一些有关哪些数据类型可以真正反映当前字典结构的信息,但没有找到任何具体答案。我最关心的是内存大小。 python 中是否有任何数据结构可以减轻这些限制?我读过 pyjudy 库,但看起来它是 32 位的,几乎不再开发了。如果有任何建议,我将不胜感激。
I need to keep a dictionary in memory all the time ~25GB before
writing it to hdf file.
这是否意味着数据是 25GB,或者生成的字典在内存中是 25GB?这意味着如果元组中的每个元素都是一个单词,那么您将拥有 很多 个 3 元组。我认为您实际上不需要所有这些内存。但是,我真的很怀疑三词元组到整数的字典真的是25GB。
根据我的 /usr/share/dict/words,平均一个单词大约有 10 个字符。在最常见的情况下,每个都是一个字节。每条记录 30 个字节,没有整数,您可能有 4 个字节的键。所以每条记录 34 个字节。当然,字典会增加开销。但我们仍然很容易地谈论超过 6 亿个 3 元组。当然,在这种情况下,这是不同的单词元组,因为您要计算字典值中的每一个。
不完全明白你的问题是什么,我会指出你在shelve。它为您提供了一些看起来像字典(界面方面)但有磁盘支持的东西。
你真的试过这个吗?过早的优化是万恶之源:)
我不知道是否还有其他 dict 实现,但我想你有 2.5 个选项:
- 在本地优化您的编码 - 即分别为您的单词编制索引,以便您获得
{'a': 1, 'b': 2, ...}
的字典,然后使用 {(1,2,3): 1}
作为三元组字典。这将减少内存使用,因为字符串不会重复多次。
2.1。在其他语言的扩展中实现上述内容。
2.2。只是使用另一种语言。与您存储的小值相比,python 中的整个字典和值总是会有严重的开销。
编辑:对 das-g 评论的更长的回答,因为我认为它值得一个。 Python 擅长只复制变化的数据。当不可变值被分配给一个新变量时,它们会保留在一个地方。这允许以下操作几乎没有新分配:
# VmRSS: 27504 kB
In [1]: a=['a'*1024]
# VmRSS: 27540 kB
In [2]: a=['a'*1024]*10000
# VmRSS: 27540 kB
但是对于来自不同地方的相同值,情况并非如此。如 - 它们每次都是从头开始创建(例如从文件中读取),而不是从现有值复制:
# VmRSS: 27540 kB
In [4]: a=['a'*1024 for _ in range(10000)]
# VmRSS: 38280 kB
In [5]: b=['a'*1024 for _ in range(10000)]
# VmRSS: 48840 kB
这就是为什么如果单词是从一些进程外源读取的,那么值得自己对它们进行重复数据删除,因为 Python 不会自动为您完成。
所以在实践中,你甚至可以通过做一些看起来很愚蠢的事情来节省内存:
def increase_triple(a, b, c):
triples[(a,b,c)] += 1
与:
WORDS = {}
def dedup(s):
s_dedup = WORDS.get(s)
if s_dedup is None:
WORDS[s] = s
return s
else:
return s_dedup
def increase_triple(a, b, c):
triples[(dedup(a),dedup(b),dedup(c))] += 1
正如@StefanPochmann 在评论中提到的那样,标准函数 intren() 的功能与上述 dedup()
的功能几乎相同。更好。
用传统的方式处理大数据 small/mid-size 数据,在效率和可维护性方面往往是错误的方法。即使你今天完成了它,也不能保证你明天就能完成它,原因有几个(例如,你的数据增长超过你的可用内存,数据分区等)。
取决于您输入的行为,您应该查看批处理引擎(Hadoop/Mapreduce、Apache Spark) or stream processing engines(Apache Storm、Spark Streaming)。
如果您想继续使用 python,Apache Spark 有 python 接口。
最后,有一个名为Apache Zeppelin的项目,用于交互式大数据分析(我的论文主题)。它支持 Apache Spark 和其他几个平台。
老实说,我会推荐一个数据库,但如果你执意要将其保存在 Python...
从单词到索引的映射可能会有所帮助。将 ('a', 'b', 'c') 的键替换为 (1, 2, 3),其中 (1, 2, 3) 是查找 table 中的值.
lookup_table = {'a':1, 'b':2, 'c':3}
如果您的大部分单词都很长并且重复很多,这将是主要用途。如果您有 (a,b,c)、(a,c,b)、(b,a,c)、(b,c,a)、(c,a,b) 和 (c,b,a ), 每个单词只存储一次而不是 6 次。
编辑:这取决于您生成字符串的方式。
"ab c" is "ab c" : True
"a"+"b c" is "ab c" : False
另一种方法是读取文件,但是当输出增长到某个点时,对键(a-z)进行排序,并输出到文件:
aaab:101
acbc:92
add:109
然后擦除字典并继续处理。一旦文件完成处理,您就可以合并这些值。这不会占用太多内存,因为所有文件都已排序;您只需要在内存中为每个文件保留一行。 (想想合并排序。)
或者你可以只使用数据库,让它来处理这个问题。
我正在阅读 xml 格式的大型文本语料库,并将某些单词出现的次数存储在字典中,其中键是三个元素的元组 {('a','b','c'):1}
。该字典的大小不断增长,同时其值得到更新。
在将字典写入 hdf 文件之前,我需要一直将字典保存在内存中 ~25GB。
我试图找到一些有关哪些数据类型可以真正反映当前字典结构的信息,但没有找到任何具体答案。我最关心的是内存大小。 python 中是否有任何数据结构可以减轻这些限制?我读过 pyjudy 库,但看起来它是 32 位的,几乎不再开发了。如果有任何建议,我将不胜感激。
I need to keep a dictionary in memory all the time ~25GB before writing it to hdf file.
这是否意味着数据是 25GB,或者生成的字典在内存中是 25GB?这意味着如果元组中的每个元素都是一个单词,那么您将拥有 很多 个 3 元组。我认为您实际上不需要所有这些内存。但是,我真的很怀疑三词元组到整数的字典真的是25GB。
根据我的 /usr/share/dict/words,平均一个单词大约有 10 个字符。在最常见的情况下,每个都是一个字节。每条记录 30 个字节,没有整数,您可能有 4 个字节的键。所以每条记录 34 个字节。当然,字典会增加开销。但我们仍然很容易地谈论超过 6 亿个 3 元组。当然,在这种情况下,这是不同的单词元组,因为您要计算字典值中的每一个。
不完全明白你的问题是什么,我会指出你在shelve。它为您提供了一些看起来像字典(界面方面)但有磁盘支持的东西。
你真的试过这个吗?过早的优化是万恶之源:)
我不知道是否还有其他 dict 实现,但我想你有 2.5 个选项:
- 在本地优化您的编码 - 即分别为您的单词编制索引,以便您获得
{'a': 1, 'b': 2, ...}
的字典,然后使用{(1,2,3): 1}
作为三元组字典。这将减少内存使用,因为字符串不会重复多次。
2.1。在其他语言的扩展中实现上述内容。
2.2。只是使用另一种语言。与您存储的小值相比,python 中的整个字典和值总是会有严重的开销。
编辑:对 das-g 评论的更长的回答,因为我认为它值得一个。 Python 擅长只复制变化的数据。当不可变值被分配给一个新变量时,它们会保留在一个地方。这允许以下操作几乎没有新分配:
# VmRSS: 27504 kB
In [1]: a=['a'*1024]
# VmRSS: 27540 kB
In [2]: a=['a'*1024]*10000
# VmRSS: 27540 kB
但是对于来自不同地方的相同值,情况并非如此。如 - 它们每次都是从头开始创建(例如从文件中读取),而不是从现有值复制:
# VmRSS: 27540 kB
In [4]: a=['a'*1024 for _ in range(10000)]
# VmRSS: 38280 kB
In [5]: b=['a'*1024 for _ in range(10000)]
# VmRSS: 48840 kB
这就是为什么如果单词是从一些进程外源读取的,那么值得自己对它们进行重复数据删除,因为 Python 不会自动为您完成。
所以在实践中,你甚至可以通过做一些看起来很愚蠢的事情来节省内存:
def increase_triple(a, b, c):
triples[(a,b,c)] += 1
与:
WORDS = {}
def dedup(s):
s_dedup = WORDS.get(s)
if s_dedup is None:
WORDS[s] = s
return s
else:
return s_dedup
def increase_triple(a, b, c):
triples[(dedup(a),dedup(b),dedup(c))] += 1
正如@StefanPochmann 在评论中提到的那样,标准函数 intren() 的功能与上述 dedup()
的功能几乎相同。更好。
用传统的方式处理大数据 small/mid-size 数据,在效率和可维护性方面往往是错误的方法。即使你今天完成了它,也不能保证你明天就能完成它,原因有几个(例如,你的数据增长超过你的可用内存,数据分区等)。
取决于您输入的行为,您应该查看批处理引擎(Hadoop/Mapreduce、Apache Spark) or stream processing engines(Apache Storm、Spark Streaming)。
如果您想继续使用 python,Apache Spark 有 python 接口。
最后,有一个名为Apache Zeppelin的项目,用于交互式大数据分析(我的论文主题)。它支持 Apache Spark 和其他几个平台。
老实说,我会推荐一个数据库,但如果你执意要将其保存在 Python...
从单词到索引的映射可能会有所帮助。将 ('a', 'b', 'c') 的键替换为 (1, 2, 3),其中 (1, 2, 3) 是查找 table 中的值.
lookup_table = {'a':1, 'b':2, 'c':3}
如果您的大部分单词都很长并且重复很多,这将是主要用途。如果您有 (a,b,c)、(a,c,b)、(b,a,c)、(b,c,a)、(c,a,b) 和 (c,b,a ), 每个单词只存储一次而不是 6 次。
编辑:这取决于您生成字符串的方式。
"ab c" is "ab c" : True
"a"+"b c" is "ab c" : False
另一种方法是读取文件,但是当输出增长到某个点时,对键(a-z)进行排序,并输出到文件:
aaab:101
acbc:92
add:109
然后擦除字典并继续处理。一旦文件完成处理,您就可以合并这些值。这不会占用太多内存,因为所有文件都已排序;您只需要在内存中为每个文件保留一行。 (想想合并排序。)
或者你可以只使用数据库,让它来处理这个问题。