Есть ли способ сделать это? Я не могу показаться простым способом для интерфейса pandas с построением CDF.
Построение CDF серии pandas в python
Ответ 1
Я полагаю, что функциональность, которую вы ищете, заключается в методе Hist объекта Series, который оборачивает функцию hist() в matplotlib.
Здесь соответствующая документация
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.
...
Например
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()
Ответ 2
График функции CDF или кумулятивного распределения в основном представляет собой график, по оси X - отсортированные значения, а по оси Y - кумулятивное распределение. Итак, я бы создал новую серию с отсортированными значениями как индекс и кумулятивное распределение как значения.
Сначала создайте примерную серию:
import pandas as pd
import numpy as np
ser = pd.Series(np.random.normal(size=100))
Сортировка серии:
ser = ser.sort_values()
Теперь, прежде чем продолжить, добавьте снова последнее (и самое большое) значение. Этот шаг особенно важен для небольших размеров выборки, чтобы получить непредвзятый CDF:
ser[len(ser)] = ser.iloc[-1]
Создайте новую серию с отсортированными значениями как индекс и кумулятивное распределение как значения:
cum_dist = np.linspace(0.,1.,len(ser))
ser_cdf = pd.Series(cum_dist, index=ser)
Наконец, постройте функцию как шаги:
ser_cdf.plot(drawstyle='steps')
Ответ 3
Это самый простой способ.
import pandas as pd
df = pd.Series([i for i in range(100)])
df.hist( cumulative = True )
Ответ 4
Я пришел сюда в поисках такого сюжета с барами и линией CDF:
Это может быть достигнуто так:
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')
Если вы хотите, чтобы удалить вертикальную линию, то он объяснил, как добиться этого здесь. Или вы можете просто сделать:
ax.set_xlim((ax.get_xlim()[0], series.max()))
Я также видел элегантное решение здесь о том, как сделать это с seaborn
.
Ответ 5
Для меня это казалось простым способом:
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))
Ответ 6
Я нашел другое решение в "чистых" пандах, которое не требует указания количества бинов для использования в гистограмме:
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()
Ответ 7
Если вас также интересуют значения, а не только сюжет.
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)
Альтернативный пример с образцом, взятым из непрерывного распределения, или у вас есть много отдельных значений:
# 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)
Только для непрерывных распределений
Пожалуйста, обратите внимание, если очень разумно сделать предположение, что в выборке имеется только один случай каждого значения (обычно встречается в случае непрерывных распределений), тогда groupby()
+ agg('count')
не требуется (так как количество всегда 1).
В этом случае процентный ранг может быть использован для прямого доступа к cdf.
При выборе ярлыка используйте свое суждение! :)
# 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)