En el último artículo, aprendimos sobre las dos estructuras de datos básicas de pandas: Series y DataFrames. También construimos un par por nuestra cuenta y aprendimos los conceptos básicos de indexación y selección.
Hoy aprenderemos un poco más sobre seleccionar y filtrar elementos de las estructuras de datos de Pandas. Esto puede parecer un tema increíblemente básico, pero es muy útil. Por eso es importante entenderlo bien antes de abordar temas más avanzados.
Saber cómo manipular datos es una de las habilidades más importantes para cualquiera que trabaje en ciencia de datos y aprendizaje automático, y la base de esas habilidades es la selección y filtrado de datos.
¡Bien, comencemos!
Jugando con Series
Seleccionar elementos de un objeto Series es bastante directo, los siguientes son ejemplos de diferentes formas de seleccionar elementos de una serie pequeña de 8 elementos
import pandas as pd
import numpy as np
ser = pd.Series(np.arange(8), index=['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])
print(ser)
a 0
b 1
c 2
d 3
e 4
f 5
g 6
h 7
dtype: int64
# Puedes seleccionar elementos de una serie usando su índice
ser['d']
3
# También puedes pasar una lista de elementos de índice si necesitas recuperar más de un elemento
ser[['a', 'd', 'g']]
a 0
d 3
g 6
dtype: int64
¡Pandas es tan genial que incluso te deja hacer selección de slices basadas en índices! Hay una distinción importante entre esto y los slices regulares: El último elemento del slice está incluido.
# Selecciona todos los elementos de b a g (ambos extremos incluidos)
ser['b':'g']
b 1
c 2
d 3
e 4
f 5
g 6
dtype: int64
El hecho de que no estés usando el índice por defecto no significa que la selección basada en posición no esté permitida. Todavía puedes seleccionar elementos de una Serie usando enteros.
# Selecciona el tercero (índice 2, ¿recuerdas? 0-indexado) de nuestra serie
ser[2]
2
# Ahora, selecciona los elementos en los índices 2, 3 y 6
ser[[2,3,6]]
c 2
d 3
g 6
dtype: int64
# Y finalmente, la selección por slices todavía está disponible (pero en este caso, el último elemento es excluido como de costumbre)
ser[2:8]
c 2
d 3
e 4
f 5
g 6
h 7
dtype: int64
Jugando con DataFrames
Debido a una dimensión extra, seleccionar elementos de DataFrames es más complejo que de Series. Comenzaremos con el escenario más básico: Seleccionar columnas completas.
pokedata = {'Name': ['Abra', 'Koffing', 'Milcery', 'Pikachu', 'Shellder', 'Vulpix'],
'Type': ['Psychic', 'Poison', 'Fairy', 'Electric', 'Water', 'Fire'],
'HP': [25, 40, 45, 35, 30, 38],
'Speed': [90, 35, 34, 90, 40, 65],
'Color': ['Yellow', 'Purple', 'White', 'Yellow', 'Purple', 'Red'],
'FirstGen': [True, True, False, True, True, True]}
# Usaremos la columna Name como índice
pframe = pd.DataFrame(pokedata).set_index('Name')
pframe
Name | Type | HP | Speed | Color | FirstGen |
---|---|---|---|---|---|
Abra | Psychic | 25 | 90 | Yellow | True |
Koffing | Poison | 40 | 35 | Purple | True |
Milcery | Fairy | 45 | 34 | White | False |
Pikachu | Electric | 35 | 90 | Yellow | True |
Shellder | Water | 30 | 40 | Purple | True |
Vulpix | Fire | 38 | 65 | Red | True |
# Puedes seleccionar una columna del frame pasando el nombre entre corchetes
pframe['Type']
Name
Abra Psychic
Koffing Poison
Milcery Fairy
Pikachu Electric
Shellder Water
Vulpix Fire
Name: Type, dtype: object
# Si pasas una lista de nombres de columnas las recuperarás en ese orden
pframe[['FirstGen', 'HP', 'Color']]
Name | FirstGen | HP | Color |
---|---|---|---|
Abra | True | 25 | Yellow |
Koffing | True | 40 | Purple |
Milcery | False | 45 | White |
Pikachu | True | 35 | Yellow |
Shellder | True | 30 | Purple |
Vulpix | True | 38 | Red |
Los corchetes también soportan selección basada en contenido. Seleccionemos filas que satisfacen criterios específicos para ver cómo funciona.
# Selecciona todos los Pokemon con velocidad menor a 50
pframe[pframe['Speed'] < 50]
Name | Type | HP | Speed | Color | FirstGen |
---|---|---|---|---|---|
Koffing | Poison | 40 | 35 | Purple | True |
Milcery | Fairy | 45 | 34 | White | False |
Shellder | Water | 30 | 40 | Purple | True |
# Selecciona todos los Pokemon amarillos
pframe[pframe['Color'] == 'Yellow']
Name | Type | HP | Speed | Color | FirstGen |
---|---|---|---|---|---|
Abra | Psychic | 25 | 90 | Yellow | True |
Pikachu | Electric | 35 | 90 | Yellow | True |
# Selecciona todos los Pokemon de primera generación con HP mayor a 37
pframe[(pframe['FirstGen'] == True) & (pframe['HP'] > 37)]
Name | Type | HP | Speed | Color | FirstGen |
---|---|---|---|---|---|
Koffing | Poison | 40 | 35 | Purple | True |
Vulpix | Fire | 38 | 65 | Red | True |
Puedes ser tan específico como quieras con esta forma de filtrar. Seleccionar subconjuntos de filas es una habilidad muy útil, así que juega un poco seleccionando basándote en tus propias condiciones.
Bien, creo que estamos bien cuando se trata de seleccionar basándose en etiquetas de columna, ahora seleccionemos filas específicas basándose en el índice. Para esto, Pandas te ofrece dos funciones muy valiosas: loc e iloc.
loc te permite seleccionar basándose en etiquetas de eje, mientras que iloc te permite seleccionar basándose en enteros que representan la posición de la fila. De nuevo es más fácil de entender con ejemplos:
# Selecciona la fila con índice Shellder
pframe.loc['Shellder']
Type Water
HP 30
Speed 40
Color Purple
FirstGen True
Name: Shellder, dtype: object
# Puedes pasar una lista de valores de índice y obtener las filas en el orden especificado
pframe.loc[['Shellder', 'Abra', 'Pikachu']]
Name | Type | HP | Speed | Color | FirstGen |
---|---|---|---|---|---|
Shellder | Water | 30 | 40 | Purple | True |
Abra | Psychic | 25 | 90 | Yellow | True |
Pikachu | Electric | 35 | 90 | Yellow | True |
# También es posible obtener solo un subconjunto de columnas usando loc
# Obtengamos datos para Shellder, pero solo el Type y Color
pframe.loc['Shellder', ['Type', 'Color']]
Type Water
Color Purple
Name: Shellder, dtype: object
# Si en cambio, necesitas seleccionar elementos basándote en el orden, puedes usar iloc
# Por ejemplo, la siguiente línea selecciona la tercera fila (índice 2, porque 0-indexado)
pframe.iloc[2]
Type Fairy
HP 45
Speed 34
Color White
FirstGen False
Name: Milcery, dtype: object
# Tal como loc, puedes pasar una lista de índices y retornará un dataframe con filas en ese orden
pframe.iloc[[2,4,0]]
Name | Type | HP | Speed | Color | FirstGen |
---|---|---|---|---|---|
Milcery | Fairy | 45 | 34 | White | False |
Shellder | Water | 30 | 40 | Purple | True |
Abra | Psychic | 25 | 90 | Yellow | True |
# ¿Recuerdas ese pequeño truco para seleccionar solo un subconjunto de columnas? También funciona para iloc
# Esto selecciona la tercera fila, y solo el Type (columna en posición 0) y HP (columna en posición 1)
pframe.iloc[2, [0, 1]]
Type Fairy
HP 45
Name: Milcery, dtype: object
Una palabra sobre índices numéricos
loc e iloc son bastante directos, pero es importante entender la diferencia entre ellos. Esto es especialmente cierto cuando se trata de índices numéricos. Un dataframe con índices numéricos que no están en orden, comenzando en 0 y sin interrupción se comportará raro a menos que recuerdes cómo difieren esas funciones. Toma el siguiente dataframe como ejemplo:
frame = pd.DataFrame(np.arange(36).reshape(6,6),
columns = ['a', 'b', 'c', 'd', 'e', 'f' ],
index = [5, 3, 1, 4, 2, 0])
frame
a | b | c | d | e | f | |
---|---|---|---|---|---|---|
5 | 0 | 1 | 2 | 3 | 4 | 5 |
3 | 6 | 7 | 8 | 9 | 10 | 11 |
1 | 12 | 13 | 14 | 15 | 16 | 17 |
4 | 18 | 19 | 20 | 21 | 22 | 23 |
2 | 24 | 25 | 26 | 27 | 28 | 29 |
0 | 30 | 31 | 32 | 33 | 34 | 35 |
# Ahora, revisemos qué retornan loc[2] e iloc[2]
frame.loc[2]
a 24
b 25
c 26
d 27
e 28
f 29
Name: 2, dtype: int64
frame.iloc[2]
a 12
b 13
c 14
d 15
e 16
f 17
Name: 1, dtype: int64
¿Puedes ver que retornan filas diferentes? Esto sucede porque loc[2]
busca una fila con un índice con un valor de dos, en este caso, la penúltima fila. Por otro lado, iloc[2]
solo busca la tercera fila, la que tiene índice posicional 2, comenzando desde 0. ¡Si recuerdas esto, no tendrás problema lidiando con dataframes con índices numéricos!
La selección es un tema complejo
Una de las grandes cosas sobre Pandas es lo fácil que hace seleccionar solo los datos que necesitas. Como ya sabes, casi toda aplicación avanzada descansa en esta base, ¡y ahora sabes cómo usarla!
Ahora que podemos seleccionar datos y entender cómo funcionan los índices, podemos lidiar con dos temas interesantes: Reindexación y eliminación de entradas.
¡Gracias por leer!
Qué hacer después
- 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)