我的数据可以有多个事件在一个给定的日期或没有事件在一个日期。我把这些事件,按日期计数,然后画出来。然而,当我绘制它们时,我的两个级数并不总是匹配的。
idx = pd.date_range(df['simpleDate'].min(), df['simpleDate'].max())
s = df.groupby(['simpleDate']).size()
在上面的代码中,idx变成了一个范围,比如说30个日期。09-01-2013至09-30
但是S可能只有25或26天,因为在给定的日期内没有任何事件发生。然后我得到一个AssertionError的大小不匹配时,我试图绘制:
fig, ax = plt.subplots()
ax.bar(idx.to_pydatetime(), s, color='green')
解决这个问题的正确方法是什么?我是想从IDX中删除没有值的日期,还是(我宁愿这样做)将计数为0的缺失日期添加到系列中。我宁愿有一个完整的30天的图表,值为0。如果这种方法是正确的,有什么开始的建议吗?我是否需要某种动态重索引函数?
下面是S (df.groupby(['simpleDate']).size())的一个片段,注意04和05没有条目。
09-02-2013 2
09-03-2013 10
09-06-2013 5
09-07-2013 1
这里有一个很好的方法来填充缺失的日期到一个数据帧,你可以选择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
一个问题是,如果有重复的值,重新索引将失败。假设我们正在处理带有时间戳的数据,我们希望按日期对其进行索引:
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
(这意味着索引有重复,而不是它本身是一个dup)
相反,我们可以使用.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可以用于列系列以填充空白。
一个更快的解决方法是使用.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
另一种方法是重新采样,除了处理丢失的日期外,还可以处理重复的日期。例如:
df.resample('D').mean()
Resample是一个像groupby一样的延迟操作,因此您需要在它之后执行另一个操作。在这种情况下,mean工作得很好,但你也可以使用许多其他的熊猫方法,如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,以便清楚地说明这是如何工作的,但是您可以根据OP的要求添加fillna(0)来将nan替换为零,或者使用interpolate()之类的东西来根据相邻行填充非零值。
你总是可以使用DataFrame.merge()利用从'All Dates' DataFrame到'Missing Dates' 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')