我想加快我的循环,因为我必须对900 000个数据进行循环。
为了简单起见,我给你看一个例子。
我想添加一个列名“计数”来计算同一个球员得分低于目标得分的次数。但是每一行的目标都会改变。
输入:
index Nom player score target score
0 0 felix 3 10
1 1 felix 8 7
2 2 theo 4 5
3 3 patrick 12 6
4 4 sophie 7 6
5 5 sophie 3 6
6 6 felix 2 4
7 7 felix 2 2
8 8 felix 2 3
结果:
index Nom player score target score Count
0 0 felix 3 10 5
1 1 felix 8 7 4
2 2 theo 4 5 1
3 3 patrick 12 6 0
4 4 sophie 7 6 1
5 5 sophie 3 6 1
6 6 felix 2 4 4
7 7 felix 2 2 0
8 8 felix 2 3 3
下面的代码是我目前使用的,但是有没有可能加快速度?我看过一些关于矢量化的文章,有没有可能应用到我的计算上?如果有,怎么做
df2 = df.copy()
df2['Count']= [np.count_nonzero((df.values[:,1] == row[2] )& ( df.values[:,2] < row[4]) ) for row in df.itertuples()]
print(df2)
3条答案
按热度按时间crcmnpdw1#
O(n log n)
解决方案的Jérôme Richard's insights可以转换为pandas
。速度的提高取决于 Dataframe 中组的数量和大小。产出
jq6vz3qz2#
您可以尝试:
图纸:
编辑:快速基准测试:
我的计算机上的指纹:
所以我的答案应该比原来的答案快,但迈克尔·什琴斯尼的答案是最快的。
mznpcxlj3#
当前代码存在两个主要问题,CPython字符串对象比较慢,尤其是字符串比较,而且当前算法具有二次复杂度:它会比较所有与当前行匹配的行,对于每一行,后者是大 Dataframe 的最大问题。
实施
第一件要做的事情是用更快的东西来代替字符串比较,Strings对象可以用
np.array
转换成原生字符串,然后,使用np.unique
可以提取唯一字符串及其位置,这基本上帮助我们将字符串匹配问题替换为整数匹配问题。比较原生整数通常要快得多,主要是因为像Numpy这样的处理器可以使用高效的SIMD指令来比较整数。下面是如何将字符串列转换为标签索引:现在,我们可以通过标签(球员姓名)有效地对分数进行分组。问题是Numpy没有提供任何分组函数。虽然使用多个
np.argsort
可以有效地做到这一点,但一个基本的基于Python dict的方法在实践中被证明是非常快的。下面是代码,它通过标签对分数进行分组,并对与每个标签相关的分数集进行排序(对下一步很有用):现在可以使用
scoreByGroups
来有效地查找给定标签的小于给定值的分数,只需读取scoreByGroups[label]
(常数时间),然后对得到的数组(O(log n)
)进行二进制搜索,具体方法如下:结果
在我的机器上,示例输入的结果代码花费了0.305 ms,而初始代码花费了1.35 ms。这意味着此实现大约快了4.5倍。遗憾的是,2/3的时间花费在创建包含新列的新 Dataframe 上。请注意,提供的代码应该比大型 Dataframe 上的初始代码快得多这是由于
O(n log n)
复杂度而不是O(n²)
复杂度。更快地实施大型 Dataframe
在大型 Dataframe 上,由于Numpy的开销,为每个项调用
np.searchsorted
是昂贵的。一个容易消除此开销的解决方案是使用Numba。可以使用列表而不是字典来优化计算,因为标签是0..len(labelIds)
范围内的整数。也可以部分并行地完成计算。使用
pd.factorize
可以显著加快字符串到int的转换,尽管这仍然是一个代价高昂的过程。以下是基于Numba的完整解决方案:
在我的6核机器上,这段代码在大型 Dataframe 上要快得多。这是最快的答案之一。在9000行的随机 Dataframe 上,它只比@MichaelSzczesny快2.5。字符串到int的转换占用了40 - 45%的时间,并且创建了新的Pandas Dataframe (带附加列)花费了25%的时间。Numba函数花费的时间实际上最后很小。大部分时间最终都浪费在了开销中。
请注意,使用类别数据可以只进行一次(预计算),它对其他计算也很有用,因此实际上可能并不那么昂贵。