如何计算包含一组列中的值和 Pandas 数据框中另一列中的另一个值的行数?

zab*_*bop 5 python lambda dataframe python-3.x pandas

# import packages, set nan
import pandas as pd
import numpy as np
nan = np.nan
Run Code Online (Sandbox Code Playgroud)

问题

我有一个数据框,有一定数量的观察作为列,测量作为行。该结果的意见A, B, C, D ...。它也有一个类别列,它表示类别的的测量。分类:a, b, c, d .... 如果一列nan在一行中包含 a ,则意味着尚未进行该测量期间的观察(因此nan不是 an observation,而是缺少它)。一个MRE

data = {'observation0': ['A','A','A','A','B'],'observation1': ['B','B','B','C',nan], 'category': ['a', 'b', 'c','a','b']}
df = pd.DataFrame.from_dict(data)
Run Code Online (Sandbox Code Playgroud)

df 看起来像这样:

在此处输入图片说明

我想计算A, B, C, D...使用每个测量类别(即)观察到的每个观察结果(即)的次数a, b, c, d ...

我想得到:

obs_A_in_cat_a    2
obs_A_in_cat_b    1
obs_A_in_cat_c    1
obs_B_in_cat_a    1
obs_B_in_cat_b    2
obs_B_in_cat_c    1
obs_C_in_cat_a    1
obs_C_in_cat_b    0
obs_C_in_cat_c    0
Run Code Online (Sandbox Code Playgroud)

观察值A出现在带有index 0和 的行中3(见上面的 df),而测量值categorya,所以obs_A_in_cat_a也是2。观察A中只出现一次(行index 1)与测量categoryb,所以obs_A_in_cat_b1,等等。


我的解决方案

首先我收集观察结果,注意不要包括 nans

observations = pd.unique(pd.concat([df[col] for col in df.columns if 'observation' in col]).dropna())
Run Code Online (Sandbox Code Playgroud)

它们所属的不同类别:

categories = pd.unique(df['category'])
Run Code Online (Sandbox Code Playgroud)

然后,迭代观察。如果是靠这个

for observation in observations:
    for category in categories:
        df['obs_'+observation+'_in_cat_'+category]=\
        df.apply(lambda row: int(observation in [row[col]
                                                 for col in df.columns
                                                 if 'observation' in col]
                                 and row['category'] == category),axis=1)
Run Code Online (Sandbox Code Playgroud)

lambda 函数检查是否observation出现在 each 中row,以及测量值是否在迭代中当前考虑的类别中。创建新列,标题为 obs_OBSERVATION_in_cat_CATEGORY,其中OBSERVATIONis A, B, C, D ...CATEGORYisa, b, c, d ...如果observationXcategoryY测量期间进行了in a ,obs_OBSERVATIONX_in_cat_CATEGORYY1在与该测量对应的行中,否则为0

结果df(部分)如下所示:

在此处输入图片说明

完成使用sum()ming 新创建的列的值,选择具有条件列表理解的值

df[[col for col in df.columns if '_in_cat_' in col]].sum()
Run Code Online (Sandbox Code Playgroud)

这给了我想要得到的输出,如上所示。整个笔记本在这里


问题

这种方法看似有效,但速度太慢,在现实生活中不易应用。我怎样才能让它更快?我正在寻找类似的东西:

how_many_times_each_observation_was_made_using_each_category_of_measurement(
df,
list_of_observation_columns,
category_column)
Run Code Online (Sandbox Code Playgroud)

jez*_*ael 5

解决方案MultiIndexwith DataFrame.meltGroupBy.size对于计数值,0通过Series.reindex以下方式添加缺失的组合:

s = df.melt('category').groupby(['value','category']).size()
s = s.reindex(pd.MultiIndex.from_product(s.index.levels), fill_value=0)
print (s)
value  category
A      a           2
       b           1
       c           1
B      a           1
       b           2
       c           1
C      a           1
       b           0
       c           0
dtype: int64
Run Code Online (Sandbox Code Playgroud)

最后可以通过f-strings 将其展平:

s.index = s.index.map(lambda x: f'obs_{x[0]}_in_cat_{x[1]}')   
print (s)
obs_A_in_cat_a    2
obs_A_in_cat_b    1
obs_A_in_cat_c    1
obs_B_in_cat_a    1
obs_B_in_cat_b    2
obs_B_in_cat_c    1
obs_C_in_cat_a    1
obs_C_in_cat_b    0
obs_C_in_cat_c    0
dtype: int64
Run Code Online (Sandbox Code Playgroud)