![](/img/trans.png)
[英]How to efficiently get the mean of the elements in two list of lists in Python
[英]How to efficiently get count for item in list of lists in python
我有如下三个列表。
mylist = [[5274919, ["my cat", "little dog", "fish", "rat"]],
[5274920, ["my cat", "parrot", "little dog"]],
[5274991, ["little dog", "fish", "duck"]]]
myconcepts = ["my cat", "little dog"]
hatedconcepts = ["rat", "parrot"]
对于myconcepts
中的每个概念,我想使用mylist
计算与之相关的所有其他概念。 然后从中删除hatedconcepts
的概念。 所以,我的 output 应该如下所示。
{"my cat": [("my cat", 2), ("little dog", 2), ("fish", 1)],
"little dog": [("little dog", 3), ("my cat", 2), ("fish", 2), ("duck", 1)]}
我正在使用这段代码来做到这一点。
import collections
myoutput = []
for concept in myconcepts:
mykeywords = []
for item in mylist:
if concept in item[1]:
for mykeyword in item[1]:
if mykeyword in hatedconcepts:
pass
else:
mykeywords.append(mykeyword)
if len(mykeywords) > 0:
sorted_keywords = collections.Counter(mykeywords).most_common()
myoutput.append(tuple((concept, sorted_keywords)))
print(myoutput)
代码的output为:
[('my cat', [('my cat', 2), ('little dog', 2), ('fish', 1)]), ('little dog', [('little dog', 3), ('my cat', 2), ('fish', 2), ('duck', 1)])]
但是现在我有一个巨大的mylist
,大小为 3GB 和近 9000 myconcepts
。 hatedconcepts
计数只有 20 个。看起来使用我当前的代码运行大约需要两周时间。 主要原因可能是我当前的程序是O^3
,效率不高。 因此,我正在寻找使我当前的程序更有效率的方法。 我什至可以接受甚至需要 5-6 天才能运行的 pythonic 解决方案。 请让我知道你的想法。
我在以下位置添加了一部分mylist
: https://drive.google.com/file/d/1M3EhIRwwKwD3Kv4zDsmXaH1D73tx0eF3/view?usp=sharing只是为了了解它的外观。
如果需要,我很乐意提供更多详细信息。
正如其他评论和答案所表明的那样,这个操作最好由 Spark 或数据库处理。 也就是说,这是我的看法,我引入了一些集合操作并最小化了重复循环。
from collections import defaultdict
def get_counts(lst, concepts, hated_concepts):
result = {concept: defaultdict(int) for concept in concepts}
concepts_set = set(concepts)
hated_concepts_set = set(hated_concepts)
for _, inner_list in lst:
# ignore hated concepts
relevant = set(inner_list).difference(hated_concepts_set)
# determine which concepts need to be updated
to_update = relevant.intersection(concepts_set)
for concept in to_update:
for word in relevant:
result[concept][word] += 1
return result
Output 在下方。 您提到 output “必须排序”,但我不清楚所需的排序是什么。 一些计时测试表明这比您在样本数据上提供的代码快 9 倍。
{
'my cat': defaultdict(<class 'int'>, {'my cat': 2, 'fish': 1, 'little dog': 2}),
'little dog': defaultdict(<class 'int'>, {'my cat': 2, 'fish': 2, 'little dog': 3, 'duck': 1})
}
emj_functn avg 0.9355s
get_counts avg 0.1141s
性能测试脚本:
import random
import string
import time
words = list({
''.join(random.choice(string.ascii_lowercase) for _ in range(5))
for _ in range(1000)
})
test_list = [[random.randint(1e6, 1e7), [random.choice(words) for _ in range(100)]] for _ in range(1000)]
test_concepts = [random.choice(words) for _ in range(100)]
test_hated_concepts = [random.choice(words) for _ in range(50)]
def emj_functn(lst, concepts, hated_concepts):
...
def get_counts(lst, concepts, hated_concepts):
...
TEST_CASES = 10
start_time = time.time()
for _ in range(TEST_CASES):
emj_functn(test_list, test_concepts, test_hated_concepts)
end_time = time.time()
avg = (end_time - start_time) / TEST_CASES
print(f'emj_functn avg {avg:.4}s')
start_time = time.time()
for _ in range(TEST_CASES):
get_counts(test_list, test_concepts, test_hated_concepts)
end_time = time.time()
avg = (end_time - start_time) / TEST_CASES
print(f'get_counts avg {avg:.4}s')
我试图让它变快,避免一些重复的循环。 请检查这是否加快了速度。
from itertools import chain
from collections import Counter, defaultdict
database = defaultdict(set)
output = {}
# created a map for different concepts, so we only search the indices where a certain concept is
for index, (_, concepts) in enumerate(mylist):
for concept in concepts:
database[concept].add(index)
for concept in myconcepts:
search_indices = database[concept]
all_counts = Counter(chain.from_iterable(mylist[i][1] for i in search_indices))
for hc in hatedconcepts:
if hc in all_counts: all_counts.pop(hc)
output[concept] = sorted(all_counts.items(), key=lambda x: x[1], reverse=True)
试试这个:
from collections import Counter
req={}
for i in myconcepts:
x=sum([j[1] for j in mylist if i in j[1]],[])
x=[i for i in x if i not in hatedconcepts]
req[i]=dict(Counter(x))
print(req)
output:
{'my cat': {'my cat': 2, 'little dog': 2, 'fish': 1}, 'little dog': {'my cat': 2, 'little dog': 3, 'fish': 2, 'duck': 1}}
如果您有类似字数统计的示例,我建议您使用Apache Spark或Apache Hadoop ,事实上,这些框架专注于此。
两者都有框架可以与 python 一起使用。
但是如果你只想坚持使用 python。
我建议并行化:
my_list
拆分为n
个子列表my_sub_lists
my_list = ["my cat", "little dog", "fish", "rat", "my cat","little dog" ]
# split my_list into n=2 sublists
my_sub_lists = [["my cat", "little dog", "fish"], ["rat", "my cat","little dog"]]
并行计算my_sub_lists
的项目计数
Process 1: Counter(["my cat", "little dog", "fish"])
Process 2 : Counter("rat", "my cat","little dog"])
你会得到一些中间聚合。 my_sub_counts
my_sub_counts = [{"my cat":1, "little dog":1, "fish":1}, {"rat":1, "my cat":1,"little dog":1}]
合并中间结果以获得最终的项目计数。
result = {"my cat":2, "little dog":2, "fish":1, "rat":1}
合并中间聚合会更容易,因为它会更小。
我意识到这有点晚了,但只是想把我的答案放在那里。
在我写我的之前,我没有注意到bphi
的回答。 这个想法几乎相同,但这个答案是有序的。
from collections import Counter, defaultdict
s_myconcepts = set(myconcepts)
s_hatedconcepts = set(hatedconcepts)
myoutput = defaultdict(list)
for _, item in mylist:
item = set(item)
for concept in item.intersection(s_myconcepts):
myoutput[concept].extend(item - s_hatedconcepts)
myoutput = {k: Counter(v).most_common() for k, v in myoutput.items()}
这是一个很好的问题,有一个很好的示例数据集。
通过采用流累加器架构,您应该能够将它的运行时间缩短到几个小时,并且只使用非常少量的 memory。
我注意到子列表的内容非常规则,只是具有重复嵌套结构的列表和本身不包含任何方括号的字符串。 这允许遵循以下策略:
定义一个 chunk_size ,理想情况下它是文件中描述子列表的文本字符数的两倍以上,我使用了 ~2Megs。
将文件中的下一个 chunk_size 个字符读入缓冲区。
使用正则表达式.finditer从缓冲区中提取连续的子列表
eval() 每个 sub_list 然后在结果上运行 Accumulator class 实例以根据您的包含/排除规则增加计数。
对缓冲区中的所有子列表重复
通过finditer保存缓冲区末尾未使用的字符
重复文件中的下一个缓冲区,(在前一个缓冲区末尾添加任何未使用的字符)
当文件用完时,从 Accumulator 实例中提取运行总计。
我首先找到了在您的 11meg 示例中使用最多的短语,然后使用第 1、第 3 和第 5 个作为要包含的单词的测试列表,第 2、第 4 和第 6 个用于要排除的单词。
我有两种解决问题的方法,因此我可以比较结果以确保它们相符。
Simple 解决方案占用 Streaming 的三分之二时间,因此速度更快(当您可以将所有内容放入内存时)。
Streaming 解决方案占用 Simple 解决方案 memory 的 1/150 ,并且应该在 memory 中保持相当稳定,用于更大的文件大小,而 Simple 解决方案将需要更多 memory。
Streaming 解决方案运行 11 兆文件所需的时间不到 5 秒,因此运行 11 兆文件可能需要几个小时。
我打算就此写一篇博客文章,其中将显示我的最终代码。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.