Spec-Zone.ru › pandas 0.19

Инструменты вычислений

Статистические функции

Процентное изменение

Series, DataFrame, и Panel все имеют метод pct_change, чтобы вычислить процентное изменение за заданное количество периодов (используя fill_method для заполнения значений NA/null до вычисления процентного изменения).

In [1]: ser = pd.Series(np.random.randn(8))

In [2]: ser.pct_change()
Out[2]: 
0         NaN
1   -1.602976
2    4.334938
3   -0.247456
4   -2.067345
5   -1.142903
6   -1.688214
7   -9.759729
dtype: float64
In [3]: df = pd.DataFrame(np.random.randn(10, 4))

In [4]: df.pct_change(periods=3)
Out[4]: 
          0         1         2         3
0       NaN       NaN       NaN       NaN
1       NaN       NaN       NaN       NaN
2       NaN       NaN       NaN       NaN
3 -0.218320 -1.054001  1.987147 -0.510183
4 -0.439121 -1.816454  0.649715 -4.822809
5 -0.127833 -3.042065 -5.866604 -1.776977
6 -2.596833 -1.959538 -2.111697 -3.798900
7 -0.117826 -2.169058  0.036094 -0.067696
8  2.492606 -1.357320 -1.205802 -1.558697
9 -1.012977  2.324558 -1.003744 -0.371806

Ковариация

Объект Series имеет метод cov для вычисления ковариации между рядами (исключая значения NA/null).

In [5]: s1 = pd.Series(np.random.randn(1000))

In [6]: s2 = pd.Series(np.random.randn(1000))

In [7]: s1.cov(s2)
Out[7]: 0.00068010881743108746

Аналогично, DataFrame имеет метод cov для вычисления парных ковариаций между рядами в DataFrame, также исключая значения NA/null.

Примечание

При предположении, что пропущенные данные пропущены случайным образом, это приводит к оценке ковариационной матрицы, которая является несмещенной. Однако для многих применений эта оценка может быть неприемлемой, потому что оценка ковариационной матрицы не гарантирует, что она будет положительно полуопределённой. Это может привести к оценкам корреляций, у которых абсолютные значения превышают единицу, и/или к необратимой ковариационной матрице. Подробнее см. Оценку ковариационных матриц.

In [8]: frame = pd.DataFrame(np.random.randn(1000, 5), columns=['a', 'b', 'c', 'd', 'e'])

In [9]: frame.cov()
Out[9]: 
          a         b         c         d         e
a  1.000882 -0.003177 -0.002698 -0.006889  0.031912
b -0.003177  1.024721  0.000191  0.009212  0.000857
c -0.002698  0.000191  0.950735 -0.031743 -0.005087
d -0.006889  0.009212 -0.031743  1.002983 -0.047952
e  0.031912  0.000857 -0.005087 -0.047952  1.042487

DataFrame.cov также поддерживает необязательное ключевое слово min_periods, которое указывает минимальное требуемое количество наблюдений для каждой пары столбцов, чтобы получить действительный результат.

In [10]: frame = pd.DataFrame(np.random.randn(20, 3), columns=['a', 'b', 'c'])

In [11]: frame.ix[:5, 'a'] = np.nan

In [12]: frame.ix[5:10, 'b'] = np.nan

In [13]: frame.cov()
Out[13]: 
          a         b         c
a  1.210090 -0.430629  0.018002
b -0.430629  1.240960  0.347188
c  0.018002  0.347188  1.301149

In [14]: frame.cov(min_periods=12)
Out[14]: 
          a         b         c
a  1.210090       NaN  0.018002
b       NaN  1.240960  0.347188
c  0.018002  0.347188  1.301149

Корреляция

Предоставлены несколько методов для вычисления корреляций:

Название метода Описание
pearson (default) Стандартный коэффициент корреляции
kendall Коэффициент корреляции Кендалла Тау
spearman Ранговый коэффициент корреляции Спирмена

Все эти значения в настоящее время вычисляются с использованием парных полных наблюдений.

Примечание

Пожалуйста, обратитесь к примечаниям, связанным с этим методом вычисления корреляционных матриц, в разделе ковариации.

In [15]: frame = pd.DataFrame(np.random.randn(1000, 5), columns=['a', 'b', 'c', 'd', 'e'])

In [16]: frame.ix[::2] = np.nan

# Series with Series
In [17]: frame['a'].corr(frame['b'])
Out[17]: 0.013479040400098775

In [18]: frame['a'].corr(frame['b'], method='spearman')
Out[18]: -0.0072898851595406371

# Pairwise correlation of DataFrame columns
In [19]: frame.corr()
Out[19]: 
          a         b         c         d         e
a  1.000000  0.013479 -0.049269 -0.042239 -0.028525
b  0.013479  1.000000 -0.020433 -0.011139  0.005654
c -0.049269 -0.020433  1.000000  0.018587 -0.054269
d -0.042239 -0.011139  0.018587  1.000000 -0.017060
e -0.028525  0.005654 -0.054269 -0.017060  1.000000

Обратите внимание, что нечисловые столбцы будут автоматически исключены из расчёта корреляции.

Как и cov, corr также поддерживает необязательное ключевое слово min_periods.

In [20]: frame = pd.DataFrame(np.random.randn(20, 3), columns=['a', 'b', 'c'])

In [21]: frame.ix[:5, 'a'] = np.nan

In [22]: frame.ix[5:10, 'b'] = np.nan

In [23]: frame.corr()
Out[23]: 
          a         b         c
a  1.000000 -0.076520  0.160092
b -0.076520  1.000000  0.135967
c  0.160092  0.135967  1.000000

In [24]: frame.corr(min_periods=12)
Out[24]: 
          a         b         c
a  1.000000       NaN  0.160092
b       NaN  1.000000  0.135967
c  0.160092  0.135967  1.000000

Реализованный связанный метод corrwith в DataFrame для вычисления корреляции между имеющими одинаковые метки рядами, содержащимися в разных объектах DataFrame.

In [25]: index = ['a', 'b', 'c', 'd', 'e']

In [26]: columns = ['one', 'two', 'three', 'four']

In [27]: df1 = pd.DataFrame(np.random.randn(5, 4), index=index, columns=columns)

In [28]: df2 = pd.DataFrame(np.random.randn(4, 4), index=index[:4], columns=columns)

In [29]: df1.corrwith(df2)
Out[29]: 
one     -0.125501
two     -0.493244
three    0.344056
four     0.004183
dtype: float64

In [30]: df2.corrwith(df1, axis=1)
Out[30]: 
a   -0.675817
b    0.458296
c    0.190809
d   -0.186275
e         NaN
dtype: float64

Ранжирование данных

Метод rank генерирует ранжирование данных, при этом ничьи получают средний ранг (по умолчанию) для группы:

In [31]: s = pd.Series(np.random.np.random.randn(5), index=list('abcde'))

In [32]: s['d'] = s['b'] # so there's a tie

In [33]: s.rank()
Out[33]: 
a    5.0
b    2.5
c    1.0
d    2.5
e    4.0
dtype: float64

rank также является методом DataFrame и может ранжировать либо строки (axis=0) либо столбцы (axis=1). Значения NaN исключаются из ранжирования.

In [34]: df = pd.DataFrame(np.random.np.random.randn(10, 6))

In [35]: df[4] = df[2][:5] # some ties

In [36]: df
Out[36]: 
          0         1         2         3         4         5
0 -0.904948 -1.163537 -1.457187  0.135463 -1.457187  0.294650
1 -0.976288 -0.244652 -0.748406 -0.999601 -0.748406 -0.800809
2  0.401965  1.460840  1.256057  1.308127  1.256057  0.876004
3  0.205954  0.369552 -0.669304  0.038378 -0.669304  1.140296
4 -0.477586 -0.730705 -1.129149 -0.601463 -1.129149 -0.211196
5 -1.092970 -0.689246  0.908114  0.204848       NaN  0.463347
6  0.376892  0.959292  0.095572 -0.593740       NaN -0.069180
7 -1.002601  1.957794 -0.120708  0.094214       NaN -1.467422
8 -0.547231  0.664402 -0.519424 -0.073254       NaN -1.263544
9 -0.250277 -0.237428 -1.056443  0.419477       NaN  1.375064

In [37]: df.rank(1)
Out[37]: 
     0    1    2    3    4    5
0  4.0  3.0  1.5  5.0  1.5  6.0
1  2.0  6.0  4.5  1.0  4.5  3.0
2  1.0  6.0  3.5  5.0  3.5  2.0
3  4.0  5.0  1.5  3.0  1.5  6.0
4  5.0  3.0  1.5  4.0  1.5  6.0
5  1.0  2.0  5.0  3.0  NaN  4.0
6  4.0  5.0  3.0  1.0  NaN  2.0
7  2.0  5.0  3.0  4.0  NaN  1.0
8  2.0  5.0  3.0  4.0  NaN  1.0
9  2.0  3.0  1.0  4.0  NaN  5.0

rank необязательно принимает параметр ascending, который по умолчанию равен true; когда false, данные ранжируются в обратном порядке, при этом более высоким значениям присваивается меньший ранг.

rank поддерживает различные методы разбиения ничьих, указанные с помощью параметра method.

  • average : средний ранг связанной группы
  • min : наименьший ранг в группе
  • max : наибольший ранг в группе
  • first : ранги присваиваются в порядке их появления в массиве

Функции окна

Предупреждение

До версии 0.18.0, pd.rolling_*, pd.expanding_*, и pd.ewm* были функциями уровня модуля и теперь устарели. Их заменили на использование объектов Rolling, Expanding и EWM, и соответствующий вызов метода.

Предупреждение об устаревании покажет новый синтаксис, см. пример здесь. Предыдущую документацию можно просмотреть здесь.

Для работы с данными предоставлено множество функций окон для вычисления распространённых статистик окна или скользящего среднего. Среди них: количество, сумма, среднее значение, медиана, корреляция, дисперсия, ковариация, стандартное отклонение, асимметрия и эксцесс.

Начиная с версии 0.18.1, функции rolling() и expanding() могут быть использованы непосредственно из объектов DataFrameGroupBy, см. документацию по группировке.

Примечание

API для статистик окон очень похож на работу с объектами GroupBy, см. документацию здесь

Мы работаем с данными rolling, expanding и exponentially weighted через соответствующие объекты Rolling, Expanding и EWM.

In [38]: s = pd.Series(np.random.randn(1000), index=pd.date_range('1/1/2000', periods=1000))

In [39]: s = s.cumsum()

In [40]: s
Out[40]: 
2000-01-01    -0.268824
2000-01-02    -1.771855
2000-01-03    -0.818003
2000-01-04    -0.659244
2000-01-05    -1.942133
2000-01-06    -1.869391
2000-01-07     0.563674
                ...    
2002-09-20   -68.233054
2002-09-21   -66.765687
2002-09-22   -67.457323
2002-09-23   -69.253182
2002-09-24   -70.296818
2002-09-25   -70.844674
2002-09-26   -72.475016
Freq: D, dtype: float64

Они создаются методами Series и DataFrame.

In [41]: r = s.rolling(window=60)

In [42]: r
Out[42]: Rolling [window=60,center=False,axis=0]

Эти объекты обеспечивают автодополнение доступных методов и свойств.

In [14]: r.
r.agg         r.apply       r.count       r.exclusions  r.max         r.median      r.name        r.skew        r.sum
r.aggregate   r.corr        r.cov         r.kurt        r.mean        r.min         r.quantile    r.std         r.var

В целом, эти методы имеют одинаковый интерфейс. Все они принимают следующие аргументы:

  • window: размер скользящего окна
  • min_periods: пороговое значение для количества ненулевых точек данных (в противном случае результат — NA)
  • center: логическое значение, устанавливает ли метки в центре (по умолчанию False)

Предупреждение

Аргументы freq и how были в API до изменений 0.18.0. Они устарели в новом API. Вы можете просто перегруппировать входные данные до создания функции окна.

Например, вместо s.rolling(window=5,freq='D').max() для получения максимального значения на 5-дневном скользящем окне можно использовать s.resample('D').max().rolling(window=5).max(), который сначала перегруппирует данные к ежедневным, затем предоставляет 5-дневное скользящее окно.

Затем мы можем вызвать методы на этих объектах rolling. Они возвращают объекты с аналогичными индексами:

In [43]: r.mean()
Out[43]: 
2000-01-01          NaN
2000-01-02          NaN
2000-01-03          NaN
2000-01-04          NaN
2000-01-05          NaN
2000-01-06          NaN
2000-01-07          NaN
                ...    
2002-09-20   -62.694135
2002-09-21   -62.812190
2002-09-22   -62.914971
2002-09-23   -63.061867
2002-09-24   -63.213876
2002-09-25   -63.375074
2002-09-26   -63.539734
Freq: D, dtype: float64
In [44]: s.plot(style='k--')
Out[44]: <matplotlib.axes._subplots.AxesSubplot at 0x7ff282080dd0>

In [45]: r.mean().plot(style='k')
Out[45]: <matplotlib.axes._subplots.AxesSubplot at 0x7ff282080dd0>
_images/rolling_mean_ex.png

Они также могут применяться к объектам DataFrame. Это просто синтаксический сахар для применения оператора скользящего окна ко всем столбцам DataFrame:

In [46]: df = pd.DataFrame(np.random.randn(1000, 4),
   ....:                   index=pd.date_range('1/1/2000', periods=1000),
   ....:                   columns=['A', 'B', 'C', 'D'])
   ....: 

In [47]: df = df.cumsum()

In [48]: df.rolling(window=60).sum().plot(subplots=True)
Out[48]: 
array([<matplotlib.axes._subplots.AxesSubplot object at 0x7ff28c067210>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7ff27e03a0d0>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7ff280bca510>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7ff28155b910>], dtype=object)
_images/rolling_mean_frame.png

Краткое описание методов

Мы предоставляем ряд общих статистических функций:

Метод Описание
count() Количество ненулевых наблюдений
sum() Сумма значений
mean() Среднее значение значений
median() Арифметическая медиана значений
min() Минимум
max() Максимум
std() Стандартное отклонение (исправленное по Бесселю)
var() Дисперсия (несмещённая)
skew() Выборка асимметрии (третий момент)
kurt() Выборка эксцесса (четвёртый момент)
quantile() Выборочный квантиль (%)
apply() Обобщённое применение
cov() Ковариация (бинарная)
corr() Корреляция (бинарная)

Функция apply() принимает дополнительный func аргумент и выполняет общие вычисления скользящего среднего. Аргумент func должен быть функцией, которая производит одно значение из входного массива ndarray. Предположим, что мы хотим вычислить среднее абсолютное отклонение на скользящей основе:

In [49]: mad = lambda x: np.fabs(x - x.mean()).mean()

In [50]: s.rolling(window=60).apply(mad).plot(style='k')
Out[50]: <matplotlib.axes._subplots.AxesSubplot at 0x7ff2846d3ad0>
_images/rolling_apply_ex.png

Скользящие окна

Передача win_type в .rolling генерирует общее вычисление скользящего окна, которое взвешено согласно win_type. Доступны следующие методы:

Метод Описание
sum() Сумма значений
mean() Среднее значение

Веса, используемые в окне, задаются ключевым словом win_type. Список распознаваемых типов:

  • boxcar
  • triang
  • blackman
  • hamming
  • bartlett
  • parzen
  • bohman
  • blackmanharris
  • nuttall
  • barthann
  • kaiser (требуется beta)
  • gaussian (требуется std)
  • general_gaussian (требуется power, width)
  • slepian (требуется width).
In [51]: ser = pd.Series(np.random.randn(10), index=pd.date_range('1/1/2000', periods=10))

In [52]: ser.rolling(window=5, win_type='triang').mean()
Out[52]: 
2000-01-01         NaN
2000-01-02         NaN
2000-01-03         NaN
2000-01-04         NaN
2000-01-05   -1.037870
2000-01-06   -0.767705
2000-01-07   -0.383197
2000-01-08   -0.395513
2000-01-09   -0.558440
2000-01-10   -0.672416
Freq: D, dtype: float64

Обратите внимание, что окно boxcar эквивалентно mean().

In [53]: ser.rolling(window=5, win_type='boxcar').mean()
Out[53]: 
2000-01-01         NaN
2000-01-02         NaN
2000-01-03         NaN
2000-01-04         NaN
2000-01-05   -0.841164
2000-01-06   -0.779948
2000-01-07   -0.565487
2000-01-08   -0.502815
2000-01-09   -0.553755
2000-01-10   -0.472211
Freq: D, dtype: float64

In [54]: ser.rolling(window=5).mean()
Out[54]: 
2000-01-01         NaN
2000-01-02         NaN
2000-01-03         NaN
2000-01-04         NaN
2000-01-05   -0.841164
2000-01-06   -0.779948
2000-01-07   -0.565487
2000-01-08   -0.502815
2000-01-09   -0.553755
2000-01-10   -0.472211
Freq: D, dtype: float64

Для некоторых функций окон требуется указать дополнительные параметры:

In [55]: ser.rolling(window=5, win_type='gaussian').mean(std=0.1)
Out[55]: 
2000-01-01         NaN
2000-01-02         NaN
2000-01-03         NaN
2000-01-04         NaN
2000-01-05   -1.309989
2000-01-06   -1.153000
2000-01-07    0.606382
2000-01-08   -0.681101
2000-01-09   -0.289724
2000-01-10   -0.996632
Freq: D, dtype: float64

Примечание

Для .sum() с win_type, весы для окна не нормируются. Передача настраиваемых весов [1, 1, 1] даст другой результат, чем передача весов [2, 2, 2], например. При передаче win_type вместо явного указания весов, веса уже нормированы так, что наибольший вес равен 1.

В отличие от этого, характер вычисления .mean() таков, что веса нормируются относительно друг друга. Веса [1, 1, 1] и [2, 2, 2] дают одинаковый результат.

Оконные данные с учетом времени

Новое в версии 0.19.0.

Нововведение в версии 0.19.0 — возможность передать сдвиг (или преобразуемое значение) в метод .rolling() и получить окна переменной длины, основанные на переданном временном окне. Для каждой временной точки это включает все предшествующие значения, которые появляются в указанном временном интервале.

Это может быть особенно полезно для индекса с нерегулярной временной частотой.

In [56]: dft = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]},
   ....:                    index=pd.date_range('20130101 09:00:00', periods=5, freq='s'))
   ....: 

In [57]: dft
Out[57]: 
                       B
2013-01-01 09:00:00  0.0
2013-01-01 09:00:01  1.0
2013-01-01 09:00:02  2.0
2013-01-01 09:00:03  NaN
2013-01-01 09:00:04  4.0

Это индекс с регулярной частотой. Использование целочисленного параметра окна работает для сдвига по частоте окна.

In [58]: dft.rolling(2).sum()
Out[58]: 
                       B
2013-01-01 09:00:00  NaN
2013-01-01 09:00:01  1.0
2013-01-01 09:00:02  3.0
2013-01-01 09:00:03  NaN
2013-01-01 09:00:04  NaN

In [59]: dft.rolling(2, min_periods=1).sum()
Out[59]: 
                       B
2013-01-01 09:00:00  0.0
2013-01-01 09:00:01  1.0
2013-01-01 09:00:02  3.0
2013-01-01 09:00:03  2.0
2013-01-01 09:00:04  4.0

Указание сдвига позволяет более интуитивно указать частоту сдвига.

In [60]: dft.rolling('2s').sum()
Out[60]: 
                       B
2013-01-01 09:00:00  0.0
2013-01-01 09:00:01  1.0
2013-01-01 09:00:02  3.0
2013-01-01 09:00:03  2.0
2013-01-01 09:00:04  4.0

Использование нерегулярного, но все же монотонного индекса, сдвига с целочисленным окном не вносит никаких специальных вычислений.

In [61]: dft = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]},
   ....:                    index = pd.Index([pd.Timestamp('20130101 09:00:00'),
   ....:                                      pd.Timestamp('20130101 09:00:02'),
   ....:                                      pd.Timestamp('20130101 09:00:03'),
   ....:                                      pd.Timestamp('20130101 09:00:05'),
   ....:                                      pd.Timestamp('20130101 09:00:06')],
   ....:                                     name='foo'))
   ....: 

In [62]: dft
Out[62]: 
                       B
foo                     
2013-01-01 09:00:00  0.0
2013-01-01 09:00:02  1.0
2013-01-01 09:00:03  2.0
2013-01-01 09:00:05  NaN
2013-01-01 09:00:06  4.0

In [63]: dft.rolling(2).sum()
Out[63]: 
                       B
foo                     
2013-01-01 09:00:00  NaN
2013-01-01 09:00:02  1.0
2013-01-01 09:00:03  3.0
2013-01-01 09:00:05  NaN
2013-01-01 09:00:06  NaN

Использование временных спецификаций генерирует переменные окна для этих разреженных данных.

In [64]: dft.rolling('2s').sum()
Out[64]: 
                       B
foo                     
2013-01-01 09:00:00  0.0
2013-01-01 09:00:02  1.0
2013-01-01 09:00:03  3.0
2013-01-01 09:00:05  NaN
2013-01-01 09:00:06  4.0

Кроме того, теперь мы разрешаем необязательный параметр on для указания столбца (вместо по умолчанию индекса) в DataFrame.

In [65]: dft = dft.reset_index()

In [66]: dft
Out[66]: 
                  foo    B
0 2013-01-01 09:00:00  0.0
1 2013-01-01 09:00:02  1.0
2 2013-01-01 09:00:03  2.0
3 2013-01-01 09:00:05  NaN
4 2013-01-01 09:00:06  4.0

In [67]: dft.rolling('2s', on='foo').sum()
Out[67]: 
                  foo    B
0 2013-01-01 09:00:00  0.0
1 2013-01-01 09:00:02  1.0
2 2013-01-01 09:00:03  3.0
3 2013-01-01 09:00:05  NaN
4 2013-01-01 09:00:06  4.0

Скользящие окна с учетом времени против ресемплирования

Использование .rolling() с временным индексом очень похоже на ресемплирование. Оба они работают и выполняют редуктивные операции с временными индексированными объектами pandas.

При использовании .rolling() со сдвигом. Сдвиг — это временная разница. Возьмите окно, направленное в прошлое, и агрегируйте все значения в этом окне (включая конечную точку, но не начальную). Это новое значение в этой точке результата. Это окна переменной длины во времени для каждой точки входных данных. Вы получите результат той же длины, что и входные данные.

При использовании .resample() со сдвигом. Постройте новый индекс, который является частотой сдвига. Для каждого интервала частоты агрегируйте точки из входных данных в окне, направленном в прошлое, которые попадают в этот интервал. Результат этой агрегации — выходные данные для этой точки частоты. Окна имеют фиксированный размер в пространстве частоты. Результат будет иметь форму регулярной частоты между мин и макс исходного объекта.

Подводя итог, .rolling() — это операция скользящего окна во времени, а .resample() — операция скользящего окна по частоте.

Центрирование окон

По умолчанию метки устанавливаются на правом краю окна, но доступно ключевое слово center для установки меток в центре.

In [68]: ser.rolling(window=5).mean()
Out[68]: 
2000-01-01         NaN
2000-01-02         NaN
2000-01-03         NaN
2000-01-04         NaN
2000-01-05   -0.841164
2000-01-06   -0.779948
2000-01-07   -0.565487
2000-01-08   -0.502815
2000-01-09   -0.553755
2000-01-10   -0.472211
Freq: D, dtype: float64

In [69]: ser.rolling(window=5, center=True).mean()
Out[69]: 
2000-01-01         NaN
2000-01-02         NaN
2000-01-03   -0.841164
2000-01-04   -0.779948
2000-01-05   -0.565487
2000-01-06   -0.502815
2000-01-07   -0.553755
2000-01-08   -0.472211
2000-01-09         NaN
2000-01-10         NaN
Freq: D, dtype: float64

Бинарные функции окна

cov() и corr() могут вычислять статистику скользящего окна относительно двух Series или любой комбинации DataFrame/Series или DataFrame/DataFrame. Вот поведение в каждом случае:

  • два Series: вычисляют статистику для пар.
  • DataFrame/Series: вычисляют статистику для каждого столбца DataFrame с переданным Series, возвращая DataFrame.
  • DataFrame/DataFrame: по умолчанию вычисляют статистику для совпадающих имен столбцов, возвращая DataFrame. Если передано ключевое слово pairwise=True, вычисляются статистические данные для каждой пары столбцов, возвращая Panel, в котором items — это даты, которые рассматриваются (см. следующий раздел).

Например:

In [70]: df2 = df[:20]

In [71]: df2.rolling(window=5).corr(df2['B'])
Out[71]: 
                   A    B         C         D
2000-01-01       NaN  NaN       NaN       NaN
2000-01-02       NaN  NaN       NaN       NaN
2000-01-03       NaN  NaN       NaN       NaN
2000-01-04       NaN  NaN       NaN       NaN
2000-01-05 -0.262853  1.0  0.334449  0.193380
2000-01-06 -0.083745  1.0 -0.521587 -0.556126
2000-01-07 -0.292940  1.0 -0.658532 -0.458128
...              ...  ...       ...       ...
2000-01-14  0.519499  1.0 -0.687277  0.192822
2000-01-15  0.048982  1.0  0.167669 -0.061463
2000-01-16  0.217190  1.0  0.167564 -0.326034
2000-01-17  0.641180  1.0 -0.164780 -0.111487
2000-01-18  0.130422  1.0  0.322833  0.632383
2000-01-19  0.317278  1.0  0.384528  0.813656
2000-01-20  0.293598  1.0  0.159538  0.742381

[20 rows x 4 columns]

Вычисление скользящих парных ковариаций и корреляций

В финансовом анализе и других областях часто необходимо вычислять матрицы ковариаций и корреляций для набора временных рядов. Нередко также интересуют матрицы скользящих ковариаций и корреляций. Это можно сделать, передав ключевой аргумент pairwise, который в случае DataFrame входных данных даст Panel, в котором items — это даты, которые рассматриваются. В случае единственного аргумента DataFrame аргумент pairwise можно даже опустить:

Примечание

Пропущенные значения игнорируются, и каждый элемент вычисляется с использованием парных полных наблюдений. Пожалуйста, обратитесь к разделу ковариация для особенностей, связанных с этим методом вычисления матриц ковариаций и корреляций.

In [72]: covs = df[['B','C','D']].rolling(window=50).cov(df[['A','B','C']], pairwise=True)

In [73]: covs[df.index[-50]]
Out[73]: 
          A         B          C
B  2.667506  1.671711   1.938634
C  8.513843  1.938634  10.556436
D -7.714737 -1.434529  -7.082653
In [74]: correls = df.rolling(window=50).corr()

In [75]: correls[df.index[-50]]
Out[75]: 
          A         B         C         D
A  1.000000  0.604221  0.767429 -0.776170
B  0.604221  1.000000  0.461484 -0.381148
C  0.767429  0.461484  1.000000 -0.748863
D -0.776170 -0.381148 -0.748863  1.000000

Эффективно получить временной ряд корреляций между двумя столбцами можно с помощью индексирования .loc.

In [76]: correls.loc[:, 'A', 'C'].plot()
Out[76]: <matplotlib.axes._subplots.AxesSubplot at 0x7ff27e0f0c50>
_images/rolling_corr_pairwise_ex.png

Агрегирование

После создания объектов Rolling, Expanding или EWM доступны несколько методов для выполнения нескольких вычислений на данных. Это очень похоже на .groupby(...).agg , показанное здесь.

In [77]: dfa = pd.DataFrame(np.random.randn(1000, 3),
   ....:                    index=pd.date_range('1/1/2000', periods=1000),
   ....:                    columns=['A', 'B', 'C'])
   ....: 

In [78]: r = dfa.rolling(window=60,min_periods=1)

In [79]: r
Out[79]: Rolling [window=60,min_periods=1,center=False,axis=0]

Мы можем агрегировать, передав функцию всему DataFrame, или выбрать Series (или несколько Series) с помощью стандартного getitem.

In [80]: r.aggregate(np.sum)
Out[80]: 
                   A          B         C
2000-01-01  0.314226  -0.001675  0.071823
2000-01-02  1.206791   0.678918 -0.267817
2000-01-03  1.421701   0.600508 -0.445482
2000-01-04  1.912539  -0.759594  1.146974
2000-01-05  2.919639  -0.061759 -0.743617
2000-01-06  2.665637   1.298392 -0.803529
2000-01-07  2.513985   1.923089 -1.928308
...              ...        ...       ...
2002-09-20  1.447669 -12.360302  2.734381
2002-09-21  1.871783 -13.896542  3.086102
2002-09-22  2.540658 -12.594402  3.162542
2002-09-23  2.974674 -12.727703  3.861005
2002-09-24  1.391366 -13.584590  3.790683
2002-09-25  2.027313 -15.083214  3.377896
2002-09-26  1.290363 -13.569459  3.809884

[1000 rows x 3 columns]

In [81]: r['A'].aggregate(np.sum)
Out[81]: 
2000-01-01    0.314226
2000-01-02    1.206791
2000-01-03    1.421701
2000-01-04    1.912539
2000-01-05    2.919639
2000-01-06    2.665637
2000-01-07    2.513985
                ...   
2002-09-20    1.447669
2002-09-21    1.871783
2002-09-22    2.540658
2002-09-23    2.974674
2002-09-24    1.391366
2002-09-25    2.027313
2002-09-26    1.290363
Freq: D, Name: A, dtype: float64

In [82]: r[['A','B']].aggregate(np.sum)
Out[82]: 
                   A          B
2000-01-01  0.314226  -0.001675
2000-01-02  1.206791   0.678918
2000-01-03  1.421701   0.600508
2000-01-04  1.912539  -0.759594
2000-01-05  2.919639  -0.061759
2000-01-06  2.665637   1.298392
2000-01-07  2.513985   1.923089
...              ...        ...
2002-09-20  1.447669 -12.360302
2002-09-21  1.871783 -13.896542
2002-09-22  2.540658 -12.594402
2002-09-23  2.974674 -12.727703
2002-09-24  1.391366 -13.584590
2002-09-25  2.027313 -15.083214
2002-09-26  1.290363 -13.569459

[1000 rows x 2 columns]

Как вы можете видеть, результат агрегации будет содержать выбранные столбцы или все столбцы, если не выбрано ничего.

Применение нескольких функций сразу

С оконными Series вы также можете передать список или словарь функций для агрегирования, выведя DataFrame:

In [83]: r['A'].agg([np.sum, np.mean, np.std])
Out[83]: 
                 sum      mean       std
2000-01-01  0.314226  0.314226       NaN
2000-01-02  1.206791  0.603396  0.408948
2000-01-03  1.421701  0.473900  0.365959
2000-01-04  1.912539  0.478135  0.298925
2000-01-05  2.919639  0.583928  0.350682
2000-01-06  2.665637  0.444273  0.464115
2000-01-07  2.513985  0.359141  0.479828
...              ...       ...       ...
2002-09-20  1.447669  0.024128  1.034827
2002-09-21  1.871783  0.031196  1.031417
2002-09-22  2.540658  0.042344  1.026341
2002-09-23  2.974674  0.049578  1.030021
2002-09-24  1.391366  0.023189  1.024793
2002-09-25  2.027313  0.033789  1.022099
2002-09-26  1.290363  0.021506  1.024751

[1000 rows x 3 columns]

Если передается словарь, ключи будут использоваться для именования столбцов. В противном случае будет использоваться имя функции (хранящееся в объекте функции).

In [84]: r['A'].agg({'result1' : np.sum,
   ....:             'result2' : np.mean})
   ....: 
Out[84]: 
             result2   result1
2000-01-01  0.314226  0.314226
2000-01-02  0.603396  1.206791
2000-01-03  0.473900  1.421701
2000-01-04  0.478135  1.912539
2000-01-05  0.583928  2.919639
2000-01-06  0.444273  2.665637
2000-01-07  0.359141  2.513985
...              ...       ...
2002-09-20  0.024128  1.447669
2002-09-21  0.031196  1.871783
2002-09-22  0.042344  2.540658
2002-09-23  0.049578  2.974674
2002-09-24  0.023189  1.391366
2002-09-25  0.033789  2.027313
2002-09-26  0.021506  1.290363

[1000 rows x 2 columns]

На оконном DataFrame вы можете передать список функций для применения к каждому столбцу, что дает агрегированный результат с иерархическим индексом:

In [85]: r.agg([np.sum, np.mean])
Out[85]: 
                   A                    B                   C          
                 sum      mean        sum      mean       sum      mean
2000-01-01  0.314226  0.314226  -0.001675 -0.001675  0.071823  0.071823
2000-01-02  1.206791  0.603396   0.678918  0.339459 -0.267817 -0.133908
2000-01-03  1.421701  0.473900   0.600508  0.200169 -0.445482 -0.148494
2000-01-04  1.912539  0.478135  -0.759594 -0.189899  1.146974  0.286744
2000-01-05  2.919639  0.583928  -0.061759 -0.012352 -0.743617 -0.148723
2000-01-06  2.665637  0.444273   1.298392  0.216399 -0.803529 -0.133921
2000-01-07  2.513985  0.359141   1.923089  0.274727 -1.928308 -0.275473
...              ...       ...        ...       ...       ...       ...
2002-09-20  1.447669  0.024128 -12.360302 -0.206005  2.734381  0.045573
2002-09-21  1.871783  0.031196 -13.896542 -0.231609  3.086102  0.051435
2002-09-22  2.540658  0.042344 -12.594402 -0.209907  3.162542  0.052709
2002-09-23  2.974674  0.049578 -12.727703 -0.212128  3.861005  0.064350
2002-09-24  1.391366  0.023189 -13.584590 -0.226410  3.790683  0.063178
2002-09-25  2.027313  0.033789 -15.083214 -0.251387  3.377896  0.056298
2002-09-26  1.290363  0.021506 -13.569459 -0.226158  3.809884  0.063498

[1000 rows x 6 columns]

Передача словаря функций по умолчанию имеет другое поведение, см. следующий раздел.

Применение различных функций к столбцам DataFrame

Передав словарь в aggregate вы можете применить различное агрегирование к столбцам DataFrame:

In [86]: r.agg({'A' : np.sum,
   ....:        'B' : lambda x: np.std(x, ddof=1)})
   ....: 
Out[86]: 
                   A         B
2000-01-01  0.314226       NaN
2000-01-02  1.206791  0.482437
2000-01-03  1.421701  0.417825
2000-01-04  1.912539  0.851468
2000-01-05  2.919639  0.837474
2000-01-06  2.665637  0.935441
2000-01-07  2.513985  0.867770
...              ...       ...
2002-09-20  1.447669  1.084259
2002-09-21  1.871783  1.088368
2002-09-22  2.540658  1.084707
2002-09-23  2.974674  1.084936
2002-09-24  1.391366  1.079268
2002-09-25  2.027313  1.091334
2002-09-26  1.290363  1.060255

[1000 rows x 2 columns]

Имена функций также могут быть строками. Для того, чтобы строка была допустимой, она должна быть реализована в оконном объекте

In [87]: r.agg({'A' : 'sum', 'B' : 'std'})
Out[87]: 
                   A         B
2000-01-01  0.314226       NaN
2000-01-02  1.206791  0.482437
2000-01-03  1.421701  0.417825
2000-01-04  1.912539  0.851468
2000-01-05  2.919639  0.837474
2000-01-06  2.665637  0.935441
2000-01-07  2.513985  0.867770
...              ...       ...
2002-09-20  1.447669  1.084259
2002-09-21  1.871783  1.088368
2002-09-22  2.540658  1.084707
2002-09-23  2.974674  1.084936
2002-09-24  1.391366  1.079268
2002-09-25  2.027313  1.091334
2002-09-26  1.290363  1.060255

[1000 rows x 2 columns]

Кроме того, вы можете передать вложенный словарь, чтобы указать различные агрегации для разных столбцов.

In [88]: r.agg({'A' : ['sum','std'], 'B' : ['mean','std'] })
Out[88]: 
                   A                   B          
                 sum       std      mean       std
2000-01-01  0.314226       NaN -0.001675       NaN
2000-01-02  1.206791  0.408948  0.339459  0.482437
2000-01-03  1.421701  0.365959  0.200169  0.417825
2000-01-04  1.912539  0.298925 -0.189899  0.851468
2000-01-05  2.919639  0.350682 -0.012352  0.837474
2000-01-06  2.665637  0.464115  0.216399  0.935441
2000-01-07  2.513985  0.479828  0.274727  0.867770
...              ...       ...       ...       ...
2002-09-20  1.447669  1.034827 -0.206005  1.084259
2002-09-21  1.871783  1.031417 -0.231609  1.088368
2002-09-22  2.540658  1.026341 -0.209907  1.084707
2002-09-23  2.974674  1.030021 -0.212128  1.084936
2002-09-24  1.391366  1.024793 -0.226410  1.079268
2002-09-25  2.027313  1.022099 -0.251387  1.091334
2002-09-26  1.290363  1.024751 -0.226158  1.060255

[1000 rows x 4 columns]

Расширяющиеся окна

Общая альтернатива скользящим статистикам — использование расширяющегося окна, которое дает значение статистики со всеми доступными данными до этого момента времени.

Они следуют аналогичному интерфейсу .rolling, где метод .expanding возвращает объект Expanding.

Поскольку эти вычисления являются частным случаем скользящих статистик, они реализованы в pandas таким образом, что следующие два вызова эквивалентны:

In [89]: df.rolling(window=len(df), min_periods=1).mean()[:5]
Out[89]: 
                   A         B         C         D
2000-01-01 -1.388345  3.317290  0.344542 -0.036968
2000-01-02 -1.123132  3.622300  1.675867  0.595300
2000-01-03 -0.628502  3.626503  2.455240  1.060158
2000-01-04 -0.768740  3.888917  2.451354  1.281874
2000-01-05 -0.824034  4.108035  2.556112  1.140723

In [90]: df.expanding(min_periods=1).mean()[:5]
Out[90]: 
                   A         B         C         D
2000-01-01 -1.388345  3.317290  0.344542 -0.036968
2000-01-02 -1.123132  3.622300  1.675867  0.595300
2000-01-03 -0.628502  3.626503  2.455240  1.060158
2000-01-04 -0.768740  3.888917  2.451354  1.281874
2000-01-05 -0.824034  4.108035  2.556112  1.140723

Они имеют аналогичный набор методов к методам .rolling.

Краткое описание методов

Функция Описание
count() Количество ненулевых наблюдений
sum() Сумма значений
mean() Среднее значение
median() Арифметическая медиана значений
min() Минимум
max() Максимум
std() Смещённое стандартное отклонение
var() Смещённая дисперсия
skew() Смещённый коэффициент асимметрии (3-й момент)
kurt() Смещённый эксцесс (4-й момент)
quantile() Выборовый квантиль (значение в %)
apply() Общая функция
cov() Смещённая ковариация (бинарная)
corr() Корреляция (бинарная)

Помимо отсутствия параметра window, эти функции имеют тот же интерфейс, что и их аналоги .rolling. Как и выше, все они принимают параметры:

  • min_periods: порог ненулевых точек данных, необходимый для вычисления статистики. По умолчанию используется минимум, необходимый для вычисления статистики. Никаких значений NaNs не будет выведено, как только будет встречено min_periods ненулевых точек данных.
  • center: логическое значение, указывает, нужно ли устанавливать метки в центре (по умолчанию False)

Примечание

Результаты методов .rolling и .expanding не возвращают NaN если в текущем окне присутствует хотя бы min_periods ненулевых значений. Это отличается от cumsum, cumprod, cummax и cummin, которые возвращают NaN в выводе, где NaN встречается во входных данных.

Статистика с расширяющимся окном будет более стабильной (и менее отзывчивой), чем её аналог с роликовым окном, поскольку увеличение размера окна уменьшает относительное влияние отдельной точки данных. В качестве примера, вот результат mean() для предыдущего временного ряда:

In [91]: s.plot(style='k--')
Out[91]: <matplotlib.axes._subplots.AxesSubplot at 0x7ff29c7378d0>

In [92]: s.expanding().mean().plot(style='k')
Out[92]: <matplotlib.axes._subplots.AxesSubplot at 0x7ff29c7378d0>
_images/expanding_mean_frame.png

Экспоненциально взвешенные окна

Связанный набор функций — экспоненциально взвешенные версии нескольких из перечисленных выше статистик. Аналогичный интерфейс .rolling и .expanding доступен через метод .ewm, чтобы получить объект EWM. Предоставляется ряд методов расширения EW (экспоненциально взвешенных):

Функция Описание
mean() EW скользящее среднее
var() EW скользящая дисперсия
std() EW скользящее стандартное отклонение
corr() EW скользящая корреляция
cov() EW скользящая ковариация

В общем случае взвешенное скользящее среднее вычисляется как

y_t = \frac{\sum_{i=0}^t w_i x_{t-i}}{\sum_{i=0}^t w_i},

где x_t — входное значение, а y_t — результат.

Функции EW поддерживают два варианта экспоненциальных весов. По умолчанию, adjust=True, используются веса w_i = (1 - \alpha)^i, что даёт

y_t = \frac{x_t + (1 - \alpha)x_{t-1} + (1 - \alpha)^2 x_{t-2} + ... + (1 - \alpha)^t x_{0}}{1 + (1 - \alpha) + (1 - \alpha)^2 + ... + (1 - \alpha)^t}

Когда adjust=False указано, скользящие средние вычисляются как

y_0 &= x_0 \\ y_t &= (1 - \alpha) y_{t-1} + \alpha x_t,

что эквивалентно использованию весов

w_i = \begin{cases} \alpha (1 - \alpha)^i & \text{if } i < t \\ (1 - \alpha)^i & \text{if } i = t. \end{cases}

Примечание

Эти уравнения иногда записываются в терминах \alpha' = 1 - \alpha, например

y_t = \alpha' y_{t-1} + (1 - \alpha') x_t.

Разница между вышеупомянутыми двумя вариантами возникает потому, что мы имеем дело с рядами, имеющими конечную историю. Рассмотрим ряд с бесконечной историей:

y_t = \frac{x_t + (1 - \alpha)x_{t-1} + (1 - \alpha)^2 x_{t-2} + ...} {1 + (1 - \alpha) + (1 - \alpha)^2 + ...}

Заметим, что знаменатель является геометрической прогрессией с начальным членом 1 и отношением 1 - \alpha, поэтому

y_t &= \frac{x_t + (1 - \alpha)x_{t-1} + (1 - \alpha)^2 x_{t-2} + ...} {\frac{1}{1 - (1 - \alpha)}}\\ &= [x_t + (1 - \alpha)x_{t-1} + (1 - \alpha)^2 x_{t-2} + ...] \alpha \\ &= \alpha x_t + [(1-\alpha)x_{t-1} + (1 - \alpha)^2 x_{t-2} + ...]\alpha \\ &= \alpha x_t + (1 - \alpha)[x_{t-1} + (1 - \alpha) x_{t-2} + ...]\alpha\\ &= \alpha x_t + (1 - \alpha) y_{t-1}

что показывает эквивалентность двух вышеупомянутых вариантов для бесконечных рядов. Когда adjust=True у нас y_0 = x_0 и из последнего представления выше у нас y_t = \alpha x_t + (1 - \alpha) y_{t-1}, поэтому существует предположение, что x_0 является не обычным значением, а скорее экспоненциально взвешенным моментом бесконечного ряда до этого момента.

Должно выполняться условие 0 < \alpha \leq 1, и хотя с версии 0.18.0 можно передавать \alpha напрямую, часто проще использовать либо **продолжительность**, **центр масс (com)** или **период полураспада** EW момента:

\alpha = \begin{cases} \frac{2}{s + 1}, & \text{для продолжительности}\ s \geq 1\\ \frac{1}{1 + c}, & \text{для центра масс}\ c \geq 0\\ 1 - \exp^{\frac{\log 0.5}{h}}, & \text{для периода полураспада}\ h > 0 \end{cases}

Следует указать ровно один из **продолжительности**, **центра масс**, **периода полураспада** и **альфа** для функций EW:

  • **Продолжительность** соответствует тому, что обычно называют «EW скользящим средним за N дней».
  • **Центр масс** имеет более физическое толкование и может быть представлен в терминах продолжительности: c = (s - 1) / 2.
  • **Период полураспада** — это период времени, за который экспоненциальный вес уменьшается вдвое.
  • **Альфа** задаёт коэффициент сглаживания напрямую.

Вот пример для одномерного временного ряда:

In [93]: s.plot(style='k--')
Out[93]: <matplotlib.axes._subplots.AxesSubplot at 0x7ff29c73bdd0>

In [94]: s.ewm(span=20).mean().plot(style='k')
Out[94]: <matplotlib.axes._subplots.AxesSubplot at 0x7ff29c73bdd0>
_images/ewma_ex.png

EWM имеет аргумент min_periods, который имеет то же значение, что и для всех методов .expanding и .rolling: значения вывода не будут задаваться, пока не будет встречено как минимум min_periods ненулевых значений в (расширяющемся) окне. (Это изменение с версий до 0.15.0, в которых аргумент min_periods влиял только на min_periods последовательных записей, начиная с первого ненулевого значения.)

В EWM также есть аргумент ignore_na, определяющий, как промежуточные нулевые значения влияют на вычисление весов. Когда ignore_na=False (по умолчанию), веса рассчитываются на основе абсолютных позиций, поэтому промежуточные нулевые значения влияют на результат. Когда ignore_na=True (что воспроизводит поведение в версиях до 0.15.0), веса рассчитываются, игнорируя промежуточные нулевые значения. Например, предположим adjust=True, если ignore_na=False, взвешенное среднее значение 3, NaN, 5 вычисляется как

\frac{(1-\alpha)^2 \cdot 3 + 1 \cdot 5}{(1-\alpha)^2 + 1}

В то время как, если ignore_na=True, взвешенное среднее значение вычисляется как

\frac{(1-\alpha) \cdot 3 + 1 \cdot 5}{(1-\alpha) + 1}.

Функции var(), std(), и cov() имеют аргумент bias, определяющий, должен ли результат содержать смещенные или несмещенные статистические данные. Например, если bias=True, ewmvar(x) вычисляется как ewmvar(x) = ewma(x**2) - ewma(x)**2; в то время как если bias=False (по умолчанию), статистические данные смещенной дисперсии масштабируются с помощью факторов несмещения

\frac{\left(\sum_{i=0}^t w_i\right)^2}{\left(\sum_{i=0}^t w_i\right)^2 - \sum_{i=0}^t w_i^2}.

(Для w_i = 1, это сводится к обычному коэффициенту N / (N - 1), где N = t + 1.) Смотрите Взвешенная выборочная дисперсия для получения дополнительной информации.

© 2008–2012, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team
Licensed under the 3-clause BSD License.
https://pandas.pydata.org/pandas-docs/version/0.19.2/computation.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API