[英]count number of rows before a value group by another column in 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
0
和3
的行中(見上面的 df),而測量category
為a
,因此obs_A_in_cat_a
為2
。 觀察A
在category
為 : b
的測量中僅出現一次(行index
1
),因此obs_A_in_cat_b
為1
,依此類推。
首先,我收集觀察結果, 注意不要包括 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,其中OBSERVATION
為A, B, C, D...
, CATEGORY
為a, b, c, d...
如果在測量期間進行了categoryY
Y 中的observationX
X,則obs_OBSERVATIONX_in_cat_CATEGORYY
為1
在對應於該測量的行中,否則為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.melt
, GroupBy.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
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.