【问题标题】:Dataframe Slice does not remove Index ValuesDataframe Slice 不会删除索引值
【发布时间】:2015-09-15 11:34:25
【问题描述】:

我最近遇到了一个大型数据框及其关联的多索引的问题。 这个简化的例子将演示这个问题。

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

查看数据框如何减少,但索引没有。

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]])

当数据框很大时,问题就会出现。

【问题讨论】:

  • 当我从数据框中删除一些行时也是如此。人们会期望,如果行被删除,为什么索引会保留这些值!

标签: python pandas


【解决方案1】:

我理解您的担忧,但我相信您必须看看 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]])

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

【讨论】:

  • 里卡多 - 你的回答很有帮助 - 谢谢。我想你有一点 90 v 30 的错字。
  • 谢谢 - 你给了我必要的洞察力。但是,我认为我会避免使用 iterrows(),然后在切片操作之后执行 .reset_index(),然后立即执行 .set_index()。这导致多索引的占用空间减少。
  • 哦,是的,这是一个错字。首先,我编写了大于 30 的代码,但随后它返回了所有值。关于重置索引,我认为您需要它原来的多索引。不过好吧,我很高兴能以某种方式提供帮助。
  • 感谢@RicardoSilveira 的解释。有没有办法只获取当前过滤/提取/查询/丢弃数据帧的索引?我从事数据分析和可视化工作,我通过从同一个 df 准备多个切片来从单个数据帧准备多个输出。现在,为了可视化数据,我需要使用索引作为基础。但是每次我做 df_slide.index 时,它都会给我原始索引中的所有索引。相反,我想我是否可以获取属于 df_slice 的索引。是否可以通过其他一些功能或方法?
【解决方案2】:

您可以通过以下方式使 MultiIndex 独一无二

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

产生索引

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

【讨论】:

  • 谢谢 - 这会奏效。我仍然想知道为什么它没有从根本上改变。
【解决方案3】:

我首选的方法是

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

【讨论】:

    猜你喜欢
    • 2018-07-04
    • 2017-03-25
    • 2023-01-25
    • 2019-01-25
    • 2014-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-05
    相关资源
    最近更新 更多