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

Python list appending takes long time?

我有一个函数可以在给定列表(一个列表)和其他列表之间找到常见的、不常见的项目及其比率 每个用户(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)

我看了 (Memory errors and list limits?) 和 () they deal with constructed list. And I couldnot deal with suggested answer for ().

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

  • 你是或应该在这里只使用sets因为顺序在你的列表中没有意义并且你正在做很多集合的交叉。那么,您可以将 get_user_list() 函数更改为 return 集合而不是列表吗?这将防止您正在进行的所有不必要的转换。 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,它是内置的,不会耗尽您的内存。