【问题标题】:Find the number of previous consecutive occurences of value different than current row value in pandas dataframe在熊猫数据框中查找与当前行值不同的先前连续出现的值的数量
【发布时间】:2020-02-16 19:16:07
【问题描述】:

假设我们有以下 pandas 数据框:

df = pd.DataFrame({'x':[0,0,1,0,0,0,0],'y':[1,1,1,1,1,1,0],'z':[0,1,1,1,0,0,1]})

   x  y  z
0  0  1  0
1  0  1  1
2  1  1  1
3  0  1  1
4  0  1  0
5  0  1  0
6  0  0  1

所有数据框由 1 或 0 填充。分别查看每一列,如果当前行值与先前值不同,我需要计算先前连续值的数量:

   x  y  z
0        
1        1
2  2     
3  1     
4        3
5        
6     6  2

我尝试编写一个 lambda 函数并将其应用于整个数据帧,但我失败了。有什么想法吗?

【问题讨论】:

  • 什么填充了数据框的其余部分?
  • 当前值与前一个相同时,输出数据帧有缺失值。因此,第一行也有缺失值。
  • 为什么使用 0 和 1 而不是实际的布尔值?
  • 一般问题可以扩展为在数据框中允许多于 0 或 1 个值。我认为仅限制为布尔值可以提供更快的实现,但我不想将解决方案仅限于二进制值。

标签: python pandas dataframe


【解决方案1】:

让我们试试这个:

def f(col):
    x = (col != col.shift().bfill())
    s = x.cumsum()
    return s.groupby(s).transform('count').shift().where(x)

df.apply(f).fillna('')

输出:

   x  y  z
0         
1        1
2  2      
3  1      
4        3
5         
6     6  2

详情:

使用apply,在数据框的每一列上应用自定义函数。 找到列中的差异点,然后使用cumsum 创建连续值组,然后使用groupbytransform 为每条记录创建一个count,然后使用where 屏蔽列中的值差异点。

【讨论】:

  • 很好的结果和解释。我很好奇是否存在基于将函数应用于行而不是列的另一种解决方案:df.apply(function, axis=1)。预期的结果完全一样。
  • 在 pandas 中使用 apply(axis=1) 是一个很大的“不可以”。使用该语句,您将逐行迭代,这违背了对过程进行矢量化的目的。大多数时候,您可以找到一种不逐行进行的方法。使用axis=1 应用是最慢的操作之一。
【解决方案2】:

您可以尝试以下方法,首先确定“运行”,然后获取“运行”长度。你只会在它切换的地方进入,所以它是除了最后一个之外的运行的长度。

import pandas as pd
import numpy as np

def func(x,missing=np.NaN):
        runs = np.cumsum(np.append(0,np.diff(x)!=0))
        switches = np.where(np.diff(x!=0))[0] + 1
        out = np.repeat(missing,len(x))
        out[switches] = np.bincount(runs)[:-1]
        # thanks to Scott see comments below 
        ##out[switches] = pd.value_counts(runs,sort=False)[:-1]
        return(out)

df.apply(func)

    x   y   z
0   NaN NaN NaN
1   NaN NaN 1.0
2   2.0 NaN NaN
3   1.0 NaN NaN
4   NaN NaN 3.0
5   NaN NaN NaN
6   NaN 6.0 2.0

运行长度编码的良好实现可能会更快..但我在python中对它不太熟悉..

【讨论】:

  • 为了获得更好的性能,您可以更改您的out[switches] = pd.value_counts(runs, sort=False)[:-1] 并使用out[switches] = np.bincount(runs)[:-1]
  • 我很惊讶您的解决方案比 ScottBoston 的解决方案快近 3 倍,而另一方面,ScottBoston 的解决方案似乎更紧凑。
  • @lpj 是的,使用 numpy 进行计算总是比使用 pandas 快。 pandas 使用了很多 numpy 以及额外的。如果您只能使用 numpy 编写逻辑,那么它将始终运行得更快。因此,我上面的建议是更改我们的 pd.value_counts 为 np.bincount。我对 pandas 比对 numpy 更熟悉。我在这里从很棒的 numpy 答案中学习。
  • 嗨,斯科特,感谢您的建议。是的,在这种情况下 np.bincount 效果很好,我会在答案中包含它。顺便说一句,漂亮的熊猫代码。
  • 你好@StupidWolf。你的代码很棒。没问题。随意使用该代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-14
  • 2020-12-08
  • 1970-01-01
相关资源
最近更新 更多