复制所有缺失日期数据框的前一天行

时间:2019-01-31 00:09:27

标签: python pandas dataframe

以下是输入熊猫数据框的示例:

**LastUpdate**                         **Whatever**                 ...

2017-12-30                              xxx                          ...

2017-12-30                              yyy                          ...

2017-12-30                              zzz                          ...

2018-01-01                              yyy                          ...

2018-01-03                              zzz                          ...

这是预期的DF(输出):

**LastUpdate**                         **Whatever**                 ...

2017-12-30                              xxx                          ...

2017-12-30                              yyy                          ...

2017-12-30                              zzz                          ...

2017-12-31                              xxx                          ...

2017-12-31                              yyy                          ...

2017-12-31                              zzz                          ...

2018-01-01                              yyy                          ...

2018-01-02                              yyy                          ...

2018-01-03                              zzz                          ...

如您所见,数据中的丢失日期将仅重复前一天的行,以便我用(所有)前一天数据填充缺失的日期。事实是,每天的行数可能会有所不同,所以这实际上并没有帮助。

重要提示:两天之内可能会缺少 多于一天 (它可能会在2018年1月1日至2018年1月5日,因此我需要将这两天之间所有缺少的日期相加,并使用与2018年1月1日相同的数据(行/内容的行数完全相同),即有可用数据的最后一天

我已经进行了一些研究,并提出了resampleffillreset_index方法,但由于它需要唯一的日期,因此它似乎不适合我的具体情况索引,这里不是这种情况,因为一天可能会关联几行。

到目前为止,我已经尝试过:

df['Last Update'] = pd.to_datetime(df['Last Update'])
df.set_index("Last Update", inplace=True)
dfResult = df.resample('D').ffill().reset_index()

产生cannot reindex a non-unique index with a method or limit(这完全是有道理的),但我真的想不出一种方法来实现自己的目标。 让我知道是否有任何不清楚的地方,或者如果您需要更多其他信息,我们将不胜感激

2 个答案:

答案 0 :(得分:3)

设置

# This solution should also work for multiple columns.
# Setup.
df['Whatever2'] = df['Whatever'].map({'xxx':'a', 'yyy':'b', 'zzz':'c'})
df

  LastUpdate Whatever Whatever2
0 2017-12-30      xxx         a
1 2017-12-30      yyy         b
2 2017-12-30      zzz         c
3 2018-01-01      yyy         b
4 2018-01-05      zzz         c
5 2018-01-06      xxx         a
6 2018-01-06      xxx         a
7 2018-01-09      yyy         b

解决方案

使用set_index + unstack,然后再次使用reindexstack

# If required, convert "LastUpdate" to `datetime`.
# df['LastUpdate'] = pd.to_datetime(df['LastUpdate'], errors='coerce')

(df.set_index(['LastUpdate', df.groupby('LastUpdate').cumcount()])
   .unstack(1, fill_value='')
   .reindex(pd.date_range(df['LastUpdate'].min(), df['LastUpdate'].max()))
   .ffill()
   .replace('', np.nan)
   .stack(1)
   .reset_index(level=1, drop=True)
   .rename_axis('LastUpdate').reset_index())

   LastUpdate Whatever Whatever2
0  2017-12-30      xxx         a
1  2017-12-30      yyy         b
2  2017-12-30      zzz         c
3  2017-12-31      xxx         a
4  2017-12-31      yyy         b
5  2017-12-31      zzz         c
6  2018-01-01      yyy         b
7  2018-01-02      yyy         b
8  2018-01-03      yyy         b
9  2018-01-04      yyy         b
10 2018-01-05      zzz         c
11 2018-01-06      xxx         a
12 2018-01-06      xxx         a
13 2018-01-07      xxx         a
14 2018-01-07      xxx         a
15 2018-01-08      xxx         a
16 2018-01-08      xxx         a
17 2018-01-09      yyy         b

详细信息

首先,设置索引。使用cumcount获取重复日期的计数。这是确定新日期必须重复多少次所必需的。

df.groupby('LastUpdate').cumcount().to_numpy()
# array([0, 1, 2, 0, 0, 0, 1, 0])

df.set_index(['LastUpdate', df.groupby('LastUpdate').cumcount()])

             Whatever Whatever2
LastUpdate                     
2017-12-30 0      xxx         a
           1      yyy         b
           2      zzz         c
2018-01-01 0      yyy         b
2018-01-05 0      zzz         c
2018-01-06 0      xxx         a
           1      xxx         a
2018-01-09 0      yyy         b

接下来,使用unstack。我使用fill_value=''作为下一步(向前填充)的方块。

_.unstack(1, fill_value='')


           Whatever           Whatever2      
                  0    1    2         0  1  2
LastUpdate                                   
2017-12-30      xxx  yyy  zzz         a  b  c
2018-01-01      yyy                   b      
2018-01-05      zzz                   c      
2018-01-06      xxx  xxx              a  a   
2018-01-09      yyy                   b      

您现在可以使用reindex来添加缺少的日期:

_.reindex(pd.date_range(df['LastUpdate'].min(), df['LastUpdate'].max()))

           Whatever           Whatever2          
                  0    1    2         0    1    2
2017-12-30      xxx  yyy  zzz         a    b    c
2017-12-31      NaN  NaN  NaN       NaN  NaN  NaN
2018-01-01      yyy                   b          
2018-01-02      NaN  NaN  NaN       NaN  NaN  NaN
2018-01-03      NaN  NaN  NaN       NaN  NaN  NaN
2018-01-04      NaN  NaN  NaN       NaN  NaN  NaN
2018-01-05      zzz                   c          
2018-01-06      xxx  xxx              a    a     
2018-01-07      NaN  NaN  NaN       NaN  NaN  NaN
2018-01-08      NaN  NaN  NaN       NaN  NaN  NaN
2018-01-09      yyy                   b          

现在,向前填充可将昨天的i th 数据分配给缺失日期中的相应位置。

_.ffill()

           Whatever           Whatever2      
                  0    1    2         0  1  2
2017-12-30      xxx  yyy  zzz         a  b  c
2017-12-31      xxx  yyy  zzz         a  b  c
2018-01-01      yyy                   b      
2018-01-02      yyy                   b      
2018-01-03      yyy                   b      
2018-01-04      yyy                   b      
2018-01-05      zzz                   c      
2018-01-06      xxx  xxx              a  a   
2018-01-07      xxx  xxx              a  a   
2018-01-08      xxx  xxx              a  a   
2018-01-09      yyy                   b      

用NaN和stack替换填充值。

_.replace('', np.nan).stack(1)

             Whatever Whatever2
2017-12-30 0      xxx         a
           1      yyy         b
           2      zzz         c
2017-12-31 0      xxx         a
           1      yyy         b
           2      zzz         c
2018-01-01 0      yyy         b
2018-01-02 0      yyy         b
2018-01-03 0      yyy         b
2018-01-04 0      yyy         b
2018-01-05 0      zzz         c
2018-01-06 0      xxx         a
           1      xxx         a
2018-01-07 0      xxx         a
           1      xxx         a
2018-01-08 0      xxx         a
           1      xxx         a
2018-01-09 0      yyy         b

在那之后,它正在清理索引。

答案 1 :(得分:2)

这就是我的做法。我将使用一个稍微复杂些的示例,该示例是从您的示例输入中扩展而来的,目的是证明我的方法满足所有要求:

  • 数据中缺少日期的天只重复了前一天的行
  • 所有连续的失踪日被属于最近的非缺失日的所有行填满
  • 支持多列
df = pd.DataFrame(columns = ['LastUpdate', 'Whatever', 'Column2'],
                  data = [['2017-12-30', 'xxx', 'a'],
                          ['2017-12-30', 'yyy', 'b'],                        
                          ['2017-12-30', 'zzz', 'c'],                        
                          ['2018-01-01', 'yyy', 'b'],                          
                          ['2018-01-05', 'zzz', 'c'],
                          ['2018-01-06', 'xxx', 'a'],
                          ['2018-01-06', 'xxx', 'a'],
                          ['2018-01-09', 'yyy', 'b']])

df
    LastUpdate   Whatever   Column2
0   2017-12-30   xxx        a
1   2017-12-30   yyy        b
2   2017-12-30   zzz        c
3   2018-01-01   yyy        b
4   2018-01-05   zzz        c
5   2018-01-06   xxx        a
6   2018-01-06   xxx        a
7   2018-01-09   yyy        b
  1. LastUpdate列设置为df的索引,并将索引类型设置为DatetimeIndex:
df.set_index('LastUpdate', drop=True, inplace=True)
df.index = pd.to_datetime(df.index)
  1. 创建一个日期范围,其中包括原始df索引的最小值和最大值之间的所有日期(存在和缺失)。
all_days = pd.date_range(df.index.min(), df.index.max(), freq='D')

  1. 创建一个时间戳列表,代表原始df索引中缺少的日期:
missing_dates = [i for i in all_days if i not in df.index]

  1. 为每个丢失的日期创建一个新数据框列表。其中一些将具有多行,而另一些将具有单行。每个数据框都将在给定的缺失日期编制索引:
new_dfs = []
most_recent = df.index[0]
for i in missing_dates:
    if i-1 in df.index:
        most_recent = i-1
    to_insert = pd.DataFrame(df.loc[most_recent])
    print(to_insert.shape)
    print(to_insert)
    if to_insert.shape[1] == 1: # Ensure new df's row-index contains the date if most recent non-missing date had only one row 
        to_insert = to_insert.T
    shift_amt = i - most_recent
    to_insert = to_insert.shift(shift_amt.days, freq='D')
    new_dfs.append(to_insert)
  1. 最后一步。对于每个要插入的新数据框,我们将原始df分为上半部分和下半部分,并使用pd.concat组合上半部分,缺少日期的新数据框和下半部分:
for i in new_dfs:
    top_idx = pd.date_range(df.index.min(), i.shift(-1, freq='D').index.min(), freq='D')
    top = df.loc[top_idx]
    bottom_len = len(df.index) - len(top)
    bottom = df.iloc[-bottom_len:]
    df = pd.concat([top, i, bottom])

结果数据框如下所示。所有缺少的日期(包括单次和连续的)都填充了与属于最近的非缺失日期的那一行相同的行:

df

            Whatever   Column2
2017-12-30  xxx        a
2017-12-30  yyy        b
2017-12-30  zzz        c
2017-12-31  xxx        a
2017-12-31  yyy        b
2017-12-31  zzz        c
2018-01-01  yyy        b
2018-01-02  yyy        b
2018-01-03  yyy        b
2018-01-04  yyy        b
2018-01-05  zzz        c
2018-01-06  xxx        a
2018-01-06  xxx        a
2018-01-07  xxx        a
2018-01-07  xxx        a
2018-01-08  xxx        a
2018-01-08  xxx        a
2018-01-09  yyy        b