Dataframe切片不会删除索引值

5

我最近在处理一个大型数据框和其相关的多重索引时遇到了问题。下面这个简化的例子将展示这个问题。

import pandas as pd
import numpy as np

np.random.seed(1)
idx = pd.MultiIndex.from_product([['A','B'],[5,6]])


df = pd.DataFrame(data= np.random.randint(1,100,(4)),index= idx,columns =['P'])
print df

这将产生:

      P
A 5  38
  6  13
B 5  73
  6  10

现在快速查看索引。
print df.index

MultiIndex(levels=[[u'A', u'B'], [5, 6]],
           labels=[[0, 0, 1, 1], [0, 1, 0, 1]])

如果我切分这个数据框,我会注意到多重索引永远不会压缩。即使进行了深度复制。
在切片操作中,减少索引的内存占用的最佳方法是什么?
df_slice = df[df['P']>20]
print df_slice
print df_slice.index

      P
A 5  38
B 5  73

看一下数据框(dataframe)如何减少,但是索引没有改变。

MultiIndex(levels=[[u'A', u'B'], [5, 6]],
           labels=[[0, 1], [0, 0]])

即使使用 .copy(deep=True)
df_slice = df[df['P']>20].copy(deep=True)
print df_slice.index


MultiIndex(levels=[[u'A', u'B'], [5, 6]]
    ,labels=[[0, 1], [0, 0]])

我原本期望MultiIndex将6移除,如下所示:

MultiIndex(levels=[[u'A', u'B'], [5]]
    ,labels=[[0, 1], [0, 0]])

问题出现在实践中的数据框架很大时。

当我从数据框中删除一些行时,同样的情况也会发生。人们会期望如果行被删除了,为什么索引还会保留那些值! - Meet
3个回答

5
我了解您的关注,但我认为您需要看看pandas低级应用程序中正在发生的事情。
首先,我们必须声明索引应该是不可变的。您可以在此处查看更多文档 -> http://pandas.pydata.org/pandas-docs/stable/indexing.html#setting-metadata 当您创建一个数据框对象时,让我们将其命名为df,并且您想要访问它的行,基本上您所做的就是传递一个布尔系列,Pandas将与其相应的索引进行匹配。
请参考以下示例:
index = pd.MultiIndex.from_product([['A','B'],[5,6]])
df = pd.DataFrame(data=np.random.randint(1,100,(4)), index=index, columns=["P"])

      P
A 5   5
  6  51
B 5  93
  6  76

现在,假设我们想选择 P > 90 的行。你会怎么做?是不是这样:df[df["P"] > 90]?但是看看 df["P"] > 90 实际上返回了什么。
A  5     True
   6     True
B  5     True
   6    False
Name: P, dtype: bool

如您所见,它返回一个布尔系列,与原始索引匹配。为什么?因为Pandas需要映射哪些索引值具有等效的真值,以便选择适当的结果。因此,基本上在您的切片操作期间,您将始终携带此索引,因为它是对象的映射元素。
然而,希望并没有消失。根据您的应用程序,如果您认为它实际上占用了大量内存,您可以花费一点时间执行以下操作:
def df_sliced_index(df):
    new_index = []
    rows = []
    for ind, row in df.iterrows():
        new_index.append(ind)
        rows.append(row)
    return pd.DataFrame(data=rows, index=pd.MultiIndex.from_tuples(new_index))

df_sliced_index(df[df['P'] > 90]).index

我相信这会产生我所期望的输出结果:
MultiIndex(levels=[[u'B'], [5]], labels=[[0], [0]])

但是,如果数据量太大,让你担心索引的大小,我想知道它可能会花费你多少时间。


Ricardo - 你的回答很有帮助 - 谢谢。我认为你有一个小的90 v 30的错别字。 - Dickster
谢谢 - 你给了我必要的见解。然而,我认为我会避免使用iterrows(),在切片操作之后进行.reset_index(),紧接着再进行.set_index()。这样可以减少多重索引的占用空间。 - Dickster
哦,是的,那是个笔误。我先写了大于30的代码,但是它返回了所有的值。关于重置索引,我以为你需要它们作为多级索引。但好吧,我很高兴我能在某种程度上帮到你。 - Ricardo Silveira
感谢@RicardoSilveira的解释。有没有一种方法可以仅获取当前过滤/提取/查询/删除数据框的索引?我从事数据分析和可视化工作,并通过从同一df准备多个切片来准备多个输出。现在,为了可视化数据,我需要使用索引作为基础。但是,每次我执行df_slide.index时,它都会给我所有原始索引的索引。相反,我希望如果我只能获取属于df_slice的索引。是否可以通过其他函数或方法实现? - Meet

1

您可以通过以下方式使MultiIndex唯一:

df_slice.index = pd.MultiIndex.from_tuples(df_slice.index.unique(), names=idx.names)

which yields the index

MultiIndex(levels=[[u'A', u'B'], [5]],
           labels=[[0, 1], [0, 0]])

谢谢 - 那会起作用。我仍然想知道为什么它没有根本改变。 - Dickster

0

我的首选方法是

old_idx = df_slice.index
new_idx = pd.MultiIndex.from_tuples(old_idx.to_series(), names=old_idx.names)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接