DataFrame:获取两个日期时间之间每个id的平均值;如果为NaN,则获取最后一个非NaN值

2024-06-03 16:55:41 发布

您现在位置:Python中文网/ 问答频道 /正文

假设我有以下pd.DataFramedf.to_dict()):

    eff_timestamp       val         id  begin_timestamp     end_timestamp
0   2021-01-01 00:00:00 0.677085    1   2021-01-01 02:00:00 2021-01-01 05:30:00
1   2021-01-01 01:00:00 -0.356381   1   2021-01-01 02:00:00 2021-01-01 05:30:00
2   2021-01-01 02:00:00 1.697311    1   2021-01-01 02:00:00 2021-01-01 05:30:00
3   2021-01-01 03:00:00 0.910820    1   2021-01-01 02:00:00 2021-01-01 05:30:00
4   2021-01-01 04:00:00 -1.024458   1   2021-01-01 02:00:00 2021-01-01 05:30:00
5   2021-01-01 05:00:00 -0.430950   1   2021-01-01 02:00:00 2021-01-01 05:30:00
6   2021-01-01 06:00:00 -1.124934   1   2021-01-01 02:00:00 2021-01-01 05:30:00
7   2021-01-01 07:00:00 0.791751    1   2021-01-01 02:00:00 2021-01-01 05:30:00
8   2021-01-02 00:00:00 0.629035    2   2021-01-02 02:00:00 2021-01-02 05:30:00
9   2021-01-02 01:00:00 0.445033    2   2021-01-02 02:00:00 2021-01-02 05:30:00
10  2021-01-02 02:00:00 NaN         2   2021-01-02 02:00:00 2021-01-02 05:30:00
11  2021-01-02 03:00:00 NaN         2   2021-01-02 02:00:00 2021-01-02 05:30:00
12  2021-01-02 04:00:00 NaN         2   2021-01-02 02:00:00 2021-01-02 05:30:00
13  2021-01-02 05:00:00 NaN         2   2021-01-02 02:00:00 2021-01-02 05:30:00
14  2021-01-02 06:00:00 NaN         2   2021-01-02 02:00:00 2021-01-02 05:30:00
15  2021-01-02 07:00:00 -0.637133   2   2021-01-02 02:00:00 2021-01-02 05:30:00

我想为每个唯一id获取一个值,它保存begin_timestampend_timestamp之间的val的平均值(基于eff_timestamp)。如果该值返回np.nan,我想得到最后一个可用值,它不是np.nan。我知道如何获得开始和结束时间戳之间的“val”平均值:

sliced = df[(df.eff_timestamp > df.begin_timestamp) & (df.eff_timestamp < df.end_timestamp)]
sliced
>>>
    eff_timestamp       val         id  begin_timestamp     end_timestamp
3   2021-01-01 03:00:00 0.910820    1   2021-01-01 02:00:00 2021-01-01 05:30:00
4   2021-01-01 04:00:00 -1.024458   1   2021-01-01 02:00:00 2021-01-01 05:30:00
5   2021-01-01 05:00:00 -0.430950   1   2021-01-01 02:00:00 2021-01-01 05:30:00
11  2021-01-02 03:00:00 NaN         2   2021-01-02 02:00:00 2021-01-02 05:30:00
12  2021-01-02 04:00:00 NaN         2   2021-01-02 02:00:00 2021-01-02 05:30:00
13  2021-01-02 05:00:00 NaN         2   2021-01-02 02:00:00 2021-01-02 05:30:00

sliced.groupby('id').val.mean()
>>>
id
1   -0.181529
2         NaN
Name: val, dtype: float64

因为id=2只有介于2021-01-02 02:00:002021-01-02 05:30:00之间的NaN值,所以返回NaN。但是,在这种情况下,我希望得到值0.445033,因为这是该患者的最后一个非NaN值。我该怎么做?因此,输出应为:

id
1   -0.181529
2   0.445033
Name: val, dtype: float64

Tags: nameiddfnpvalnantimestampend
3条回答

从您的代码继续,一旦您确定对于给定的id(让我们 调用它nid)如果有一个NaN值,我们可以确定切片开始的索引:

slice_start = sliced[sliced['id'] == nid].index[0]

现在我们可以得到切片之前的“val”系列部分:

portion = df.loc[:slice_start, 'val'][df['id'] == nid]

并使用last_valid_index()获取所需的值:

val = df.loc[portion.last_valid_index(), 'val']

您可以尝试:

idx=df[df['val'].isna()].index-1

out=sliced.groupby('id')['val'].mean().fillna(df.loc[idx].groupby('id',sort=False)['val'].first())

out的输出:

id
1   -0.181529
2    0.445033
Name: val, dtype: float64

使用在获得上面的sliced时省略的数据帧。称之为left_out

# just the negation of what you've used
left_out = df[~((df.eff_timestamp > df.begin_timestamp) & (df.eff_timestamp < df.end_timestamp))]  

然后,根据上面的注释,为了过滤掉begin_timestamp之前的值:

values_before_the_period = left_out.sort_values(["eff_timestamp", "begin_timestamp"])[left_out['eff_timestamp'] < left_out['begin_timestamp']]

注意sort_values可能不是必需的,如果它已经完成了

这样,我们可以获得一个映射,该映射包含begin_timestamp和end_timestamp之间的时间段之前的最后一个非nan值

mapping = values_before_the_period.groupby('id').tail(1)[['id', 'val']].set_index('id').to_dict()

# {'val': {1: -0.3563813741494545, 2: 0.445032587866597}}

将其制作成一系列:

mapping_s = pd.Series((mapping['val']))
mapping_s

1   -0.356381
2    0.445033
dtype: float64

现在,这可以与您获得的s相结合,在s中只替换np.nan

s.combine_first(mapping_s)

1   -0.181529
2    0.445033
Name: val, dtype: float64

相关问题 更多 >