【问题标题】:how to convert a dataframe of counts to a probability density function如何将计数数据帧转换为概率密度函数
【发布时间】:2026-01-03 04:35:01
【问题描述】:

假设我对整数有以下观察:

df = pd.DataFrame({'observed_scores': [100, 100, 90, 85, 100, ...]})

我知道这可以作为输入来制作密度图:

df['observed_scores'].plot.density()

但假设我有一个计数表:

df = pd.DataFrame({'observed_scores': [100, 95, 90, 85, ...], 'counts': [1534, 1399, 3421, 8764, ...})

比完整的observed_scores 系列更便宜(我有很多观察)。

我知道可以使用计数绘制直方图,但是如何绘制密度图?如果可能的话,是否可以在不必将计数表拆分/解开成数千行的情况下完成?

【问题讨论】:

标签: python pandas scikit-learn


【解决方案1】:

IIUC,statsmodels 可让您拟合加权 KDE:

from statsmodels.nonparametric.kde import KDEUnivariate

df = pd.DataFrame({'observed_scores': [100, 95, 90, 85],
                   'counts': [1534, 1399, 3421, 8764]})

kde1= KDEUnivariate(df.observed_scores)
kde_noweight = KDEUnivariate(df.observed_scores)
kde1.fit(weights=df.counts, fft=False)
kde_noweight.fit()
plt.plot(kde1.support, kde1.density)
plt.plot(kde_noweight.support, kde_noweight.density)
plt.legend(['weighted', 'unweighted'])

输出:

【讨论】:

  • 这仍然是直方图,并不是真正的密度函数。
  • 你是对的@QuangHoang,我的错!我相应地编辑了
【解决方案2】:

您可以使用 scipy 手动构造加权 kde。只要您将bw_method 指定为标量,这将完美匹配。允许默认时,拟合变得不一致

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats

np.random.seed(410112)
# Real counts data to check with
df = pd.DataFrame({'observed_scores': np.random.randint(1, 100, 40000)})

# Aggregated to value_counts, which is what you have access to
df1 = df.groupby('observed_scores').size().to_frame('counts').reset_index()

代码

def weighted_kde(y, weights, bw_method=None):
    sample_range = np.nanmax(y) - np.nanmin(y)
    ind = np.linspace(
        np.nanmin(y) - 0.5 * sample_range,
        np.nanmax(y) + 0.5 * sample_range,
        1000)

    gkde = stats.gaussian_kde(y, bw_method=bw_method, weights=weights)

    y = gkde.evaluate(ind)
    
    return ind, y

检查输出

bw_method=0.5

fig, ax = plt.subplots()
# Underlying data, with pandas kde
df['observed_scores'].plot.density(ax=ax, bw_method=bw_method, label='pandas density', lw=2)

# From aggregated counts data
ind, y = weighted_kde(df1['observed_scores'], df1['counts'], bw_method=bw_method)
ax.plot(ind, y, label='Manual Weighted KDE', lw=2, linestyle='--')

ax.legend()
plt.show()

【讨论】:

  • 我明白了,所以加权 KDE 就是答案。知道为什么我必须手动设置带宽吗?我通常依靠内置的参考模型来获得带宽的最佳价值。
  • @irene 我不确定为什么会出现不一致。他们 应该 都使用默认值做同样的事情,因为我的函数只是 pandas 源代码,但如果没有指定,我必须错过 pandas 正在做的其他事情来选择 bw。那或者我的版本和熊猫正在使用的版本之间的辛辣版本有所不同。你仍然可以使用bw_method=None'scott',这会让 scipy 选择合适的 bw。它可能看起来不像默认的 pandas 可能会选择,但我认为这不是一个真正的问题。