在Pandas DataFrame中比较前一行的值

59
import pandas as pd
data={'col1':[1,3,3,1,2,3,2,2]}
df=pd.DataFrame(data,columns=['col1'])
print df


         col1  
    0     1          
    1     3          
    2     3          
    3     1          
    4     2          
    5     3          
    6     2          
    7     2          

我有以下Pandas DataFrame,想要创建另一列来比较col1的前一行是否相等。最好的方法是什么?这将会像下面的DataFrame一样。谢谢

    col1  match  
0     1   False     
1     3   False     
2     3   True     
3     1   False     
4     2   False     
5     3   False     
6     2   False     
7     2   True     
4个回答

91

你需要使用eqshift函数:

df['match'] = df.col1.eq(df.col1.shift())
print (df)
   col1  match
0     1  False
1     3  False
2     3   True
3     1  False
4     2  False
5     3  False
6     2  False
7     2   True

或者使用==而不是eq,但在大型数据帧中它会慢一些:


df['match'] = df.col1 == df.col1.shift()
print (df)
   col1  match
0     1  False
1     3  False
2     3   True
3     1  False
4     2  False
5     3  False
6     2  False
7     2   True

时间:

import pandas as pd
data={'col1':[1,3,3,1,2,3,2,2]}
df=pd.DataFrame(data,columns=['col1'])
print (df)
#[80000 rows x 1 columns]
df = pd.concat([df]*10000).reset_index(drop=True)

df['match'] = df.col1 == df.col1.shift()
df['match1'] = df.col1.eq(df.col1.shift())
print (df)

In [208]: %timeit df.col1.eq(df.col1.shift())
The slowest run took 4.83 times longer than the fastest. This could mean that an intermediate result is being cached.
1000 loops, best of 3: 933 µs per loop

In [209]: %timeit df.col1 == df.col1.shift()
1000 loops, best of 3: 1 ms per loop

2
“==”通常不比使用“eq”慢(例如,当我测试它们时,我的结果与您相反)。 - Alex Riley
1
@ajcr - 感谢您的评论。我在Windows下进行了多次测试,如果与标量进行比较,则时间相同,但是如果比较2个Series,则eqnelt等比==!=>更快。在更大的df中,您的计时是多少? - jezrael
@jezrael,我想请问一下,如果我们中间有空值该怎么处理? - Pyd
太好了!我最近从R转换到Python。你们有什么方法可以一组人一起做这件事吗?先谢谢了! - mar355
1
@jezrael 非常感谢。为了让我更容易理解,您能否使用 df.col1 == df.col1.shift() 这个代码呢?我觉得这个代码更容易理解。谢谢! - mar355
显示剩余3条评论

14

1) pandas方法: 使用diff:

df['match'] = df['col1'].diff().eq(0)

2) NumPy方法:使用np.ediff1d

df['match'] = np.ediff1d(df['col1'].values, to_begin=np.NaN) == 0

两者都会产生:

enter image description here

时间:(对于 @jezrael 使用的相同DF

%timeit df.col1.eq(df.col1.shift())
1000 loops, best of 3: 731 µs per loop

%timeit df['col1'].diff().eq(0)
1000 loops, best of 3: 405 µs per loop

1
我尝试了pandas和numpy方法,但对于字符串并没有起作用。其中pandas的结果是unsupported operand type(s) for -: 'str' and 'str'cannot convert 'to_begin' to array with dtype 'dtype('O')' as required for input ary - Jack Armstrong

9

这里是基于NumPy数组的方法,使用切片,让我们可以使用对输入数组的视图以提高效率 -

def comp_prev(a):
    return np.concatenate(([False],a[1:] == a[:-1]))

df['match'] = comp_prev(df.col1.values)

样例运行 -

In [48]: df['match'] = comp_prev(df.col1.values)

In [49]: df
Out[49]: 
   col1  match
0     1  False
1     3  False
2     3   True
3     1  False
4     2  False
5     3  False
6     2  False
7     2   True

运行时测试 -
In [56]: data={'col1':[1,3,3,1,2,3,2,2]}
    ...: df0=pd.DataFrame(data,columns=['col1'])
    ...: 

#@jezrael's soln1
In [57]: df = pd.concat([df0]*10000).reset_index(drop=True)

In [58]: %timeit df['match'] = df.col1 == df.col1.shift() 
1000 loops, best of 3: 1.53 ms per loop

#@jezrael's soln2
In [59]: df = pd.concat([df0]*10000).reset_index(drop=True)

In [60]: %timeit df['match'] = df.col1.eq(df.col1.shift())
1000 loops, best of 3: 1.49 ms per loop

#@Nickil Maveli's soln1   
In [61]: df = pd.concat([df0]*10000).reset_index(drop=True)

In [64]: %timeit df['match'] = df['col1'].diff().eq(0) 
1000 loops, best of 3: 1.02 ms per loop

#@Nickil Maveli's soln2
In [65]: df = pd.concat([df0]*10000).reset_index(drop=True)

In [66]: %timeit df['match'] = np.ediff1d(df['col1'].values, to_begin=np.NaN) == 0
1000 loops, best of 3: 1.52 ms per loop

# Posted approach in this post
In [67]: df = pd.concat([df0]*10000).reset_index(drop=True)

In [68]: %timeit df['match'] = comp_prev(df.col1.values)
1000 loops, best of 3: 376 µs per loop

6

我很惊讶没有人在这里提到滚动方法。滚动可以轻松用于验证前n个值是否全部相同或执行任何自定义操作。虽然与使用diff或shift相比速度不够快,但它可以轻松适应更大的窗口:

df['match'] = df['col1'].rolling(2).apply(lambda x: len(set(x)) != len(x),raw= True).replace({0 : False, 1: True})

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接