根据另一列的实施规则将列添加到数据框

时间:2020-09-30 21:35:15

标签: python pandas function

我正在协助一个新生儿项目,该项目主要是研究如何根据一定的分数给予药物剂量。从本质上讲,我得到了一个很大的数据框,其中充满了不同日期和时间点的许多不同婴儿和分数。下面是一个示例:

import numpy as np
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  1: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, 7, 5, 10, 14, 5, 4, 11, 4, 4, 6, 7, 4, 6, 12, 6, 6]
})
print(df)

我要做的第一件事是使用以下命令按时间顺序排列婴儿和时间戳记(这很重要):

df = df.groupby(['baby', 'dateandtime'])

所以现在我想弄清楚该怎么做的是创建一列添加到我的数据框中,以便在每个时间点,我有-1(减少剂量),0(保持相同剂量)或+1(增加剂量),具体取决于团队根据规则制定的“得分”。我得到的规则如下:如果三个连续得分的总和≥24或最近一个或两个得分的总和≥12,则为+1。如果过去24小时的平均得分小于8,则为-1。分数保持低于8至少48小时后。否则为0。

所以我的问题是:我真的不知道如何编写此规则,特别是因为我不确定如何告诉python“过去48小时内”。我想,如果我知道如何创建一个函数来执行此规则,则可以使用命令df.apply(rule).reset_index(),然后将其与原始groupby df合并以得到最终结果。关于如何将此规则转换为代码的任何想法?我很茫然。

3 个答案:

答案 0 :(得分:2)

对于规则的第二部分,

-1,如果在至少48小时内得分低于8分之后,过去24小时的平均得分为<8分。

我想知道这是否意味着:

  1. 过去24小时的平均得分是<8
  2. 在这24小时内,前48小时的最高得分是<8(即过去24-74小时的最高得分是<8)

得分小于8的48小时可能是第一个障碍,然后平均得分小于8的随后的24小时是减少剂量的第二个障碍。

重要的是,如果没有这样的48小时周期数据,是否要解决第一个障碍就很难确定。

以下是在每个观察值的过去24小时和过去72小时之间生成最大分数的步骤。 (我很抱歉在这里写了些脏代码)

collect_set

并将此数据与原始数据集合并;

df.dateandtime = pd.to_datetime(df['dateandtime']) # change column type for ease of indexing
df = df.sort_values(by=['baby','dateandtime'])
df= df.reset_index(drop=True)


def filter_df_for_datetime_between_timedeltas(df, varname, base_datetime, time_gr= -72, time_le= -24, unit='h'):
    df['hour_diff'] = df[varname] - base_datetime
    return df[ (pd.Timedelta(time_gr, unit=unit) < df.hour_diff) & 
                (df.hour_diff < pd.Timedelta(time_le, unit=unit))]
    
    
df_24h_to_72h_max = pd.DataFrame() 

for baby in list(df.baby.unique()):
    df_baby = df[df.baby==baby]
    for row in df_baby.iterrows():
        base_datetime = row[1]['dateandtime']
        df_row_24h_to_72h = filter_df_for_datetime_between_timedeltas(df_baby, 'dateandtime', base_datetime)
        # print( df_row_24h_to_72h)
        if len(df_row_24h_to_72h):
            data_row = df_row_24h_to_72h[df_row_24h_to_72h.score==df_row_24h_to_72h.score.max()][::-1].iloc[0]
            # print(data_row)
            row_stat = dict(**row[1][:2],
                            max_24h_to_72h = data_row.score.max(),
                            max_24h_to_72h_time = data_row.dateandtime,
                            hour_diff = -data_row.hour_diff )
            df_24h_to_72h_max = df_24h_to_72h_max.append(pd.DataFrame([row_stat]), ignore_index=True)
        
        
df_24h_to_72h_max

答案 1 :(得分:1)

如何尝试类似的事情:

Map(fdc, listDF_zoo, main = names(listDF_zoo))

成功的关键在于滚动。此处更多信息:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.rolling.html

从这里开始,您只需做一个简单的函数即可检查相应的列并为每一行返回一个值。

答案 2 :(得分:1)

请仔细检查数学和逻辑,但我认为这是一条路。您绝对需要按婴儿分组,否则一个婴儿到下一个婴儿的数字会有所重叠。

df.dateandtime = pd.to_datetime(df['dateandtime']) # change column type for ease of indexing
df = df.set_index('dateandtime')
df.sort_index(inplace = True)

#Calculate conditions
df['sum_3_scores'] = df.groupby('baby')['score'].rolling(3).sum().reset_index(0,drop=True)
df['max_2_scores'] = df.groupby('baby')['score'].rolling(2).max().reset_index(0,drop=True)
df['max_48hr_score'] = df.groupby('baby')['score'].rolling('48h').mean().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_2_scores'] >= 12:
        return 1
    if data['max_48hr_score'] < 8: #if the score has been below 8 for 48 hours the average of the past 24 hours will be < 8
        return -1
    return 0

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

#just for a nicely ordered output
df.reset_index().set_index(['baby','dateandtime']).sort_index()

产量:

                          score  sum_3_scores  max_2_scores  max_48hr_score  rule
baby dateandtime                                                                 
A    2009-07-14 00:01:00     14           NaN           NaN       14.000000     0
     2009-07-16 21:00:00      4           NaN          14.0        4.000000     1
     2009-07-17 00:01:00     10          28.0          10.0        7.000000     1
     2009-07-17 06:00:00      5          19.0          10.0        6.333333    -1
     2009-07-19 05:00:00      5          20.0           5.0        5.000000    -1
     2009-07-19 09:00:00     11          21.0          11.0        8.000000     0
     2009-07-19 13:00:00      7          23.0          11.0        7.666667    -1
     2009-07-19 17:00:00      3          21.0           7.0        6.500000    -1
     2009-08-02 17:00:00      6          16.0           6.0        6.000000    -1
B    2009-07-13 21:00:00     12           NaN           NaN       12.000000     0
     2009-07-14 18:00:00      4           NaN          12.0        8.000000     1
     2009-07-15 00:04:00      6          22.0           6.0        7.333333    -1
     2009-07-15 15:04:00      4          14.0           6.0        6.500000    -1
     2009-07-16 00:01:00      7          17.0           7.0        5.250000    -1
     2009-07-16 06:00:00      6          17.0           7.0        5.400000    -1
     2009-07-18 13:00:00      4          17.0           6.0        4.000000    -1
     2009-07-19 01:00:00      6          16.0           6.0        5.000000    -1
     2009-07-20 17:00:00      6          16.0           6.0        6.000000    -1