在 Pandas 数据框中为缺失的日期插入行。

3

我有一个包含多年捐赠者信息的数据集,需要在捐赠者跳过一年时插入行。实际数据框中有数千条记录,但示例如下:

import pandas as pd
df = pd.DataFrame([['A','2011',10], ['A','2012',10],['A','2013',10],['B','2011',20], 
                   ['B','2013',20]],columns=['donor_id','year','donation'])
df

donor_id    year    donation
0   A   2011    10
1   A   2012    10
2   A   2013    10
3   B   2011    20
4   B   2013    20

我需要在2012年为捐赠者B插入零捐款,因此最终应该看起来像这样。
donor_id    year    donation
0   A   2011    10
1   A   2012    10
2   A   2013    10
3   B   2011    20
4   B   2012    0
5   B   2013    20

我尝试了几种类似的解决方案,但还没有成功。 这个 解决方案看起来正是我需要的,但我失去了大约一半的数据帧行,并且无法弄清楚为什么会发生这种情况。

df = pd.read_csv(r'filepath')
df = df.drop_duplicates(subset=['donor_id','year'])
df['year_DT'] = pd.to_datetime(df['year'])

df = (df.set_index('year_DT').
      groupby('donor_id').
      apply(lambda x: x.asfreq(freq='Y')).
      drop('donor_id', axis=1))

df = df.reset_index()
df["Index"] = df.groupby('donor_id').cumcount()+1
4个回答

2

您可以使用.groupby(),根据donor_id列对每个组应用自定义函数。

在这个函数中,您将把实际的组与从range(<min year of this group>, <max year of this group>+1)创建的新的pd.Series合并。

之后,缺失的行(NaNs)将被填充为实际值:

def fn(x):
    out = x.merge(
        pd.Series(range(x["year"].min(), x["year"].max() + 1), name="year"),
        how="right",
    )
    out["donor_id"] = out["donor_id"].ffill()
    out["donation"] = out["donation"].fillna(0)
    return out


df["year"] = df["year"].astype(int)
df = df.groupby("donor_id").apply(fn).reset_index(drop=True)
print(df)

输出:

  donor_id  year  donation
0        A  2011      10.0
1        A  2012      10.0
2        A  2013      10.0
3        B  2011      20.0
4        B  2012       0.0
5        B  2013      20.0

1

我建议为您的数据框生成一个新的索引,然后使用reindex重置它。

df.year = df.year.astype(int)

years = list(range(df['year'].astype(int).min(), df['year'].astype(int).max()+1))
ids = list(df.donor_id.unique())

new_index = pd.MultiIndex.from_product([ids, years], names=['donor_id', 'year'])

df_new = df.set_index(['donor_id', 'year'])
df_new.reindex(new_index, fill_value=0)
df_new = df_new.reset_index()

# Output:
    donor_id    year    donation
0   A   2011    10
1   A   2012    10
2   A   2013    10
3   B   2011    20
4   B   2012    0
5   B   2013    20


1

一个选项是使用complete,它来自于pyjanitor,该工具可以抽象出暴露缺失行的过程:

# pip install pyjanitor
import pandas as pd
import janitor
df.complete('donor_id', 'year',fill_value = 0)

  donor_id  year  donation
0        A  2011        10
1        A  2012        10
2        A  2013        10
3        B  2011        20
4        B  2012         0
5        B  2013        20

1
unique =  set(df.year.unique())
data = df.groupby("donor_id").agg({"year":lambda x: unique - set(x)}).explode("year").dropna().reset_index()
data["donation"] = 0
pd.concat([df, data]).sort_values(["donor_id", "year"])

输出:

donor_id    year    donation
0   A   2011    10
1   A   2012    10
2   A   2013    10
3   B   2011    20
0   B   2012    0
4   B   2013    20

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接