填写MultiIndex Pandas Dataframe中的日期空白

时间:2013-06-25 01:54:25

标签: python pandas numpy dataframe multi-index

我想修改一个pandas MultiIndex DataFrame,使每个索引组包含指定范围之间的日期。我希望每个小组填写缺少日期2013-06-11至2013-12-31,其值为0(或NaN)。

Group A, Group B, Date,           Value
loc_a    group_a  2013-06-11      22
                  2013-07-02      35
                  2013-07-09      14
                  2013-07-30       9
                  2013-08-06       4
                  2013-09-03      40
                  2013-10-01      18
         group_b  2013-07-09       4
                  2013-08-06       2
                  2013-09-03       5
         group_c  2013-07-09       1
                  2013-09-03       2
loc_b    group_a  2013-10-01       3

我已经看到了reindex的一些讨论,但这是针对一个简单的(非分组的)时间序列数据。

有一种简单的方法吗?


以下是我在完成此操作时所做的一些尝试。例如:一旦我被['A', 'B']取消堆叠,我就可以重新索引。

df = pd.DataFrame({'A': ['loc_a'] * 12 + ['loc_b'],
                'B': ['group_a'] * 7 + ['group_b'] * 3 + ['group_c'] * 2 + ['group_a'],
                'Date': ["2013-06-11",
                        "2013-07-02",
                        "2013-07-09",
                        "2013-07-30",
                        "2013-08-06",
                        "2013-09-03",
                        "2013-10-01",
                        "2013-07-09",
                        "2013-08-06",
                        "2013-09-03",
                        "2013-07-09",
                        "2013-09-03",
                        "2013-10-01"],
                 'Value': [22, 35, 14,  9,  4, 40, 18, 4, 2, 5, 1, 2, 3]})

df.Date = df['Date'].apply(lambda x: pd.to_datetime(x).date())
df = df.set_index(['A', 'B', 'Date'])

dt_start = dt.datetime(2013,6,1)
all_dates = [(dt_start + dt.timedelta(days=x)).date() for x in range(0,60)]

df2 = df.unstack(['A', 'B'])
df3 = df2.reindex(index=all_dates).fillna(0)
df4 = df3.stack(['A', 'B'])

## df4 is about where I want to get, now I'm trying to get it back in the form of df...

df5 = df4.reset_index()
df6 = df5.rename(columns={'level_0' : 'Date'})
df7 = df6.groupby(['A', 'B', 'Date'])['Value'].sum()

最后几行让我有点难过。我希望在df6我可以set_index返回['A', 'B', 'Date'],但是这些值不会在初始df DataFrame中分组时对其进行分组。

有关如何重新索引未堆叠的DataFrame,重新堆叠以及使DataFrame采用与原始格式相同的格式的想法吗?

2 个答案:

答案 0 :(得分:13)

您的问题并不清楚您到底错过了哪些日期;我只是假设您想要填写NaN表示您在其他地方有观察的日期。如果这个假设有问题,我的解决方案将不得不修改。

附注:包含一行来创建DataFrame

可能会很不错
In [55]: df = pd.DataFrame({'A': ['loc_a'] * 12 + ['loc_b'],
   ....:                    'B': ['group_a'] * 7 + ['group_b'] * 3 + ['group_c'] * 2 + ['group_a'],
   ....:                    'Date': ["2013-06-11",
   ....:                            "2013-07-02",
   ....:                            "2013-07-09",
   ....:                            "2013-07-30",
   ....:                            "2013-08-06",
   ....:                            "2013-09-03",
   ....:                            "2013-10-01",
   ....:                            "2013-07-09",
   ....:                            "2013-08-06",
   ....:                            "2013-09-03",
   ....:                            "2013-07-09",
   ....:                            "2013-09-03",
   ....:                            "2013-10-01"],
   ....:                     'Value': [22, 35, 14,  9,  4, 40, 18, 4, 2, 5, 1, 2, 3]})

In [56]: 

In [56]: df.Date = pd.to_datetime(df.Date)

In [57]: df = df.set_index(['A', 'B', 'Date'])

In [58]: 

In [58]: print(df)
                          Value
A     B       Date             
loc_a group_a 2013-06-11     22
              2013-07-02     35
              2013-07-09     14
              2013-07-30      9
              2013-08-06      4
              2013-09-03     40
              2013-10-01     18
      group_b 2013-07-09      4
              2013-08-06      2
              2013-09-03      5
      group_c 2013-07-09      1
              2013-09-03      2
loc_b group_a 2013-10-01      3

要填充未观察到的值,我们将使用unstackstack方法。取消堆叠将创建我们感兴趣的NaN,然后我们将它们叠加起来使用。

In [71]: df.unstack(['A', 'B'])
Out[71]: 
              Value                           
A             loc_a                      loc_b
B           group_a  group_b  group_c  group_a
Date                                          
2013-06-11       22      NaN      NaN      NaN
2013-07-02       35      NaN      NaN      NaN
2013-07-09       14        4        1      NaN
2013-07-30        9      NaN      NaN      NaN
2013-08-06        4        2      NaN      NaN
2013-09-03       40        5        2      NaN
2013-10-01       18      NaN      NaN        3


In [59]: df.unstack(['A', 'B']).fillna(0).stack(['A', 'B'])
Out[59]: 
                          Value
Date       A     B             
2013-06-11 loc_a group_a     22
                 group_b      0
                 group_c      0
           loc_b group_a      0
2013-07-02 loc_a group_a     35
                 group_b      0
                 group_c      0
           loc_b group_a      0
2013-07-09 loc_a group_a     14
                 group_b      4
                 group_c      1
           loc_b group_a      0
2013-07-30 loc_a group_a      9
                 group_b      0
                 group_c      0
           loc_b group_a      0
2013-08-06 loc_a group_a      4
                 group_b      2
                 group_c      0
           loc_b group_a      0
2013-09-03 loc_a group_a     40
                 group_b      5
                 group_c      2
           loc_b group_a      0
2013-10-01 loc_a group_a     18
                 group_b      0
                 group_c      0
           loc_b group_a      3

根据需要重新排序索引级别。

我不得不将fillna(0)放在中间,以免NaN被丢弃。 stack确实有dropna个参数。我认为将其设置为false会保留所有NaN行。可能是一个错误?

答案 1 :(得分:12)

您可以根据现有多索引级别的笛卡尔积来创建一个新的多索引。然后,使用新索引重新索引数据框。

new_index = pd.MultiIndex.from_product(df.index.levels)
new_df = df.reindex(new_index)

# Optional: convert missing values to zero, and convert the data back
# to integers. See explanation below.
new_df = new_df.fillna(0).astype(int)

就是这样!新数据框具有所有可能的索引值。现有数据已正确编入索引。

继续阅读以获得更详细的解释。

说明

设置样本数据

import pandas as pd

df = pd.DataFrame({'A': ['loc_a'] * 12 + ['loc_b'],
                   'B': ['group_a'] * 7 + ['group_b'] * 3 + ['group_c'] * 2 + ['group_a'],
                   'Date': ["2013-06-11",
                           "2013-07-02",
                           "2013-07-09",
                           "2013-07-30",
                           "2013-08-06",
                           "2013-09-03",
                           "2013-10-01",
                           "2013-07-09",
                           "2013-08-06",
                           "2013-09-03",
                           "2013-07-09",
                           "2013-09-03",
                           "2013-10-01"],
                    'Value': [22, 35, 14,  9,  4, 40, 18, 4, 2, 5, 1, 2, 3]})

df.Date = pd.to_datetime(df.Date)

df = df.set_index(['A', 'B', 'Date'])

以下是示例数据的样子

                          Value
A     B       Date
loc_a group_a 2013-06-11     22
              2013-07-02     35
              2013-07-09     14
              2013-07-30      9
              2013-08-06      4
              2013-09-03     40
              2013-10-01     18
      group_b 2013-07-09      4
              2013-08-06      2
              2013-09-03      5
      group_c 2013-07-09      1
              2013-09-03      2
loc_b group_a 2013-10-01      3

制作新索引

使用from_product我们可以创建一个新的多索引。这个新索引是来自旧索引所有级别的所有值的Cartesian product

new_index = pd.MultiIndex.from_product(df.index.levels)

重新索引

使用新索引重新索引现有数据框。

new_df = df.reindex(new_index)

现在存在所有可能的组合。缺失值为null(NaN)。

扩展的重新索引数据框如下所示:

                          Value
loc_a group_a 2013-06-11   22.0
              2013-07-02   35.0
              2013-07-09   14.0
              2013-07-30    9.0
              2013-08-06    4.0
              2013-09-03   40.0
              2013-10-01   18.0
      group_b 2013-06-11    NaN
              2013-07-02    NaN
              2013-07-09    4.0
              2013-07-30    NaN
              2013-08-06    2.0
              2013-09-03    5.0
              2013-10-01    NaN
      group_c 2013-06-11    NaN
              2013-07-02    NaN
              2013-07-09    1.0
              2013-07-30    NaN
              2013-08-06    NaN
              2013-09-03    2.0
              2013-10-01    NaN
loc_b group_a 2013-06-11    NaN
              2013-07-02    NaN
              2013-07-09    NaN
              2013-07-30    NaN
              2013-08-06    NaN
              2013-09-03    NaN
              2013-10-01    3.0
      group_b 2013-06-11    NaN
              2013-07-02    NaN
              2013-07-09    NaN
              2013-07-30    NaN
              2013-08-06    NaN
              2013-09-03    NaN
              2013-10-01    NaN
      group_c 2013-06-11    NaN
              2013-07-02    NaN
              2013-07-09    NaN
              2013-07-30    NaN
              2013-08-06    NaN
              2013-09-03    NaN
              2013-10-01    NaN

整数列中的空值

您可以看到新数据框中的数据已从整数转换为浮点数。 Pandas can't have nulls in an integer column。或者,我们可以将所有空值转换为0,并将数据转换回整数。

new_df = new_df.fillna(0).astype(int)

结果

                          Value
loc_a group_a 2013-06-11     22
              2013-07-02     35
              2013-07-09     14
              2013-07-30      9
              2013-08-06      4
              2013-09-03     40
              2013-10-01     18
      group_b 2013-06-11      0
              2013-07-02      0
              2013-07-09      4
              2013-07-30      0
              2013-08-06      2
              2013-09-03      5
              2013-10-01      0
      group_c 2013-06-11      0
              2013-07-02      0
              2013-07-09      1
              2013-07-30      0
              2013-08-06      0
              2013-09-03      2
              2013-10-01      0
loc_b group_a 2013-06-11      0
              2013-07-02      0
              2013-07-09      0
              2013-07-30      0
              2013-08-06      0
              2013-09-03      0
              2013-10-01      3
      group_b 2013-06-11      0
              2013-07-02      0
              2013-07-09      0
              2013-07-30      0
              2013-08-06      0
              2013-09-03      0
              2013-10-01      0
      group_c 2013-06-11      0
              2013-07-02      0
              2013-07-09      0
              2013-07-30      0
              2013-08-06      0
              2013-09-03      0
              2013-10-01      0