pandas - index - Seleccione una sección transversal de varias claves de un DataFrame
pandas select columns (4)
Convertir a un panel, entonces la indexación es directa
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
Tengo un DataFrame "df" con (time, ticker) Multiindex y columnas de datos 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
Me gustaría seleccionar una sección transversal de segundo nivel (nivel = 1) con varias teclas. En este momento, puedo hacerlo usando una tecla, es decir
df.xs(''SPY'', level=1)
Lo que me da una serie temporal de SPY. ¿Cuál es la mejor manera de seleccionar una sección transversal de varias teclas, es decir, una sección transversal combinada de SPY y GLD, algo así como:
df.xs([''SPY'', ''GLD''], level=1)
?
Hay mejores formas de hacerlo con las versiones más recientes de Pandas:
regression_df.loc[(slice(None), [''SPY'', ''GLD'']), :]
Este enfoque requiere que el índice se df.sort_index()
lexicográficamente (use df.sort_index()
).
No pude encontrar una forma más directa que no sea usar 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
Por lo que vale, hice lo siguiente:
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)
Este enfoque es similar a seleccionar, pero evita la iteración sobre todas las filas con un lambda.
Sin embargo, comparé esto con el enfoque del Panel, y parece que la solución del Panel es más rápida (2.91 ms para index / loc vs 1.48 ms para to_panel / to_frame:
foo.to_panel()[:,:,[''Green'',''Red'']].to_frame()
Veces:
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
ACTUALIZAR
Después de revisar este tema (nuevamente), observé lo siguiente:
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
Si no le importa conservar el orden original y la denominación de los niveles, puede utilizar:
%%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
Y si solo estás seleccionando en el primer nivel:
%%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
versus:
%%timeit
foo.to_panel()[:,[''A'',''B''],:].to_frame()
1000 loops, best of 3: 1.16 ms per loop
Otra actualización
Si ordena el índice del ejemplo foo
, muchas de las veces anteriores mejoran (los tiempos se han actualizado para reflejar un índice pre-ordenado). Sin embargo, cuando el índice está ordenado, puede usar la solución descrita por user674155:
%%timeit
foo.loc[(slice(None), [''Blue'',''Red'']),:]
1000 loops, best of 3: 582 µs per loop
Este es el más eficiente e intuitivo en mi opinión (el usuario no necesita entender los paneles y cómo se crean a partir de marcos).
Nota: incluso si el índice aún no se ha ordenado, la clasificación del índice de foo
sobre la marcha es comparable en rendimiento a la opción to_panel
.