如何在 groupby 中填写日期限制

Posted

技术标签:

【中文标题】如何在 groupby 中填写日期限制【英文标题】:How to fillna limited by date in a groupby 【发布时间】:2021-05-26 17:15:01 【问题描述】:

我正在使用以下 Dataframe,其中包含一些 NaN 值。

df = pd.DataFrame('day':[pd.datetime(2020,1,1),pd.datetime(2020,1,3),pd.datetime(2020,1,4),pd.datetime(2020,1,5),pd.datetime(2020,1,6),pd.datetime(2020,1,7),pd.datetime(2020,1,8),pd.datetime(2020,1,8),pd.datetime(2020,6,9)],
                   'TradeID':['01','02','03','04','05','06','07','08','09'],
                   'Security': ['GOOGLE', 'GOOGLE', 'APPLE', 'GOOGLE', 'GOOGLE','GOOGLE','GOOGLE','GOOGLE','GOOGLE'], 
                   'ID': ['ID001', 'ID001', 'ID001', 'ID001', 'ID001','ID001','ID001','ID001','ID001'], 
                   'BSType': ['B', 'S', 'B', 'B', 'B','S','S','S','B'], 
                   'Price':[105.901,106.969,np.nan,107.037,107.038,107.136,np.nan,107.25,np.nan],
                   'Quantity':[1000000,-300000,np.nan,7500000,100000,-100000,np.nan,-7800000,np.nan]
                  )

Out[318]: 
         day TradeID Security     ID BSType    Price   Quantity
0 2020-01-01      01   GOOGLE  ID001      B  105.901  1000000.0
1 2020-01-03      02   GOOGLE  ID001      S  106.969  -300000.0
2 2020-01-04      03    APPLE  ID001      B      NaN        NaN
3 2020-01-05      04   GOOGLE  ID001      B  107.037  7500000.0
4 2020-01-06      05   GOOGLE  ID001      B  107.038   100000.0
5 2020-01-07      06   GOOGLE  ID001      S  107.136  -100000.0
6 2020-01-08      07   GOOGLE  ID001      S      NaN        NaN
7 2020-01-08      08   GOOGLE  ID001      S  107.250 -7800000.0
8 2020-06-09      09   GOOGLE  ID001      B      NaN        NaN

我的目标是使用 ffill 方法仅针对相同的安全性、相同的 ID 并在接下来的 60 天(不是接下来的 60 次观察,因为每天可能有多个观察)进行填充。

这是我尝试过但不起作用的方法,它不会替换我的任何 NaN 值

df=df.groupby(['day',"Security","ID"], as_index=False).fillna(method='ffill',limit=60)

预期的输出应如下所示:(请注意,仅填充了第二对 NaN 值)

不应填充第一对 NaN 值,因为它们的安全性不同。 第二对 NaN 值应该用之前的观察值填充。 不应填写 NaN 上的第三对,因为它们超出了 60 天的范围。
Out[320]: 
         day TradeID Security     ID BSType    Price   Quantity
0 2020-01-01      01   GOOGLE  ID001      B  105.901  1000000.0
1 2020-01-03      02   GOOGLE  ID001      S  106.969  -300000.0
2 2020-01-04      03    APPLE  ID001      B      NaN        NaN
3 2020-01-05      04   GOOGLE  ID001      B  107.037  7500000.0
4 2020-01-06      05   GOOGLE  ID001      B  107.038   100000.0
5 2020-01-07      06   GOOGLE  ID001      S  107.136  -100000.0
6 2020-01-08      07   GOOGLE  ID001      S  107.136  -100000.0
7 2020-01-08      08   GOOGLE  ID001      S  107.250 -7800000.0
8 2020-06-09      09   GOOGLE  ID001      B      NaN        NaN

所以,我的问题是,¿是否有一种可行的方法来填充 NaN 值,限制 ffill 方法在特定时期内?

非常感谢您抽出宝贵时间。

【问题讨论】:

【参考方案1】:

您可以 groupSecurityID 上的数据框以及额外的 grouperday 频率设置为 60 days 然后使用 ffill 转发填充值下一个60 days:

g = pd.Grouper(key='day', freq='60d')
df.assign(**df.groupby(["Security","ID", g]).ffill())

         day TradeID Security     ID BSType    Price   Quantity
0 2020-01-01      01   GOOGLE  ID001      B  105.901  1000000.0
1 2020-01-03      02   GOOGLE  ID001      S  106.969  -300000.0
2 2020-01-04      03    APPLE  ID001      B      NaN        NaN
3 2020-01-05      04   GOOGLE  ID001      B  107.037  7500000.0
4 2020-01-06      05   GOOGLE  ID001      B  107.038   100000.0
5 2020-01-07      06   GOOGLE  ID001      S  107.136  -100000.0
6 2020-01-08      07   GOOGLE  ID001      S  107.136  -100000.0
7 2020-01-08      08   GOOGLE  ID001      S  107.250 -7800000.0
8 2020-06-09      09   GOOGLE  ID001      B      NaN        NaN

【讨论】:

您好,非常感谢您提供这个有用的提示。它工作得很好。如果我想将此命令仅限于某一列,即仅在一列上使用此标准填充 NaN 值,那该怎么办。我试过 df.assign(**df.groupby(["Security","ID", g])["Quantity"].ffill()) 但它不起作用。 @GuillermoCambroneroPérez 如果您想转发填充任何特定列,那么您可以使用df.assign(**df.groupby(["Security","ID", g])[['Quantity']].ffill())df.assign(**df.groupby(["Security","ID", g])['Quantity'].ffill().to_frame())【参考方案2】:

这是我的尝试,但不确定这是否特别具有可扩展性:

filled_df = df.groupby(["Security","ID"], as_index=False).fillna(method='ffill')
diffs = df.groupby(["Security","ID"])["day"].diff().dt.days
df["diffs"] = diffs
df["price_isna"] = df["Price"].isna()
df["quantity_isna"] = df["Quantity"].isna()
df = df.drop(columns=["Price", "Quantity"]).merge(filled_df, on=["day", "TradeID", "BSType"])

def reverse_fillna(value, value_isna, diffs, time_limit=60):
    if (value_isna and (diffs <= time_limit)) or (not value_isna):
        return value
    else:
        return np.nan
    
df['Price'] = df.apply(lambda row: reverse_fillna(row['Price'], row['price_isna'], row['diffs']), axis=1)
df['Quantity'] = df.apply(lambda row: reverse_fillna(row['Quantity'], row['quantity_isna'], row['diffs']), axis=1)

df.drop(columns=["price_isna", "quantity_isna", "diffs"], inplace=True)

【讨论】:

以上是关于如何在 groupby 中填写日期限制的主要内容,如果未能解决你的问题,请参考以下文章

pandas 如何使用 groupby 在标签中按日期对列进行分组?

如何在子查询中填写日期?

如何在 google BigQuery 中填写缺失的日期

如何从用户在android studio中提供的日期中减去固定天数?

如何在关系 Laravel 中使用“groupBy”

如何在 Oracle 中按组填写缺失的日期