Wie kann ich die empirische CDF eines Zahlenfeldes in matplotlib in Python zeichnen? Ich suche nach dem cdf-Analogon der "hist" -Funktion von Pylab.
Eines fällt mir ein:
from scipy.stats import cumfreq
a = array([...]) # my array of numbers
num_bins = 20
b = cumfreq(a, num_bins)
plt.plot(b)
Ist das richtig? Gibt es einen einfacheren/besseren Weg?
vielen Dank.
Das scheint (fast) genau das zu sein, was Sie wollen. Zwei Dinge:
Erstens sind die Ergebnisse ein Tupel von vier Elementen. Der dritte ist die Größe der Behälter. Der zweite ist der Startpunkt des kleinsten Behälters. Der erste Wert ist die Anzahl der Punkte in oder unter jeder Ablage. (Der letzte Punkt ist die Anzahl der Punkte außerhalb der Grenzen. Da Sie jedoch noch keine Punkte festgelegt haben, werden alle Punkte zusammengefasst.)
Zweitens sollten Sie die Ergebnisse neu skalieren, sodass der Endwert 1 ist, um den üblichen Konventionen einer CDF zu folgen, aber ansonsten ist es richtig.
Das macht es unter der Haube:
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
Es führt die Histogrammierung durch und erzeugt dann eine kumulierte Summe der Zählungen in jedem Fach. Der i-te Wert des Ergebnisses ist also die Anzahl der Feldwerte, die kleiner oder gleich dem Maximum des i-ten Bin ist. Der endgültige Wert ist also nur die Größe des ursprünglichen Arrays.
Zum Plotten müssen Sie schließlich den Anfangswert der Bin und die Bin-Größe verwenden, um zu bestimmen, welche X-Achsen-Werte Sie benötigen.
Eine andere Option ist die Verwendung von numpy.histogram
, der die Normalisierung durchführen kann und die Bin-Kanten zurückgibt. Sie müssen die kumulierte Summe der resultierenden Zählungen selbst vornehmen.
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:]
ist der obere Rand jedes Fachs.)
Wenn Sie linspace
mögen und Einzeiler bevorzugen, können Sie Folgendes tun:
plt.plot(np.sort(a), np.linspace(0, 1, len(a), endpoint=False))
Bei meinem Geschmack mache ich fast immer:
# a is the data array
x = np.sort(a)
y = np.arange(len(x))/float(len(x))
plt.plot(x, y)
Was für mich auch dann funktioniert, wenn es >O(1e6)
Datenwerte gibt . Wenn Sie wirklich ein Down Sample benötigen, würde ich das einstellen
x = np.sort(a)[::down_sampling_step]
Bearbeiten, um auf Kommentar/Bearbeitung zu antworten, warum ich endpoint=False
oder die y
wie oben definiert verwende. Nachfolgend einige technische Details.
Die empirische CDF wird normalerweise formal als definiert
CDF(x) = "number of samples <= x"/"number of samples"
um genau dieser formalen Definition zu entsprechen, müssen Sie y = np.arange(1,len(x)+1)/float(len(x))
verwenden, damit wir y = [1/N, 2/N ... 1]
erhalten. Bei diesem Schätzer handelt es sich um einen unverzerrten Schätzer, der sich im Grenzbereich der unendlichen Stichproben Wikipedia-Referenz mit der wahren CDF konvergiert.
Ich neige dazu, y = [0, 1/N, 2/N ... (N-1)/N]
zu verwenden, da (a) es einfacher ist,/idomatisch zu codieren, (b) aber formal immer noch gerechtfertigt ist, da man CDF(x)
immer mit 1-CDF(x)
im Konvergenznachweis austauschen kann und (c) mit dem (einfachen) Downsampling arbeitet oben beschriebene Methode.
In einigen besonderen Fällen ist es nützlich zu definieren
y = (arange(len(x))+0.5)/len(x)
was zwischen diesen beiden Konventionen ist. Im Ergebnis heißt das: "Es besteht eine 1/(2N)
-Wahrscheinlichkeit eines niedrigeren Werts als der niedrigste, die ich in meinem Beispiel gesehen habe, und eine 1/(2N)
-Wahrscheinlichkeit eines höheren Wertes als der bisher größte.
Für große Stichproben und vernünftige Verteilungen ist die im Hauptteil der Antwort angegebene Konvention jedoch leicht zu schreiben, ein unparteiischer Schätzer der wahren CDF und arbeitet mit der Downsampling-Methode.
Sie können die Funktion ECDF
aus der Bibliothek scikits.statsmodels verwenden:
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)
Mit Version 0.4 wurde scicits.statsmodels
in statsmodels
umbenannt. ECDF
befindet sich jetzt im Modul distributions
(während statsmodels.tools.tools.ECDF
abgeschrieben wird).
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()
Haben Sie das Argument kumulativ = wahr in pyplot.hist ausprobiert?
One-Liner basierend auf Daves Antwort:
plt.plot(np.sort(arr), np.linspace(0, 1, len(arr), endpoint=False))
Edit: Dies wurde auch von hans_meine in den Kommentaren vorgeschlagen.
Ich habe eine triviale Ergänzung der AFoglia-Methode, um die CDF zu normalisieren
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
Durch die Normalisierung des Histo wird seine IntegralEinheit gebildet, was bedeutet, dass der Cdf nicht normalisiert wird. Du musst es selbst skalieren.
Wenn Sie das tatsächliche echte ECDF anzeigen möchten (was, wie David B bemerkt hat, eine Schrittfunktion ist, die um 1/n an jedem der n Datenpunkte steigt), empfehle ich, Code zu schreiben, um zwei "Plot" -Punkte für jeden Datenpunkt zu erzeugen:
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)
Auf diese Weise erhalten Sie eine Darstellung mit den n Schritten, die für ein ECDF charakteristisch sind. Dies ist insbesondere für Datensätze, die klein genug sind, um die Schritte sichtbar zu machen, Nizza. Es besteht auch keine Notwendigkeit, mit Histogrammen (mit der Gefahr einer Verzerrung des gezogenen ECDF) Binning zu beginnen.
Was möchten Sie mit der CDF machen? Um es zu planen, ist das ein Anfang. Sie können ein paar verschiedene Werte ausprobieren:
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()
Histogramm listet verschiedene Regeln für die Anzahl der Fächer auf, z. num_bins ~ sqrt( len(a) )
.
(Kleingedrucktes: hier laufen zwei ganz verschiedene Dinge ab,
plot
interpoliert eine glatte Kurve durch die etwa 20 Binärwerte.Beides kann bei Daten, die "klumpig" sind oder lange Schwänze haben, ausfallen, selbst bei 1D-Daten - 2D-3D-Daten werden immer schwieriger.
Siehe auch Density_estimation und nter Verwendung der Dichteschätzung des scipy-Gaußschen Kernels ).
Dies verwendet 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)
`` `
Wir können einfach die step
-Funktion von matplotlib
verwenden, die eine schrittweise Darstellung erzeugt, die die Definition der empirischen CDF darstellt:
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)
Die letzte vertikale Linie bei max(data)
wurde manuell hinzugefügt. Ansonsten stoppt der Plot nur auf dem Level 1 - 1/len(data)
.
Alternativ können wir die where='post'
-Option verwenden, um step()
levels = np.linspace(1. / len(data), 1, len(data))
plt.step(sorted(data), levels, where='post')
in diesem Fall wird die anfängliche vertikale Linie von Null nicht gezeichnet.
Es ist ein Einzeiler in Seaborn mit dem kumulativen Parameter = True. Bitte schön,
import seaborn as sns
sns.kdeplot(a, cumulative=True)
(Dies ist eine Kopie meiner Antwort auf die Frage: CDF einer Pandaserie in Python plotten )
Ein CDF- oder kumulatives Verteilungsfunktionsdiagramm ist im Wesentlichen ein Diagramm mit auf der X-Achse sortierten Werten und auf der Y-Achse der kumulativen Verteilung. Ich würde also eine neue Serie mit den sortierten Werten als Index und der kumulativen Verteilung als Werte erstellen.
Erstellen Sie zuerst eine Beispielserie:
import pandas as pd
import numpy as np
ser = pd.Series(np.random.normal(size=100))
Sortieren Sie die Serie:
ser = ser.order()
Bevor Sie fortfahren, fügen Sie den letzten (und größten) Wert erneut hinzu. Dieser Schritt ist besonders für kleine Stichprobengrößen wichtig, um einen unverfälschten CDF zu erhalten:
ser[len(ser)] = ser.iloc[-1]
Erstellen Sie eine neue Serie mit den sortierten Werten als Index und der kumulativen Verteilung als Werte
cum_dist = np.linspace(0.,1.,len(ser))
ser_cdf = pd.Series(cum_dist, index=ser)
Zum Schluss stellen Sie die Funktion als Schritte auf:
ser_cdf.plot(drawstyle='steps')
Wenn Sie annehmen, dass vals Ihre Werte enthält, können Sie die CDF einfach wie folgt zeichnen:
y = numpy.arange(0, 101)
x = numpy.percentile(vals, y)
plot(x, y)
Um es zwischen 0 und 1 zu skalieren, dividieren Sie einfach y durch 100.
Meines Erachtens erfüllt keine der bisherigen Methoden die vollständige (und strenge) Aufgabe, den empirischen CDF zu zeichnen, was die ursprüngliche Frage des Fragestellers war. Ich poste meinen Vorschlag für alle verlorenen und sympathischen Seelen.
Mein Vorschlag hat folgendes: 1) er betrachtet die empirische CDF als definiert im ersten Ausdruck hier , dh wie in AW Van der Waarts Asymptotic statistics (1998), 2) wird dies explizit gezeigt das schrittweise Verhalten der Funktion, 3) zeigt explizit, dass die empirische CDF von rechts kontinuierlich ist, indem sie Markierungen zeigt, um Diskontinuitäten aufzulösen, 4) die Null- und Eins-Werte an den Extremen bis zu benutzerdefinierten Rändern verlängert. Ich hoffe es hilft jemandem: -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 )
Keine der bisherigen Antworten deckt das ab, was ich wollte, als ich hier gelandet bin.
def empirical_cdf(x, data):
"evaluate ecdf of data at points x"
return np.mean(data[None, :] <= x[:, None], axis=1)
Es wertet die empirische CDF eines gegebenen Datensatzes an einem Array von Punkten x aus, die nicht sortiert werden müssen. Es gibt kein Binning und keine externen Bibliotheken.
Eine äquivalente Methode, die besser für große x skaliert werden kann, ist das Sortieren der Daten und die Verwendung von 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)