分组日期滚动中最新非空值的 Pandas 日期索引

Posted

技术标签:

【中文标题】分组日期滚动中最新非空值的 Pandas 日期索引【英文标题】:Pandas date index of latest non null value in grouped date rolling 【发布时间】:2021-03-10 14:13:35 【问题描述】:

我正在尝试按组获取滚动时间窗口中值不为空的最新日期。它在没有组的情况下工作得很好,但似乎分组会打乱一切。

这是可重现的示例:

import pandas as pd
from datetime import datetime as dt
import numpy as np

df = pd.DataFrame()

df["date"] = [dt(2020, 10, i+1) for i in range(10)]
df["group"] = ["a" if int(i/3) == (i/3) else "b" for i in range(10)]
df["value"] = [i if int(i/2) == (i/2) else np.nan for i in range(10)]

数据框

        date group  value
0 2020-10-01     a    0.0
1 2020-10-02     b    NaN
2 2020-10-03     b    2.0
3 2020-10-04     a    NaN
4 2020-10-05     b    4.0
5 2020-10-06     b    NaN
6 2020-10-07     a    6.0
7 2020-10-08     b    NaN
8 2020-10-09     b    8.0
9 2020-10-10     a    NaN

目标输出:

        date group  value  output
0 2020-10-01     a    0.0  2020-10-01
1 2020-10-02     b    NaN  NaT
2 2020-10-03     b    2.0  2020-10-03
3 2020-10-04     a    NaN  2020-10-01
4 2020-10-05     b    4.0  2020-10-05
5 2020-10-06     b    NaN  2020-10-05
6 2020-10-07     a    6.0  2020-10-07
7 2020-10-08     b    NaN  2020-10-05
8 2020-10-09     b    8.0  2020-10-09
9 2020-10-10     a    NaN  2020-10-07

我的尝试:

df = df.set_index("date").sort_index(ascending = True)

def latest_non_null_value_index(x):
        y = x[np.isnan(x) == False]
        print(y.index)
        if len(y) > 0:
            return y.index[-1]
        else:
            return np.nan

latest_index = df\
        .groupby(["group"])\
        .rolling("35D")\
        ["value"]\
        .apply(lambda x: latest_non_null_value_index(x).timestamp())\
        .reset_index()
  
def to_datetime_from_timestamp(x):
  if pd.isnull(x) == False:
      return dt.fromtimestamp(x)
  else:
      return pd.NaT
           
latest_index["value"] = latest_index["value"]\
    .apply(to_datetime_from_timestamp)

我得到了什么:

  group       date               value
0     a 2020-10-01 2020-10-01 02:00:00
1     a 2020-10-04 2020-10-01 02:00:00
2     a 2020-10-07 2020-10-03 02:00:00
3     a 2020-10-10 2020-10-03 02:00:00
4     b 2020-10-02                 NaT
5     b 2020-10-03 2020-10-06 02:00:00
6     b 2020-10-05 2020-10-07 02:00:00
7     b 2020-10-06 2020-10-07 02:00:00
8     b 2020-10-08 2020-10-07 02:00:00
9     b 2020-10-09 2020-10-10 02:00:00

知道我在这里错过了什么吗?

编辑:我在获取最新值时似乎没有这个问题......它确实与索引有关。

EDIT2:如果我能以某种方式将函数应用于 2 列,我可以将日期作为第二列并获得解决方法

【问题讨论】:

【参考方案1】:

您可以使用 pd.fillna 和 "ffill" 来转发填充缺失的值

import pandas as pd
from datetime import datetime as dt
import numpy as np

df = pd.DataFrame()

df["date"] = [dt(2020, 10, i+1) for i in range(10)]
df["group"] = ["a" if int(i/3) == (i/3) else "b" for i in range(10)]
df["value"] = [i if int(i/2) == (i/2) else np.nan for i in range(10)]

df = df.sort_values("date")  # Just make sure that row are properly ordered

date = df["date"].copy()
date[df.value.isna()] = pd.NaT
latest_index = date.groupby(df.group).fillna(method="ffill")

这不会考虑您的滚动时间范围,但您可以像这样删除时间窗口之外的值:

latest_index[(df.date - latest_index).dt.days > 35] = pd.NaT

但这不是超级整洁,因此您可以尝试对滚动窗口使用最大聚合,如下所示:

df = df.set_index("date", drop=False)
df = df.sort_index()

date = pd.to_numeric(df["date"].copy())  # it wasn't letting me aggregate dates so we have to convert to float then back to dates
date[df.value.isna()] = None
latest_index = date.groupby(df.group).rolling("35D").max()
latest_index = pd.to_datetime(latest_index)

【讨论】:

这适用于无限时间窗口,但不适用于滚动窗口。让我试着想出一个更好的例子来说明。还是很有用的。 对不起,我忽略了滚动部分。因此,在您的示例中,您似乎不想回溯超过 35 天的日期,而这一天没有缺失值。那是对的吗?如果是这样,您可以检查填充值与当前日期之间的差异是否超过 35 天。 ```latest_index[(df["date"] - latest_index).dt.days > 35] = pd.NaT" 是的,这种方式运行良好 :) 我仍然很想知道这些索引发生了什么...... pandas 的默认设置是按列将分组移动到索引中。通常你可以设置as_index=False,如果你确实希望发生这种情况,但我认为这不会起作用,因为你也在使用rolling。解决此问题的最简单方法是运行 latest_index.reset_index()

以上是关于分组日期滚动中最新非空值的 Pandas 日期索引的主要内容,如果未能解决你的问题,请参考以下文章

pandas DataFrame中按日期(在索引中)的加权平均分组(每列不同的操作)

弹性搜索:索引具有空值的日期字段

pandas-对列中具有相同值的连续行进行分组和聚合

Pandas:使用日期时间索引进行分组前向填充

Python Pandas 索引排序/分组/日期时间

Pandas Python - 计数和分组日期时间索引