【问题标题】:How to iterate over a pandas dataframe while referencing previous rows?如何在引用前一行的同时迭代熊猫数据框?
【发布时间】:2018-03-16 12:01:01
【问题描述】:

我正在迭代 Python 数据帧,发现它非常慢。我知道在 Pandas 中您尝试对所有内容进行矢量化,但在这种情况下,我特别需要迭代(或者如果可以进行矢量化,我不清楚该怎么做)。

逻辑很简单:您有两列“A”和“B”以及一个结果列“信号”。如果 A 等于 1,则将 signal 设置为 1。如果 B 等于 1,则将 signal 设置为 0。否则,信号与之前的相同。也就是说,A列是“on”信号,B列是“off”信号,“signal”代表状态。

这是我的代码:

def signals(indata):
    numrows = len(indata)
    data = pd.DataFrame(index= range(0,numrows))
    data['A'] = indata['A']
    data['B'] = indata['B']
    data['signal'] = 0


    for i in range(1,numrows):
        if data['A'].iloc[i] == 1:
            data['signal'].iloc[i] = 1
        elif data['B'].iloc[i] == 1:
            data['signal'].iloc[i] = 0
        else:
            data['signal'].iloc[i] = data['signal'].iloc[i-1]
    return data

输入/输出示例:

indata = pd.DataFrame(index = range(0,10))
indata['A'] = [0, 1, 0, 0, 0, 0, 1, 0, 0, 0]
indata['B'] = [1, 0, 0, 0, 1, 0, 0, 0, 1, 1]

signals(indata)

输出:

    A   B   signal
0   0   1   0
1   1   0   1
2   0   0   1
3   0   0   1
4   0   1   0
5   0   0   0
6   1   0   1
7   0   0   1
8   0   1   0
9   0   1   0

这个简单的逻辑让我的计算机在 2000 行包含随机生成的数据的数据帧上运行 46 秒。

【问题讨论】:

    标签: python pandas dataframe iteration


    【解决方案1】:
    df['signal'] = df.A.groupby((df.A != df.B).cumsum()).transform('head', 1)
    
    df
       A  B  signal
    0  0  1       0
    1  1  0       1
    2  0  0       1
    3  0  0       1
    4  0  1       0
    5  0  0       0
    6  1  0       1
    7  0  0       1
    8  0  1       0
    9  0  1       0
    

    这里的逻辑涉及根据AB之间的不等式将您的系列分组,每个组的值由A确定。

    【讨论】:

    • 观察pandas如何处理这个逻辑门操作很有趣,我仍然对它有效的事实感到震惊。
    • 这真是太棒了,你能解释一下它是如何工作的吗?
    • 这是一个有趣的解决方案,但是对于更复杂的迭代,我无法自己提出这个逻辑。我真的只是在寻找一种简单、易于复制的方法来减少此类问题的处理时间。
    • @karakumy 很遗憾听到您不能采用这个答案来解决您的问题。
    【解决方案2】:

    你根本不需要迭代,你可以做一些布尔索引

    #set condition for A
    indata.loc[indata.A == 1,'signal'] = 1
    #set condition for B
    indata.loc[indata.B == 1,'signal'] = 0
    #forward fill NaN values
    indata.signal.fillna(method='ffill',inplace=True)
    

    【讨论】:

      【解决方案3】:

      对我的问题最简单的答案是在迭代数据帧时不写入数据帧。我在 numpy 中创建了一个零数组,然后在数组中执行了我的迭代逻辑。然后我将数组写入数据框中的列。

      def signals3(indata):
          numrows = len(indata)
          data = pd.DataFrame(index= range(0,numrows))
      
          data['A'] = indata['A'] 
          data['B'] = indata['B']
          out_signal = np.zeros(numrows)
      
          for i in range(1,numrows):
              if data['A'].iloc[i] == 1:
                  out_signal[i] = 1
              elif data['B'].iloc[i] == 1:
                  out_signal[i] = 0
              else:
                  out_signal[i] = out_signal[i-1]
      
      
          data['signal'] = out_signal
      
          return data
      

      在包含 2000 行随机数据的数据帧上,这仅需 43 毫秒,而不是 46 秒(快约 1000 倍)。

      我还尝试了一种变体,将数据框列 A 和 B 分配给系列,然后遍历系列。这有点快(27 毫秒)。但似乎大部分的缓慢都在于写入数据帧。

      coldspeed 和 djk 的答案都比我的解决方案快(大约 4.5 毫秒),但在实践中我可能只是迭代系列,即使这不是最佳的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-02-20
        • 2020-05-17
        • 2015-12-09
        • 2020-01-14
        • 1970-01-01
        • 2021-09-17
        • 1970-01-01
        相关资源
        最近更新 更多