[英]Is there a way to run this Python snippet faster?
from collections import defaultdict
dct = defaultdict(list)
for n in range(len(res)):
for i in indices_ordered:
dct[i].append(res[n][i])
请注意, res
是长度为5000的pandas系列的列表, indices_ordered
是长度为20000的字符串的列表。在我的Mac(2.3 GHz Intel Core i5和16 GB 2133 MHz LPDDR3)中,需要23分钟才能运行此代码。 我对Python很陌生,但是我觉得更聪明的编码(也许更少的循环)会有所帮助。
编辑:
这是一个如何创建数据( res
和indices_ordered
)以使其能够在代码段上方运行的示例(该代码段稍作更改以访问唯一字段而不是按字段名称,因为我找不到如何用字段构造内联Series的indices_ordered
)名称)
import random, string, pandas
index_sz = 20000
res_sz = 5000
indices_ordered = [''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(10)) for i in range(index_sz)]
res = [pandas.Series([random.randint(0,10) for i in range(index_sz)], index = random.sample(indices_ordered, index_sz)) for i in range(res_sz)]
编辑:现在可以使用测试数据,很显然,下面的更改对运行时没有影响。 所描述的技术仅在内部循环非常有效时(大约5-10 dict查找)才有效,通过删除某些所述查找,它仍然更加有效。 在这里, r[i]
项查找使其他任何事物相差一个数量级 ,因此优化根本不相关。
您的外部循环需要进行5000次迭代,而内部循环需要进行20000次迭代。 这意味着您将在23分钟内执行1亿次迭代,即每次迭代需要13.8μs。 即使在Python中,这种速度也不快。
我会尝试通过从内部循环中剥离所有不必要的工作来减少运行时间。 特别:
for n in range(len(res))
转换for n in range(len(res))
res[n]
将for r in res
转换for r in res
。 我不知道大熊猫中物品的查找效率如何,但最好是在外部而不是在内部循环中进行。 score
属性查找移到外部循环。 defaultdict
并预先创建列表并使用普通dict。 append
列表方法的查找,并预先准备内部循环所需的(append, i)
对。 这是实现以上建议的代码:
# pre-create the lists
lsts = [[] for _ in range(len(indices_ordered))]
# prepare the pairs (appendfn, i)
fast_append = [(l.append, i)
for (l, i) in zip(lsts, indices_ordered)]
for r in res:
# pre-fetch res[n].score
r_score = r.score
for append, i in fast_append:
append(r_score[i])
# finally, create the dict out of the lists
dct = {i: lst for (i, lst) in zip(indices_ordered, lsts)}
这里的问题是您要遍历每个单个值的indices_ordered
。 只需删除indices_ordered
。 剥它早在数量级测试时序:
import random
import string
import numpy as np
import pandas as pd
from collections import defaultdict
index_sz = 200
res_sz = 50
indices_ordered = [''.join(random.choice(string.ascii_uppercase + string.digits)
for _ in range(10)) for i in range(index_sz)]
res = [pd.Series([random.randint(0,10) for i in range(index_sz)],
index = random.sample(indices_ordered, index_sz))
for i in range(res_sz)]
def your_way(res, indices_ordered):
dct = defaultdict(list)
for n in range(len(res)):
for i in indices_ordered:
dct[i].append(res[n][i])
def my_way(res):
dct = defaultdict(list)
for item in res:
for string_item, value in item.iteritems():
dct[string_item].append(value)
给出:
%timeit your_way(res, indices_ordered)
160 ms ± 5.45 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit my_way(res)
6.79 ms ± 47.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
这减少了整个方法的时间复杂度,因为您不必每次都执行indicies_ordered
并分配值,因此随着数据大小的增加,差异将变得更加明显。
仅增加一个数量级:
index_sz = 2000
res_sz = 500
给出:
%timeit your_way(res, indices_ordered)
17.8 s ± 999 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit my_way(res)
543 ms ± 9.07 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
您确实应该使用DataFrame
。
这是一种直接创建数据的方法:
import pandas as pd
import numpy as np
import random
import string
index_sz = 3
res_sz = 10
indices_ordered = [''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(3)) for i in range(index_sz)]
df = pd.DataFrame(np.random.randint(10, size=(res_sz, index_sz)), columns=indices_ordered)
无需对任何内容进行排序或索引。 基本上可以将DataFrame作为数组或dict访问。
它应该比处理defaultdict,列表和Series快得多。
df
现在看起来像:
>>> df
7XQ VTV 38Y
0 6 9 5
1 5 5 4
2 6 0 7
3 0 0 8
4 7 8 9
5 8 6 4
6 2 4 9
7 3 2 2
8 7 6 0
9 8 0 1
>>> df['7XQ']
0 6
1 5
2 6
3 0
4 7
5 8
6 2
7 3
8 7
9 8
Name: 7XQ, dtype: int64
>>> df['7XQ'][:5]
0 6
1 5
2 6
3 0
4 7
Name: 7XQ, dtype: int64
使用原始大小,此脚本在我的笔记本电脑上不到3秒的时间内输出了5000 rows × 20000 columns
DataFrame。
在pd.Series
对象的输入列表上使用pandas magic(带有2行代码):
all_data = pd.concat([*res])
d = all_data.groupby(all_data.index).apply(list).to_dict()
暗示的动作:
pd.concat([*res])
-将所有系列连接为一个单个的,保留每个系列对象的索引( pandas.concat ) all_data.groupby(all_data.index).apply(list).to_dict()
-在all_data.index
确定一组具有相同索引标签值的all_data.index
,然后将每个组值放入具有.apply(list)
的列表中,并最终转换分组结果放入字典.to_dict()
( pandas.Series.groupby )
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.