Python列表追加需要很长时间?
我有一个函数,可以在给定列表(一个列表)和其他列表之间查找常见的、不常见的项目及其比率 每个用户(4000个用户)的列表(60000个列表)。在循环下运行需要太长的时间和很高的内存使用率 使用部分列表构造和崩溃。我认为由于返回的列表很长,元素(元组)很重, 所以我将它分为两个函数,如下所示,但在元组中添加列表项似乎有问题,Python列表追加需要很长时间?,python,list,memory-efficient,Python,List,Memory Efficient,我有一个函数,可以在给定列表(一个列表)和其他列表之间查找常见的、不常见的项目及其比率 每个用户(4000个用户)的列表(60000个列表)。在循环下运行需要太长的时间和很高的内存使用率 使用部分列表构造和崩溃。我认为由于返回的列表很长,元素(元组)很重, 所以我将它分为两个函数,如下所示,但在元组中添加列表项似乎有问题, [(用户、[项目]、费率),(用户、[项目]、费率),…]。我想根据返回的值创建一个dataframes 我应该对一个算法做些什么来绕过这个问题并减少内存使用 Iam使用py
[(用户、[项目]、费率),(用户、[项目]、费率),…]
。我想根据返回的值创建一个dataframes
我应该对一个算法做些什么来绕过这个问题并减少内存使用
Iam使用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
不常见项目功能:
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)
我看着()和
()他们处理构造的列表。我不能处理建议的答案(), < P>对于数据和速度这样大的数据,你应该考虑两件事。< /P>
- 您现在或应该只处理
,因为顺序在列表中没有意义,而且您正在进行大量集合的相交。那么,您能否更改集合
函数以返回集合而不是列表?这将防止您正在进行的所有不必要的转换。清单2也一样,只要马上做一套就行了get\u user\u list()
- 在查找“不常见项”时,应在集合上使用对称差分运算符。更快、更少的列表->设置转换
- 在循环结束时,是否确实要创建一个包含240M子列表的列表?这可能是你的记忆爆炸。我建议使用以键为用户名的字典。如果有公共项,您只需要在其中创建一个条目。如果存在“稀疏”匹配,您将得到一个非常小的数据容器
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 ,这是内置的,不会炸毁你的内存。
有两件事你需要考虑的是速度和内存管理这样大的数据。
- 您现在或应该只处理
,因为顺序在列表中没有意义,而且您正在进行大量集合的相交。那么,您能否更改集合
函数以返回集合而不是列表?这将防止您正在进行的所有不必要的转换。清单2也一样,只要马上做一套就行了get\u user\u list()
- 在查找“不常见项”时,应在集合上使用对称差分运算符。更快、更少的列表->设置转换
- 在循环结束时,是否确实要创建一个包含240M子列表的列表?这可能是你的记忆爆炸。我建议使用以键为用户名的字典。如果有公共项,您只需要在其中创建一个条目。如果存在“稀疏”匹配,您将得到一个非常小的数据容器
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
,它不会耗尽你的内存。看看常用项
,我发现两个问题。第一,调用set(list1)
两次(两次调用所需时间相同,与列表的大小有关),即使第二次调用将生成相同的set
值,因为list1
没有更改。第二,交叉口
可以采取任何措施;首先从list2
中设置并没有什么特别的好处<代码>x=集合(列表1);y=x.交点(列表2);return user,x,len(x)/len(x.union(list2))
@chepner谢谢你的评论,我会更改它。只需查看常见项目
,我就发现两个问题。第一,调用set(list1)
两次(两次调用所需时间相同,与列表的大小有关),即使第二次调用将生成相同的set
值,因为list1
没有更改。第二,交叉口
可以采取任何措施;首先从list2
中设置并没有什么特别的好处<代码>x=集合(列表1);y=x.交点(列表2);return user,x,len(x)/len(x.union(list2))
@chepner谢谢你的评论,我会修改它。(第3点)这是主要问题,这就是原因