将缺失的日期添加到熊猫数据框
- 2024-12-12 08:41:00
- admin 原创
- 125
问题描述:
我的数据在给定日期可能有多个事件,也可能在某个日期没有事件。我获取这些事件,按日期计数并绘制它们。但是,当我绘制它们时,我的两个系列并不总是匹配。
idx = pd.date_range(df['simpleDate'].min(), df['simpleDate'].max())
s = df.groupby(['simpleDate']).size()
在上面的代码中,idx变成了一个范围,比如说 30 个日期。09-01-2013 至 09-30-2013 但是S可能只有 25 或 26 天,因为给定日期没有发生任何事件。然后,当我尝试绘图时,由于大小不匹配,我得到了一个 AssertionError:
fig, ax = plt.subplots()
ax.bar(idx.to_pydatetime(), s, color='green')
解决这个问题的正确方法是什么?我想从IDX中删除没有值的日期,还是(我更愿意这样做)将缺失日期添加到系列中,计数为 0。我宁愿有一个 30 天的完整图表,其中有 0 个值。如果这种方法正确,有什么关于如何开始的建议吗?我需要某种动态reindex
函数吗?
这是S ( )的片段df.groupby(['simpleDate']).size()
,请注意没有 04 和 05 的条目。
09-02-2013 2
09-03-2013 10
09-06-2013 5
09-07-2013 1
解决方案 1:
您可以使用Series.reindex
:
import pandas as pd
idx = pd.date_range('09-01-2013', '09-30-2013')
s = pd.Series({'09-02-2013': 2,
'09-03-2013': 10,
'09-06-2013': 5,
'09-07-2013': 1})
s.index = pd.DatetimeIndex(s.index)
s = s.reindex(idx, fill_value=0)
print(s)
产量
2013-09-01 0
2013-09-02 2
2013-09-03 10
2013-09-04 0
2013-09-05 0
2013-09-06 5
2013-09-07 1
2013-09-08 0
...
解决方案 2:
更快捷的解决方法是使用.asfreq()
。这不需要创建新的索引来在内调用.reindex()
。
# "broken" (staggered) dates
dates = pd.Index([pd.Timestamp('2012-05-01'),
pd.Timestamp('2012-05-04'),
pd.Timestamp('2012-05-06')])
s = pd.Series([1, 2, 3], dates)
print(s.asfreq('D'))
2012-05-01 1.0
2012-05-02 NaN
2012-05-03 NaN
2012-05-04 2.0
2012-05-05 NaN
2012-05-06 3.0
Freq: D, dtype: float64
解决方案 3:
另一种方法是resample
,它除了可以处理缺失日期外,还可以处理重复日期。例如:
df.resample('D').mean()
resample
是一个延迟操作,groupby
因此您需要在其后执行另一个操作。在这种情况下mean
效果很好,但您也可以使用许多其他 Pandas 方法max
,例如sum
、等。
这是原始数据,但多了“2013-09-03”的条目:
val
date
2013-09-02 2
2013-09-03 10
2013-09-03 20 <- duplicate date added to OP's data
2013-09-06 5
2013-09-07 1
结果如下:
val
date
2013-09-02 2.0
2013-09-03 15.0 <- mean of original values for 2013-09-03
2013-09-04 NaN <- NaN b/c date not present in orig
2013-09-05 NaN <- NaN b/c date not present in orig
2013-09-06 5.0
2013-09-07 1.0
我将缺失的日期保留为 NaN,以清楚地说明其工作原理,但您可以fillna(0)
按照 OP 的要求将 NaN 替换为零,或者使用类似方法interpolate()
根据相邻行填充非零值。
解决方案 4:
一个问题是,reindex
如果存在重复值,则将会失败。假设我们正在处理带时间戳的数据,我们希望按日期进行索引:
df = pd.DataFrame({
'timestamps': pd.to_datetime(
['2016-11-15 1:00','2016-11-16 2:00','2016-11-16 3:00','2016-11-18 4:00']),
'values':['a','b','c','d']})
df.index = pd.DatetimeIndex(df['timestamps']).floor('D')
df
产量
timestamps values
2016-11-15 "2016-11-15 01:00:00" a
2016-11-16 "2016-11-16 02:00:00" b
2016-11-16 "2016-11-16 03:00:00" c
2016-11-18 "2016-11-18 04:00:00" d
由于2016-11-16
日期重复,尝试重新索引:
all_days = pd.date_range(df.index.min(), df.index.max(), freq='D')
df.reindex(all_days)
失败:
...
ValueError: cannot reindex from a duplicate axis
(这意味着索引有重复,而不是它本身就是重复)
相反,我们可以用来.loc
查找范围内所有日期的条目:
df.loc[all_days]
产量
timestamps values
2016-11-15 "2016-11-15 01:00:00" a
2016-11-16 "2016-11-16 02:00:00" b
2016-11-16 "2016-11-16 03:00:00" c
2016-11-17 NaN NaN
2016-11-18 "2016-11-18 04:00:00" d
fillna
如果需要,可以在列系列上使用来填补空白。
解决方案 5:
这是一个将缺失日期填写到数据框中的好方法,您可以选择fill_value
来days_back
填写,并使用排序顺序 ( date_order
) 对数据框进行排序:
def fill_in_missing_dates(df, date_col_name = 'date',date_order = 'asc', fill_value = 0, days_back = 30):
df.set_index(date_col_name,drop=True,inplace=True)
df.index = pd.DatetimeIndex(df.index)
d = datetime.now().date()
d2 = d - timedelta(days = days_back)
idx = pd.date_range(d2, d, freq = "D")
df = df.reindex(idx,fill_value=fill_value)
df[date_col_name] = pd.DatetimeIndex(df.index)
return df
解决方案 6:
s.asfreq('D').interpolate().asfreq('Q')
解决方案 7:
您始终可以使用DataFrame.merge()
从“所有日期”DataFrame 到“缺失日期”DataFrame 的左连接。示例如下。
# example DataFrame with missing dates between min(date) and max(date)
missing_df = pd.DataFrame({
'date':pd.to_datetime([
'2022-02-10'
,'2022-02-11'
,'2022-02-14'
,'2022-02-14'
,'2022-02-24'
,'2022-02-16'
])
,'value':[10,20,5,10,15,30]
})
# first create a DataFrame with all dates between specified start<-->end using pd.date_range()
all_dates = pd.DataFrame(pd.date_range(missing_df['date'].min(), missing_df['date'].max()), columns=['date'])
# from the all_dates DataFrame, left join onto the DataFrame with missing dates
new_df = all_dates.merge(right=missing_df, how='left', on='date')