【问题标题】:Dropping a masked pandas multiindex slice inplace doesn't appear to work就地删除蒙面熊猫多索引切片似乎不起作用
【发布时间】:2019-05-15 08:24:31
【问题描述】:

我正在尝试在第二个(最内层)级别获取 Pandas 2 级多索引数据帧的切片,对切片应用掩码,然后就地“删除”原始数据帧中的掩码切片行. 我在一行代码中完成了这一切,以尽量避免链式分配问题,并确保我将“drop”操作应用于原始数据帧。

掩码由复杂的数学运算生成,最终以与切片长度相同的布尔 numpy 数组的形式出现。

但是,当我在“删除”操作后检查原始数据帧时,它仍然包含应该删除的数据。我浏览了很多页面试图解决这个问题,并尝试了许多语法排列,但无济于事。

我没有收到有关 SettingWithCopyWarning 的警告。

以下代码是我的代码的简化模型,它演示了问题,并希望传达我想要做的事情:

>>> import numpy as np
>>> import pandas as pd
>>> pd.__version__
u'0.23.4'
>>> index = pd.MultiIndex(levels=[[u'bar', u'baz', u'foo', u'qux'], 
                                  [u'one', u'two', u'three', u'four']], 
                          labels=[[0, 0, 1, 1, 2, 2, 3, 3], 
                                  [0, 0, 1, 1, 2, 2, 3, 3]], 
                          names=[u'first', u'second'])
>>> df = pd.DataFrame(np.random.randn(8, 4), index=index)
>>> df.columns = ['c0', 'c1', 'c2', 'c3']
>>> df
                    c0        c1        c2        c3
first second
bar   one    -2.366973 -0.887149 -0.301309  1.312207
      one     1.266500  0.864888 -1.407567  0.265077
baz   two    -1.926091 -0.671274 -0.295846  0.679759
      two    -0.212970  0.136552  0.219074  0.541827
foo   three  -0.698288 -2.059952  0.248811  0.947879
      three  -2.017481  0.163013 -0.906551 -0.102474
qux   four   -1.083530  0.097077  0.224977  0.251739
      four    0.943804  1.356789 -0.953357  0.592986

从切片生成掩码:

>>> two_data = df[df.index.get_level_values('second') == 'two']
>>> mask = (two_data['c1'] > 0)
>>> mask = mask.values
array([False,  True])

证明当 not 就地 (inplace=False) 时删除掩码切片值有效:

>>> df[df.index.get_level_values('second') == 'two'][mask].drop('two', level=1)
Empty DataFrame
Columns: [c0, c1, c2, c3]
Index: []
>>> df[df.index.get_level_values('second') == 'two'].iloc[mask].drop('two', level=1)
Empty DataFrame
Columns: [c0, c1, c2, c3]
Index: []

如预期的那样,原始数据框仍然完好无损:

>>> df
                    c0        c1        c2        c3
first second
bar   one    -2.366973 -0.887149 -0.301309  1.312207
      one     1.266500  0.864888 -1.407567  0.265077
baz   two    -1.926091 -0.671274 -0.295846  0.679759
      two    -0.212970  0.136552  0.219074  0.541827
foo   three  -0.698288 -2.059952  0.248811  0.947879
      three  -2.017481  0.163013 -0.906551 -0.102474
qux   four   -1.083530  0.097077  0.224977  0.251739
      four    0.943804  1.356789 -0.953357  0.592986

现在尝试将行删除。在这两种情况下,预期的行都被删除:

>>> df[df.index.get_level_values('second') == 'two'][mask].drop('two', level=1, inplace=True)
>>> df
                    c0        c1        c2        c3
first second
bar   one    -2.366973 -0.887149 -0.301309  1.312207
      one     1.266500  0.864888 -1.407567  0.265077
baz   two    -1.926091 -0.671274 -0.295846  0.679759
      two    -0.212970  0.136552  0.219074  0.541827
foo   three  -0.698288 -2.059952  0.248811  0.947879
      three  -2.017481  0.163013 -0.906551 -0.102474
qux   four   -1.083530  0.097077  0.224977  0.251739
      four    0.943804  1.356789 -0.953357  0.592986

尝试使用 iloc 作为掩码的另一种形式,但无济于事:

>>> df[df.index.get_level_values('second') == 'two'].iloc[mask].drop('two', level=1, inplace=True)
>>> df
                    c0        c1        c2        c3
first second
bar   one    -2.366973 -0.887149 -0.301309  1.312207
      one     1.266500  0.864888 -1.407567  0.265077
baz   two    -1.926091 -0.671274 -0.295846  0.679759
      two    -0.212970  0.136552  0.219074  0.541827
foo   three  -0.698288 -2.059952  0.248811  0.947879
      three  -2.017481  0.163013 -0.906551 -0.102474
qux   four   -1.083530  0.097077  0.224977  0.251739
      four    0.943804  1.356789 -0.953357  0.592986

如果就地让我们工作,预期的结果是:

                    c0        c1        c2        c3
first second
bar   one    -2.366973 -0.887149 -0.301309  1.312207
      one     1.266500  0.864888 -1.407567  0.265077
baz   two    -1.926091 -0.671274 -0.295846  0.679759
foo   three  -0.698288 -2.059952  0.248811  0.947879
      three  -2.017481  0.163013 -0.906551 -0.102474
qux   four   -1.083530  0.097077  0.224977  0.251739
      four    0.943804  1.356789 -0.953357  0.592986

请告知应该如何做。我预计这会起作用,因为我认为 loc[].iloc[].drop() 在一行上的顺序应用会解决对原始数据帧的源数据的删除操作。

【问题讨论】:

  • 即使进一步简化为 df.loc[('baz','two'),:].drop('two', level=1, inplace=True) 也不会产生任何数据从 df 中删除
  • 我无法重现您的结果,因为您尚未播种数据。如果您需要可验证的答案,请添加np.random.seed(0) 并重新撰写您的帖子。

标签: pandas dataframe multi-index


【解决方案1】:

我无法重现您的数据和预期输出,但我可以建议使用 eval 和布尔索引:

df = df[~df.eval('second == "two" and c1 > 0')]

或者,使用query

df = df.query('not (second == "two" and c1 > 0)')

如果你这样做有点不同,通过查询索引并删除它们:

df = df.drop(df.query('second == "two" and c1 > 0').index)

或者,

df.drop(df.query('second == "two" and c1 > 0').index, inplace=True)

但请记住,这两种方法(类似于上面的方法)都会生成 DataFrame 的副本。无法就地执行此操作(即使 inplace=True 生成一个副本并将其分配回原始 DataFrame 对象)。

【讨论】:

  • 感谢您的建议。但它假定掩码可以有意义地应用于整个表。但是,在我的情况下,这不是真的 - 掩码是从切片生成的,并且仅在该切片上已知。我相信掩码只能应用于原始数据帧的切片部分是这个问题的一个约束。实际上,数据框很大,我只想删除精心挑选的行。
  • @AndrewMedlin 我已经编辑过了,但是您需要了解 drop 返回 整个 数据帧的副本,无论被删除的行数多么小.即使使用inplace=True,您仍然会在内部创建一个副本并将其映射回原始对象。在这里仔细考虑你的方法。我已经用替代解决方案编辑了我的答案。
  • @AndrewMedlin 如果还有问题,请告诉我,我会尽力解决。
猜你喜欢
  • 2019-10-28
  • 1970-01-01
  • 2017-02-22
  • 2014-12-03
  • 2020-10-04
  • 2018-07-28
  • 1970-01-01
  • 2018-07-04
  • 2021-08-07
相关资源
最近更新 更多