有效地基于频率数据拟合Scipy中的分布

时间:2018-08-15 10:34:54

标签: python scipy statistics

我有一些要适合发行版的数据。数据由频率给出。我的意思是说,我有自己观察到的每件事,也有自己观察到的次数。像这样:

function formHandler(req, res, next) {
    let isValid = validate(fields.token)
    if (!isValid) {
        res.status(500).send({ error: 'invalid!' })
    }

其中每个元组中的第一个数字是我观察到的事件,第二个数字是对该事件的总观察值。

借助Scipy,我可以使用对scipy.stats.lognorm.fit的调用来拟合(例如)对数正态分布。但是,此例程希望看到所有观察结果的列表,而不是频率。我可以这样拟合分布:

data = [(1, 34), (2, 1023), (3, 3243), (4, 879), (5, 202), (6, 10)]

但是,哇,这似乎效率很低。

在Scipy或其他类似工具中,是否存在基于频率的分布?如果不是,是否有更好的方法来拟合分布而不必创建潜在的巨型值列表?

2 个答案:

答案 0 :(得分:1)

不幸的是,看着source,似乎对数据的“物化”方面进行了硬编码。不过,该函数并不复杂,因此您可以创建自己的版本。 TBH如果您的总氮仍然可以控制,尽管效率低下,我可能还是会data = np.array(data); expanded_data = np.repeat(data[:,0], data[:,1]),因为寿命很短。

另一种选择是使用pomegranate,它支持传递权重:

import numpy as np
import scipy.stats
import matplotlib.pyplot as plt
import pomegranate as pg

data = [(1, 34), (2, 1023), (3, 3243), (4, 879), (5, 202), (6, 10)]

data = np.array(data)
expanded = np.repeat(data[:,0], data[:,1].astype(int))

scipy_shape, _, scipy_scale = scipy_params = scipy.stats.lognorm.fit(expanded, floc=0)
scipy_sigma, scipy_mu = scipy_shape, np.log(scipy_scale)

pg_dist = pg.LogNormalDistribution(0, 1)
pg_dist.fit(data[:,0], weights=data[:,1])
pg_mu, pg_sigma = pg_dist.parameters

fig = plt.figure()
ax = fig.add_subplot(111)

x = np.linspace(0.1, 10, 100)
ax.plot(data[:,0], data[:, 1] / data[:,1].sum(), label="freq")
ax.plot(x, scipy.stats.lognorm(*scipy_params).pdf(x),
        label=r"scipy: $\mu$ {:1.3f} $\sigma$ {:1.3f}".format(scipy_mu, scipy_sigma), alpha=0.5)
ax.plot(x, pg_dist.probability(x),
        label=r"pomegranate: $\mu$ {:1.3f} $\sigma$ {:1.3f}".format(pg_mu, pg_sigma), linestyle='--', alpha=0.5)
ax.legend(loc='upper right')
fig.savefig("compare.png")

给我

comparison of scipy with pg

答案 1 :(得分:0)

您可以根据频率分布随机抽取样本,并拟合:

import scipy
import numpy as np

data = np.array(
    [(1, 34), (2, 1023), (3, 3243), (4, 879), (5, 202), (6, 10)], 
    dtype=float,
)
values = data[0]
weights = data[1]
seed = 87

gen = np.random.default_rng(seed)
sample = gen.choices(
    values, size=500, p=weights/np.sum(weights))

params = scipy.stats.lognorm.fit(values)