Ускорение группового различия в Pandas

Рассмотрим следующее решение для вычисления разницы внутри группы в Pandas:

df =  df.set_index(['ticker', 'date']).sort_index()[['value']]
df['diff'] = np.nan
idx = pd.IndexSlice

for ix in df.index.levels[0]:
    df.loc[ idx[ix,:], 'diff'] = df.loc[idx[ix,:], 'value' ].diff()

Для:

> df
   date ticker  value
0    63      C   1.65
1    88      C  -1.93
2    22      C  -1.29
3    76      A  -0.79
4    72      B  -1.24
5    34      A  -0.23
6    92      B   2.43
7    22      A   0.55
8    32      A  -2.50
9    59      B  -1.01

Он возвращает:

> df
             value  diff
ticker date             
A      22     0.55   NaN
       32    -2.50 -3.05
       34    -0.23  2.27
       76    -0.79 -0.56
B      59    -1.01   NaN
       72    -1.24 -0.23
       92     2.43  3.67
C      22    -1.29   NaN
       63     1.65  2.94
       88    -1.93 -3.58

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

Есть ли способ ускорить это в Pandas? Является ли перебор значений индекса хорошим способом решения этой проблемы? Можно ли использовать numba для этого?


person Amelio Vazquez-Reina    schedule 10.02.2015    source источник


Ответы (2)


Вот еще один способ, который должен быть намного быстрее.

Сначала отсортируйте по тикеру и дате:

In [11]: df = df.set_index(['ticker', 'date']).sort_index()

In [12]: df
Out[12]:
             value
ticker date
A      22     0.55
       32    -2.50
       34    -0.23
       76    -0.79
B      59    -1.01
       72    -1.24
       92     2.43
C      22    -1.29
       63     1.65
       88    -1.93

Добавьте столбец различий:

In [13]: df['diff'] = df['value'].diff()

Чтобы заполнить NaN, мы можем найти первую строку следующим образом (может быть более приятный способ):

In [14]: s = pd.Series(df.index.labels[0])

In [15]: s != s.shift()
Out[15]:
0     True
1    False
2    False
3    False
4     True
5    False
6    False
7     True
8    False
9    False
dtype: bool

In [16]: df.loc[(s != s.shift()).values 'diff'] = np.nan

In [17]: df
Out[17]:
             value  diff
ticker date
A      22     0.55   NaN
       32    -2.50 -3.05
       34    -0.23  2.27
       76    -0.79 -0.56
B      59    -1.01   NaN
       72    -1.24 -0.23
       92     2.43  3.67
C      22    -1.29   NaN
       63     1.65  2.94
       88    -1.93 -3.58
person Andy Hayden    schedule 10.02.2015
comment
В моем расчете 10 000 DataFrame (с теми же характеристиками, что и у OP) это заняло около 40 мс. - person Andy Hayden; 10.02.2015

В качестве альтернативы вы можете выполнять сортировку и индексацию внутри каждой группы. Хотя еще не проверено временем:

In [11]: def value_and_diff(subdf):
             subdf = subdf.set_index('date').sort_index()
             return pd.DataFrame({'value': subdf['value'],
                                  'diff': subdf['value'].diff()})

In [12]: df.groupby('ticker').apply(value_and_diff)
Out[12]:
             diff  value
ticker date
A      22     NaN   0.55
       32   -3.05  -2.50
       34    2.27  -0.23
       76   -0.56  -0.79
B      59     NaN  -1.01
       72   -0.23  -1.24
       92    3.67   2.43
C      22     NaN  -1.29
       63    2.94   1.65
       88   -3.58  -1.93
person Andy Hayden    schedule 10.02.2015
comment
Спасибо @Энди. Интересно, что вы сортируете записи внутри приложения (например, в отличие от их сортировки перед запуском группировки и применения). Это потому, что groupby не гарантирует сохранение исходного порядка? - person Amelio Vazquez-Reina; 10.02.2015
comment
Кроме того, глядя на этот ответ от Джеффа, я вижу, что он применяет transform(Series.diff) вместо diff, как в вашем коде. Знаете ли вы, когда использовать одно против другого для определения различий внутри группы? - person Amelio Vazquez-Reina; 10.02.2015
comment
@AmelioVazquez-Reina в подобных ситуациях (когда функция не уменьшается), преобразование и применение одинаковы. Оглядываясь назад, я думаю, что глобальная сортировка может быть быстрее... Я ошибочно подумал, что это проблема, вызывающая наибольшее замедление. Думаю, у меня есть лучшее решение. - person Andy Hayden; 10.02.2015