CDF plotten van een reeks panda’s in python

Is er een manier om dit te doen? Ik kan geen gemakkelijke manier lijken om panda’s-series te koppelen aan het plotten van een CDF.


Antwoord 1, autoriteit 100%

Ik geloof dat de functionaliteit die u zoekt in de hist-methode van een Series-object zit dat de hist()-functie in matplotlib omhult

Hier is de relevante documentatie

In [10]: import matplotlib.pyplot as plt
In [11]: plt.hist?
...
Plot a histogram.
Compute and draw the histogram of *x*. The return value is a
tuple (*n*, *bins*, *patches*) or ([*n0*, *n1*, ...], *bins*,
[*patches0*, *patches1*,...]) if the input contains multiple
data.
...
cumulative : boolean, optional, default : False
    If `True`, then a histogram is computed where each bin gives the
    counts in that bin plus all bins for smaller values. The last bin
    gives the total number of datapoints.  If `normed` is also `True`
    then the histogram is normalized such that the last bin equals 1.
    If `cumulative` evaluates to less than 0 (e.g., -1), the direction
    of accumulation is reversed.  In this case, if `normed` is also
    `True`, then the histogram is normalized such that the first bin
    equals 1.
...

Bijvoorbeeld

In [12]: import pandas as pd
In [13]: import numpy as np
In [14]: ser = pd.Series(np.random.normal(size=1000))
In [15]: ser.hist(cumulative=True, density=1, bins=100)
Out[15]: <matplotlib.axes.AxesSubplot at 0x11469a590>
In [16]: plt.show()

Antwoord 2, autoriteit 39%

Als u ook geïnteresseerd bent in de waarden, niet alleen in de plot.

import pandas as pd
# If you are in jupyter
%matplotlib inline

Dit werkt altijd (discrete en continue distributies)

# Define your series
s = pd.Series([9, 5, 3, 5, 5, 4, 6, 5, 5, 8, 7], name = 'value')
df = pd.DataFrame(s)
# Get the frequency, PDF and CDF for each value in the series
# Frequency
stats_df = df \
.groupby('value') \
['value'] \
.agg('count') \
.pipe(pd.DataFrame) \
.rename(columns = {'value': 'frequency'})
# PDF
stats_df['pdf'] = stats_df['frequency'] / sum(stats_df['frequency'])
# CDF
stats_df['cdf'] = stats_df['pdf'].cumsum()
stats_df = stats_df.reset_index()
stats_df

voer hier de afbeeldingsbeschrijving in

# Plot the discrete Probability Mass Function and CDF.
# Technically, the 'pdf label in the legend and the table the should be 'pmf'
# (Probability Mass Function) since the distribution is discrete.
# If you don't have too many values / usually discrete case
stats_df.plot.bar(x = 'value', y = ['pdf', 'cdf'], grid = True)

voer hier de afbeeldingsbeschrijving in

Alternatief voorbeeld met een steekproef getrokken uit een continue verdeling of je hebt veel individuele waarden:

# Define your series
s = pd.Series(np.random.normal(loc = 10, scale = 0.1, size = 1000), name = 'value')
# ... all the same calculation stuff to get the frequency, PDF, CDF
# Plot
stats_df.plot(x = 'value', y = ['pdf', 'cdf'], grid = True)

voer hier de afbeeldingsbeschrijving in

Alleen voor continue distributies

Let op als het heel redelijk is om aan te nemen dat elke waarde in de steekproef slechts één keer voorkomt(meestal aangetroffen in het geval van continue distributies), dan is de groupby()+ agg('count')is niet nodig (aangezien de telling altijd 1) is.

In dit geval kan een procentrang worden gebruikt om rechtstreeks naar de cdf te gaan.

Gebruik je gezond verstand bij het nemen van dit soort snelkoppelingen! 🙂

# Define your series
s = pd.Series(np.random.normal(loc = 10, scale = 0.1, size = 1000), name = 'value')
df = pd.DataFrame(s)
# Get to the CDF directly
df['cdf'] = df.rank(method = 'average', pct = True)
# Sort and plot
df.sort_values('value').plot(x = 'value', y = 'cdf', grid = True)

voer hier de afbeeldingsbeschrijving in


Antwoord 3, autoriteit 17%

Een CDF- of cumulatieve verdelingsfunctieplot is in feite een grafiek met op de X-as de gesorteerde waarden en op de Y-as de cumulatieve verdeling. Dus ik zou een nieuwe reeks maken met de gesorteerde waarden als index en de cumulatieve verdeling als waarden.

Maak eerst een voorbeeldreeks:

import pandas as pd
import numpy as np
ser = pd.Series(np.random.normal(size=100))

Sorteer de serie:

ser = ser.sort_values()

Voeg nu, voordat u verder gaat, opnieuw de laatste (en grootste) waarde toe. Deze stap is vooral belangrijk voor kleine steekproefomvang om een ​​onbevooroordeelde CDF te krijgen:

ser[len(ser)] = ser.iloc[-1]

Maak een nieuwe reeks met de gesorteerde waarden als index en de cumulatieve verdeling als waarden:

cum_dist = np.linspace(0.,1.,len(ser))
ser_cdf = pd.Series(cum_dist, index=ser)

Tot slot, plot de functie als stappen:

ser_cdf.plot(drawstyle='steps')

Antwoord 4, autoriteit 12%

Dit is de gemakkelijkste manier.

import pandas as pd
df = pd.Series([i for i in range(100)])
df.hist( cumulative = True )

Afbeelding van cumulatief histogram


Antwoord 5, autoriteit 12%

Ik kwam hier op zoek naar een plot als dit met balken eneen CDF-regel:
voer hier de afbeeldingsbeschrijving in

Het kan als volgt worden bereikt:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
series = pd.Series(np.random.normal(size=10000))
fig, ax = plt.subplots()
ax2 = ax.twinx()
n, bins, patches = ax.hist(series, bins=100, normed=False)
n, bins, patches = ax2.hist(
    series, cumulative=1, histtype='step', bins=100, color='tab:orange')
plt.savefig('test.png')

Als je de verticale lijn wilt verwijderen, wordt hieruitgelegd hoe je dat doet. Of je kunt gewoon doen:

ax.set_xlim((ax.get_xlim()[0], series.max()))

Ik zag hierook een elegante oplossing over hoe je dit kunt doen met seaborn.


Antwoord 6, autoriteit 3%

Ik heb een andere oplossing gevonden in “pure” Panda’s, waarvoor het niet nodig is om het aantal te gebruiken bakken in een histogram op te geven:

import pandas as pd
import numpy as np # used only to create example data
series = pd.Series(np.random.normal(size=10000))
cdf = series.value_counts().sort_index().cumsum()
cdf.plot()

Antwoord 7, autoriteit 2%

Voor mij leek dit een eenvoudige manier om het te doen:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
heights = pd.Series(np.random.normal(size=100))
# empirical CDF
def F(x,data):
    return float(len(data[data <= x]))/len(data)
vF = np.vectorize(F, excluded=['data'])
plt.plot(np.sort(heights),vF(x=np.sort(heights), data=heights))

Antwoord 8

Als u een “echte” empirische CDF wilt plotten, die precies springt naar de waarden van uw dataset a, en met de sprong bij elke waarde evenredig met de frequentie van de waarde , NumPy heeft ingebouwde functies om het werk te doen:

import matplotlib.pyplot as plt
import numpy as np
def ecdf(a):
    x, counts = np.unique(a, return_counts=True)
    y = np.cumsum(counts)
    x = np.insert(x, 0, x[0])
    y = np.insert(y/y[-1], 0, 0.)
    plt.plot(x, y, drawstyle='steps-post')
    plt.grid(True)
    plt.savefig('ecdf.png')

De aanroep van unique()retourneert de gegevenswaarden in gesorteerde volgorde samen met hun corresponderende frequenties. De optie drawstyle='steps-post'in de plot()-aanroep zorgt ervoor dat de sprongen plaatsvinden waar ze zouden moeten. Om een ​​sprong te forceren bij de kleinste gegevenswaarde, voegt de code een extra element in vóór xen y.

Voorbeeld van gebruik:

xvec = np.array([7,1,2,2,7,4,4,4,5.5,7])
ecdf(xvec)

Een ander gebruik:

df = pd.DataFrame({'x':[7,1,2,2,7,4,4,4,5.5,7]})
ecdf(df['x'])

met uitvoer:

voer hier de afbeeldingsbeschrijving in

Other episodes