【问题标题】:How to make assignment to hierarchical column of pandas dataframe using boolean mask?如何使用布尔掩码对熊猫数据框的分层列进行分配?
【发布时间】:2016-07-20 22:51:39
【问题描述】:

我有一个这样的数据框:

import pandas as pd
df = pd.DataFrame({
    "time": [1, 2, 1, 2], 
    "site": ['a', 'a', 'b', 'b'], 
    "val1": [11, 12, 21, 22],
    "val2": [101, 102, 201, 202]
})
df.set_index(['time', 'site'], inplace=True, append=False)
df = df.unstack("site")
print df

     val1     val2     
site    a   b    a    b
time                   
1      11  21  101  201
2      12  22  102  202

我想更改一些与布尔过滤器匹配的值。例如:

ix = df.val1 > 20
print ix

site      a     b
time             
1     False  True
2     False  True

df.val1[ix] = 50 是很自然的尝试。这会完成预期的分配,但会发出警告:SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead

所以现在我正在尝试使用df.loc 实现类似的目标。但是我找不到任何方法来使用df.loc 这种布尔掩码。这似乎是因为我使用的是分层列,即,如果我只有一组值 (val1),我不会遇到太多麻烦。不幸的是,docs 中没有很好地涵盖在分层列上使用布尔过滤器的分配。

我尝试过引用 df.loc[:,'val1',ix],但这给出了 IndexingError: Too many indexers。我已经尝试过df.loc[:,'val1'][ix] = 50,它有效,但给出了SettingWithCopyWarning

我可以使用df.val1 = df.val1.where(~ix, other=50),但这似乎不直观、低效且不灵活(例如,它不能轻易扩展为现有值加 10)。

我应该使用其他一些索引方法来根据布尔掩码将值分配给数据帧的分层列吗?

已编辑以扩展问题:

我没有意识到这会是一个问题,但我实际上想根据 val1val2 列中的值进行过滤并更改两组列中的值,如下所示:

ix = (df.val1 > 20) | (df.val2 < 102)
df.val1[ix] = 50
df.val2[ix] = 150

有没有一种简单的索引方法可以做到这一点?使用 numpy ndarrays 很容易,但使用 pandas 数据框似乎要复杂得多。

【问题讨论】:

  • 您可以选择展平列吗?

标签: python pandas boolean-expression hierarchical


【解决方案1】:

您可以使用列表来选择您的列

idx = df[['val1']] > 20

idx
Out[39]: 
       val1      
site      a     b
time             
1     False  True
2     False  True

df[idx] = 50

df
Out[41]: 
     val1     val2     
site    a   b    a    b
time                   
1      11  50  101  201
2      12  50  102  202

【讨论】:

  • 谢谢,这是对我提出的问题的一个很好的回答。不幸的是,我忘了提到我还想更改 val2 列中的相应条目,例如ix = (df.val1 &gt; 20) | (df.val2 &gt; 200); df.val1[ix] = 50; df.val2[ix] = 150。知道怎么做吗?使用标准的 numpy ndarrays 非常简单,但在 pandas 中似乎更棘手。
【解决方案2】:

当您首先按列名从数据框中选择一个系列,然后尝试使用布尔掩码并为其分配值时,就会出现此问题。具体来说,带有布尔掩码的赋值在内部被转换为extracted_data.where(-mask, other=value, inplace=True),这会引发SettingWithCopyWarning。

如果 pandas 能够保证这种操作会改变原始数据帧,而不是发出这个警告,那就太好了。 (顺便说一下,如果链式操作的顺序颠倒了,df[ix]["val1"] = 500df[ix][["val1", "val2"]] = 500 不会发出警告但无法更新原始数据帧)。在此问题得到解决之前,有几种解决方法。

(1) 受到@cncggvg 答案的启发:构造一个索引来指定所有需要更新的元素,而不是将两个索引操作链接在一起。

# create a partial index for the boolean operation
# note: this specifies the second-level columns it will act on, but not 
# the first level, since that was given unambiguously in the df[col] expression
ix = (df["val1"] > 20) | (df["val2"] < 102)
# build an index that specifies both the first and second-level columns
ix2 = pd.concat({"val1": ix}, axis=1)
# or, to do the same assignment on multiple first-level columns:
ix2 = pd.concat({"val1": ix, "val2": ix}, axis=1)
# do the assignment in one step, with no chaining
df[ix2] = 50
# or derive new values from current values
df[ix2] = df[ix2]+50

(2) 使用我自己的.where(..., inplace=False),避免使用隐含的series.where(..., inplace=True)

ix = (df["val1"] > 20) | (df["val2"] < 102)
df["val1"] = df["val1"].where(~ix, other=50)
df["val2"] = df["val2"].where(~ix, other=50)

# or to assign both columns at once:
# note: this should work with df[["val1", "val2"]] = ..., but pandas 0.18
# doesn't realize that that gives the same set of columns as cols.columns
cols = df[["val1", "val2"]]
df[cols.columns] = cols.where(~ix, other=50)
# or with a calculation:
df[cols.columns] = cols.where(~ix, other=cols+50)

这些都比我想要的更麻烦,所以我可能只是将我的数据框的相关部分复制到 numpy 数组中,然后从那里处理它们。根据http://penandpants.com/2014/09/05/performance-of-pandas-series-vs-numpy-arrays/ 的说法,无论如何这应该有更好的性能。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-02
    • 2012-09-17
    • 1970-01-01
    • 2017-10-15
    • 2018-09-23
    • 2021-12-28
    • 2013-01-17
    相关资源
    最近更新 更多