Ya hemos cubierto la mayoría de los fundamentos de trabajar con datos usando la biblioteca Pandas. Hay un tema más que me gustaría discutir antes de concluir la serie: La función Apply.
En el artículo anterior, aprendimos cómo crear subgrupos de datos usando la función groupby. Esto es bastante útil cuando quieres obtener una mejor comprensión de ciertos subconjuntos de datos o realizar agregaciones de grupo. Hoy añadiremos otro recurso a tu caja de herramientas que te permitirá usar esos grupos para mucho más.
Apply te permite realizar cálculos más complejos en los grupos que creas, funciona así: La función que proporcionas a apply se llama en cada uno de los grupos, y los resultados se concatenan en una sola estructura de datos final.
De nuevo, esto es mucho más fácil de entender con ejemplos prácticos, ¡así que empecemos!
Aplicaciones básicas de apply
Usaremos la misma tabla con datos de Pokémon que usamos en el último artículo.
Primero, importemos pandas y examinemos el contenido de nuestro DataFrame.
import pandas as pd
pdata = pd.read_csv('./sample_data/poke_colors.csv')
pdata
Name | Color | Evolves | HP | Attack | Defense | SpAtk | SpDef | Speed | |
---|---|---|---|---|---|---|---|---|---|
0 | Caterpie | Green | True | 45 | 30 | 35 | 20 | 20 | 45 |
1 | Metapod | Green | True | 50 | 20 | 55 | 25 | 25 | 30 |
2 | Scyther | Green | False | 70 | 110 | 80 | 55 | 80 | 105 |
3 | Bulbasaur | Green | True | 45 | 49 | 49 | 65 | 65 | 45 |
4 | Dratini | Blue | True | 41 | 64 | 45 | 50 | 50 | 50 |
5 | Squirtle | Blue | True | 44 | 48 | 65 | 50 | 64 | 43 |
6 | Poliwag | Blue | True | 40 | 50 | 40 | 40 | 40 | 90 |
7 | Poliwhirl | Blue | True | 65 | 65 | 65 | 50 | 50 | 90 |
8 | Charmander | Red | True | 39 | 52 | 43 | 60 | 50 | 65 |
9 | Magmar | Red | False | 65 | 95 | 57 | 100 | 85 | 93 |
10 | Paras | Red | True | 35 | 70 | 55 | 45 | 55 | 25 |
11 | Parasect | Red | False | 60 | 95 | 80 | 60 | 80 | 30 |
12 | Pikachu | Yellow | True | 35 | 55 | 40 | 50 | 50 | 90 |
13 | Abra | Yellow | True | 25 | 20 | 15 | 105 | 55 | 90 |
14 | Psyduck | Yellow | True | 50 | 52 | 48 | 65 | 50 | 55 |
15 | Kadabra | Yellow | True | 40 | 35 | 30 | 120 | 70 | 10 |
El argumento más importante de Apply es una función. Esta función se ejecutará en cada grupo de datos y los resultados se concatenarán en una estructura de datos final. Crearemos una función simple que devuelve los dos Pokémon con el valor de ataque más alto, algo así:
# Dos pokémon con el ataque más alto
def highest_attack(data_frame):
# Recuerda cómo funciona [], esto selecciona las últimas dos (más altas) entradas de Attack después de ordenar
return data_frame.sort_values(by='Attack')[-2:]
# Probémoslo en el dataframe completo
highest_attack(pdata)
Name | Color | Evolves | HP | Attack | Defense | SpAtk | SpDef | Speed | |
---|---|---|---|---|---|---|---|---|---|
11 | Parasect | Red | False | 60 | 95 | 80 | 60 | 80 | 30 |
2 | Scyther | Green | False | 70 | 110 | 80 | 55 | 80 | 105 |
Ahora veamos cómo usar apply para hacer algo un poco más interesante. Queremos encontrar los dos pokémon con el valor de ataque más alto por color. Para hacer esto, los agruparemos por Color y luego pasaremos highest_attack a apply, algo así:
# Ahora, encontremos cuáles son los dos pokémon con el ataque más alto en cada grupo de color:
pdata.groupby('Color').apply(highest_attack)
Name | Color | Evolves | HP | Attack | Defense | SpAtk | SpDef | Speed | ||
---|---|---|---|---|---|---|---|---|---|---|
Color | ||||||||||
Blue | 4 | Dratini | Blue | True | 41 | 64 | 45 | 50 | 50 | 50 |
7 | Poliwhirl | Blue | True | 65 | 65 | 65 | 50 | 50 | 90 | |
Green | 3 | Bulbasaur | Green | True | 45 | 49 | 49 | 65 | 65 | 45 |
2 | Scyther | Green | False | 70 | 110 | 80 | 55 | 80 | 105 | |
Red | 9 | Magmar | Red | False | 65 | 95 | 57 | 100 | 85 | 93 |
11 | Parasect | Red | False | 60 | 95 | 80 | 60 | 80 | 30 | |
Yellow | 14 | Psyduck | Yellow | True | 50 | 52 | 48 | 65 | 50 | 55 |
12 | Pikachu | Yellow | True | 35 | 55 | 40 | 50 | 50 | 90 |
¡Observa cómo la tabla final es el resultado de concatenar juntos los resultados de ejecutar highest_attack en cada grupo!
Funciones con argumentos adicionales
Las funciones que pasas al método apply pueden recibir argumentos adicionales. Creemos otra versión de nuestra función, esta vez llamada highest_attribute, que te permite especificar el atributo a tomar en consideración y los n pokémon más altos que quieres seleccionar de cada grupo:
# Establecemos el atributo predeterminado como HP y el n predeterminado a 2
def highest_attribute(data_frame, attribute='HP', n=2):
return data_frame.sort_values(by=attribute)[-n:]
pdata.groupby('Color').apply(highest_attribute, 'Defense', 3)
Name | Color | Evolves | HP | Attack | Defense | SpAtk | SpDef | Speed | ||
---|---|---|---|---|---|---|---|---|---|---|
Color | ||||||||||
Blue | 4 | Dratini | Blue | True | 41 | 64 | 45 | 50 | 50 | 50 |
5 | Squirtle | Blue | True | 44 | 48 | 65 | 50 | 64 | 43 | |
7 | Poliwhirl | Blue | True | 65 | 65 | 65 | 50 | 50 | 90 | |
Green | 3 | Bulbasaur | Green | True | 45 | 49 | 49 | 65 | 65 | 45 |
1 | Metapod | Green | True | 50 | 20 | 55 | 25 | 25 | 30 | |
2 | Scyther | Green | False | 70 | 110 | 80 | 55 | 80 | 105 | |
Red | 10 | Paras | Red | True | 35 | 70 | 55 | 45 | 55 | 25 |
9 | Magmar | Red | False | 65 | 95 | 57 | 100 | 85 | 93 | |
11 | Parasect | Red | False | 60 | 95 | 80 | 60 | 80 | 30 | |
Yellow | 15 | Kadabra | Yellow | True | 40 | 35 | 30 | 120 | 70 | 10 |
12 | Pikachu | Yellow | True | 35 | 55 | 40 | 50 | 50 | 90 | |
14 | Psyduck | Yellow | True | 50 | 52 | 48 | 65 | 50 | 55 |
Observa cómo los parámetros adicionales se pasan a la función apply, no a sort_values mismo. Internamente, apply se asegura de que los parámetros correctos se pasen a cualquier función que esté aplicando.
Usando lambdas como argumento para apply
Como nota final, a veces no querrás escribir una definición de función completa si lo que quieres lograr es muy simple. En este caso, puedes pasar una función lambda. En nuestro siguiente ejemplo usaremos este enfoque para seleccionar de cada grupo el pokémon cuyo nombre aparece primero en orden alfabético en cada grupo:
pdata.groupby('Color').apply(lambda df: df.sort_values('Name').head(1) )
Name | Color | Evolves | HP | Attack | Defense | SpAtk | SpDef | Speed | ||
---|---|---|---|---|---|---|---|---|---|---|
Color | ||||||||||
Blue | 4 | Dratini | Blue | True | 41 | 64 | 45 | 50 | 50 | 50 |
Green | 3 | Bulbasaur | Green | True | 45 | 49 | 49 | 65 | 65 | 45 |
Red | 8 | Charmander | Red | True | 39 | 52 | 43 | 60 | 50 | 65 |
Yellow | 13 | Abra | Yellow | True | 25 | 20 | 15 | 105 | 55 | 90 |
La práctica hace la perfección
Apply es una función increíblemente flexible que, si se usa de maneras creativas, te permite resolver una gran variedad de problemas en manipulación y transformación de datos. Este artículo te expuso a los conceptos básicos de la función, pero asegúrate de estudiarla más y experimentar con conjuntos de datos reales.
Como comentario de cierre, me gustaría compartir una cita del libro Python For Data Analysis (2da edición), en el que esta serie está ampliamente basada:
Más allá de estas mecánicas de uso básico, obtener el máximo provecho de apply puede requerir algo de creatividad. Lo que ocurre dentro de la función pasada depende de ti; solo necesita devolver un objeto pandas o un valor escalar.
Eso es todo el Pandas que tengo que compartir, por ahora
Con este artículo, concluimos nuestra serie Pandas práctico. Ha sido muy divertido escribir, y realmente espero que hayas aprendido una o dos cosas interesantes en el camino.
Pandas, como cualquier otra herramienta de software o habilidad, requiere una buena cantidad de práctica antes de que se vuelva verdaderamente útil. No te preocupes si no sabes inmediatamente cómo abordar un conjunto de datos o qué función llamar, con experiencia y exposición continua se volverá algo natural.
Si necesitas ayuda, recuerda que Pandas tiene algunas de las mejores documentaciones disponibles y una comunidad enorme y útil que te guiará para encontrar una solución. ¡Te deseo un proceso de aprendizaje feliz y productivo!
¡Gracias por leer!
Qué hacer a continuación
- Comparte este artículo con amigos y colegas. Gracias por ayudarme a llegar a personas que podrían encontrar útil esta información.
- Puedes encontrar el código fuente para esta serie en este repositorio.
- Este artículo está basado en el libro: Python for Data Analysis.
- Envíame un email con preguntas, comentarios o sugerencias (está en la página Autor)