Выберите сечение с несколькими ключами из DataFrame
У меня есть DataFrame "df" с столбцами данных (время, тиккер) Multiindex и bid/ask/etc:
tod last bid ask volume
time ticker
2013-02-01 SPY 1600 149.70 150.14 150.17 1300
SLV 1600 30.44 30.38 30.43 3892
GLD 1600 161.20 161.19 161.21 3860
Я хотел бы выбрать сечение второго уровня (level = 1) с помощью нескольких клавиш. Прямо сейчас, я могу сделать это, используя один ключ, т.е.
df.xs('SPY', level=1)
который дает мне временные ряды SPY. Каков наилучший способ выбора поперечного сечения с несколькими ключами, то есть комбинированное поперечное сечение как SPY, так и GLD, что-то вроде:
df.xs(['SPY', 'GLD'], level=1)
?
Ответы
Ответ 1
Преобразуйте в панель, затем индексирование будет прямым
In [20]: df = pd.DataFrame(dict(time = pd.Timestamp('20130102'),
A = np.random.rand(3),
ticker=['SPY','SLV','GLD'])).set_index(['time','ticker'])
In [21]: df
Out[21]:
A
time ticker
2013-01-02 SPY 0.347209
SLV 0.034832
GLD 0.280951
In [22]: p = df.to_panel()
In [23]: p
Out[23]:
<class 'pandas.core.panel.Panel'>
Dimensions: 1 (items) x 1 (major_axis) x 3 (minor_axis)
Items axis: A to A
Major_axis axis: 2013-01-02 00:00:00 to 2013-01-02 00:00:00
Minor_axis axis: GLD to SPY
In [24]: p.ix[:,:,['SPY','GLD']]
Out[24]:
<class 'pandas.core.panel.Panel'>
Dimensions: 1 (items) x 1 (major_axis) x 2 (minor_axis)
Items axis: A to A
Major_axis axis: 2013-01-02 00:00:00 to 2013-01-02 00:00:00
Minor_axis axis: SPY to GLD
Ответ 2
Я не мог найти более прямой способ, кроме использования select
:
>>> df
last tod
A SPY 1 1600
SLV 2 1600
GLD 3 1600
>>> df.select(lambda x: x[1] in ['SPY','GLD'])
last tod
A SPY 1 1600
GLD 3 1600
Ответ 3
Есть лучшие способы сделать это с более поздними версиями Pandas:
regression_df.loc[(slice(None), ['SPY', 'GLD']), :]
Этот подход требует, чтобы индекс был лексикографически отсортирован (используйте df.sort_index()
).
Ответ 4
Для чего это необходимо, я сделал следующее:
foo = pd.DataFrame(np.random.rand(12,3),
index=pd.MultiIndex.from_product([['A','B','C','D'],['Green','Red','Blue']],
names=['Letter','Color']),
columns=['X','Y','Z']).sort_index()
foo.reset_index()\
.loc[foo.reset_index().Color.isin({'Green','Red'})]\
.set_index(foo.index.names)
Этот подход похож на select, но избегает итерации по всем строкам с помощью лямбда.
Однако я сравнивал это с подходом Panel, и кажется, что решение Panel быстрее (2,91 мс для индекса /loc vs 1,48 мс для to_panel/to_frame:
foo.to_panel()[:,:,['Green','Red']].to_frame()
Время:
In [56]:
%%timeit
foo.reset_index().loc[foo.reset_index().Color.isin({'Green','Red'})].set_index(foo.index.names)
100 loops, best of 3: 2.91 ms per loop
In [57]:
%%timeit
foo2 = foo.reset_index()
foo2.loc[foo2.Color.eq('Green') | foo2.Color.eq('Red')].set_index(foo.index.names)
100 loops, best of 3: 2.85 ms per loop
In [58]:
%%timeit
foo2 = foo.reset_index()
foo2.loc[foo2.Color.ne('Blue')].set_index(foo.index.names)
100 loops, best of 3: 2.37 ms per loop
In [54]:
%%timeit
foo.to_panel()[:,:,['Green','Red']].to_frame()
1000 loops, best of 3: 1.18 ms per loop
UPDATE
После повторного просмотра этой темы (снова) я заметил следующее:
In [100]:
%%timeit
foo2 = pd.DataFrame({k: foo.loc[k] for k in foo.index if k[1] in ['Green','Red']}).transpose()
foo2.index.names = foo.index.names
foo2.columns.names = foo2.columns.names
100 loops, best of 3: 1.97 ms per loop
In [101]:
%%timeit
foo2 = pd.DataFrame.from_dict({k: foo.loc[k] for k in foo.index if k[1] in ['Green','Red']}, orient='index')
foo2.index.names = foo.index.names
foo2.columns.names = foo2.columns.names
100 loops, best of 3: 1.82 ms per loop
Если вы не заботитесь о сохранении исходного порядка и наименовании уровней, вы можете использовать:
%%timeit
pd.concat({key: foo.xs(key, axis=0, level=1) for key in ['Green','Red']}, axis=0)
1000 loops, best of 3: 1.31 ms per loop
И если вы просто выбираете на первом уровне:
%%timeit
pd.concat({key: foo.loc[key] for key in ['A','B']}, axis=0, names=foo.index.names)
1000 loops, best of 3: 1.12 ms per loop
против
%%timeit
foo.to_panel()[:,['A','B'],:].to_frame()
1000 loops, best of 3: 1.16 ms per loop
Другое обновление
Если вы отсортируете индекс примера foo
, многие из вышеперечисленных значений улучшаются (времена были обновлены, чтобы отразить предварительно отсортированный индекс). Однако, когда индекс отсортирован, вы можете использовать решение, описанное user674155:
%%timeit
foo.loc[(slice(None), ['Blue','Red']),:]
1000 loops, best of 3: 582 µs per loop
Это наиболее эффективный и интуитивно понятный, по моему мнению (пользователю не нужно понимать панели и как они созданы из фреймов).
Примечание. Даже если индекс еще не отсортирован, сортировка индекса foo
"на лету" сопоставима по производительности с опцией to_panel
.