在Pandas数据框中,条件均值和前N行的总和

8

关注的是这个典型的pandas数据框:

      Measurement  Trigger  Valid
   0          2.0    False   True
   1          4.0    False   True
   2          3.0    False   True
   3          0.0     True  False
   4        100.0    False   True
   5          3.0    False   True
   6          2.0    False   True
   7          1.0     True   True

每当TriggerTrue时,我希望计算最近3个(从当前开始)有效测量值的总和和平均值。如果列ValidTrue,则认为测量值有效。因此,让我们使用上面数据框中的两个示例来澄清:
  1. Index 3:应使用索引2,1,0。期望的Sum = 9.0,Mean = 3.0
  2. Index 7:应使用索引7,6,5。期望的Sum = 6.0,Mean = 2.0

我尝试过使用pandas.rolling和创建新的移位列,但没有成功。请参见我的测试摘录(应直接运行):

import unittest
import pandas as pd
import numpy as np
from pandas.util.testing import assert_series_equal

def create_sample_dataframe_2():
    df = pd.DataFrame(
        {"Measurement" : [2.0,   4.0,   3.0,   0.0,   100.0, 3.0,   2.0,   1.0 ],
         "Valid"       : [True,  True,  True,  False, True,  True,  True,  True],
         "Trigger"     : [False, False, False, True,  False, False, False, True],
         })
    return df

def expected_result():
    return pd.DataFrame({"Sum" : [np.nan, np.nan, np.nan, 9.0, np.nan, np.nan, np.nan, 6.0],
                         "Mean" :[np.nan, np.nan, np.nan, 3.0, np.nan, np.nan, np.nan, 2.0]})

class Data_Preparation_Functions(unittest.TestCase):

    def test_backsummation(self):
        N_SUMMANDS = 3
        temp_vars = []

        df = create_sample_dataframe_2()
        for i in range(0,N_SUMMANDS):
            temp_var = "M_{0}".format(i)
            df[temp_var] = df["Measurement"].shift(i)
            temp_vars.append(temp_var)

        df["Sum"]  = df[temp_vars].sum(axis=1)
        df["Mean"] = df[temp_vars].mean(axis=1)
        df.loc[(df["Trigger"]==False), "Sum"] = np.nan
        df.loc[(df["Trigger"]==False), "Mean"] = np.nan

        assert_series_equal(expected_result()["Sum"],df["Sum"])
        assert_series_equal(expected_result()["Mean"],df["Mean"])

    def test_rolling(self):
        df = create_sample_dataframe_2()
        df["Sum"]  = df[(df["Valid"] == True)]["Measurement"].rolling(window=3).sum()
        df["Mean"] = df[(df["Valid"] == True)]["Measurement"].rolling(window=3).mean()

        df.loc[(df["Trigger"]==False), "Sum"] = np.nan
        df.loc[(df["Trigger"]==False), "Mean"] = np.nan
        assert_series_equal(expected_result()["Sum"],df["Sum"])
        assert_series_equal(expected_result()["Mean"],df["Mean"])


if __name__ == '__main__':
    suite = unittest.TestLoader().loadTestsFromTestCase(Data_Preparation_Functions)
    unittest.TextTestRunner(verbosity=2).run(suite)

任何帮助或解决方案都将不胜感激。谢谢和干杯!编辑:澄清:这是我期望的结果数据框:
      Measurement  Trigger  Valid   Sum   Mean
   0          2.0    False   True   NaN    NaN
   1          4.0    False   True   NaN    NaN
   2          3.0    False   True   NaN    NaN
   3          0.0     True  False   9.0    3.0
   4        100.0    False   True   NaN    NaN
   5          3.0    False   True   NaN    NaN
   6          2.0    False   True   NaN    NaN
   7          1.0     True   True   6.0    2.0

编辑2:另一个澄清:

我确实没有计算错误,而是没有把我的意图表达得尽可能清楚。这里再试一次,使用相同的数据框:

Desired dataframe, relevant fields highlighted

首先看一下Trigger列:我们在索引3(绿色矩形)中找到第一个True。所以索引3是我们开始查找的点。在索引3没有有效的测量值(列ValidFalse;红色矩形)。因此,我们开始往回追溯时间,直到我们累积了三行,其中ValidTrue。这发生在索引2、1和0。对于这三个索引,我们计算列Measurement(蓝色矩形)的总和和平均值:

  • 总和:2.0 + 4.0 + 3.0 = 9.0
  • 平均值:(2.0 + 4.0 + 3.0) / 3 = 3.0
现在我们开始这个小算法的下一次迭代:再次查找Trigger列中的下一个True。我们在索引7处找到它(绿色矩形)。在索引7处还有一个有效的测量值,因此这次我们将其包括在内。对于我们的计算,我们使用索引7、6和5(绿色矩形),从而得到:
  • SUM:1.0 + 2.0 + 3.0 = 6.0
  • MEAN:(1.0 + 2.0 + 3.0)/ 3 = 2.0
我希望这能更好地解释这个小问题。

我看到了一个类似的问题,基本上是要检测cumsum,现在我去找出来! - ileadall42
1个回答

11
这里有一个选项,采用3期滚动平均并求和。
df['RollM'] = df.Measurement.rolling(window=3,min_periods=0).mean()

df['RollS'] = df.Measurement.rolling(window=3,min_periods=0).sum()

现在将假触发器设置为 NaN

df.loc[df.Trigger == False,['RollS','RollM']] = np.nan

产出
   Measurement  Trigger  Valid     RollM  RollS
0          2.0    False   True       NaN    NaN
1          4.0    False   True       NaN    NaN
2          3.0    False   True       NaN    NaN
3          0.0     True  False  2.333333    7.0
4        100.0    False   True       NaN    NaN
5          3.0    False   True       NaN    NaN
6          2.0    False   True       NaN    NaN
7          1.0     True   True  2.000000    6.0

编辑,更新以反映有效的论点

df['mean'],df['sum'] = np.nan,np.nan

roller = df.Measurement.rolling(window=3,min_periods=0).agg(['mean','sum'])

df.loc[(df.Trigger == True) & (df.Valid == True),['mean','sum']] = roller

df.loc[(df.Trigger == True) & (df.Valid == False),['mean','sum']] = roller.shift(1)

收益率

  Measurement  Trigger  Valid  mean  sum
0          2.0    False   True   NaN  NaN
1          4.0    False   True   NaN  NaN
2          3.0    False   True   NaN  NaN
3          0.0     True  False   3.0  9.0
4        100.0    False   True   NaN  NaN
5          3.0    False   True   NaN  NaN
6          2.0    False   True   NaN  NaN
7          1.0     True   True   2.0  6.0

这不完全是我想要的。为了澄清,我在原帖中添加了表格格式的预期结果。 - bolla
1
你能进一步解释这是如何不正确的吗?它与你的示例相匹配,除了索引三中的数字,我认为你计算错误了,计算的是前面三个值,不包括当前值@bolla。 - DJK

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