【问题标题】:Insert 0-values for missing dates within MultiIndex为 MultiIndex 中的缺失日期插入 0 值
【发布时间】:2019-03-10 02:39:01
【问题描述】:

假设我有一个 MultiIndex,它包含日期和一些类别(在下面的示例中为简单起见),并且对于每个类别,我都有一个包含某个过程值的时间序列。 当有观察时我只有一个值,现在我想在那个日期没有观察时添加一个“0”。 我发现了一种看起来非常低效的方法(堆叠和取消堆叠会在数百万个类别的情况下创建许多列)。

import datetime as dt
import pandas as pd

days= 4
#List of all dates that should be in the index
all_dates = [datetime.date(2013, 2, 13) - dt.timedelta(days=x)
    for x in range(days)]
df = pd.DataFrame([
    (datetime.date(2013, 2, 10), 1, 4),
    (datetime.date(2013, 2, 10), 2, 7),
    (datetime.date(2013, 2, 11), 2, 7),
    (datetime.date(2013, 2, 13), 1, 2),
    (datetime.date(2013, 2, 13), 2, 3)],
    columns = ['date', 'category', 'value'])
df.set_index(['date', 'category'], inplace=True)
print df
print df.unstack().reindex(all_dates).fillna(0).stack()
# insert 0 values for missing dates
print all_dates

                        value
date       category       
2013-02-10 1             4
           2             7
2013-02-11 2             7
2013-02-13 1             2
           2             3

                      value
            category       
2013-02-13 1             2
           2             3
2013-02-12 1             0
           2             0
2013-02-11 1             0
           2             7
2013-02-10 1             4
           2             7
[datetime.date(2013, 2, 13), datetime.date(2013, 2, 12),
    datetime.date(2013, 2, 11),     datetime.date(2013, 2, 10)]

有人知道实现相同目标的更聪明的方法吗?

编辑:我发现了另一种实现相同目标的可能性:

import datetime as dt
import pandas as pd

days= 4
#List of all dates that should be in the index
all_dates = [datetime.date(2013, 2, 13) - dt.timedelta(days=x) for x in range(days)]
df = pd.DataFrame([(datetime.date(2013, 2, 10), 1, 4, 5),
(datetime.date(2013, 2, 10), 2,1, 7),
(datetime.date(2013, 2, 10), 2,2, 7),
(datetime.date(2013, 2, 11), 2,3, 7),
(datetime.date(2013, 2, 13), 1,4, 2),
(datetime.date(2013, 2, 13), 2,4, 3)],
columns = ['date', 'category', 'cat2', 'value'])
date_col = 'date'
other_index = ['category', 'cat2']
index = [date_col] + other_index
df.set_index(index, inplace=True)
grouped = df.groupby(level=other_index)
df_list = []
for i, group in grouped:
    df_list.append(group.reset_index(level=other_index).reindex(all_dates).fillna(0))
print pd.concat(df_list).set_index(other_index, append=True)

                    value
           category cat2       
2013-02-13 1        4         2
2013-02-12 0        0         0
2013-02-11 0        0         0
2013-02-10 1        4         5
2013-02-13 0        0         0
2013-02-12 0        0         0
2013-02-11 0        0         0
2013-02-10 2        1         7
2013-02-13 0        0         0
2013-02-12 0        0         0
2013-02-11 0        0         0
2013-02-10 2        2         7
2013-02-13 0        0         0
2013-02-12 0        0         0
2013-02-11 2        3         7
2013-02-10 0        0         0
2013-02-13 2        4         3
2013-02-12 0        0         0
2013-02-11 0        0         0
2013-02-10 0        0         0

【问题讨论】:

  • 我喜欢你的 stack/unstack 方法。我不确定是否有更好的方法来添加行。如果您知道所有类别,也许您可​​以制作一个包含所有日期/类别的 DF,并将其与包含数据的 DF 合并。这将留下您可以用零填充的 NA。我不知道这是否会更快.....
  • 迭代组的版本不会为我的本地数据集抛出内存错误(stack/unstack 版本会)

标签: python pandas


【解决方案1】:

您可以根据所需索引级别的笛卡尔积创建新的多索引。然后,使用新索引重新索引您的数据框。

(date_index, category_index) = df.index.levels
new_index = pd.MultiIndex.from_product([all_dates, category_index])
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 datetime as dt
import pandas as pd

days= 4
#List of all dates that should be in the index
all_dates = [dt.date(2013, 2, 13) - dt.timedelta(days=x)
    for x in range(days)]
df = pd.DataFrame([
    (dt.date(2013, 2, 10), 1, 4),
    (dt.date(2013, 2, 10), 2, 7),
    (dt.date(2013, 2, 11), 2, 7),
    (dt.date(2013, 2, 13), 1, 2),
    (dt.date(2013, 2, 13), 2, 3)],
    columns = ['date', 'category', 'value'])
df.set_index(['date', 'category'], inplace=True)

示例数据如下所示

                     value
date       category
2013-02-10 1             4
           2             7
2013-02-11 2             7
2013-02-13 1             2
           2             3

新建索引

使用from_product 我们可以创建一个新的多索引。这个新索引是您传递给函数的所有值的Cartesian product

(date_index, category_index) = df.index.levels

new_index = pd.MultiIndex.from_product([all_dates, category_index])

重新索引

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

现在所有可能的组合都出现了。缺失值为空 (NaN)。

new_df = df.reindex(new_index)

现在,扩展的、重新索引的数据框如下所示:

              value
2013-02-13 1    2.0
           2    3.0
2013-02-12 1    NaN
           2    NaN
2013-02-11 1    NaN
           2    7.0
2013-02-10 1    4.0
           2    7.0

整数列中的空值

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

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

结果

              value
2013-02-13 1      2
           2      3
2013-02-12 1      0
           2      0
2013-02-11 1      0
           2      7
2013-02-10 1      4
           2      7

【讨论】:

    【解决方案2】:

    查看此答案:How to fill the missing record of Pandas dataframe in pythonic way?

    你可以这样做:

    import datetime
    import pandas as pd
    
    #make an empty dataframe with the index you want
    def get_datetime(x):
        return datetime.date(2013, 2, 13)- datetime.timedelta(days=x)
    
    all_dates = [ get_datetime(x) for x in range(4)]
    categories = [1,2,3,4]
    index = [ [date, cat] for cat in categories for date in all_dates ]
    
    #this df will be just an index
    df = pd.DataFrame(index)
    df =print df.set_index([0,1])
    df.columns = ['date', 'category']
    df = df.set_index(['date', 'category'])
    
    
    #now if your original df is called df_original you can reindex against the other values
    df_orig = df_orig.reindex_axis(df.index)
    
    #and to add zeros
    df_orig.fillna(0)
    

    【讨论】:

    • 构建元组列表实际上是我想要避免的。对于 MultiIndex,这会很快导致内存错误(我有一个),因为它构建了一个随着级别和 level_values 的数量而增长的列表,并且操作本身要容易得多(对于每个唯一的组键,添加所有不存在的日期) .遍历组并创建包含所有日期的小型 dfs,然后连接可能对内存更友好。
    • 我在我的问题中添加了迭代和连接。
    猜你喜欢
    • 2021-08-03
    • 2021-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-01
    • 2013-05-23
    • 2021-01-07
    • 1970-01-01
    相关资源
    最近更新 更多