Diferencia entre mapa, mapa de aplicación y métodos de aplicación en pandas

468

¿Me puede decir cuándo usar estos métodos de vectorización con ejemplos básicos?

Veo que mapes un Seriesmétodo, mientras que el resto son DataFramemétodos. Sin embargo, me confundí applyy los applymapmétodos. ¿Por qué tenemos dos métodos para aplicar una función a un DataFrame? Una vez más, ¡ejemplos simples que ilustran el uso serían geniales!

marillion
fuente
55
Corrígeme si me equivoco, pero creo que esas funciones no son métodos de vectorización, ya que implican un bucle sobre los elementos en los que se aplican.
Tanguy
1
No puedo ver la diferencia aquí: gist.github.com/MartinThoma/e320cbb937afb4ff766f75988f1c65e6
Martin Thoma

Respuestas:

534

Directamente del libro Python for Data Analysis de Wes McKinney , pág. 132 (Recomiendo este libro):

Otra operación frecuente es aplicar una función en matrices 1D a cada columna o fila. El método de aplicación de DataFrame hace exactamente esto:

In [116]: frame = DataFrame(np.random.randn(4, 3), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])

In [117]: frame
Out[117]: 
               b         d         e
Utah   -0.029638  1.081563  1.280300
Ohio    0.647747  0.831136 -1.549481
Texas   0.513416 -0.884417  0.195343
Oregon -0.485454 -0.477388 -0.309548

In [118]: f = lambda x: x.max() - x.min()

In [119]: frame.apply(f)
Out[119]: 
b    1.133201
d    1.965980
e    2.829781
dtype: float64

Muchas de las estadísticas de matriz más comunes (como suma y media) son métodos DataFrame, por lo que no es necesario usar apply.

También se pueden usar las funciones de Python con elementos. Suponga que desea calcular una cadena formateada a partir de cada valor de coma flotante en el marco. Puedes hacer esto con applymap:

In [120]: format = lambda x: '%.2f' % x

In [121]: frame.applymap(format)
Out[121]: 
            b      d      e
Utah    -0.03   1.08   1.28
Ohio     0.65   0.83  -1.55
Texas    0.51  -0.88   0.20
Oregon  -0.49  -0.48  -0.31

La razón del nombre applymap es que Series tiene un método de mapa para aplicar una función de elemento:

In [122]: frame['e'].map(format)
Out[122]: 
Utah       1.28
Ohio      -1.55
Texas      0.20
Oregon    -0.31
Name: e, dtype: object

En resumen, applyfunciona en una fila / columna de un DataFrame, applymapfunciona por elementos en un DataFrame y por mapelementos en una Serie.

jeremiahbuddha
fuente
31
estrictamente hablando, applymap internamente se implementa a través de aplicar con un poco más de parámetro de función pasado recapitulación (rougly hablando reemplazando funca lambda x: [func(y) for y in x], y aplicando por columnas)
Alko
55
Gracias por la explicación. Dado mapy applymaptanto elemento a elemento de trabajo, que se puede esperar de un único método (ya sea mapo applymap) que trabajaría tanto para una serie y una trama de datos. Probablemente hay otras consideraciones de diseño, y Wes McKinney decidió proponer dos métodos diferentes.
marillion
2
Está en la página 129 en mi copia por alguna razón. No hay etiqueta para la segunda edición ni nada.
Jody
1
¿Hay alguna manera de hacer applymapjunto con la groupbyfunción en los pandas?
everestial007
¿Cómo aplicar una función en datos agrupados en columnas?
hhh
84

Comparar map, applymapy : El contexto es importanteapply

Primera diferencia importante: DEFINICIÓN

  • map se define SOLO en la serie
  • applymap se define SOLAMENTE en DataFrames
  • apply se define en AMBOS

Segunda diferencia importante: ARGUMENTO DE ENTRADA

  • mapacepta dicts Series, o invocable
  • applymapy applyacepta solo callables

Tercera gran diferencia: COMPORTAMIENTO

  • map es elementwise para Series
  • applymap es elementwise para DataFrames
  • applytambién funciona en elementos pero es adecuado para operaciones y agregaciones más complejas. El comportamiento y el valor de retorno dependen de la función.

Cuarta diferencia importante (la más importante): CASO DE USO

  • mapestá destinado a asignar valores de un dominio a otro, por lo que está optimizado para el rendimiento (por ejemplo, df['A'].map({1:'a', 2:'b', 3:'c'}))
  • applymapes bueno para transformaciones de elementos a través de múltiples filas / columnas (por ejemplo, df[['A', 'B', 'C']].applymap(str.strip))
  • applyes para aplicar cualquier función que no se pueda vectorizar (por ejemplo, df['sentences'].apply(nltk.sent_tokenize))

Resumiendo

ingrese la descripción de la imagen aquí

Notas al pie

  1. mapcuando se pasa un diccionario / Serie, se asignarán elementos basados ​​en las claves de ese diccionario / Serie. Los valores faltantes se registrarán como NaN en la salida.
  2. applymapen versiones más recientes se ha optimizado para algunas operaciones. Encontrará un applymappoco más rápido que applyen algunos casos. Mi sugerencia es probarlos a ambos y usar lo que funcione mejor.

  3. mapestá optimizado para mapeos y transformación por elementos. Las operaciones que involucran diccionarios o series permitirán a los pandas usar rutas de código más rápidas para un mejor rendimiento.

  4. Series.applydevuelve un escalar para operaciones de agregación, Serie de lo contrario Del mismo modo para DataFrame.apply. Tenga en cuenta que applytambién tiene fastpaths cuando se le llama con ciertas funciones NumPy tales como mean, sum, etc.
cs95
fuente
70

Hay gran información en estas respuestas, pero estoy agregando la mía para resumir claramente qué métodos funcionan en forma de matriz versus en función de elemento. jeremiahbuddha hizo esto principalmente, pero no mencionó Series.apply. No tengo el representante para comentar.

  • DataFrame.apply opera en filas o columnas enteras a la vez.

  • DataFrame.applymap, Series.applyy Series.mapoperar en un elemento a la vez.

Hay mucha superposición entre las capacidades de Series.applyy Series.map, lo que significa que cualquiera de los dos funcionará en la mayoría de los casos. Sin embargo, tienen algunas pequeñas diferencias, algunas de las cuales se discutieron en la respuesta de osa.

Queso Marred
fuente
38

Agregando a las otras respuestas, en un Seriestambién hay un mapa y aplicar .

Apply puede hacer un DataFrame de una serie ; sin embargo, map solo colocará una serie en cada celda de otra serie, lo que probablemente no sea lo que desea.

In [40]: p=pd.Series([1,2,3])
In [41]: p
Out[31]:
0    1
1    2
2    3
dtype: int64

In [42]: p.apply(lambda x: pd.Series([x, x]))
Out[42]: 
   0  1
0  1  1
1  2  2
2  3  3

In [43]: p.map(lambda x: pd.Series([x, x]))
Out[43]: 
0    0    1
1    1
dtype: int64
1    0    2
1    2
dtype: int64
2    0    3
1    3
dtype: int64
dtype: object

Además, si tuviera una función con efectos secundarios, como "conectarse a un servidor web", probablemente la usaría applysolo por razones de claridad.

series.apply(download_file_for_every_element) 

Mappuede usar no solo una función, sino también un diccionario u otra serie. Digamos que quieres manipular permutaciones .

Tomar

1 2 3 4 5
2 1 4 5 3

El cuadrado de esta permutación es

1 2 3 4 5
1 2 5 3 4

Puedes calcularlo usando map. No estoy seguro de si la autoaplicación está documentada, pero funciona 0.15.1.

In [39]: p=pd.Series([1,0,3,4,2])

In [40]: p.map(p)
Out[40]: 
0    0
1    1
2    4
3    2
4    3
dtype: int64
osa
fuente
3
Además, .apply () te permite pasar kwargs a la función mientras que .map () no.
neilxdims
19

@jeremiahbuddha mencionó que apply funciona en filas / columnas, mientras que applymap funciona por elementos. Pero parece que todavía puede usar el método de cálculo basado en elementos ...

    frame.apply(np.sqrt)
    Out[102]: 
                   b         d         e
    Utah         NaN  1.435159       NaN
    Ohio    1.098164  0.510594  0.729748
    Texas        NaN  0.456436  0.697337
    Oregon  0.359079       NaN       NaN

    frame.applymap(np.sqrt)
    Out[103]: 
                   b         d         e
    Utah         NaN  1.435159       NaN
    Ohio    1.098164  0.510594  0.729748
    Texas        NaN  0.456436  0.697337
    Oregon  0.359079       NaN       NaN
usuario2921752
fuente
29
Buena captura con esto. La razón por la que esto funciona en su ejemplo es porque np.sqrt es un ufunc, es decir, si le da una matriz, transmitirá la función sqrt en cada elemento de la matriz. Entonces, cuando apply empuja np.sqrt en cada columna, np.sqrt funciona en cada uno de los elementos de las columnas, por lo que esencialmente obtiene el mismo resultado que applymap.
jeremiahbuddha
11

Solo quería señalar, mientras luchaba con esto por un momento

def f(x):
    if x < 0:
        x = 0
    elif x > 100000:
        x = 100000
    return x

df.applymap(f)
df.describe()

esto no modifica el marco de datos en sí, debe reasignarse

df = df.applymap(f)
df.describe()
muon
fuente
1
A veces tengo problemas para determinar si tienes que reasignar o no después de hacer algo con el df. Es principalmente prueba y error para mí, pero apuesto a que hay una lógica de cómo funciona (que me estoy perdiendo).
marillion
2
en general, un marco de datos de pandas solo se modifica mediante la reasignación df = modified_dfo si establece la marca inplace=True. También el marco de datos cambiará si pasa un marco de datos a una función por referencia y la función modifica el marco de datos
muon
1
Esto no es del todo cierto, piense .ixo .whereetc. No estoy seguro de cuál es la explicación completa para cuándo necesita reasignar y cuándo no.
Thanos
10

Probablemente la explicación más simple es la diferencia entre apply y applymap:

apply toma la columna completa como parámetro y luego asigna el resultado a esta columna

applymap toma el valor de la celda separada como parámetro y asigna el resultado a esta celda.

Nota: si apply devuelve el valor único, tendrá este valor en lugar de la columna después de la asignación y, finalmente, tendrá solo una fila en lugar de una matriz.

Kath
fuente
3

Mi punto de vista:

Desde el punto de vista de la función:

Si la función tiene variables que deben compararse dentro de una columna / fila, use apply.

por ejemplo: lambda x: x.max()-x.mean().

Si la función se aplica a cada elemento:

1> Si se encuentra una columna / fila, use apply

2> Si se aplica a todo el marco de datos, use applymap

majority = lambda x : x > 17
df2['legal_drinker'] = df2['age'].apply(majority)

def times10(x):
  if type(x) is int:
    x *= 10 
  return x
df2.applymap(times10)
Vicky Miao
fuente
Proporcione df2 también para una mejor claridad para que podamos probar su código.
Ashish Anand
1

Basado en la respuesta de cs95

  • map se define SOLO en la serie
  • applymap se define SOLAMENTE en DataFrames
  • apply se define en AMBOS

da algunos ejemplos

In [3]: frame = pd.DataFrame(np.random.randn(4, 3), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])

In [4]: frame
Out[4]:
            b         d         e
Utah    0.129885 -0.475957 -0.207679
Ohio   -2.978331 -1.015918  0.784675
Texas  -0.256689 -0.226366  2.262588
Oregon  2.605526  1.139105 -0.927518

In [5]: myformat=lambda x: f'{x:.2f}'

In [6]: frame.d.map(myformat)
Out[6]:
Utah      -0.48
Ohio      -1.02
Texas     -0.23
Oregon     1.14
Name: d, dtype: object

In [7]: frame.d.apply(myformat)
Out[7]:
Utah      -0.48
Ohio      -1.02
Texas     -0.23
Oregon     1.14
Name: d, dtype: object

In [8]: frame.applymap(myformat)
Out[8]:
            b      d      e
Utah     0.13  -0.48  -0.21
Ohio    -2.98  -1.02   0.78
Texas   -0.26  -0.23   2.26
Oregon   2.61   1.14  -0.93

In [9]: frame.apply(lambda x: x.apply(myformat))
Out[9]:
            b      d      e
Utah     0.13  -0.48  -0.21
Ohio    -2.98  -1.02   0.78
Texas   -0.26  -0.23   2.26
Oregon   2.61   1.14  -0.93


In [10]: myfunc=lambda x: x**2

In [11]: frame.applymap(myfunc)
Out[11]:
            b         d         e
Utah    0.016870  0.226535  0.043131
Ohio    8.870453  1.032089  0.615714
Texas   0.065889  0.051242  5.119305
Oregon  6.788766  1.297560  0.860289

In [12]: frame.apply(myfunc)
Out[12]:
            b         d         e
Utah    0.016870  0.226535  0.043131
Ohio    8.870453  1.032089  0.615714
Texas   0.065889  0.051242  5.119305
Oregon  6.788766  1.297560  0.860289
Alfa
fuente
0

FOMO:

El siguiente ejemplo muestra applyy se applymapaplica a a DataFrame.

mapLa función es algo que aplica solo en la serie. No puede aplicar map en DataFrame.

Lo que hay que recordar es que apply puede hacer cualquier cosa applymap , pero applytiene opciones eXtra .

Las opciones del factor X son: axisy result_typedonde result_typesolo funciona cuando axis=1(para columnas).

df = DataFrame(1, columns=list('abc'),
                  index=list('1234'))
print(df)

f = lambda x: np.log(x)
print(df.applymap(f)) # apply to the whole dataframe
print(np.log(df)) # applied to the whole dataframe
print(df.applymap(np.sum)) # reducing can be applied for rows only

# apply can take different options (vs. applymap cannot)
print(df.apply(f)) # same as applymap
print(df.apply(sum, axis=1))  # reducing example
print(df.apply(np.log, axis=1)) # cannot reduce
print(df.apply(lambda x: [1, 2, 3], axis=1, result_type='expand')) # expand result

Como nota al margen, la mapfunción Serie no debe confundirse con la mapfunción Python .

El primero se aplica en Series, para asignar los valores, y el segundo a cada elemento de un iterable.


Por último, no confunda el applymétodo del marco de datos con groupbyapply método .

prosti
fuente