【问题标题】:Pandas fill in missing monthly dates in DataFrame, fill up one specific column with zerosPandas 在 DataFrame 中填写缺失的每月日期,用零填充特定列
【发布时间】:2022-02-26 15:01:44
【问题描述】:

我遇到了关于 Pandas 以及如何在 DataFrame 中填写缺失日期的问题。 给定DataFrame的结构如下:

     Amount  Code     Type   Date
0     34.97  J36J     74343 2016-01-01
1     16.32  J36J     74343 2016-04-01
2     10.30  J36J     69927 2015-12-01
3     10.45  J36J     69927 2016-07-01
4      5.63  J36J     69927 2017-03-01
5     15.79  J36J     69927 2018-09-01
6     15.00  J36J     69927 2019-06-01
7      6.44  J36J     69926 2016-03-01
8      6.47  J36J     69926 2017-03-01
9     15.00  J36J     69926 2018-07-01
10    15.00  J36J     69926 2019-06-01
  • 金额:嗯,金额
  • 代码:Productcode 在整个 DataFrame 中都是相同的
  • Type:A Producttype,有很多不同的
  • 日期:跨越 2015 年 12 月至 2020 年 9 月。

我的目标是为涵盖此时间跨度的每种类型提供每月条目。 意思是,每个材料应该有 58 个条目。 “人为”创建的每月条目的数量应为 0。 因此,我的预期输出将是(仅针对一种类型,例如)

     Amount  Code     Type   Date
0     34.97  J36J     74343 2016-01-01
1     16.32  J36J     74343 2016-02-01
2     0      J36J     74343 2016-03-01
3     0      J36J     74343 2016-04-01
4     0      J36J     74343 2016-05-01
5     0      J36J     74343 2016-06-01
6     0      J36J     74343 2016-07-01
7     0      J36J     74343 2016-08-01
8     0      J36J     74343 2016-09-01
9     0      J36J     74343 2016-10-01
10    0      J36J     74343 2016-11-01
11    0      J36J     74343 2016-12-01

幸运的是,有人已经有同样的问题 (Pandas fill in missing dates in DataFrame with multiple columns)

我根据我的情况调整了非常有用的答案:

df.Date=pd.to_datetime(df.Date)
s=pd.date_range(df.Date.min(),df.Date.max(),freq='MS')

df=df.set_index(['Code','Type','Date']).\
      Amount.unstack().reindex(columns=s,fill_value=0).stack().reset_index()
df

这工作得很好,但后来我检查了生成的 DataFrame,似乎有些日期丢失了。

398     74343  J36J 2016-01-01  34.97
399     74343  J36J 2016-02-01   0.00
400     74343  J36J 2016-04-01  16.32
401     74343  J36J 2016-05-01   0.00
402     74343  J36J 2016-06-01   0.00
403     74343  J36J 2016-08-01   0.00
404     74343  J36J 2016-10-01   0.00
405     74343  J36J 2016-11-01   0.00
406     74343  J36J 2016-12-01   0.00

你们中有人知道这可能是什么原因吗? 我假设可能是因为我选择的频率('MS')?但我认为其他任何一个都不合适。 (https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html) 还是我必须手动设置数据范围? 在我最初的 DataFrame 中,显然并非所有日期都可用。

对此问题的任何帮助表示赞赏。

BR

【问题讨论】:

    标签: python pandas dataframe


    【解决方案1】:

    这是一个微妙的,很有趣。

    import pandas as pd
    data = {'Amount' :[34.97, 16.32, 10.3, 10.45, 5.63, 15.79, 15, 6.44, 6.47, 15, 15],
    'Code': ['J36J','J36J','J36J','J36J','J36J','J36J','J36J','J36J','J36J','J36J','J36J'],
    'Type': [74343,74343,69927,69927,9927,69927,69927,69926,69926,69926,69926],
    'Date': ['1/1/2016','4/1/2016','12/1/2015','7/1/2016','3/1/2017','9/1/2018','6/1/2019','3/1/2016','3/1/2017','7/1/2018','6/1/2019']}
    df = pd.DataFrame(data)
    df['Date'] = pd.to_datetime(df['Date'], format='%m/%d/%Y')
    df
    

    这得到了上述值的起点。然后弄清楚发生了什么需要一段时间,问题是我们对所有类型都使用相同的 s 而不是单独使用。因此,如果日期是另一种类型,则不会被覆盖。

    为了解决这个问题,我把它分成几部分,以便我们可以重新组合在一起。

    outdf = pd.DataFrame(columns = df.columns)
    s=pd.date_range(df.Date.min(),df.Date.max(),freq='MS')
    for name, subdf in df.groupby('Type'):
        thisdf=subdf.set_index(['Code','Type','Date']).\
            Amount.unstack().reindex(columns=s,fill_value=0).stack().reset_index()
        thisdf.rename(columns={0: "Amount", "level_2": "Date"}, errors="raise",inplace=True)
        thisdf.reset_index(inplace=True)
        thisdf = thisdf[['Code', 'Type', 'Date', 'Amount']]
        outdf = pd.concat([outdf,thisdf])
        outdf = outdf[['Code', 'Type', 'Date', 'Amount']]
    
    outdf.reset_index(inplace=True)
    outdf = outdf[['Code', 'Type', 'Date', 'Amount']]
    

    所以我们所做的就是将它分解成单独的项目,然后在每次通过 groupby 后将它们粘在一起。这样我们就不会错过其他类型的日期。

    【讨论】:

    • 哇,这是夏洛克的下一级作品,非常感谢!这就是为什么我如此喜欢 StackOverflow,你总能学到新东西。
    【解决方案2】:

    一种选择是使用pyjanitor 中的complete 函数来公开隐式缺失的行;之后你可以填写fillna:

    # pip install pyjanitor
    import pandas as pd
    import janitor
    
    # create a dictionary of dates, spanning from December 2015 to September 2020
    new_dates = {"Date": pd.date_range(start = "2015-12-01", end = "2020-09-01", freq="MS")}
    
    # apply with `complete` for each group of `Type` and `Code`, 
    # and fill the null values in `Amount` with 0:
    
    df.complete(new_dates, by=["Type", "Code"], sort=True).fillna({'Amount': 0})
    
         Amount  Code   Type       Date
    0       0.0  J36J   9927 2015-12-01
    1       0.0  J36J   9927 2016-01-01
    2       0.0  J36J   9927 2016-02-01
    3       0.0  J36J   9927 2016-03-01
    4       0.0  J36J   9927 2016-04-01
    ..      ...   ...    ...        ...
    227     0.0  J36J  74343 2020-05-01
    228     0.0  J36J  74343 2020-06-01
    229     0.0  J36J  74343 2020-07-01
    230     0.0  J36J  74343 2020-08-01
    231     0.0  J36J  74343 2020-09-01
    
    [232 rows x 4 columns]
    
    
    

    【讨论】:

      猜你喜欢
      • 2022-08-15
      • 2021-09-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-01
      • 2020-01-13
      • 2017-12-22
      • 2011-08-21
      相关资源
      最近更新 更多