Comment puis-je tracer le CDF empirique d'un tableau de nombres dans matplotlib en Python? Je cherche l'analogue cdf de la fonction "hist" du pylab.
Une chose à laquelle je peux penser est:
from scipy.stats import cumfreq
a = array([...]) # my array of numbers
num_bins = 20
b = cumfreq(a, num_bins)
plt.plot(b)
Est-ce correct cependant? Y a-t-il un moyen plus facile/meilleur?
merci.
Cela semble être (presque) exactement ce que vous voulez. Deux choses:
Premièrement, les résultats sont un tuple de quatre éléments. Le troisième est la taille des bacs. La seconde est le point de départ du plus petit bac. Le premier est le nombre de points dans ou sous chaque groupe. (Le dernier est le nombre de points en dehors des limites, mais puisque vous n'en avez défini aucun, tous les points seront regroupés.)
Deuxièmement, vous voudrez redimensionner les résultats pour que la valeur finale soit 1, afin de suivre les conventions habituelles d'un CDF, mais sinon, c'est correct.
Voici ce qu'il fait sous le capot:
def cumfreq(a, numbins=10, defaultreallimits=None):
# docstring omitted
h,l,b,e = histogram(a,numbins,defaultreallimits)
cumhist = np.cumsum(h*1, axis=0)
return cumhist,l,b,e
Il effectue l'histogramme, puis génère une somme cumulative des comptes dans chaque groupe. La ith valeur du résultat est donc le nombre de valeurs de tableau inférieures ou égales au maximum de la ith bin. Donc, la valeur finale est juste la taille du tableau initial.
Enfin, pour le tracer, vous devez utiliser la valeur initiale de la corbeille et sa taille pour déterminer les valeurs de l'axe des x dont vous aurez besoin.
Une autre option consiste à utiliser numpy.histogram
qui peut effectuer la normalisation et renvoyer les bords du bac. Vous aurez besoin de faire la somme cumulée des comptes résultants vous-même.
a = array([...]) # your array of numbers
num_bins = 20
counts, bin_edges = numpy.histogram(a, bins=num_bins, normed=True)
cdf = numpy.cumsum(counts)
pylab.plot(bin_edges[1:], cdf)
(bin_edges[1:]
est le bord supérieur de chaque bac.)
Si vous aimez linspace
et préférez les un-liners, vous pouvez faire:
plt.plot(np.sort(a), np.linspace(0, 1, len(a), endpoint=False))
Compte tenu de mes goûts, je fais presque toujours:
# a is the data array
x = np.sort(a)
y = np.arange(len(x))/float(len(x))
plt.plot(x, y)
Ce qui fonctionne pour moi même s'il existe des valeurs de données >O(1e6)
. Si vous avez vraiment besoin d'un échantillon inférieur, je le définirais.
x = np.sort(a)[::down_sampling_step]
Edit pour répondre aux commentaires/modifier les raisons pour lesquelles j'utilise endpoint=False
ou la y
telle que définie ci-dessus. Voici quelques détails techniques.
Le CDF empirique est généralement défini officiellement comme suit:
CDF(x) = "number of samples <= x"/"number of samples"
afin de correspondre exactement à cette définition formelle, vous devez utiliser y = np.arange(1,len(x)+1)/float(len(x))
pour obtenir le code y = [1/N, 2/N ... 1]
. Cet estimateur est un estimateur non biaisé qui convergera vers le véritable CDF dans la limite d'un nombre infini d'échantillons Réf. Wikipedia. .
J'ai tendance à utiliser y = [0, 1/N, 2/N ... (N-1)/N]
car (a) il est plus facile de coder/plus idomatic, (b) mais est toujours formellement justifié car on peut toujours échanger CDF(x)
avec 1-CDF(x)
dans la preuve de convergence et (c) fonctionne avec le downsampling méthode décrite ci-dessus.
Dans certains cas particuliers, il est utile de définir
y = (arange(len(x))+0.5)/len(x)
qui est intermédiaire entre ces deux conventions. Ce qui, en fait, dit "il y a une chance _1/(2N)
d'une valeur inférieure à celle la plus basse que j'ai vue dans mon échantillon, et une chance _1/(2N)
d'une valeur supérieure à la plus grande que j'ai vue jusqu'à présent.
Cependant, pour les grands échantillons et les distributions raisonnables, la convention donnée dans le corps de la réponse est facile à écrire, est un estimateur non biaisé du CDF réel et fonctionne avec la méthodologie de sous-échantillonnage.
Vous pouvez utiliser la fonction ECDF
à partir de scikits.statsmodels library:
import numpy as np
import scikits.statsmodels as sm
import matplotlib.pyplot as plt
sample = np.random.uniform(0, 1, 50)
ecdf = sm.tools.ECDF(sample)
x = np.linspace(min(sample), max(sample))
y = ecdf(x)
plt.step(x, y)
Avec la version 0.4, scicits.statsmodels
a été renommé statsmodels
. ECDF
est maintenant situé dans le module distributions
(alors que statsmodels.tools.tools.ECDF
est amorti).
import numpy as np
import statsmodels.api as sm # recommended import according to the docs
import matplotlib.pyplot as plt
sample = np.random.uniform(0, 1, 50)
ecdf = sm.distributions.ECDF(sample)
x = np.linspace(min(sample), max(sample))
y = ecdf(x)
plt.step(x, y)
plt.show()
Avez-vous essayé l'argument cumulatif = True de pyplot.hist?
One-Liner basé sur la réponse de Dave:
plt.plot(np.sort(arr), np.linspace(0, 1, len(arr), endpoint=False))
Edit: cela a également été suggéré par hans_meine dans les commentaires.
J'ai un ajout trivial à la méthode de AFoglia, pour normaliser le CDF
n_counts,bin_edges = np.histogram(myarray,bins=11,normed=True)
cdf = np.cumsum(n_counts) # cdf not normalized, despite above
scale = 1.0/cdf[-1]
ncdf = scale * cdf
La normalisation de l'histo rend son unité entière, ce qui signifie que le fichier cdf ne sera pas normalisé. Vous devez le mesurer vous-même.
Si vous souhaitez afficher le véritable fichier ECDF réel (qui, comme l'a noté David B, est une fonction progressive augmentant de 1/n à chacun des n points de données), ma suggestion est d'écrire du code pour générer deux points de "tracé" pour chaque point de données:
a = array([...]) # your array of numbers
sorted=np.sort(a)
x2 = []
y2 = []
y = 0
for x in sorted:
x2.extend([x,x])
y2.append(y)
y += 1.0 / len(a)
y2.append(y)
plt.plot(x2,y2)
De cette façon, vous obtiendrez un tracé avec les n étapes correspondant à un fichier ECDF, ce qui est particulièrement agréable pour les ensembles de données suffisamment petits pour être visibles. De plus, il n’est pas nécessaire de faire un binning avec des histogrammes (ce qui risque d’introduire un biais dans le fichier ECDF dessiné).
Que voulez-vous faire avec le CDF? Pour le tracer, c'est un début. Vous pouvez essayer différentes valeurs, comme ceci:
from __future__ import division
import numpy as np
from scipy.stats import cumfreq
import pylab as plt
hi = 100.
a = np.arange(hi) ** 2
for nbins in ( 2, 20, 100 ):
cf = cumfreq(a, nbins) # bin values, lowerlimit, binsize, extrapoints
w = hi / nbins
x = np.linspace( w/2, hi - w/2, nbins ) # care
# print x, cf
plt.plot( x, cf[0], label=str(nbins) )
plt.legend()
plt.show()
Histogramme Répertorie diverses règles relatives au nombre de bacs, par exemple. num_bins ~ sqrt( len(a) )
.
(Fine print: deux choses très différentes se passent ici,
plot
interpole une courbe régulière à travers les 20 valeurs regroupées, par exemple.L'une ou l'autre de ces solutions peut aller très loin avec des données "volumineuses" Ou a de longues queues, même pour des données 1d - 2d, les données 3D deviennent de plus en plus difficiles.
Voir aussi Density_estimation And en utilisant l’estimation de la densité du noyau gaussien scipy ).
Ceci utilise bokeh
`` `
from bokeh.plotting import figure, show
from statsmodels.distributions.empirical_distribution import ECDF
ecdf = ECDF(pd_series)
p = figure(title="tests", tools="save", background_fill_color="#E8DDCB")
p.line(ecdf.x,ecdf.y)
show(p)
`` `
Nous pouvons simplement utiliser la fonction step
de matplotlib
, qui fait un tracé pas à pas, qui est la définition du CDF empirique:
import numpy as np
from matplotlib import pyplot as plt
data = np.random.randn(11)
levels = np.linspace(0, 1, len(data) + 1) # endpoint 1 is included by default
plt.step(sorted(list(data) + [max(data)]), levels)
La ligne verticale finale à max(data)
a été ajoutée manuellement. Sinon, l'intrigue s'arrête juste au niveau 1 - 1/len(data)
.
Sinon, nous pouvons utiliser l'option where='post'
pour step()
levels = np.linspace(1. / len(data), 1, len(data))
plt.step(sorted(data), levels, where='post')
auquel cas la ligne verticale initiale à partir de zéro n'est pas tracée.
C'est un one-line dans Seaborn utilisant le paramètre cumulatif = True. Voici,
import seaborn as sns
sns.kdeplot(a, cumulative=True)
(Ceci est une copie de ma réponse à la question: Tracé en CDF d’une série de pandas en python )
Un tracé de fonction de distribution CDF ou cumulative est essentiellement un graphique avec sur l’axe X les valeurs triées et sur l’axe Y la distribution cumulée. Donc, je créerais une nouvelle série avec les valeurs triées en tant qu'index et la distribution cumulative en tant que valeurs.
Commencez par créer un exemple de série:
import pandas as pd
import numpy as np
ser = pd.Series(np.random.normal(size=100))
Triez les séries:
ser = ser.order()
Maintenant, avant de continuer, ajoutez à nouveau la dernière (et la plus grande) valeur. Cette étape est importante, en particulier pour les petits échantillons, afin d’obtenir un CDF non biaisé:
ser[len(ser)] = ser.iloc[-1]
Créer une nouvelle série avec les valeurs triées comme index et la distribution cumulative comme valeurs
cum_dist = np.linspace(0.,1.,len(ser))
ser_cdf = pd.Series(cum_dist, index=ser)
Enfin, tracez la fonction sous forme d'étapes:
ser_cdf.plot(drawstyle='steps')
En supposant que vals conserve vos valeurs, vous pouvez simplement tracer le CDF comme suit:
y = numpy.arange(0, 101)
x = numpy.percentile(vals, y)
plot(x, y)
Pour le mettre à l'échelle entre 0 et 1, il suffit de diviser y par 100.
À mon avis, aucune des méthodes précédentes ne fait le travail complet (et strict) de tracer le CDF empirique, ce qui était la question initiale du demandeur. Je poste ma proposition pour toute âme perdue et sympathique.
Ma proposition est la suivante: 1) elle considère le CDF empirique défini comme dans la première expression ici , c'est-à-dire, comme dans AW statistiques asymptotiques de AW Van der Waart, elle montre explicitement le comportement pas à pas de la fonction, 3) il montre explicitement que le CDF empirique est continu à partir de la droite en montrant les marques pour résoudre les discontinuités, 4) il étend les valeurs zéro et un aux extrêmes jusqu'aux marges définies par l'utilisateur. J'espère que ça aide quelqu'un: -D.
def plot_cdf( data, xaxis = None, figsize = (20,10), line_style = 'b-',
ball_style = 'bo', xlabel = r"Random variable $X$", ylabel = "$N$-samples
empirical CDF $F_{X,N}(x)$" ):
# Contribution of each data point to the empirical distribution
weights = 1/data.size * np.ones_like( data )
# CDF estimation
cdf = np.cumsum( weights )
# Plot central part of the CDF
plt.figure( figsize = (20,10) )
plt.step( np.sort( a ), cdf, line_style, where = 'post' )
# Plot valid points at discontinuities
plt.plot( np.sort( a ), cdf, ball_style )
# Extract plot axis and extend outside the data range
if not xaxis == None:
(xmin, xmax, ymin, ymax) = plt.axis( )
xmin = xaxis[0]
xmax = xaxis[1]
plt.axes( [xmin, xmax, ymin, ymax] )
else:
(xmin,xmax,_,_) = plt.axis()
plt.plot( [xmin, a.min(), a.min()], np.zeros( 3 ), line_style )
plt.plot( [a.max(), xmax], np.ones( 2 ), line_style )
plt.xlabel( xlabel )
plt.ylabel( ylabel )
Aucune des réponses à ce jour ne couvre ce que je voulais quand j'ai atterri ici, à savoir:
def empirical_cdf(x, data):
"evaluate ecdf of data at points x"
return np.mean(data[None, :] <= x[:, None], axis=1)
Il évalue le CDF empirique d'un ensemble de données donné sur un tableau de points x, qui ne doivent pas être triés. Il n'y a pas de binning intermédiaire ni de bibliothèques externes.
Une méthode équivalente qui s'adapte mieux aux grands x consiste à trier les données et à utiliser np.searchsorted:
def empirical_cdf(x, data):
"evaluate ecdf of data at points x"
data = np.sort(data)
return np.searchsorted(data, x)/float(data.size)