it-swarm.com.de

Wie zeichnet man empirisches Cdf in Matplotlib in Python auf?

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. 

55
user248237dfsf

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

16
AFoglia

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.

77
Dave

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

Haben Sie das Argument kumulativ = wahr in pyplot.hist ausprobiert?

15
Andrej Panjkov

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.

6
1''

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.

3
Pete

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.

3
drjoga

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,

  • binning/Histogramming der Rohdaten
  • 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 ).

3
denis

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)

`` `

2
sushmit

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. 

2
jolvi

Es ist ein Einzeiler in Seaborn mit dem kumulativen Parameter = True. Bitte schön,

import seaborn as sns
sns.kdeplot(a, cumulative=True)
1
dohmatob

(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')
1
kadee

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.

1
user1966078

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

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)