【问题标题】:Pandas filling missing dates and values within group熊猫填补组内缺失的日期和值
【发布时间】:2017-12-12 04:33:27
【问题描述】:

我有一个如下所示的数据框

x = pd.DataFrame({'user': ['a','a','b','b'], 'dt': ['2016-01-01','2016-01-02', '2016-01-05','2016-01-06'], 'val': [1,33,2,1]})

我想做的是在日期列中找到最小和最大日期,然后扩展该列以包含所有日期,同时为val 列填写0。所以想要的输出是

            dt user  val
0   2016-01-01    a    1
1   2016-01-02    a   33
2   2016-01-03    a    0
3   2016-01-04    a    0
4   2016-01-05    a    0
5   2016-01-06    a    0
6   2016-01-01    b    0
7   2016-01-02    b    0
8   2016-01-03    b    0
9   2016-01-04    b    0
10  2016-01-05    b    2
11  2016-01-06    b    1

我已经尝试过herehere 提到的解决方案,但它们不是我想要的。 任何指针都非常感谢。

【问题讨论】:

    标签: python pandas dataframe


    【解决方案1】:

    初始数据框:

                dt  user    val
    0   2016-01-01     a      1
    1   2016-01-02     a     33
    2   2016-01-05     b      2
    3   2016-01-06     b      1
    

    首先,将日期转换为日期时间:

    x['dt'] = pd.to_datetime(x['dt'])
    

    然后,生成日期和唯一用户:

    dates = x.set_index('dt').resample('D').asfreq().index
    
    >> DatetimeIndex(['2016-01-01', '2016-01-02', '2016-01-03', '2016-01-04',
                   '2016-01-05', '2016-01-06'],
                  dtype='datetime64[ns]', name='dt', freq='D')
    
    users = x['user'].unique()
    
    >> array(['a', 'b'], dtype=object)
    

    这将允许您创建一个 MultiIndex:

    idx = pd.MultiIndex.from_product((dates, users), names=['dt', 'user'])
    
    >> MultiIndex(levels=[[2016-01-01 00:00:00, 2016-01-02 00:00:00, 2016-01-03 00:00:00, 2016-01-04 00:00:00, 2016-01-05 00:00:00, 2016-01-06 00:00:00], ['a', 'b']],
               labels=[[0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5], [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]],
               names=['dt', 'user'])
    

    您可以使用它来重新索引您的 DataFrame:

    x.set_index(['dt', 'user']).reindex(idx, fill_value=0).reset_index()
    Out: 
               dt user  val
    0  2016-01-01    a    1
    1  2016-01-01    b    0
    2  2016-01-02    a   33
    3  2016-01-02    b    0
    4  2016-01-03    a    0
    5  2016-01-03    b    0
    6  2016-01-04    a    0
    7  2016-01-04    b    0
    8  2016-01-05    a    0
    9  2016-01-05    b    2
    10 2016-01-06    a    0
    11 2016-01-06    b    1
    

    然后可以按用户排序:

    x.set_index(['dt', 'user']).reindex(idx, fill_value=0).reset_index().sort_values(by='user')
    Out: 
               dt user  val
    0  2016-01-01    a    1
    2  2016-01-02    a   33
    4  2016-01-03    a    0
    6  2016-01-04    a    0
    8  2016-01-05    a    0
    10 2016-01-06    a    0
    1  2016-01-01    b    0
    3  2016-01-02    b    0
    5  2016-01-03    b    0
    7  2016-01-04    b    0
    9  2016-01-05    b    2
    11 2016-01-06    b    1
    

    【讨论】:

    • 在这种情况下,所有用户共享相同的时间范围。如果每个用户有不同的日期范围 [min, max] 怎么办?
    • @luoshao23 .MultiIndex.from_product 是笛卡尔积的便利函数,但索引也可以用其他方式构造。你有什么样的数据?例如,您如何为每个用户存储最小值和最大值?它来自 DataFrame 吗?
    • @ayhan 是的,我使用最小值/最大值创建了一个date_range,然后使用.MultiIndex.from_product 为每个用户生成多级索引。最后,我 concat 将所有 DataFrames 放到一个 DataFrame 中。它需要很多时间才能起作用。你有什么提高效率的想法吗?
    • @luoshao23 如果它们都来自单个 DataFrame,则单独生成多索引并使用重新索引可能会更有效。例如,您可以使用MultiIndex.from_tuples。我认为如果您使用示例 DataFrame 发布一个新问题会更好。
    • 我搜索了很多小时,试图将数据帧索引频率从无转换为 H。这有效!谢谢。 dates = x.set_index('dt').resample('H').asfreq().index
    【解决方案2】:

    正如@ayhan 建议的那样

    x.dt = pd.to_datetime(x.dt)
    

    单线主要使用@ayhan 的想法,同时结合stack/unstackfill_value

    x.set_index(
        ['dt', 'user']
    ).unstack(
        fill_value=0
    ).asfreq(
        'D', fill_value=0
    ).stack().sort_index(level=1).reset_index()
    
               dt user  val
    0  2016-01-01    a    1
    1  2016-01-02    a   33
    2  2016-01-03    a    0
    3  2016-01-04    a    0
    4  2016-01-05    a    0
    5  2016-01-06    a    0
    6  2016-01-01    b    0
    7  2016-01-02    b    0
    8  2016-01-03    b    0
    9  2016-01-04    b    0
    10 2016-01-05    b    2
    11 2016-01-06    b    1
    

    【讨论】:

    • 这个解决方案比公认的答案更好。即使用户之间有重复的日期,它也可以工作。例如,如果另一行包含“2016-01-01”和用户“b”,则接受的解决方案将失败并出现错误 - 无法从重复轴重新索引。但是这个解决方案也适用于这种情况
    • 如果我们需要使用组内的最小/最大日期(在本例中为“用户”),这将如何改变?基本上只在组内生成/填充连续日期。
    【解决方案3】:

    一个老问题,已经有了很好的答案;这是一种替代方法,使用来自pyjanitorcomplete 函数,它可以在生成显式缺失的行时帮助抽象:

    #pip install pyjanitor
    import pandas as pd
    import janitor as jn
    
     x['dt'] = pd.to_datetime(x['dt'])
    
    # generate complete list of dates
    dates = dict(dt = pd.date_range(x.dt.min(), x.dt.max(), freq='1D'))
    
    # build the new dataframe, and fill nulls with 0
    x.complete('user', dates, sort = True).fillna(0, downcast='infer')
    
       user                   dt  val
    0     a  2016-01-01 00:00:00    1
    1     a  2016-01-02 00:00:00   33
    2     a  2016-01-03 00:00:00    0
    3     a  2016-01-04 00:00:00    0
    4     a  2016-01-05 00:00:00    0
    5     a  2016-01-06 00:00:00    0
    6     b  2016-01-01 00:00:00    0
    7     b  2016-01-02 00:00:00    0
    8     b  2016-01-03 00:00:00    0
    9     b  2016-01-04 00:00:00    0
    10    b  2016-01-05 00:00:00    2
    11    b  2016-01-06 00:00:00    1
    
    

    【讨论】:

      猜你喜欢
      • 2018-04-24
      • 2020-01-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-11
      • 1970-01-01
      相关资源
      最近更新 更多