簡體   English   中英

如何計算 Pandas dataframe 中同時包含一組列中的值和另一列中的另一個值的行數?

[英]How to count the number of rows containing both a value in a set of columns and another value in another column in a Pandas dataframe?

# import packages, set nan
import pandas as pd
import numpy as np
nan = np.nan

問題

我有一個 dataframe,其中一定數量的觀察值作為列,測量值作為行。 觀察結果A, B, C, D... 它還有一個類別列,表示測量類別 分類: a, b, c, d... 如果一列連續包含一個nan ,則意味着尚未進行該測量期間的觀察(因此nan不是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)

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

觀察A出現在index 03的行中(見上面的 df),而測量categorya ,因此obs_A_in_cat_a2 觀察Acategory為 : b的測量中僅出現一次(行index 1 ),因此obs_A_in_cat_b1 ,依此類推。


我的解決方案

首先,我收集觀察結果, 注意不要包括 nans

observations = pd.unique(pd.concat([df[col] for col in df.columns if 'observation' in col]).dropna())

它們所屬的不同類別:

categories = pd.unique(df['category'])

然后,迭代觀察。 如果是靠這個

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)

lambda function 檢查observation是否出現在每一row中,以及測量是否屬於迭代中當前考慮的類別。 創建新列,標題為 obs_OBSERVATION_in_cat_CATEGORY,其中OBSERVATIONA, B, C, D...CATEGORYa, b, c, d...如果在測量期間進行了categoryY Y 中的observationX X,則obs_OBSERVATIONX_in_cat_CATEGORYY1在對應於該測量的行中,否則為0

生成的df (它的一部分)如下所示:

在此處輸入圖像描述

完成使用sum()計算新創建的列的值,選擇具有條件列表理解的那些:

df[[col for col in df.columns if '_in_cat_' in col]].sum()

這給了我想要得到的 output,如上所示。 整個筆記本在這里


問題

這種方法似乎可行,但速度太慢,難以在現實生活中輕松應用。 我怎樣才能讓它更快? 我正在尋找類似的東西:

how_many_times_each_observation_was_made_using_each_category_of_measurement(
df,
list_of_observation_columns,
category_column)

使用MultiIndex的解決DataFrame.meltGroupBy.size用於計數值,為Series.reindex缺少的組合添加0

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

最后可以通過f-string s 將其展平:

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

您可以將melt交叉表結合使用以獲得 output:

s = df.melt("category")
s = pd.crosstab(s.value, s.category).stack()
s.index = [f"obs_{first}_in_cat_{last}" for first, last in s.index]

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

您可以通過以下方式進行:

dfT = []
for colName in ['observation0','observation1']:
    df1 = df.groupby([colName,'category'])['category'].count().to_frame()
    df1.columns = ['count']
    df1 = df1.reset_index()
    df1['label'] = 'obs_'+df1[colName]+'_cat_'+df1['category']
    df1 = df1.loc[:,['label','count']]
    dfT.append(df1)

dfT = pd.concat(dfT,axis=0).reset_index(drop=True)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM