【问题标题】:pandas: assign to multiindex using .loc with maskpandas:使用带有掩码的 .loc 分配给多索引
【发布时间】:2021-06-30 21:26:15
【问题描述】:

使用MultiIndex / advanced indexing: Using slicers 文档中的示例。

def mklbl(prefix, n):
    return ["%s%s" % (prefix, i) for i in range(n)]


miindex = pd.MultiIndex.from_product(
    [mklbl("A", 4), mklbl("B", 2), mklbl("C", 4), mklbl("D", 2)]
)


micolumns = pd.MultiIndex.from_tuples(
    [("a", "foo"), ("a", "bar"), ("b", "foo"), ("b", "bah")],
    names=["lvl0", "lvl1"]
)


dfmi = (
    pd.DataFrame(
        np.arange(len(miindex) * len(micolumns)).reshape(
            (len(miindex), len(micolumns))
        ),
        index=miindex,
        columns=micolumns,
    )
    .sort_index()
    .sort_index(axis=1)
)
>>> dfmi
lvl0           a         b
lvl1         bar  foo  bah  foo
A0 B0 C0 D0    1    0    3    2
         D1    5    4    7    6
      C1 D0    9    8   11   10
         D1   13   12   15   14
      C2 D0   17   16   19   18
...          ...  ...  ...  ...
A3 B1 C1 D1  237  236  239  238
      C2 D0  241  240  243  242
         D1  245  244  247  246
      C3 D0  249  248  251  250
         D1  253  252  255  254

[64 rows x 4 columns]

在伪代码中,我想要什么:

if D1/bar % 3 == 0 && D1/foo > 100:
    D0/bar = np.nan

差不多,但不完全:

mask = ( (dfmi.loc[pd.IndexSlice[:,:,:,"D1"], ("a","bar")] % 3 == 0)
       & (dfmi.loc[pd.IndexSlice[:,:,:,"D1"], ("a","foo")] > 100))

dfmi.loc[pd.IndexSlice[:,:,:,"D0",mask], ("a","bar")] = np.nan

问题在于,在任何给定的索引级别,掩码或选择器都可以应用 - bot 不能同时应用。例如,我可以将遮罩应用到不同的级别。这需要使用完整索引(无缺失值)生成掩码或重新对齐原始索引。如何(不排除其他方法)?


稍后...

我真的认为这会起作用,因为最内层的索引应该有一半的行,但由于某种原因它引发了ValueError。有人知道为什么吗?

>>> dfmi.swaplevel(0,3).loc[pd.IndexSlice["D0",:,:,mask.values], ("a","bar")] = np.nan
...
ValueError: cannot index with a boolean indexer that is not the same length as the index

虽然这确实有效,但我认为会有一种更简洁的方法来更改索引值。我以为我过去曾成功使用过index.set_levels。有人想解决这个问题吗?

t = mask.reset_index()
t["level_3"] = "D0"
t = t.set_index(list(t.columns.values[:4]))
mask = t.reindex(dfmi.index).fillna(False)
dfmi.loc[mask[0], ("a","bar")] = np.nan

【问题讨论】:

  • 这是个问题。
  • 请张贴预期的输出;我想测试一下我的逻辑
  • 最新编辑包括生成预期输出的代码。

标签: python pandas multidimensional-array indexing multi-index


【解决方案1】:

你可以创建一个临时的 multiIndex d0:

d0 = dfmi.loc[pd.IndexSlice[:,:,:,"D0"], ('a','bar')]

接下来,使用来自mask 的布尔值,结合mask 方法,获取您的空值:

d0 = d0.mask(mask.array)

d0更新原始数据框:

dfmi.loc[d0.index, ('a', 'bar')] = d0

【讨论】:

  • 这非常适合我的(更复杂的)工作流程;因为pd.maskpd.where 都不支持else 条件,所以我将d0.mask 替换为我的else 值:else_df.mask(mask.array).values
【解决方案2】:

一种选择是使用 numpy 的 argwhere 方法按符合所有条件的行的索引进行过滤。

例如:

is_D1 = np.array([index[-1] == "D1" for index in dfmi.index])
is_multiple_of_3 = np.array(dfmi.loc[:, ("a", "bar")] % 3 == 0)
is_greater_than_100 = np.array(dfmi.loc[:, ("a", "foo")] > 100)
mask = np.argwhere(is_D1 & is_multiple_of_3 & is_greater_than_100).flatten()
dfmi.iloc[mask - 1, dfmi.columns == ("a", "bar")] = np.nan

【讨论】:

  • 不错。有点啰嗦。你不能用我原来的面具,比如dfmi.iloc[mask.reset_index(drop=True).index[mask == True]*2, dfmi.columns == ("a","bar")] = np.nan吗?这种方法的缺点是你必须知道索引的布局。
猜你喜欢
  • 2015-09-30
  • 2018-05-24
  • 2018-09-18
  • 2021-12-02
  • 2017-03-11
  • 2021-11-17
  • 2015-02-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多