计算DataFrame分类列取值概率时,各变量概率和不为1,求问题原因
问题描述
我需要计算DataFrame中每个分类变量下各取值的概率(即每个变量内各取值占该变量总样本的比例,而非全局比例),这个需求和Stack Overflow上的《Finding frequency of each value in all categorical columns across a dataframe》几乎一致,但目标是概率而非频率。
使用的示例DataFrame如下:
df = pd.DataFrame( {'sub_code': ['CSE01', 'CSE01', 'CSE01', 'CSE02', 'CSE03', 'CSE04', 'CSE05', 'CSE06'], 'stud_level': [101, 101, 101, 101, 101, 101, 101, 101], 'grade': ['STA', 'STA', 'PSA', 'STA', 'STA', 'SSA', 'PSA', 'QSA']})
我改编了该提问下的回答,编写了如下代码:
out = (df.select_dtypes(object) .melt(var_name="Variable", value_name="Class") .value_counts(dropna=False, normalize=True) .reset_index(name="Probability") .sort_values(by=['Variable', 'Class'], ascending=[True, True]) .reset_index(drop=True))
但这段代码无法得到正确结果——每个变量下各分类取值的概率之和不为1。请问问题出在哪里?
问题原因与解决方法
你的代码核心问题是:value_counts(normalize=True)是对所有变量的所有取值做全局归一化,分母是所有变量的总样本数,而非单个变量的样本数,自然导致每个变量内的概率加起来不是1。
要实现每个变量内独立计算概率,必须按Variable分组后再统计比例,这里提供两种可行方法:
方法一:分组后直接计算归一化比例
out = (df.select_dtypes(object) .melt(var_name="Variable", value_name="Class") .groupby('Variable')['Class'] .value_counts(normalize=True, dropna=False) .rename('Probability') .reset_index() .sort_values(by=['Variable', 'Class']) .reset_index(drop=True))
方法二:先统计频率再手动计算概率
如果想更直观看到计算逻辑,可以先算每个变量内的频率,再除以该变量的总样本数:
out = (df.select_dtypes(object) .melt(var_name="Variable", value_name="Class") .value_counts(dropna=False) .reset_index(name="Frequency") .merge(df.select_dtypes(object).melt(var_name="Variable").groupby('Variable').size().reset_index(name="Total"), on="Variable") .assign(Probability=lambda x: x['Frequency'] / x['Total']) .sort_values(by=['Variable', 'Class']) .reset_index(drop=True) .drop(columns=['Frequency', 'Total']))
这两种方法都能保证每个Variable下的Probability之和为1。比如示例中的sub_code变量,CSE01的概率是3/8,其余单个取值的概率是1/8,加起来正好等于1。
内容的提问来源于stack exchange,提问作者DeltaIV
相关产品推荐
相关产品推荐

