Dataframe Slice no elimina los valores de índice

Hace poco tuve este problema con un gran dataframe y su índice múltiple asociado. Este ejemplo simplificado demostrará el problema.

import pandas as pd import numpy as np np.random.seed(1) idx = pd.MultiIndex.from_product([['A','B'],[5,6]]) df = pd.DataFrame(data= np.random.randint(1,100,(4)),index= idx,columns =['P']) print df 

Cuyos rendimientos:

  P A 5 38 6 13 B 5 73 6 10 

Ahora eche un vistazo rápido al índice

 print df.index MultiIndex(levels=[[u'A', u'B'], [5, 6]], labels=[[0, 0, 1, 1], [0, 1, 0, 1]]) 

Si rebano este dataframe, observo que el índice múltiple nunca se condensa. Incluso con una copia profunda.

¿Cuál es la mejor manera de reducir la huella de memoria del índice en una operación de división?

 df_slice = df[df['P']>20] print df_slice print df_slice.index P A 5 38 B 5 73 

Vea cómo se ha reducido el dataframe, pero no el índice.

 MultiIndex(levels=[[u'A', u'B'], [5, 6]], labels=[[0, 1], [0, 0]]) 

Incluso con una copia (deep = True)

 df_slice = df[df['P']>20].copy(deep=True) print df_slice.index MultiIndex(levels=[[u'A', u'B'], [5, 6]] ,labels=[[0, 1], [0, 0]]) 

Hubiera esperado que MultiIndex eliminara los 6 como se muestra:

 MultiIndex(levels=[[u'A', u'B'], [5]] ,labels=[[0, 1], [0, 0]]) 

El problema viene en la práctica cuando el dataframe es grande.

Entiendo su preocupación, pero creo que debe ver lo que está sucediendo en la aplicación de bajo nivel de los pandas.

Primero, debemos declarar que se supone que los índices son inmutables. Puede consultar más de su documentación aquí -> http://pandas.pydata.org/pandas-docs/stable/indexing.html#setting-metadata

Cuando cree un objeto de dataframe, llamémosle df y desee acceder a sus filas, básicamente, todo lo que hace es pasar una serie booleana que Pandas hará coincidir con su índice correspondiente.

Sigue este ejemplo:

 index = pd.MultiIndex.from_product([['A','B'],[5,6]]) df = pd.DataFrame(data=np.random.randint(1,100,(4)), index=index, columns=["P"]) P A 5 5 6 51 B 5 93 6 76 

Ahora, digamos que queremos seleccionar las filas con P> 90 . ¿Cómo lo harías tú? df[df["P"] > 90] , ¿verdad? Pero mira lo que df [“P”]> 90 realmente devuelve.

 A 5 True 6 True B 5 True 6 False Name: P, dtype: bool 

Como puede ver, devuelve una serie booleana que coincide con el índice original. ¿Por qué? Debido a que Pandas necesita asignar qué valores de índice tienen un valor verdadero equivalente, para que pueda seleccionar el resultado adecuado. Básicamente, durante sus operaciones de división, siempre llevará este índice, porque es un elemento de mapeo para el objeto.

Sin embargo, la esperanza no se ha ido. Dependiendo de su aplicación, si cree que en realidad está tomando una gran parte de su memoria, puede dedicar un poco de tiempo a hacer lo siguiente:

 def df_sliced_index(df): new_index = [] rows = [] for ind, row in df.iterrows(): new_index.append(ind) rows.append(row) return pd.DataFrame(data=rows, index=pd.MultiIndex.from_tuples(new_index)) df_sliced_index(df[df['P'] > 90]).index 

Lo que rinde lo que creo, es el resultado deseado:

 MultiIndex(levels=[[u'B'], [5]], labels=[[0], [0]]) 

Pero si los datos son demasiado grandes para preocuparse por el tamaño del índice, me pregunto cuánto puede costarle en términos de tiempo.

Puedes hacer que el MultiIndex sea único por

 df_slice.index = pd.MultiIndex.from_tuples(df_slice.index.unique(), names=idx.names) 

lo que produce el índice

 MultiIndex(levels=[[u'A', u'B'], [5]], labels=[[0, 1], [0, 0]]) 

Mi forma preferida de hacer esto es

 old_idx = df_slice.index new_idx = pd.MultiIndex.from_tuples(old_idx.to_series(), names=old_idx.names)