2015-02-10 3 views
2

Рассмотрим следующий solution для вычисления внутри-группы различий в панд:Ускорение группы-накрест разностей в панд

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). Это, по-видимому, так, потому что я повторяю каждое значение для первого уровня в основном цикле.

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

ответ

4

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

Во-первых, своего рода основаны на линеечку и дата:

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

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

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 
+0

В моем времени 10 000 DataFrame (с теми же характеристиками, что и OP) это заняло около 40 мс. –

1

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

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 
+0

Thanks @Andy. Интересно, что вы сортируете записи в приложении (например, в отличие от их сортировки * перед тем, как запускать groupby и применять). Это потому, что 'groupby' не гарантирует сохранение первоначального заказа? –

+0

Кроме того, глядя на [этот ответ] (http://stackoverflow.com/a/20671047/2832960) от Джеффа, я вижу, что он применяет 'transform (Series.diff)' вместо просто 'diff', как в вашем коде , Знаете ли вы, когда использовать один против другого для различия внутри группы? –

+1

@ AmelioVazquez-Reina в таких ситуациях (когда функция не «уменьшает»), тогда преобразование и применение одинаковы. Оглядываясь назад, я думаю, что сортировка по всему миру может быть быстрее ... Я ошибочно подумал, что это проблема, вызывающая наиболее медленное падение. Я думаю, что у меня есть лучшее решение. –

Смежные вопросы