web-dev-qa-db-fra.com

Tracé en CDF d'une série de pandas en python

Y a-t-il un moyen de faire cela? Je ne peux pas sembler être un moyen facile d’interfacer une série de pandas pour tracer un CDF. 

27
wolfsatthedoor

Je crois que la fonctionnalité que vous recherchez se trouve dans la méthode hist d'un objet Series qui enveloppe la fonction hist () dans matplotlib

Voici la documentation pertinente

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 : True
    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.

...

Par exemple

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()
53
Dan Frank

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

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éez une nouvelle série avec les valeurs triées sous forme d'index et la distribution cumulative sous forme de 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')
12
kadee

C'est le moyen le plus simple. 

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

Image de l'histogramme cumulé

8
wroscoe

Je suis venu chercher un terrain comme celui-ci avec des barres et une ligne CDF: enter image description here

Cela peut être réalisé comme ceci:

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

Si vous voulez supprimer la ligne verticale, alors il est expliqué comment accomplir cela ici . Ou vous pouvez simplement faire:

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

J'ai aussi vu une solution élégante ici sur la façon de le faire avec seaborn.

5
tommy.carstensen

Pour moi, cela semblait être un moyen simple de le faire:

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))
2
annon

Dans le cas où vous êtes également intéressé par les valeurs, pas seulement l'intrigue.

import pandas as pd

# If you are in jupyter
%matplotlib inline

Cela fonctionnera toujours (distributions discrètes et continues)

# 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

 enter image description here

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

 enter image description here

Exemple alternatif avec un échantillon tiré d'une distribution continue ou vous avez beaucoup de valeurs individuelles:

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

 enter image description here

Pour les distributions continues uniquement

Veuillez noter que s'il est très raisonnable de supposer qu'il n'y a qu'une occurrence de chaque valeur dans l'échantillon (généralement rencontrée dans le cas de distributions continues), la fonction groupby() + agg('count') n'est pas nécessaire 1).

Dans ce cas, un pourcentage peut être utilisé pour accéder directement à la cdf.

Utilisez votre meilleur jugement lorsque vous prenez ce genre de raccourci! :)

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

 enter image description here

0
Raphvanns

J'ai trouvé une autre solution dans les pandas "purs", qui n'exigent pas de spécifier le nombre de bacs à utiliser dans un histogramme:

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()
0
jk.