基于布尔值从一片多索引数据帧中删除行

Posted

技术标签:

【中文标题】基于布尔值从一片多索引数据帧中删除行【英文标题】:Drop rows from a slice of Multi-Index DataFrame based on boolean 【发布时间】:2021-12-18 21:28:32 【问题描述】:

编辑:根据要求,我提供了一个更接近我正在使用的真实数据的示例。

所以我有一张桌子data,看起来像

            value0    value1    value2
run step                              
0   0      0.12573 -0.132105  0.640423
    1       0.1049 -0.535669  0.361595
    2        1.304  0.947081 -0.703735
    3    -1.265421 -0.623274  0.041326
    4    -2.325031 -0.218792 -1.245911
    5    -0.732267 -0.544259   -0.3163
1   0     0.411631  1.042513 -0.128535
    1     1.366463 -0.665195   0.35151
    2      0.90347  0.094012 -0.743499
    3    -0.921725 -0.457726  0.220195
    4    -1.009618 -0.209176 -0.159225
    5     0.540846  0.214659  0.355373

(想想:时间序列的集合)和第二张表valid_range

    start   stop
run         
0   1   3
1   2   5

对于每个run,我想删除所有不满足start≤step≤stop 的行。

我尝试了以下(最后生成表格的代码)

for idx in valid_range.index:
    slc = data.loc[idx]
    start, stop = valid_range.loc[idx]
    cond = (start <= slc.index) & (slc.index <= stop)
    data.loc[idx] = data.loc[idx][cond]

但是,这会导致:

         value0 value1 value2
run step                     
0   0       NaN    NaN    NaN
    1       NaN    NaN    NaN
    2       NaN    NaN    NaN
    3       NaN    NaN    NaN
    4       NaN    NaN    NaN
    5       NaN    NaN    NaN
1   0       NaN    NaN    NaN
    1       NaN    NaN    NaN
    2       NaN    NaN    NaN
    3       NaN    NaN    NaN
    4       NaN    NaN    NaN
    5       NaN    NaN    NaN

我也试过data.loc[idx].drop(slc[cond].index, inplace=True),但没有任何效果...


为表格生成代码

import numpy as np
from pandas import DataFrame, MultiIndex, Index
rng = np.random.default_rng(0)

valid_range = DataFrame("start": [1, 2], "stop":[3, 5], index=Index(range(2), name="run"))
midx = MultiIndex(levels=[[],[]], codes=[[],[]], names=["run", "step"])
data = DataFrame(columns=[f"valuek" for k in range(3)], index=midx)

for run in range(2):
    for step in range(6):
        data.loc[(run, step), :] = rng.normal(size=(3))
)

【问题讨论】:

预期输出是什么?你不想要df[df['small'] &gt; 1] @HarryPlotter 这只是虚拟数据和虚拟条件。我的真实数据和情况比较复杂。 那么你应该在描述中说明这一点。提供一个更复杂、更有洞察力的示例来演示过滤的复杂性。因为正确的方法取决于它。为什么需要将它分别应用于每个动物组?条件是否取决于每个组的特定值?你想根据组做一些完全不同的事情吗? @HarryPlotter 我用非常接近我的实际数据的东西更新了这个问题 @HarryPlotter 我经常犹豫不决的原因是因为 imo 通过使事情尽可能通用,它会导致更通用的代码适用于更多人,而不是专门针对特定案例的解决方案。 【参考方案1】:

我会像这样继续分组:

(df.groupby(level=0)
   .apply(lambda x: x[x['small']>1])
   .reset_index(level=0, drop=True)    # remove duplicate index
)

给出:

                           big  small
animal animal attribute              
cow    cow    speed       30.0   20.0
              weight     250.0  150.0
falcon falcon speed      320.0  250.0
lama   lama   speed       45.0   30.0
              weight     200.0  100.0

【讨论】:

嘿,很遗憾,我不确定是否以及如何应用它。该解决方案没有为更复杂的条件留出空间(例如,需要几行代码来设置条件)。正如@HarryPlotter 所建议的,我提供了一个更复杂的示例,该示例更接近我的真实数据。【参考方案2】:

首先,基于'run'合并datavalid range,使用merge方法

>>> data

            value0     value1    value2
run step                               
0   0      0.12573  -0.132105  0.640423
    1       0.1049  -0.535669  0.361595
    2        1.304   0.947081 -0.703735
    3     -1.26542  -0.623274  0.041326
    4     -2.32503  -0.218792  -1.24591
    5    -0.732267  -0.544259   -0.3163
1   0     0.411631    1.04251 -0.128535
    1      1.36646  -0.665195   0.35151
    2      0.90347  0.0940123 -0.743499
    3    -0.921725  -0.457726  0.220195
    4     -1.00962  -0.209176 -0.159225
    5     0.540846   0.214659  0.355373


>>> valid_range

     start  stop
run             
0        1     3
1        2     5

>>> merged = data.reset_index().merge(valid_range, how='left', on='run')
>>> merged 

    run  step    value0     value1    value2  start  stop
0     0     0   0.12573  -0.132105  0.640423      1     3
1     0     1    0.1049  -0.535669  0.361595      1     3
2     0     2     1.304   0.947081 -0.703735      1     3
3     0     3  -1.26542  -0.623274  0.041326      1     3
4     0     4  -2.32503  -0.218792  -1.24591      1     3
5     0     5 -0.732267  -0.544259   -0.3163      1     3
6     1     0  0.411631    1.04251 -0.128535      2     5
7     1     1   1.36646  -0.665195   0.35151      2     5
8     1     2   0.90347  0.0940123 -0.743499      2     5
9     1     3 -0.921725  -0.457726  0.220195      2     5
10    1     4  -1.00962  -0.209176 -0.159225      2     5
11    1     5  0.540846   0.214659  0.355373      2     5

然后使用eval 选择满足条件的行。使用布尔数组屏蔽data

>>> cond = merged.eval('start < step < stop').to_numpy()
>>> data[cond]

            value0    value1    value2
run step                              
0   2        1.304  0.947081 -0.703735
1   3    -0.921725 -0.457726  0.220195
    4     -1.00962 -0.209176 -0.159225

或者,如果您愿意,这里是使用 query 的类似方法

res = (
    data.reset_index()
        .merge(valid_range, on='run', how='left')
        .query('start < step < stop')
        .drop(columns=['start','stop'])
        .set_index(['run', 'step'])
)

【讨论】:

感谢它就像一个魅力。可能会使用轻微的变化:可能会使用轻微的变化merged = data[[]].join(valid_range, on="run")step = merged.index.get_level_values("step")cond = (merged["start"] &lt;= step) &amp; (step &lt;= merged["stop"])data[cond]

以上是关于基于布尔值从一片多索引数据帧中删除行的主要内容,如果未能解决你的问题,请参考以下文章

从多索引数据帧中获取一个索引

如何从包含数据和时间作为索引的多索引数据帧中查询[重复]

Pentaho 数据集成 (PDI):将布尔值从源插入到目标

将值从一个数据帧切片复制到另一个:使用“IndexSlice”的多索引熊猫数据帧的切片是不是总是一致地排序?

基于多索引对列值求和

布尔值从false切换为true的日期列表