【发布时间】:2016-12-10 17:09:10
【问题描述】:
对于DataFrame 中的每一行,跨相同dtype 的列计算不同值的最快方法是什么(在理智的pythonicity 范围内)?
详细信息:我有一个 DataFrame 按主题(按行)按天(按列)分类结果,类似于以下生成的结果。
import numpy as np
import pandas as pd
def genSampleData(custCount, dayCount, discreteChoices):
"""generate example dataset"""
np.random.seed(123)
return pd.concat([
pd.DataFrame({'custId':np.array(range(1,int(custCount)+1))}),
pd.DataFrame(
columns = np.array(['day%d' % x for x in range(1,int(dayCount)+1)]),
data = np.random.choice(a=np.array(discreteChoices),
size=(int(custCount), int(dayCount)))
)], axis=1)
例如,如果数据集告诉我们每位顾客在每次光顾商店时点了哪些饮品,我想知道每位顾客不同饮品的数量。
# notional discrete choice outcome
drinkOptions, drinkIndex = np.unique(['coffee','tea','juice','soda','water'],
return_inverse=True)
# integer-coded discrete choice outcomes
d = genSampleData(2,3, drinkIndex)
d
# custId day1 day2 day3
#0 1 1 4 1
#1 2 3 2 1
# Count distinct choices per subject -- this is what I want to do efficiently on larger DF
d.iloc[:,1:].apply(lambda x: len(np.unique(x)), axis=1)
#0 2
#1 3
# Note: I have coded the choices as `int` rather than `str` to speed up comparisons.
# To reconstruct the choice names, we could do:
# d.iloc[:,1:] = drinkOptions[d.iloc[:,1:]]
我尝试过的:这个用例中的数据集将有比天多得多的主题(例如下面的testDf),所以我试图找到最有效的逐行操作:
testDf = genSampleData(100000,3, drinkIndex)
#---- Original attempts ----
%timeit -n20 testDf.iloc[:,1:].apply(lambda x: x.nunique(), axis=1)
# I didn't wait for this to finish -- something more than 5 seconds per loop
%timeit -n20 testDf.iloc[:,1:].apply(lambda x: len(x.unique()), axis=1)
# Also too slow
%timeit -n20 testDf.iloc[:,1:].apply(lambda x: len(np.unique(x)), axis=1)
#20 loops, best of 3: 2.07 s per loop
为了改进我最初的尝试,我们注意到pandas.DataFrame.apply() 接受了参数:
如果
raw=True传递的函数将接收 ndarray 对象。 如果您只是应用 NumPy 缩减功能,这将实现 更好的性能
这确实将运行时间缩短了一半以上:
%timeit -n20 testDf.iloc[:,1:].apply(lambda x: len(np.unique(x)), axis=1, raw=True)
#20 loops, best of 3: 721 ms per loop *best so far*
我很惊讶一个纯粹的 numpy 解决方案,似乎等同于上面的 raw=True,实际上慢了一点:
%timeit -n20 np.apply_along_axis(lambda x: len(np.unique(x)), axis=1, arr = testDf.iloc[:,1:].values)
#20 loops, best of 3: 1.04 s per loop
最后,我还尝试转置数据以执行column-wise count distinct,我认为这可能更有效(至少对于DataFrame.apply(),但似乎没有有意义的区别。
%timeit -n20 testDf.iloc[:,1:].T.apply(lambda x: len(np.unique(x)), raw=True)
#20 loops, best of 3: 712 ms per loop *best so far*
%timeit -n20 np.apply_along_axis(lambda x: len(np.unique(x)), axis=0, arr = testDf.iloc[:,1:].values.T)
# 20 loops, best of 3: 1.13 s per loop
到目前为止,我最好的解决方案是 df.apply 和 len(np.unique()) 的奇怪组合,但我还应该尝试什么?
【问题讨论】:
-
天数有代表性吗?似乎对性能的差异影响很大。
-
@ayhan 很有趣...天数代表了我的特定用例,但如果其他方法更适用于更广泛的数据集,那么对于其他用户来说值得注意
-
实际上正好相反。当您的列数较少时,将每一列与其他列进行比较似乎要快得多。我将结果发布为答案。
标签: python performance pandas numpy distinct-values