Python 列表追加需要很长时间?



我有一个函数来查找给定列表(一个列表(和其他列表之间的常见、不常见项目及其速率 每个用户(4,000 个用户(的列表(60,000 个列表(。在循环下方运行需要太长时间且使用率高 与部分列表构建和崩溃。我认为由于返回的列表很长和重元素(元组(, 所以我把它分成两个函数,如下所示,但在元组中附加列表项似乎是问题,[(user, [items],rate),(user, [items],rate),....].我想从返回的值创建一个数据帧,

我应该对算法做些什么来解决这个问题并减少内存使用量?

我正在使用python 3.7,Windows 10,64位,RAM 8G。

常用物品功能:

def common_items(user,list1, list2):
com_items = list(set(list1).intersection(set(list2)))
com_items_rate = len(com_items)/len(set(list1).union(set(list2))) 


return user, com_items, com_items_rate

不常见物品功能:

def uncommon_items(user,list1, list2):
com_items = list(set(list1).intersection(set(list2)))
com_items_rate = len(com_items)/len(set(list1).union(set(list2))) 


uncom_items = list(set(list2) - set(com_items)) # uncommon items that blonge to list2
uncom_items_rate = len(uncom_items)/len(set(list1).union(set(list2)))

return user, com_items_rate, uncom_items, uncom_items_rate # common_items_rate is also needed 

构建列表:

common_item_rate_tuple_list = [] 
for usr in users: # users.shape = 4,000
list1 = get_user_list(usr) # a function to get list1, it takes 0:00:00.015632 or less for a user
#     print(usr, len(list1))            
for list2 in df["list2"]: # df.shape = 60,000
common_item_rate_tuple = common_items(usr,list1, list2) 
common_item_rate_tuple_list.append(common_item_rate_tuple)

print(len(common_item_rate_tuple_list)) # 4,000 * 60,000 = 240,000,000‬ items
# sample of common_item_rate_tuple_list:
#[(1,[2,5,8], 0.676), (1,[7,4], 0.788), ....(4000,[1,5,7,9],0.318), (4000,[8,9,6],0.521)

我查看了(内存错误和列表限制? (在 Python 中追加到列表时的内存错误(它们处理构造的列表。我无法处理建议的答案(Python列表内存错误(。

对于这么大的数据进行速度和内存管理,您应该考虑几件事。

  • 您正在或应该在这里只使用sets,因为顺序在您的列表中没有意义,并且您正在做很多集合的交叉。 那么,您可以更改get_user_list()函数以返回集合而不是列表吗? 这将防止您正在进行的所有不必要的转换。 与 list2 相同,只需立即制作一组即可
  • 在寻找"不常见的项目"时,您应该只在集合上使用对称差分运算符。 更快,更少的列表>集合转换
  • 在循环结束时,您真的要创建一个包含 240M 子列表的列表吗? 那可能是你的记忆爆炸。 我会建议使用带有键作为用户名的字典。 并且只有在有公共项目的情况下,您才需要在其中创建一个条目。 如果有"稀疏"匹配,您将获得一个非常小的数据容器

--- 编辑示例

所以我认为你希望把它保存在数据框中太大了。 也许您可以执行所需的操作,而无需将其存储在数据框中。 字典是有道理的。 您甚至可以"动态"计算事物,而不存储数据。 无论如何。 这是一个玩具示例,显示了使用 4K 用户和 10K "其他列表"的内存问题。 当然,相交集合的大小可能会有所不同,但它是有益的:

import sys
import pandas as pd
# create list of users by index
users = list(range(4000))
match_data = list()
size_list2 = 10_000
for user in users:
for t in range(size_list2):
match_data.append(( user, (1,5,6,9), 0.55))   # 4 dummy matches and fake percentage

print(match_data[:4])
print(f'size of match: {sys.getsizeof(match_data)/1_000_000} MB')
df = pd.DataFrame(match_data)
print(df.head())
print(f'size of dataframe {sys.getsizeof(df)/1_000_000} MB')

这将产生以下结果:

[(0, (1, 5, 6, 9), 0.55), (0, (1, 5, 6, 9), 0.55), (0, (1, 5, 6, 9), 0.55), (0, (1, 5, 6, 9), 0.55)]
size of match: 335.072536 MB
0             1     2
0  0  (1, 5, 6, 9)  0.55
1  0  (1, 5, 6, 9)  0.55
2  0  (1, 5, 6, 9)  0.55
3  0  (1, 5, 6, 9)  0.55
4  0  (1, 5, 6, 9)  0.55
size of dataframe 3200.00016 MB

您可以看到,对于仅 10K 其他列表的想法,简而言之,数据帧中的 3.2GB。 这将是无法管理的。

这是一个数据结构的想法,只是为了一直使用字典。

del df
# just keep it in a dictionary
data = {}   # intended format:  key= (usr, other_list) : value= [common elements]
# some fake data
user_items = {  1: {2,3,5,7,99},
2: {3,5,88,790},
3: {2,4,100} }
# some fake "list 2 data"
list2 = [   {1,2,3,4,5},
{88, 100},
{11, 13, 200}]
for user in user_items.keys():
for idx, other_set in enumerate(list2):     # using enumerate to get the index of the other list
common_elements = user_items.get(user) & other_set   # set intersection
if common_elements:  # only put it into the dictionary if it is not empty
data[(user, idx)] = common_elements
# try a couple data pulls
print(f'for user 1 and other list 0: {data.get((1, 0))}')
print(f'for user 2 and other list 2: {data.get((2, 2))}')  # use .get() to be safe.  It will return None if no entry

此处的输出为:

for user 1 and other list 0: {2, 3, 5}
for user 2 and other list 2: None

如果您要大量处理这些数据,您的另一种选择就是将这些表放入像sqlite这样的数据库中,该数据库是内置的,不会消耗您的内存。

最新更新