如何用条件简单地检查(复数)数据帧中的前几行

问题描述 投票:1回答:2

我开始相信熊猫数据框的处理不如Excel直观,但是我还没有放弃!

因此,我只是尝试使用.shift()方法检查同一列中(但前几行中)的数据。我以下面的DF为例,因为原始文档太复杂了,无法复制到这里,但是原理是一样的。

counter = list(range(20))

df1 = pd.DataFrame(counter, columns=["Counter"])
df1["Event"] = [True, False, False, False, False, False, True, False,False,False,False,False,False,False,False,False,False,False,False,True]

enter image description here

我正在尝试创建列计数器的总和,但仅在以下情况下:

  • 如果“事件” = True,我想对事件发生之前的前10行的“计数器”值求和。
  • 如果在前10个行中没有另一个事件,则除外。在这种情况下,我只想对这两个事件之间的计数器值求和(不超过10行)。

为了澄清我的目标,这是我想到的结果:

enter image description here

到目前为止,我的尝试看起来像这样:

for index, row in df1.iterrows(): 
    if row["Event"] == True: 
        counter = 1 
        summ = 0 
        while counter < 10 and row["Event"].shift(counter) == False: 
            summ += row["Counter"].shift(counter)
            counter += 1
        else: 
            df1.at[index, "Sum"] = summ

我正在尝试首先找到Event == True,然后从那里开始使用计数器向后迭代,并在我进行操作时对计数器求和。但是它似乎有一个移位问题:

AttributeError: 'bool' object has no attribute 'shift'

[请打破我的信念,并向我展示,Excel实际上并不优越。

python pandas dataframe
2个回答
2
投票

我们需要使用cumsum创建一个子组密钥,然后执行rolling sum

n=10
s=df1.Counter.groupby(df1.Event.iloc[::-1].cumsum()).\
      rolling(n+1,min_periods=1).sum().\
      reset_index(level=0,drop=True).where(df1.Event)
df1['sum']=(s-df1.Counter).fillna(0)
df1
    Counter  Event    sum
0         0   True    0.0
1         1  False    0.0
2         2  False    0.0
3         3  False    0.0
4         4  False    0.0
5         5  False    0.0
6         6   True   15.0
7         7  False    0.0
8         8  False    0.0
9         9  False    0.0
10       10  False    0.0
11       11  False    0.0
12       12  False    0.0
13       13  False    0.0
14       14  False    0.0
15       15  False    0.0
16       16  False    0.0
17       17  False    0.0
18       18  False    0.0
19       19   True  135.0

0
投票

您肯定可以在熊猫中完成任务,就像在Excel中一样。您的方法需要进行一些调整,因为pandas.Series.shift适用于整个数组或系列,而不适用于单个值-您不能仅使用它来相对于一个值向后移动数据框。

以下循环遍历数据帧的索引,为每个事件回溯(最多)10个点:

def create_sum_column_loop(df):
    '''
    Adds a Sum column with the rolling sum of 10 Counters prior to an Event
    '''

    df["Sum"] = 0

    for index in range(df.shape[0]): 
        counter = 1 
        summ = 0 

        if df.loc[index, "Event"]:  # == True is implied
            for backup in range(1, 11):

                # handle case where index - backup is before
                # the start of the dataframe
                if index - backup < 0:
                    break

                # stop counting when we hit another event
                if df.loc[index - backup, "Event"]:
                    break

                # increment by the counter
                summ += df.loc[index - backup, "Counter"]

            df.loc[index, "Sum"] = summ

    return df

这完成了工作:

In [15]: df1_sum1 = create_sum_column(df1.copy())  # copy to preserve original
In [16]: df1_sum1
    Counter  Event  Sum
0         0   True    0
1         1  False    0
2         2  False    0
3         3  False    0
4         4  False    0
5         5  False    0
6         6   True   15
7         7  False    0
8         8  False    0
9         9  False    0
10       10  False    0
11       11  False    0
12       12  False    0
13       13  False    0
14       14  False    0
15       15  False    0
16       16  False    0
17       17  False    0
18       18  False    0
19       19   True  135

但是,熊猫的力量来自其矢量化操作。 Python是一种解释型,动态类型化的语言,这意味着它是灵活的,用户友好的(易于读取/编写/学习)并且运行缓慢。为了解决这个问题,许多常用的工作流程(包括许多pandas.Series operations)都使用来自其他语言(如C,C ++和Fortran)的经过优化,经过编译的代码编写。在后台,他们正在做相同的事情... pandas.Series确实遍历了元素并创建了一个总计,但它在C中做到了,从而使其闪电般快速。

这使学习像熊猫这样的框架变得很困难-您需要重新学习如何使用该框架进行数学运算。对于熊猫,整个游戏都在学习如何使用熊猫和numpy内置运算符来完成您的工作。

借用@YOBEN_S的df1.Counter.cumsum()

df1.Counter.cumsum()

我们可以看到结果与上面的结果相同(尽管值突然浮动):

clever solution

差异在于性能。在ipython或jupyter中,我们可以使用def create_sum_column_vectorized(df): n = 10 s = ( df.Counter # group by a unique identifier for each event. This is a # particularly clever bit, where @YOBEN_S reverses # the order of df.Event, then computes a running total .groupby(df.Event.iloc[::-1].cumsum()) # compute the rolling sum within each group .rolling(n+1,min_periods=1).sum() # drop the group index so we can align with the original DataFrame .reset_index(level=0,drop=True) # drop all non-event observations .where(df.Event) ) # remove the counter value for the actual event # rows, then fill the remaining rows with 0s df['sum'] = (s - df.Counter).fillna(0) return df 命令查看一条语句运行多长时间:

In [23]: df1_sum2 = create_sum_column_vectorized(df1)  # copy to preserve original
In [24]: df1_sum2

对于小型数据集,例如您的示例中的数据集,其差异可以忽略不计,甚至会稍微偏爱纯python循环。

对于更大的数据集,差异变得明显。让我们创建一个与您的示例类似的数据集,但它具有100,000行:

%timeit

现在,您真的可以看到矢量化方法的性能优势:

In [25]: %timeit create_sum_column_loop(df1.copy())
3.21 ms ± 54.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [26]: %timeit create_sum_column_vectorized(df1.copy())
7.76 ms ± 255 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

矢量化版本花费的时间少于一半。随着数据量的增加,这种差异将继续扩大。

© www.soinside.com 2019 - 2024. All rights reserved.