Aplicar función en el índice DataFrame

83

¿Cuál es la mejor manera de aplicar una función sobre el índice de un Pandas DataFrame? Actualmente estoy usando este enfoque detallado:

pd.DataFrame({"Month": df.reset_index().Date.apply(foo)})

donde Datees el nombre del índice y fooes el nombre de la función que estoy aplicando.

Alex Rothberg
fuente
6
hace df.index.map(foo)el trabajo?
HYRY
1
"Funciona", pero devuelve una matriz numpy en lugar de una serie Pandas.
Alex Rothberg
1
cual es tu meta final? puede pasar la matriz al constructor DataFrame. O haz algo comopd.Series(df.index).apply(foo)
Roman Pekar
Depende totalmente de cuál sea la función ...
Andy Hayden
1
Siguiendo de @HYRY, si solo desea modificar el índice de un DataFrame existente, puede hacerlodf.index = df.index.map(foo)
Ben

Respuestas:

98

Como ya sugirió HYRY en los comentarios, Series.map es el camino a seguir aquí. Simplemente establezca el índice en la serie resultante.

Ejemplo simple:

df = pd.DataFrame({'d': [1, 2, 3]}, index=['FOO', 'BAR', 'BAZ'])
df
        d
FOO     1
BAR     2
BAZ     3

df.index = df.index.map(str.lower)
df
        d
foo     1
bar     2
baz     3

Índice! = Serie

Como lo señaló @OP. la df.index.map(str.lower)llamada devuelve una matriz numpy. Esto se debe a que los índices de marcos de datos se basan en matrices numerosas, no en series.

La única forma de convertir el índice en una serie es crear una serie a partir de él.

pd.Series(df.index.map(str.lower))

Consideración

La Indexclase ahora subclasifica StringAccessorMixin, lo que significa que puede realizar la operación anterior de la siguiente manera

df.index.str.lower()

Esto todavía produce un objeto Index, no una Serie.

firelynx
fuente
1
Con un índice múltiple, puede usar la división si desea usar ambos elementos en su función, por ejemplo x[0]y x[1].
Elliott
3
Camino un poco más cortodf.index.map(str.lower)
Cero
1
@JohnGalt Gracias por señalarlo. No solo es más corto, sino más rápido, ya que str.lower es una función cython compilada y la función lambda que escribí no lo es.
firelynx
12

Suponiendo que desea hacer una columna en su DataFrame actual aplicando su función "foo" al índice. Podrías escribir ...

df['Month'] = df.index.map(foo)

Para generar la serie solo, podría hacer ...

pd.Series({x: foo(x) for x in foo.index})
suraj747
fuente
1
Se desaconseja el uso de bucles for en el sistema de eco pandas / numpy. Es muy ineficiente en memoria y se bloquea fácilmente con conjuntos de datos más grandes.
firelynx
3

Muchas respuestas devuelven el índice como una matriz, que pierde información sobre el nombre del índice, etc. (aunque podría hacerlo pd.Series(index.map(myfunc), name=index.name)). Tampoco funcionará para un MultiIndex.

La forma en que trabajé con esto es usar "renombrar":

mix = pd.MultiIndex.from_tuples([[1, 'hi'], [2, 'there'], [3, 'dude']], names=['num', 'name'])
data = np.random.randn(3)
df = pd.Series(data, index=mix)
print(df)
num  name 
1    hi       1.249914
2    there   -0.414358
3    dude     0.987852
dtype: float64

# Define a few dictionaries to denote the mapping
rename_dict = {i: i*100 for i in df.index.get_level_values('num')}
rename_dict.update({i: i+'_yeah!' for i in df.index.get_level_values('name')})
df = df.rename(index=rename_dict)
print(df)
num  name       
100  hi_yeah!       1.249914
200  there_yeah!   -0.414358
300  dude_yeah!     0.987852
dtype: float64

El único truco con esto es que su índice debe tener etiquetas únicas en blanco y negro con diferentes niveles de múltiples índices, pero tal vez alguien más inteligente que yo sepa cómo solucionarlo. Para mis propósitos, esto funciona el 95% del tiempo.

choldgraf
fuente
3

Siempre puede convertir un índice usando su to_series()método, y luego applyo map, según sus preferencias / necesidades.

ret = df.index.map(foo)                # Returns pd.Index
ret = df.index.to_series().map(foo)    # Returns pd.Series
ret = df.index.to_series().apply(foo)  # Returns pd.Series

Todo lo anterior se puede asignar directamente a una columna nueva o existente de df:

df["column"] = ret

Simplemente para la corrección: pd.Index.map, pd.Series.mapy pd.Series.applytodos funcionan elemento a elemento. A menudo uso mappara aplicar búsquedas representadas por dictso pd.Series. applyes más genérico porque puede pasar cualquier función junto con adicional argso kwargs. Las diferencias entre applyy mapse discuten más en este hilo SO . No sé por qué pd.Index.applyse omitió.

Normanius
fuente