Как вычислить вероятность значения, заданного списком выборок из дистрибутива в Python?

Не уверен, что это относится к статистике, но я пытаюсь использовать Python для достижения этого. Я просто имею список целых чисел:

data = [300,244,543,1011,300,125,300 ... ]

И я хотел бы знать вероятность появления значения с учетом этих данных. Я графовал гистограммы данных с помощью matplotlib и получил их:

введите описание изображения здесь

введите описание изображения здесь

В первом графике цифры представляют количество символов в последовательности. На втором графике это измеренное количество времени в миллисекундах. Минимум больше нуля, но не обязательно максимум. Графики были созданы с использованием миллионов примеров, но я не уверен, что могу сделать какие-либо другие предположения о распределении. Я хочу знать вероятность нового значения, учитывая, что у меня есть несколько миллионов примеров значений. На первом графике у меня есть несколько миллионов последовательностей разной длины. Хотелось бы узнать, например, вероятность длины 200.

Я знаю, что для непрерывного распределения вероятность любой точной точки должна быть равна нулю, но, учитывая поток новых значений, мне нужно иметь возможность сказать, насколько вероятно каждое значение. Я просмотрел некоторые из функций плотности плотности numpy/scipy, но я не уверен, что выбрать или как запросить новые значения, как только я запустил что-то вроде scipy.stats.norm.pdf(data). Похоже, что разные функции плотности вероятности будут соответствовать данным по-разному. Учитывая форму гистограмм, я не уверен, как решить, что использовать.

Ответ 1

Поскольку у вас нет особого распределения, но у вас может быть много образцов данных, я предлагаю использовать метод непараметрической оценки плотности. Один из типов данных, которые вы описываете (время в мс), явно непрерывный, и один метод непараметрического оценивания функции плотности вероятности (PDF) для непрерывных случайных величин - это гистограмма, о которой вы уже упоминали. Однако, как вы увидите ниже, Оценка плотности ядра (KDE) может быть лучше. Второй тип данных, которые вы описываете (количество символов в последовательности), имеет дискретный вид. Здесь оценка плотности ядра также может быть полезна и может рассматриваться как метод сглаживания для ситуаций, когда у вас нет достаточного количества выборок для всех значений дискретной переменной.

Оценка плотности

В следующем примере показано, как сначала сгенерировать образцы данных из смеси из двух гауссовых распределений, а затем применить оценку плотности ядра, чтобы найти функцию плотности вероятности:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
from sklearn.neighbors import KernelDensity

# Generate random samples from a mixture of 2 Gaussians
# with modes at 5 and 10
data = np.concatenate((5 + np.random.randn(10, 1),
                       10 + np.random.randn(30, 1)))

# Plot the true distribution
x = np.linspace(0, 16, 1000)[:, np.newaxis]
norm_vals = mlab.normpdf(x, 5, 1) * 0.25 + mlab.normpdf(x, 10, 1) * 0.75
plt.plot(x, norm_vals)

# Plot the data using a normalized histogram
plt.hist(data, 50, normed=True)

# Do kernel density estimation
kd = KernelDensity(kernel='gaussian', bandwidth=0.75).fit(data)

# Plot the estimated densty
kd_vals = np.exp(kd.score_samples(x))
plt.plot(x, kd_vals)

# Show the plots
plt.show()

Это приведет к следующему графику, где истинное распределение отображается синим цветом, гистограмма отображается зеленым цветом, а PDF, оцененный с использованием KDE, отображается красным:

Сюжет

Как вы можете видеть, в этой ситуации PDF, аппроксимированный гистограммой, не очень полезен, в то время как KDE обеспечивает гораздо лучшую оценку. Однако при большом количестве выборок данных и правильном выборе размера бункера гистограмма может также дать хорошую оценку.

Параметры, которые вы можете настроить в случае KDE, - это ядро ​​и пропускная способность. Вы можете думать о ядре как о строительном блоке для оцененного PDF, а в Scikit Learn доступны несколько функций ядра: гауссовский, тофат, epanechnikov, экспоненциальный, линейный, косинус. Изменение полосы пропускания позволяет вам корректировать компромисс между отклонениями. Увеличенная полоса пропускания приведет к увеличению смещения, что хорошо, если у вас меньше выборок данных. Меньшая полоса пропускания увеличит дисперсию (в оценку будет включено меньше выборок), но даст более точную оценку, когда доступно больше образцов.

Расчет вероятности

Для PDF вероятность получается путем вычисления интеграла по диапазону значений. Как вы заметили, это приведет к вероятности 0 для определенного значения.

Scikit Learn, похоже, не имеет встроенной функции для вычисления вероятности. Однако легко оценить интеграл PDF по диапазону. Мы можем это сделать, оценивая PDF несколько раз в пределах диапазона и суммируя полученные значения, умноженные на размер шага между каждой точкой оценки. В приведенном ниже примере образцы N получены с шагом step.

# Get probability for range of values
start = 5  # Start of the range
end = 6    # End of the range
N = 100    # Number of evaluation points 
step = (end - start) / (N - 1)  # Step size
x = np.linspace(start, end, N)[:, np.newaxis]  # Generate values in the range
kd_vals = np.exp(kd.score_samples(x))  # Get PDF values for each x
probability = np.sum(kd_vals * step)  # Approximate the integral of the PDF
print(probability)

Обратите внимание, что kd.score_samples генерирует лог-правдоподобие образцов данных. Поэтому для получения правдоподобия требуется np.exp.

То же вычисление может быть выполнено с использованием встроенных методов интеграции SciPy, что даст немного более точный результат:

from scipy.integrate import quad
probability = quad(lambda x: np.exp(kd.score_samples(x)), start, end)[0]

Например, для одного прогона первый метод вычислял вероятность как 0.0859024655305, а второй - 0.0850974209996139.

Ответ 2

ОК Я предлагаю это как отправную точку, но оценка плотности - очень широкая тема. Для вашего случая, связанного с количеством символов в последовательности, мы можем моделировать это с прямой перспективы с использованием эмпирической вероятности. Здесь вероятность - это, по сути, обобщение понятия процента. В нашей модели пространство выборки является дискретным и представляет собой целые положительные числа. Ну, тогда вы просто подсчитываете вхождения и делите на общее количество событий, чтобы получить свою оценку вероятностей. Везде, где мы имеем нулевые наблюдения, наша оценка вероятности равна нулю.

>>> samples = [1,1,2,3,2,2,7,8,3,4,1,1,2,6,5,4,8,9,4,3]
>>> from collections import Counter
>>> counts = Counter(samples)
>>> counts
Counter({1: 4, 2: 4, 3: 3, 4: 3, 8: 2, 5: 1, 6: 1, 7: 1, 9: 1})
>>> total = sum(counts.values())
>>> total
20
>>> probability_mass = {k:v/total for k,v in counts.items()}
>>> probability_mass
{1: 0.2, 2: 0.2, 3: 0.15, 4: 0.15, 5: 0.05, 6: 0.05, 7: 0.05, 8: 0.1, 9: 0.05}
>>> probability_mass.get(2,0)
0.2
>>> probability_mass.get(12,0)
0

Теперь, для ваших временных данных, более естественно моделировать это как непрерывное распределение. Вместо использования параметрического подхода, когда вы предполагаете, что ваши данные имеют некоторый дистрибутив, а затем соответствуют этому распределению вашим данным, вы должны принять непараметрический подход. Одним из простых способов является использование оценки плотности ядра. Вы можете просто подумать об этом как о сглаживании гистограммы, чтобы дать вам непрерывную функцию плотности вероятности. Доступно несколько библиотек. Возможно, наиболее простым для одномерных данных является scipy's:

>>> import scipy.stats
>>> kde = scipy.stats.gaussian_kde(samples)
>>> kde.pdf(2)
array([ 0.15086911])

Чтобы получить вероятность наблюдения в некотором интервале:

>>> kde.integrate_box_1d(1,2)
0.13855869478828692

Ответ 3

Вот одно из возможных решений. Вы подсчитываете количество вхождений каждого значения в исходный список. Будущей вероятностью для данного значения является его прошлая скорость появления, которая представляет собой просто количество прошлых событий, деленное на длину исходного списка. В Python это очень просто:

x - это список значений

from collections import Counter
c = Counter(x)

def probability(a):
    # returns the probability of a given number a
    return float(c[a]) / len(x)