Заполнение интервалов времени в MultiIndex Pandas Dataframe
Я хотел бы изменить DataFrame 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
ing, но это для простых (негрупповых) временных рядов данных.
Есть ли простой способ сделать это?
Ниже приводятся некоторые попытки сделать это. Например: после того, как я раскололся на ['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 в том же формате, что и оригинал?
Ответы
Ответ 1
В вашем вопросе не было четко указано, какие даты вы отсутствовали; Я просто предполагаю, что вы хотите заполнить 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
Чтобы получить незаметные значения, мы будем использовать методы unstack
и stack
. Unstacking создаст интересующий нас 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
. Может быть ошибка?
Ответ 2
Вы можете создать новый мультииндекс на основе декартового произведения уровней существующего мультииндекса. Затем переиндексируйте свой фрейм данных с помощью нового индекса.
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, мы можем создать новый мультииндекс. Этот новый индекс представляет собой декартово произведение всех значений из всех уровней старого индекса.
new_index = pd.MultiIndex.from_product(df.index.levels)
Reindex
Используйте новый индекс для переопределения существующего фрейма данных.
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
Нули в целочисленном столбце
Вы можете видеть, что данные в новом фрейме данных были преобразованы из ints в float. Pandas не может иметь нули в столбце целых чисел. При желании мы можем преобразовать все нули в 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