最快的方法来比较pandas数据帧中的行和上一行以及数百万行

AdO*_*AdO 16 python performance cython bigdata pandas

我正在寻找解决方案来加速我编写的函数来循环遍历pandas数据帧并比较当前行和前一行之间的列值.

例如,这是我的问题的简化版本:

   User  Time                 Col1  newcol1  newcol2  newcol3  newcol4
0     1     6     [cat, dog, goat]        0        0        0        0
1     1     6         [cat, sheep]        0        0        0        0
2     1    12        [sheep, goat]        0        0        0        0
3     2     3          [cat, lion]        0        0        0        0
4     2     5  [fish, goat, lemur]        0        0        0        0
5     3     9           [cat, dog]        0        0        0        0
6     4     4          [dog, goat]        0        0        0        0
7     4    11                [cat]        0        0        0        0
Run Code Online (Sandbox Code Playgroud)

目前我有一个循环的函数,并根据' newcol1' 自上一行以来newcol2是否User已经改变以及' Time'值的差异是否大于1 来计算' '和' ' 的值.它还看到如果这些值自上一行以来已更改,则存储在' Col1'和' Col2' 中的数组中的第一个值以及更新' newcol3'和' newcol4'.

这是我目前正在做的伪代码(因为我已经简化了我没有测试过的问题,但它与我在ipython笔记本中实际做的非常类似):

 def myJFunc(df):
...     #initialize jnum counter
...     jnum = 0;
...     #loop through each row of dataframe (not including the first/zeroeth)
...     for i in range(1,len(df)):
...             #has user changed?
...             if df.User.loc[i] == df.User.loc[i-1]:
...                     #has time increased by more than 1 (hour)?
...                     if abs(df.Time.loc[i]-df.Time.loc[i-1])>1:
...                             #update new columns
...                             df['newcol2'].loc[i-1] = 1;
...                             df['newcol1'].loc[i] = 1;
...                             #increase jnum
...                             jnum += 1;
...                     #has content changed?
...                     if df.Col1.loc[i][0] != df.Col1.loc[i-1][0]:
...                             #record this change
...                             df['newcol4'].loc[i-1] = [df.Col1.loc[i-1][0], df.Col2.loc[i][0]];
...             #different user?
...             elif df.User.loc[i] != df.User.loc[i-1]:
...                     #update new columns
...                     df['newcol1'].loc[i] = 1; 
...                     df['newcol2'].loc[i-1] = 1;
...                     #store jnum elsewhere (code not included here) and reset jnum
...                     jnum = 1;
Run Code Online (Sandbox Code Playgroud)

我现在需要将此功能应用于数百万行,并且速度非常慢,所以我试图找出加速它的最佳方法.我听说Cython可以提高功能的速度,但我没有使用它的经验(而且我是pandas和python的新手).是否可以将两行数据帧作为参数传递给函数,然后使用Cython加速它,或者是否需要创建带有" diff"值的新列,以便函数只读取和写入一行一次数据帧,以便从使用Cython中受益?任何其他速度技巧将不胜感激!

(关于使用.loc,我比较了.loc,.iloc和.ix这个比较快,所以这是我目前使用的唯一原因)

(另外,我的User列实际上是unicode而不是int,这对于快速比较可能会有问题)

Joh*_*hnE 14

我和Andy一样思考着,只是groupby添加了,我认为这是对Andy的回答的补充.添加groupby只会在你执行diffor 时将NaN放在第一行shift.(请注意,这不是一个确切答案的尝试,只是为了勾勒出一些基本技术.)

df['time_diff'] = df.groupby('User')['Time'].diff()

df['Col1_0'] = df['Col1'].apply( lambda x: x[0] )

df['Col1_0_prev'] = df.groupby('User')['Col1_0'].shift()

   User  Time                 Col1  time_diff Col1_0 Col1_0_prev
0     1     6     [cat, dog, goat]        NaN    cat         NaN
1     1     6         [cat, sheep]          0    cat         cat
2     1    12        [sheep, goat]          6  sheep         cat
3     2     3          [cat, lion]        NaN    cat         NaN
4     2     5  [fish, goat, lemur]          2   fish         cat
5     3     9           [cat, dog]        NaN    cat         NaN
6     4     4          [dog, goat]        NaN    dog         NaN
7     4    11                [cat]          7    cat         dog
Run Code Online (Sandbox Code Playgroud)

作为Andy关于存储对象的观点的后续,请注意我在这里所做的是提取列表列的第一个元素(并添加移位版本).像这样做你只需要进行一次昂贵的提取,然后就可以坚持标准的熊猫方法了.


And*_*den 8

使用pandas(构造)并向量化你的代码,即不要使用for循环,而是使用pandas/numpy函数.

'newcol1'和'newcol2'基于"用户"自上一行以来是否发生了变化,以及"时间"值的差异是否大于1.

分别计算:

df['newcol1'] = df['User'].shift() == df['User']
df.ix[0, 'newcol1'] = True # possibly tweak the first row??

df['newcol1'] = (df['Time'].shift() - df['Time']).abs() > 1
Run Code Online (Sandbox Code Playgroud)

我不清楚Col1的目的,但列中的一般python对象不能很好地扩展(你不能使用快速路径,内容分散在内存中).大多数时候你可以逃避使用别的东西......


Cython是最后一个选项,在99%的用例中不需要,但是请参阅文档的增强性能部分以获取提示.