1

我正在处理这样一个数据框:

    id        Xp_1  Xp_2   Xp_4   Xt_1  Xt_2  Xt_3  Mp_1   Mp_2  Mp_3  Mt_1  Mt_2 Mt_6
0    i24     Nan    0.27   Nan    0.45  0.20  0.25  0.27  Nan    Nan   Nan   Nan  Nan
1    i25     0.45   0.47   0.46   0.22  0.42  Nan   0.42  0.05   0.43  0.12  0.01  0.04
2    i11     Nan    Nan    0.32   0.14  0.32  0.35  0.29  0.33   Nan   Nan   0.02  0.44
3    i47     Nan    0.56   0.59   0.92  Nan   0.56  0.51  0.12   Nan   0.1   0.1   Nan

如您所见,我有两个宏组(X 和 M),每个宏组有两个子集(p 和 t)。我想要实现的是两个宏组之间的“或”条件以及宏组的每个子集之间的“与”条件。

基本上,我想将那些对每个子集至少有两个值的行保留在至少一个组中。例如:i24应该被丢弃,其实Xps我们只有一个值,而且M组没有任何值。像 i11 这样的条目应该保留,实际上 X 组不满足条件,但 M 满足。 i25 也是如此,这两个组都满足条件。

我试过这个:

keep_r = (df.groupby(lambda col: col.split("_", maxsplit=1)[0], axis=1)
            .count()
            .ge(2)
            .all(axis=1))
df = df.loc[keep_r]

但它会检查所有子集(Xp、Xt、Mp、Mt)中是否至少有两个值。相反,我想独立对待 X 和 M。

谢谢!

4

2 回答 2

3

我们可以对 2 个事物进行分组:和 ,它们X是列名的第一个和第二个字符。然后我们可以调用你的逻辑,但在and的级别。然后我们把or条件放在:Mpt.count().ge(2).all(axis=1)ptany

# to keep the `id` column aside
df = df.set_index("id")

# groups
c = df.columns
g = df.groupby([c.str[0], c.str[1]], axis=1)

# boolean mask
mask = (g.count()
         .ge(2)
         .all(axis=1, level=0)     # micros: and
         .any(axis=1))             # macros: or

# new df
ndf = df[mask]

要得到

>>> ndf

     Xp_1  Xp_2  Xp_4  Xt_1  Xt_2  Xt_3  Mp_1  Mp_2  Mp_3  Mt_1  Mt_2  Mt_6
id
i25  0.45  0.47  0.46  0.22  0.42   NaN  0.42  0.05  0.43  0.12  0.01  0.04
i11   NaN   NaN  0.32  0.14  0.32  0.35  0.29  0.33   NaN   NaN  0.02  0.44
i47   NaN  0.56  0.59  0.92   NaN  0.56  0.51  0.12   NaN   0.1   0.1   NaN

为了说明,在调用alland之前any,我们有:

>>> g.count().ge(2)

         M             X
         p      t      p     t
id
i24  False  False  False  True
i25   True   True   True  True
i11   True   True  False  True
i47   True   True   True  True

然后all超过 0 级,即用逻辑过度p, t减少这一步骤:

>>> g.count().ge(2).all(axis=1, level=0)

         M      X
id
i24  False  False
i25   True   True
i11   True  False
i47   True   True

最后any在剩下的部分M, X将其简化为带有or逻辑的布尔系列,这说明要保留哪些行:

>>> g.count().ge(2).all(axis=1, level=0).any(axis=1)

id
i24    False
i25     True
i11     True
i47     True
dtype: bool
于 2021-06-18T18:19:57.100 回答
1

IIUC 尝试创建一个MultiIndexfrom 模式str.extract

df = df.set_index('id')
df.columns = pd.MultiIndex.from_frame(df.columns.str.extract('(.)(.)_(.+)'))
0       X                                   M                              
1       p                 t                 p                 t            
2       1     2     4     1     2     3     1     2     3     1     2     6
id                                                                         
i24   NaN  0.27   NaN  0.45  0.20  0.25  0.27   NaN   NaN   NaN   NaN   NaN
i25  0.45  0.47  0.46  0.22  0.42   NaN  0.42  0.05  0.43  0.12  0.01  0.04
i11   NaN   NaN  0.32  0.14  0.32  0.35  0.29  0.33   NaN   NaN  0.02  0.44
i47   NaN  0.56  0.59  0.92   NaN  0.56  0.51  0.12   NaN  0.10  0.10   NaN

然后按级别分组01计数,然后将单独的逻辑应用于每个级别。:

keep = (
    df.groupby(axis=1, level=[0, 1]).count()
        .ge(2).all(axis=1, level=0).any(axis=1)
)
id
i24    False
i25     True
i11     True
i47     True
dtype: bool

然后向下过滤并折叠 MultiIndex:

df = df.loc[keep]
df.columns = df.columns.map(lambda c: f'{"".join(c[:-1])}_{c[-1]}')
df = df.reset_index()
    id  Xp_1  Xp_2  Xp_4  Xt_1  Xt_2  Xt_3  Mp_1  Mp_2  Mp_3  Mt_1  Mt_2  Mt_6
0  i25  0.45  0.47  0.46  0.22  0.42   NaN  0.42  0.05  0.43  0.12  0.01  0.04
1  i11   NaN   NaN  0.32  0.14  0.32  0.35  0.29  0.33   NaN   NaN  0.02  0.44
2  i47   NaN  0.56  0.59  0.92   NaN  0.56  0.51  0.12   NaN  0.10  0.10   NaN
于 2021-06-18T18:21:50.533 回答