web-dev-qa-db-fra.com

Comment calculer la probabilité d'une valeur à partir d'une liste d'échantillons d'une distribution en Python?

Je ne sais pas si cela appartient aux statistiques, mais j'essaie d'utiliser Python pour y parvenir. J'ai essentiellement juste une liste d'entiers:

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

Et j'aimerais connaître la probabilité qu'une valeur se produise compte tenu de ces données. J'ai représenté graphiquement des histogrammes des données en utilisant matplotlib et les ai obtenus:

enter image description here

enter image description here

Dans le premier graphique, les nombres représentent le nombre de caractères dans une séquence. Dans le deuxième graphique, il s'agit d'une durée mesurée en millisecondes. Le minimum est supérieur à zéro, mais il n'y a pas nécessairement de maximum. Les graphiques ont été créés à l'aide de millions d'exemples, mais je ne suis pas sûr de pouvoir faire d'autres hypothèses sur la distribution. Je veux connaître la probabilité d'une nouvelle valeur étant donné que j'ai quelques millions d'exemples de valeurs. Dans le premier graphique, j'ai quelques millions de séquences de longueurs différentes. Aimerait connaître la probabilité d'une longueur de 200, par exemple.

Je sais que pour une distribution continue, la probabilité de tout point exact est supposée être nulle, mais étant donné un flux de nouvelles valeurs, je dois être en mesure de dire la probabilité de chaque valeur. J'ai parcouru certaines des fonctions de densité de probabilité numpy/scipy, mais je ne sais pas exactement laquelle choisir ni comment interroger de nouvelles valeurs une fois que j'ai exécuté quelque chose comme scipy.stats.norm.pdf (data). Il semble que différentes fonctions de densité de probabilité s'adapteront différemment aux données. Compte tenu de la forme des histogrammes, je ne sais pas comment décider lequel utiliser.

15
qazplok11

Étant donné que vous ne semblez pas avoir une distribution spécifique en tête, mais que vous pourriez avoir beaucoup d'échantillons de données, je suggère d'utiliser une méthode d'estimation de densité non paramétrique. L'un des types de données que vous décrivez (temps en ms) est clairement continu et une méthode d'estimation non paramétrique d'une fonction de densité de probabilité (PDF) pour les variables aléatoires continues est l'histogramme que vous avez déjà mentionné. Cependant, comme vous le verrez ci-dessous, Kernel Density Estimation (KDE) peut être mieux. Le deuxième type de données que vous décrivez (nombre de caractères dans une séquence) est de type discret. Ici, l'estimation de la densité du noyau peut également être utile et peut être considérée comme une technique de lissage pour les situations où vous ne disposez pas d'une quantité suffisante d'échantillons pour toutes les valeurs de la variable discrète.

Estimation de la densité

L'exemple ci-dessous montre comment générer d'abord des échantillons de données à partir d'un mélange de 2 distributions gaussiennes, puis appliquer une estimation de densité de noyau pour trouver la fonction de densité de probabilité:

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()

Cela produira le graphique suivant, où la vraie distribution est affichée en bleu, l'histogramme est affiché en vert et le PDF estimé à l'aide de KDE est affiché en rouge:

Plot

Comme vous pouvez le voir, dans cette situation, le PDF approximé par l'histogramme n'est pas très utile, tandis que KDE fournit une bien meilleure estimation. Cependant, avec un plus grand nombre d'échantillons de données et un choix approprié de la taille du bac, l'histogramme pourrait également produire une bonne estimation.

Les paramètres que vous pouvez régler en cas de KDE sont le noyau et la bande passante . Vous pouvez considérer le noyau comme le bloc de construction du PDF estimé, et plusieurs fonctions du noyau sont disponibles dans Scikit Learn: gaussien, tophat, epanechnikov, exponentiel, linéaire, cosinus. La modification de la bande passante vous permet d'ajuster le compromis biais-variance. Une bande passante plus grande entraînera une augmentation du biais, ce qui est bon si vous avez moins d'échantillons de données. Une bande passante plus petite augmentera la variance (moins d'échantillons sont inclus dans l'estimation), mais donnera une meilleure estimation lorsque plus d'échantillons seront disponibles.

Calcul de la probabilité

Pour un PDF, la probabilité est obtenue en calculant l'intégrale sur une plage de valeurs. Comme vous l'avez remarqué, cela conduira à la probabilité 0 pour une valeur spécifique.

Scikit Learn ne semble pas avoir de fonction intégrée pour calculer la probabilité. Cependant, il est facile d'estimer l'intégrale du PDF sur une plage. Nous pouvons le faire en évaluant le PDF plusieurs fois dans la plage et en additionnant le valeurs obtenues multipliées par la taille du pas entre chaque point d'évaluation. Dans l'exemple ci-dessous, les échantillons N sont obtenus avec l'étape 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)

Veuillez noter que kd.score_samples génère une vraisemblance logarithmique des échantillons de données. Donc, np.exp est nécessaire pour obtenir la probabilité.

Le même calcul peut être effectué à l'aide des méthodes d'intégration SciPy intégrées, ce qui donnera un résultat un peu plus précis:

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

Par exemple, pour une analyse, la première méthode a calculé la probabilité comme 0.0859024655305, tandis que la deuxième méthode produisait 0.0850974209996139.

19
Andrzej Pronobis

OK, je propose cela comme point de départ, mais l'estimation des densités est un sujet très large. Pour votre cas impliquant la quantité de caractères dans une séquence, nous pouvons modéliser cela dans une perspective fréquentiste directe en utilisant la probabilité empirique . Ici, la probabilité est essentiellement une généralisation de la notion de pourcentage. Dans notre modèle, l'espace d'échantillon est discret et est tous des entiers positifs. Eh bien, il vous suffit de compter les occurrences et de les diviser par le nombre total d'événements pour obtenir votre estimation des probabilités. Partout où nous n'avons aucune observation, notre estimation de la probabilité est nulle.

>>> 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

Maintenant, pour vos données de synchronisation, il est plus naturel de modéliser cela comme une distribution continue. Au lieu d'utiliser une approche paramétrique où vous supposez que vos données ont une certaine distribution et ensuite ajustez cette distribution à vos données, vous devez adopter une approche non paramétrique. Une façon simple consiste à utiliser un estimation de la densité du noya . Vous pouvez simplement penser à cela comme un moyen de lisser un histogramme pour vous donner une fonction de densité de probabilité continue. Plusieurs bibliothèques sont disponibles. Peut-être le plus simple pour les données univariées est scipy:

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

Pour obtenir la probabilité d'une observation dans un certain intervalle:

>>> kde.integrate_box_1d(1,2)
0.13855869478828692
5
juanpa.arrivillaga

Voici une solution possible. Vous comptez le nombre d'occurrences de chaque valeur dans la liste d'origine. La probabilité future pour une valeur donnée est son taux d'occurrence passé, qui est simplement le nombre d'occurrences passées divisé par la longueur de la liste d'origine. En Python c'est très simple:

x est la liste de valeurs donnée

from collections import Counter
c = Counter(x)

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