我的数据可以有多个事件在一个给定的日期或没有事件在一个日期。我把这些事件,按日期计数,然后画出来。然而,当我绘制它们时,我的两个级数并不总是匹配的。

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

你可以使用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
...

这里有一个很好的方法来填充缺失的日期到一个数据帧,你可以选择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')

s.asfreq('D').interpolate().asfreq('Q')