我有以下数据帧,日期对应季度期间,金额(以及为了简化而没有显示的其他列)对应于关联的id分组。日期对每个id都是唯一的。
import pandas as pd
from numpy import nan
d = {'id': ['a', 'a', 'a', 'b', 'b'], 'date': ['2020-09-30', '2020-06-30', '2020-03-31',
'2020-09-30', '2020-06-30'], 'amount': [1, 2, nan , 5, nan]}
df = pd.DataFrame(data=d)
df['date'] = pd.to_datetime(df['date'])
df
id date amount
0 a 2020-09-30 1
1 a 2020-06-30 2
2 a 2020-03-31
3 b 2020-09-30 5
4 b 2020-06-30
我想将季度开始的时间延长到过去的一个设置的开始季度日期,以便每个id的季度开始和存在于相同的日期。在本例中,数据应该从2019-12-31
开始,如果缺少行,则应该填充中间的任何四分之一。
我想根据最近的现值回填缺失的值(如金额)。
输出如下所示:
id date amount
a 2020-09-30 1
a 2020-06-30 2
a 2020-03-31 2
a 2019-12-31 2
b 2020-09-30 5
b 2020-06-30 5
b 2020-03-31 5
b 2019-12-31 5
做这件事最好的方法是什么?
您需要从2019-12-31开始每三个月定义一个新的时间范围,并重新索引您的数据框架。然后用反向填充bfill
方法填充NaN
值。请看下面带注释的代码。
import pandas as pd
# Create the DataFrame according to your question
d = {'id': ['a', 'a', 'a', 'b', 'b'], 'date': ['2020-09-30', '2020-06-30', '2020-03-31',
'2020-09-30', '2020-06-30'], 'amount': [1, 2, None, 5, None]}
df = pd.DataFrame(data=d)
# Transform date to datetime column
df['date'] = pd.to_datetime(df['date'])
# Set multiindex to (id, date) as they are the "unique keys" of your amount values
df.set_index(['id', 'date'], inplace=True)
# Define new period for the datetime index (every 3 months)
index = pd.date_range('2019-12-31', '2020-09-30', freq='3M')
# Reindex the Dataframe and fill NaNs with a backward method
print(df.reindex(pd.MultiIndex.from_product([df.index.get_level_values(0).unique(), index])).fillna(method='bfill'))
# Output
# amount
# id
# a 2019-12-31 2.0
# 2020-03-31 2.0
# 2020-06-30 2.0
# 2020-09-30 1.0
# b 2019-12-31 5.0
# 2020-03-31 5.0
# 2020-06-30 5.0
# 2020-09-30 5.0
注意:设置包括id
列的多索引是必要的,因为你想重新采样你的日期,而不合并id
值a
和b
。
pyjanitor提供的完整函数为暴露缺失的行提供了一个抽象:
# pip install pyjanitor
import pandas as pd
import numpy as np
# create a mapping of the new dates
# reusing @scandav's index variable
index = pd.date_range('2019-12-31', '2020-09-30', freq='3M')
index = dict(date = index)
(df.complete('id', index)
.sort_values(['id', 'date'], ascending = [True, False])
.ffill(downcast = 'infer')
)
id date amount
0 a 2020-09-30 1
1 a 2020-06-30 2
2 a 2020-03-31 2
5 a 2019-12-31 2
3 b 2020-09-30 5
4 b 2020-06-30 5
7 b 2020-03-31 5
6 b 2019-12-31 5