Pandas 同时为每组填充缺失的日期和值

时间:2021-07-23 14:28:45

标签: python pandas

我有一个数据框 (mydf),每个组的日期按月频率如下:

<h1> Today's Weather Report</h1>

<p class="blueText"> Today is a beautiful sunny day!</p>
<button name='bttn-1' type="button">Push</button>

<p class="blueText">Today is also a beautiful hot and sunny day!</p>
<button name='bttn-2' type="button">Push-Toggle</button>

我想为每个组填充 dt,直到从 Id 日期开始的日期列中的最大日期,同时为 Sales 列填充 0。因此,每个组从各自的开始日期开始,但在相同的结束日期结束。

所以例如ID=A 将从 2020-10-01 开始一直到 2021-06-03 并且填充日期的值为 0。

所以输出将是

Dt          Id  Sales
2021-03-01  B   2
2021-04-01  B   42
2021-05-01  B   20
2021-06-01  B   4
2020-10-01  A   47
2020-11-01  A   67
2020-12-01  A   46

我尝试重新索引,但我想使用组中的日期,而不是手动添加日期范围。

我的代码是:

Dt          Id  Sales
2021-03-01  B   2
2021-04-01  B   42
2021-05-01  B   20
2021-06-01  B   4
2020-10-01  A   46
2020-11-01  A   47
2020-12-01  A   67
2021-01-01  A   0
2021-02-01  A   0
2021-03-01  A   0
2021-04-01  A   0
2021-05-01  A   0
2021-06-01  A   0

4 个答案:

答案 0 :(得分:1)

pd.MultiIndex 与列表推导结合使用的替代方法:

s = (pd.MultiIndex.from_tuples([[x, d]
      for x, y in df.groupby("Id")["Dt"]
      for d in pd.date_range(min(y), max(df["Dt"]), freq="MS")], names=["Id", "Dt"]))

print (df.set_index(["Id", "Dt"]).reindex(s, fill_value=0).reset_index())

答案 1 :(得分:0)

让我们试试:

  1. 使用 groupby.min 获取每个组的最小值
  2. 向名为 max 的聚合分钟添加一个新列,该列使用 Dt 上的 Series.max 存储帧中的最大值
  3. 根据 minmax 值为每个组创建单独的 date_range
  4. Series.explode 转换成行以获得表示新索引的 DataFrame。
  5. 创建一个 MultiIndex.from_framereindex 使用 DataFrame。
  6. reindexmidx 并设置 fillvalue=0
# Get Min Per Group
dates = mydf.groupby('Id')['Dt'].min().to_frame(name='min')
# Get max from Frame
dates['max'] = mydf['Dt'].max()

# Create MultiIndex with separate Date ranges per Group
midx = pd.MultiIndex.from_frame(
    dates.apply(
        lambda x: pd.date_range(x['min'], x['max'], freq='MS'), axis=1
    ).explode().reset_index(name='Dt')[['Dt', 'Id']]
)

# Reindex
mydf = (
    mydf.set_index(['Dt', 'Id'])
        .reindex(midx, fill_value=0)
        .reset_index()
)

mydf

           Dt Id  Sales
0  2020-10-01  A     47
1  2020-11-01  A     67
2  2020-12-01  A     46
3  2021-01-01  A      0
4  2021-02-01  A      0
5  2021-03-01  A      0
6  2021-04-01  A      0
7  2021-05-01  A      0
8  2021-06-01  A      0
9  2021-03-01  B      2
10 2021-04-01  B     42
11 2021-05-01  B     20
12 2021-06-01  B      4

数据帧:

import pandas as pd

mydf = pd.DataFrame({
    'Dt': ['2021-03-01', '2021-04-01', '2021-05-01', '2021-06-01', '2020-10-01',
           '2020-11-01', '2020-12-01'],
    'Id': ['B', 'B', 'B', 'B', 'A', 'A', 'A'],
    'Sales': [2, 42, 20, 4, 47, 67, 46]
})
mydf['Dt'] = pd.to_datetime(mydf['Dt'])

答案 2 :(得分:0)

这是一种不同的方法:

from itertools import product

# compute the min-max date range
date_range = pd.date_range(*mydf['Dt'].agg(['min', 'max']), freq='MS', name='Dt')

# make MultiIndex per group, keep only values above min date per group
idx = pd.MultiIndex.from_tuples([e for Id,Dt_min in mydf.groupby('Id')['Dt'].min().items()
                                   for e in list(product(date_range[date_range>Dt_min],
                                                         [Id]))
                                ])

# concatenate the original dataframe and the missing indexes
mydf = mydf.set_index(['Dt', 'Id'])
mydf = pd.concat([mydf,
                  mydf.reindex(idx.difference(mydf.index)).fillna(0)]
                ).sort_index(level=1).reset_index()

mydf

输出:

           Dt Id  Sales
0  2020-10-01  A   47.0
1  2020-11-01  A   67.0
2  2020-12-01  A   46.0
3  2021-01-01  A    0.0
4  2021-02-01  A    0.0
5  2021-03-01  A    0.0
6  2021-04-01  A    0.0
7  2021-05-01  A    0.0
8  2021-06-01  A    0.0
9  2021-03-01  B    2.0
10 2021-04-01  B   42.0
11 2021-05-01  B   20.0
12 2021-06-01  B    4.0

答案 3 :(得分:0)

我们可以使用 complete 中的 pyjanitor 函数来暴露缺失值:

Dt 转换为日期时间:

 df['Dt'] = pd.to_datetime(df['Dt'])

通过 Dt 创建 pd.date_range 到新值的映射,并将频率设置为每月开始 (MS):

 max_time = df.Dt.max()

 new_values = {"Dt": lambda df:pd.date_range(df.min(), max_time, freq='1MS')}

# pip install pyjanitor
import janitor
import pandas as pd
df.complete([new_values], by='Id').fillna(0)


   Id         Dt  Sales
0   A 2020-10-01   47.0
1   A 2020-11-01   67.0
2   A 2020-12-01   46.0
3   A 2021-01-01    0.0
4   A 2021-02-01    0.0
5   A 2021-03-01    0.0
6   A 2021-04-01    0.0
7   A 2021-05-01    0.0
8   A 2021-06-01    0.0
9   B 2021-03-01    2.0
10  B 2021-04-01   42.0
11  B 2021-05-01   20.0
12  B 2021-06-01    4.0

仅针对 Pandas,我们可以将 applygroupbyreindex 结合起来;幸运的是,Dt 是唯一的,因此我们可以安全地重新索引:

(df
 .set_index('Dt')
 .groupby('Id')
 .apply(lambda df: df.reindex(pd.date_range(df.index.min(), 
                                            max_time, 
                                            freq='1MS'), 
                              fill_value = 0)
                              )
 .drop(columns='Id')
 .rename_axis(['Id', 'Dt'])
 .reset_index())
 
   Id         Dt  Sales
0   A 2020-10-01     47
1   A 2020-11-01     67
2   A 2020-12-01     46
3   A 2021-01-01      0
4   A 2021-02-01      0
5   A 2021-03-01      0
6   A 2021-04-01      0
7   A 2021-05-01      0
8   A 2021-06-01      0
9   B 2021-03-01      2
10  B 2021-04-01     42
11  B 2021-05-01     20
12  B 2021-06-01      4