我有一个具有两级多重索引的 pandas DataFrame。第二级是数字的,应该对第一级索引的每个唯一值进行排序和顺序,但有间隙。如何插入“缺失”的行?输入示例:
import pandas as pd
df = pd.DataFrame(list(range(5)),
index=pd.MultiIndex.from_tuples([('A',1), ('A',3),
('B',2), ('B',3), ('B',6)]),
columns='value')
# value
#A 1 0
# 3 1
#B 2 2
# 3 3
# 6 4
预期输出:
# value
#A 1 0
# 2 NaN
# 3 1
#B 2 2
# 3 3
# 4 NaN
# 5 NaN
# 6 4
我怀疑我可以使用
resample
,但我在将数字转换为任何类似日期的内容时遇到困难。
有志者事竟成。我并不为此感到自豪,但我认为它有效。
尝试:
def f(x):
levels = x.index.remove_unused_levels().levels
x = x.reindex(pd.MultiIndex.from_product([levels[0], np.arange(levels[1][0], levels[1][-1]+1)]))
return x
df.groupby(level=0, as_index=False, group_keys=False).apply(f)
输出:
value
A 1 0.0
2 NaN
3 1.0
B 2 2.0
3 3.0
4 NaN
5 NaN
6 4.0
经过深思熟虑,我自己想出了一个解决方案。从它有多糟糕的事实来看,我面临的问题并不是一个非常典型的问题。
new_index = d.index.to_frame()\
.groupby(0)[1]\
.apply(lambda x:
pd.Series(1, index=range(x.min(), x.max() + 1))).index
d.reindex(new_index)
您可以根据缺失的索引简单地使用以下内容:
result.unstack(1).stack(0, dropna=False).fillna(0)
当您取消堆叠时,pandas 会扩展 df 以包含行和列,在上面的示例中,1 级索引将是列名称。然后,再次通过堆叠,将 df 返回到其原始形式,但是,这次您需要确保使用
dropna=False
,以便 NaN 值将出现在丢失的索引中。最后,使用 .fillna(0)
是可选的,具体取决于您想要如何处理 NaN 值。
没有考虑品味,但我认为回到列表理解会导致代码更具可读性:
df.reindex(
pd.MultiIndex.from_tuples([
(level_0, level_1)
for level_0 in df.reset_index(0).level_0.unique()
for level_1 in range(
df.reset_index(1).loc[level_0, "level_1"].min(),
df.reset_index(1).loc[level_0, "level_1"].max()+1
)
]))
# Output:
#value
#A 1 0.0
# 2 NaN
# 3 1.0
#B 2 2.0
# 3 3.0
# 4 NaN
# 5 NaN
# 6 4.0
虽然这当然比沿着
apply
路线走要慢:
list-comprehension: 2.57 ms ± 19 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
DYZ apply: 1.25 ms ± 8.75 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Scott's apply: 2.19 ms ± 9.84 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
我看到的聚会有点晚了,但对于未来的旅行者,我想我找到了解决方案:
使用产品函数中的 pandas multiindex 来生成索引级别的所有组合:
df_new_index = pd.MultiIndex.from_product([
df.index.get_level_values(0).unique(),
df.index.get_level_values(1).unique()])
df_reindexed = df.reindex(df_new_index)