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.
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()
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')
C'est le moyen le plus simple.
import pandas as pd
df = pd.Series([i for i in range(100)])
df.hist( cumulative = True )
Je suis venu chercher un terrain comme celui-ci avec des barres et une ligne CDF:
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
.
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))
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
# 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
# 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)
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)
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)
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()