【问题标题】:Pandas rolling window: developing rule based on window valuesPandas 滚动窗口:基于窗口值的开发规则
【发布时间】:2020-10-03 20:15:41
【问题描述】:

我正在开展一个新生儿项目,长话短说,新生儿根据他们在给定时间点的症状被分配一定的分数,并根据他们的分数随时间的变化情况,我们决定是否增加药物剂量,保持不变,或断奶。我们将这 3 个状态用数字表示为 +1(增加)、0(保持)或 -1(断奶)。决定做什么的规则如下:

  • 如果 3 个连续得分的总和 >= 24 或单个得分 >= 12,则增加剂量。
  • 如果您不符合增加或减少剂量的规则,请保持剂量
  • 如果至少有 48 小时无需增加剂量,则降低剂量,最近 3 次得分的总和 8。

在此人的帮助下,我们编写了用于增加剂量和维持剂量的代码。但是,我正在努力编写规则以确定如何降低剂量。这是我们的代码示例:

import pandas as pd

df = pd.DataFrame({
   'baby': ['A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'B', 'B','B', 'B', 'B', 'B', 'B','B','B'],
   'dateandtime':  ['8/2/2009  5:00:00 PM', '7/19/2009  5:00:00 PM', '7/19/2009  5:00:00 PM', '7/17/2009  6:00:00 AM','7/17/2009  12:01:00 AM', '7/14/2009  12:01:00 AM', '7/19/2009  5:00:00 AM', '7/16/2009  9:00:00 PM','7/19/2009  9:00:00 AM', '7/14/2009  6:00:00 PM', '7/15/2009  3:04:00 PM', '7/20/2009  5:00:00 PM','7/16/2009  12:01:00 AM', '7/18/2009  1:00:00 PM', '7/16/2009  6:00:00 AM', '7/13/2009  9:00:00 PM','7/19/2009  1:00:00 AM','7/15/2009  12:04:00 AM'],
   'score':  [6, 3, 3, 5, 10, 14, 5, 4, 11, 4, 4, 6, 7, 4, 6, 12, 6, 6]
    })

df.dateandtime = pd.to_datetime(df['dateandtime']) # change column type for ease of indexing
df = df.set_index('dateandtime')
df.sort_index(inplace = True)
df = df[~df.index.duplicated()] #Remove any duplicated rows

#Calculate conditions
df['sum_3_scores'] = df.groupby('baby')['score'].rolling(3).sum().reset_index(0,drop=True)
df['max_1_score'] = df.groupby('baby')['score'].rolling(1).max().reset_index(0,drop=True)

#you don't nead to calculate the 24hr mean because the 48hr max is 8 the 24hr mean will also be < 8 
#df['mean_24hr_score'] = df.groupby('baby')['score'].rolling('24h').mean().reset_index(0,drop=True)

#scoring logic
def score(data):
    if data['sum_3_scores'] >= 24 or data['max_1_score'] >= 12:
        return 1
    return 0

df['rule'] = df.apply(score, axis = 1)

df.reset_index().set_index(['baby','dateandtime']).sort_index()
print(df)

这会产生一个很好的数据框,其中包含我想要的(减少剂量的规则除外):

                    baby  score  sum_3_scores  max_1_score  rule
dateandtime                                                     
2009-07-13 21:00:00    B     12           NaN         12.0     1
2009-07-14 00:01:00    A     14           NaN         14.0     1
2009-07-14 18:00:00    B      4           NaN          4.0     0
2009-07-15 00:04:00    B      6          22.0          6.0     0
2009-07-15 15:04:00    B      4          14.0          4.0     0
2009-07-16 00:01:00    B      7          17.0          7.0     0
2009-07-16 06:00:00    B      6          17.0          6.0     0
2009-07-16 21:00:00    A      4           NaN          4.0     0
2009-07-17 00:01:00    A     10          28.0         10.0     1
2009-07-17 06:00:00    A      5          19.0          5.0     0
2009-07-18 13:00:00    B      4          17.0          4.0     0
2009-07-19 01:00:00    B      6          16.0          6.0     0
2009-07-19 05:00:00    A      5          20.0          5.0     0
2009-07-19 09:00:00    A     11          21.0         11.0     0
2009-07-19 17:00:00    A      3          19.0          3.0     0
2009-07-20 17:00:00    B      6          16.0          6.0     0
2009-08-02 17:00:00    A      6          20.0          6.0     0

编写降低剂量规则的简单方法是什么?我知道我可以使用代码 df.groupby('baby')['score'].rolling('48h') 执行 48 小时窗口,但我不清楚如何仅检查 3 个最近剂量的总和那个窗口的

【问题讨论】:

  • 当您说a single score is &gt;= 12no single score is &gt;8 时,您指的是什么时间范围?
  • @Dames 据我所知,“单次得分 >=12”是指在任何时候得分为 12。 “没有一个分数大于 8”应在 48 小时窗口内。
  • 所以当今天的分数 >= 12 时,你明天和后天会增加,一周后你会看今天并说分数超过 12,所以我们需要增加吗?您可能想说自上次增加以来得分 >= 12?
  • @Dames 因此,通常每 4 小时进行一次评分。因此,如果某个时间点的分数 >=12,我会在该时间点增加剂量。然后我会在 4 小时后检查分数,然后根据我的下一个分数再次增加或维持剂量
  • 好的,我想我明白了,我会为你制作一些东西

标签: python pandas dataframe rolling-computation


【解决方案1】:

您的设置:

import pandas as pd

df = pd.DataFrame({
   'baby': ['A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'B', 'B','B', 'B', 'B', 'B', 'B','B','B'],
   'dateandtime':  ['8/2/2009  5:00:00 PM', '7/19/2009  5:00:00 PM', '7/19/2009  5:00:00 PM', '7/17/2009  6:00:00 AM','7/17/2009  12:01:00 AM', '7/14/2009  12:01:00 AM', '7/19/2009  5:00:00 AM', '7/16/2009  9:00:00 PM','7/19/2009  9:00:00 AM', '7/14/2009  6:00:00 PM', '7/15/2009  3:04:00 PM', '7/20/2009  5:00:00 PM','7/16/2009, 12:01:00 AM', '7/18/2009  1:00:00 PM', '7/16/2009  6:00:00 AM', '7/13/2009  9:00:00 PM','7/19/2009  1:00:00 AM','7/15/2009  12:04:00 AM'],
   'score':  [6, 3, 3, 5, 10, 14, 5, 4, 11, 4, 4, 6, 7, 4, 6, 12, 6, 6]
    })

df.dateandtime = pd.to_datetime(df['dateandtime']) # change column type for ease of indexing
df = df.set_index('dateandtime')
df = df[~df.index.duplicated()] #Remove any duplicated rows

我将在.groupby() 上三次使用.diff()。手动检查max_last3sum_last3last48h_any_critical 时,我建议按babydateandtime 排序:

# this helps
df = df.sort_values(by=['baby', 'dateandtime'])
# this is okay too
df.sort_index(inplace=True)

要获得最后 3 个值的总和,首先按婴儿分组,然后获得 3 个滚动窗口,然后获得每个窗口的总和。 重要提示:如果前两个值是例如12, 13 这两个之和 >= 24 但无法构建大小为 3 的窗口!所以值将是NaN(Nan &gt;= 24) == False。要允许构建不完整的窗口,请使用min_periods=1

sum_last3 = df.groupby('baby')['score'].rolling(3, min_periods=1).sum()
df['sum_last3'] = sum_last3.reset_index(level=0, drop=True)

df['sum_last3_critical'] = df['sum_last3'] >= 24
df['sum_last3_good'] = df['sum_last3'] < 18

我仍然不确定您是要查看所有分数、最后 3 个分数还是只查看最后一个分数。此实现在最后 3 个分数中检测到 >= 12 的值。最后的替代解决方案。

max_last3 = df.groupby('baby')['score'].rolling(3, min_periods=1).max()
df['max_last3'] = max_last3.reset_index(level=0, drop=True)

df['max_last3_ciritical'] = df['max_last3'] >= 12
df['max_last3_good'] = df['max_last3'] < 8

现在您可以构建一个critical 列,指示是否必须增加剂量。

df['critical'] = df['sum_last3_critical'] | df['max_last3_ciritical']

现在您可以获得 48 小时的时间窗口并获得关键列的最大值(如果为 True,则为 1.0,如果为 False,则为 0.0)。理想情况下,您会使用.any(),但这对于GroupBy 对象不存在。因为.max() 返回一个数值,然后转换回布尔值。

last48h_any_critical = df.groupby('baby').rolling('48h')['critical'].max().astype('bool')
df['last48h_good'] = ~last48h_any_critical.reset_index(level=0, drop=True)

现在您可以确定婴儿状况良好,应该减少剂量。

df['good'] = df['last48h_good'] & df['sum_last3_good'] & df['max_last3_good']

要获得操作值,只需从 critical 列中减去 good 列。

df['action'] = df['critical'].astype(int) - df['good'].astype(int)

生成的 DataFrame 如下所示:

                    baby  score  sum_last3  sum_last3_critical  sum_last3_good  max_last3  max_last3_ciritical  max_last3_good  critical  last48h_good   good  action
dateandtime
2009-07-14 00:01:00    A     14       14.0               False            True       14.0                 True           False      True         False  False       1
2009-07-16 21:00:00    A      4       18.0               False           False       14.0                 True           False      True         False  False       1
2009-07-17 00:01:00    A     10       28.0                True           False       14.0                 True           False      True         False  False       1
2009-07-17 06:00:00    A      5       19.0               False           False       10.0                False           False     False         False  False       0
2009-07-19 05:00:00    A      5       20.0               False           False       10.0                False           False     False          True  False       0
2009-07-19 09:00:00    A     11       21.0               False           False       11.0                False           False     False          True  False       0
2009-07-19 17:00:00    A      3       19.0               False           False       11.0                False           False     False          True  False       0
2009-08-02 17:00:00    A      6       20.0               False           False       11.0                False           False     False          True  False       0
2009-07-13 21:00:00    B     12       12.0               False            True       12.0                 True           False      True         False  False       1
2009-07-14 18:00:00    B      4       16.0               False            True       12.0                 True           False      True         False  False       1
2009-07-15 00:04:00    B      6       22.0               False           False       12.0                 True           False      True         False  False       1
2009-07-15 15:04:00    B      4       14.0               False            True        6.0                False            True     False         False  False       0
2009-07-16 00:01:00    B      7       17.0               False            True        7.0                False            True     False         False  False       0
2009-07-16 06:00:00    B      6       17.0               False            True        7.0                False            True     False         False  False       0
2009-07-18 13:00:00    B      4       17.0               False            True        7.0                False            True     False          True   True      -1
2009-07-19 01:00:00    B      6       16.0               False            True        6.0                False            True     False          True   True      -1
2009-07-20 17:00:00    B      6       16.0               False            True        6.0                False            True     False          True   True      -1

备选方案

如果您不想查看最后三个值,而是要查看所有以前的值。请改用expanding

# ideally change name of max_last3 to something like max_alltime
max_last3 = df.groupby('baby')['score'].expanding().max()
df['max_last3'] = max_last3.reset_index(level=0, drop=True)

df['max_last3_ciritical'] = df['max_last3'] >= 12
df['max_last3_good'] = df['max_last3'] < 8

如果您只想查看最后一个值,您可以直接与score 进行比较:

# ideally change name of max_last3_ciritical to something like last_ciritical
df['max_last3_ciritical'] = df['score'] >= 12
df['max_last3_good'] = df['score'] < 8

【讨论】:

  • 所以这绝对是一种不同于我预期的方式哈哈。要回答您的问题,如果任何时间点的得分 >=12,则必须增加剂量(高分 = 婴儿身体状况不佳)。我认为代码中存在一个小问题,因为如果我查看婴儿 A(时间戳 = 2009-07-16 21:00:00),您的输出显示当婴儿当时得分为 4 时增加剂量.我不认为应该在那里增加剂量,因为分数是
  • Baby A 在 2009-07-16 21:00:00 被标记为 critical,因为 max_last3_critical 是真的,因为最后三个值中的至少一个是 >= 12。据我所知您想改用第二个替代选项。运行与该位交换的代码,你会得到你想要的结果。
猜你喜欢
  • 1970-01-01
  • 2012-12-27
  • 2017-03-17
  • 1970-01-01
  • 2018-12-17
  • 2021-12-11
  • 2018-01-28
相关资源
最近更新 更多