Show code cell source
import os
# Por precaución, cambiamos el directorio activo de Python a aquel que contenga este notebook
if "PAD-book" in os.listdir():
os.chdir(r"PAD-book/Laboratorio-Computacional-de-Analytics/S4 - Explorar, modificar y visualizar bases de datos/S4.TU1/")
Explorar bases de datos#
Explorar bases de datos es uno de los pasos esenciales en cualquier proyecto de analítica de datos, pues nos permite identificar aspectos útiles del caso de estudio.
En este tutorial establecemos nociones fundamentales sobre el uso del paquete pandas
para explorar datos.
Requisitos#
Para desarrollar este tutorial necesitarás:
Utilizar arreglos y operaciones básicas y vectorizadas en
numpy
.Crear, consultar y utilizar métodos para explorar y manipular objetos tipo
DataFrame
enpandas
.
Objetivos#
Al final de este tutorial podrás:
1. Indexar un DataFrame
empleando índices múltiples.
2. Aplicar distintos tipos de filtrado utilizando pandas
.
1. Indexar un DataFrame
#
Entre los aspectos a detallar al explorar una base de datos, se encuentra la llave o el ID que distingue un registro de otro. Esta llave no siempre se conoce de antemano o, en ocasiones, deseamos modificarla.
Para distinguir una observación (o fila) de otra en un DataFrame
, pandas
define un objeto de tipo Index
(o MultiIndex
).
Al analizar datos generalmente requerimos que el índice de un DataFrame
corresponda a una llave de la base de datos. Todo registro debe tener asignada una llave única, con el fin de distinguirlo de los demás registros. En ocasiones debemos usar más de una característica del registro para crear una llave, es decir, debemos definir un índice de múltiples niveles.
Importamos los paquetes pandas
y numpy
.
import numpy as np
import pandas as pd
Veamos el siguiente ejemplo para considerar un caso en el que se necesite más de una columna para indexar un DataFrame
:
Ejemplo 1#
En la siguiente celda de código declaramos un DataFrame
que contiene información de algunas personas.
nombres = pd.DataFrame([["Jorge", "Suárez", 28, "Bogotá"],
["Laura", "Poveda", 37, "Lima" ],
["Pablo", "Stecco", 30, "Lima" ],
["Jorge", "Poveda", 30, "Bogotá"]],
columns = ["Nombre", "Apellido", "Edad", "Ciudad"])
nombres
Nombre | Apellido | Edad | Ciudad | |
---|---|---|---|---|
0 | Jorge | Suárez | 28 | Bogotá |
1 | Laura | Poveda | 37 | Lima |
2 | Pablo | Stecco | 30 | Lima |
3 | Jorge | Poveda | 30 | Bogotá |
Si bien es posible utilizar la columna "nombre"
para indexar la base de datos, este índice no sería una llave, puesto que uno de los valores del índice ("Jorge"
) corresponde a más de un registro:
["Jorge", "Suárez", 28, "Bogotá"]
y["Jorge", "Poveda", 30, "Bogotá"]
.
nombres.index = nombres['Nombre']
nombres.loc['Jorge']
Nombre | Apellido | Edad | Ciudad | |
---|---|---|---|---|
Nombre | ||||
Jorge | Jorge | Suárez | 28 | Bogotá |
Jorge | Jorge | Poveda | 30 | Bogotá |
Dado que queremos utilizar el nombre para construir la llave de nuestra base de datos, podemos agregar a cada valor del índice el apellido del registro y así evitar valores duplicados.
nombres_indice = pd.Index(data = nombres[["Nombre", "Apellido"]])
nombres.index = nombres_indice
nombres
Nombre | Apellido | Edad | Ciudad | |
---|---|---|---|---|
(Jorge, Suárez) | Jorge | Suárez | 28 | Bogotá |
(Laura, Poveda) | Laura | Poveda | 37 | Lima |
(Pablo, Stecco) | Pablo | Stecco | 30 | Lima |
(Jorge, Poveda) | Jorge | Poveda | 30 | Bogotá |
Con la modificación anterior podemos referirnos a la información de "Jorge Suárez"
o de "Jorge Poveda"
.
nombres.loc[[("Jorge", "Poveda")]]
Nombre | Apellido | Edad | Ciudad | |
---|---|---|---|---|
(Jorge, Poveda) | Jorge | Poveda | 30 | Bogotá |
1.1. Objeto de tipo MultiIndex
#
La clase MultiIndex
permite crear un índice múltiple para los registros de un DataFrame
o un Series
. Podemos declarar objetos de tipo MultiIndex
a partir de métodos que reciben diferentes tipos de objeto por parámetro.
Métodos |
|
---|---|
|
A partir de un arreglo de arreglos |
|
A partir del producto cartesiano de estructuras de datos |
|
A partir de una lista de tuplas |
|
A partir de la lista actual con otra lista |
Ejemplo 2#
Creamos un MultiIndex
a partir de las columnas "nombre"
y "apellido"
y lo utilizamos para indexar el DataFrame
contenido en la variable nombres
.
Usamos el método from_arrays
.
nombres_indice = pd.MultiIndex.from_arrays([nombres["Nombre"],nombres["Apellido"]])
nombres_indice
MultiIndex([('Jorge', 'Suárez'),
('Laura', 'Poveda'),
('Pablo', 'Stecco'),
('Jorge', 'Poveda')],
names=['Nombre', 'Apellido'])
Después usamos el método reindex
para asignar el nuevo MultiIndex
.
nombres.reindex(nombres_indice)
Nombre | Apellido | Edad | Ciudad | ||
---|---|---|---|---|---|
Nombre | Apellido | ||||
Jorge | Suárez | Jorge | Suárez | 28 | Bogotá |
Laura | Poveda | Laura | Poveda | 37 | Lima |
Pablo | Stecco | Pablo | Stecco | 30 | Lima |
Jorge | Poveda | Jorge | Poveda | 30 | Bogotá |
También podemos nombrar columnas que tengan múltiples niveles a partir de un objeto MultiIndex
.
Ejemplo 3#
A continuación importamos una base de datos llamada "Bid-Cornell.csv"
que reúne información sobre un grupo de ciudadanos y los medios de comunicación que utilizan para informarse sobre noticias generales o noticias acerca del COVID-19.
df_covid_19 = pd.read_csv("./Archivos/BID-Cornell.csv", index_col = 0)
df_covid_19
medios_noti_redessociales | medios_noti_chat | medios_noti_periodicos | medios_noti_tv | medios_noti_radio | medios_covid_redessociales | medios_covid_chat | medios_covid_periodicos | medios_covid_tv | medios_covid_radio | |
---|---|---|---|---|---|---|---|---|---|---|
id | ||||||||||
1000060.0 | Siempre | Siempre | Casi siempre | A veces | Nunca | Siempre | Siempre | Siempre | A veces | Nunca |
1000734.0 | Casi siempre | A veces | A veces | Casi siempre | Casi siempre | Casi siempre | Casi siempre | A veces | Casi siempre | Casi siempre |
1000120.0 | Nunca | A veces | A veces | Siempre | Siempre | Nunca | A veces | A veces | Siempre | Siempre |
1000235.0 | Siempre | Siempre | A veces | A veces | A veces | Siempre | Siempre | Nunca | A veces | Nunca |
1000828.0 | Casi siempre | A veces | A veces | A veces | A veces | Casi siempre | Casi siempre | A veces | A veces | Nunca |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
17001614.0 | A veces | A veces | Casi siempre | Siempre | Casi siempre | Nunca | A veces | Casi siempre | Siempre | Casi siempre |
17017448.0 | Siempre | Siempre | A veces | A veces | Nunca | Siempre | Siempre | A veces | A veces | Nunca |
17013032.0 | Siempre | Siempre | Siempre | Siempre | Siempre | Siempre | Casi siempre | Siempre | Siempre | Siempre |
17014760.0 | Siempre | Siempre | Siempre | Casi siempre | Casi siempre | Siempre | Siempre | Siempre | Casi siempre | Casi siempre |
17003990.0 | Casi siempre | Nunca | Nunca | Casi siempre | Casi siempre | Casi siempre | Nunca | A veces | Casi siempre | Casi siempre |
216092 rows × 10 columns
Los nombres de las columnas de df_covid_19
están estructurados de la siguiente manera:
Prefijo:
Medios de comunicación (
"medios"
).
Contexto de la información:
Noticias generales (
"noti"
).Noticias sobre el COVID-19 (
"covid"
).
Medio de comunicación empleado:
Redes Sociales (
"redessociales"
).Chat (
"chat"
).Periodicos (
"periodicos"
).TV (
"tv"
).Radio (
"radio"
).
Por ejemplo, "medios_noti_redessociales"
representa que el medio de comunicación utilizado para informarse acerca de noticias generales es las redes sociales.
Podemos utilizar un objeto MultiIndex
para nombrar las columnas con un mejor orden, coherente con la estructura descrita. Seccionamos el nombre de cada columna donde coincidan guiones bajos ("_"
) empleando métodos de pandas
para columnas con datos de tipo str
.
#Obtenemosla lista de columnas del DataFrame y con .str tomamos para cada una
#lo que está después de "medios_" (por eso el [7:])
df_covid_19.columns = df_covid_19.columns.str[7:]
#Separamos las cadenas de texto al encontrar un "_"
#El parámetro expand nos indica si deseamos dividir la cadena en columnas separadas (creando el multiindex)
indice_multiple_columnas = df_covid_19.columns.str.split("_", expand = True)
indice_multiple_columnas
MultiIndex([( 'noti', 'redessociales'),
( 'noti', 'chat'),
( 'noti', 'periodicos'),
( 'noti', 'tv'),
( 'noti', 'radio'),
('covid', 'redessociales'),
('covid', 'chat'),
('covid', 'periodicos'),
('covid', 'tv'),
('covid', 'radio')],
)
#Reasignamos el multinindex que creamos antes a las columnas del DataFrame (df)
df_covid_19.columns = indice_multiple_columnas
df_covid_19
noti | covid | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
redessociales | chat | periodicos | tv | radio | redessociales | chat | periodicos | tv | radio | |
id | ||||||||||
1000060.0 | Siempre | Siempre | Casi siempre | A veces | Nunca | Siempre | Siempre | Siempre | A veces | Nunca |
1000734.0 | Casi siempre | A veces | A veces | Casi siempre | Casi siempre | Casi siempre | Casi siempre | A veces | Casi siempre | Casi siempre |
1000120.0 | Nunca | A veces | A veces | Siempre | Siempre | Nunca | A veces | A veces | Siempre | Siempre |
1000235.0 | Siempre | Siempre | A veces | A veces | A veces | Siempre | Siempre | Nunca | A veces | Nunca |
1000828.0 | Casi siempre | A veces | A veces | A veces | A veces | Casi siempre | Casi siempre | A veces | A veces | Nunca |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
17001614.0 | A veces | A veces | Casi siempre | Siempre | Casi siempre | Nunca | A veces | Casi siempre | Siempre | Casi siempre |
17017448.0 | Siempre | Siempre | A veces | A veces | Nunca | Siempre | Siempre | A veces | A veces | Nunca |
17013032.0 | Siempre | Siempre | Siempre | Siempre | Siempre | Siempre | Casi siempre | Siempre | Siempre | Siempre |
17014760.0 | Siempre | Siempre | Siempre | Casi siempre | Casi siempre | Siempre | Siempre | Siempre | Casi siempre | Casi siempre |
17003990.0 | Casi siempre | Nunca | Nunca | Casi siempre | Casi siempre | Casi siempre | Nunca | A veces | Casi siempre | Casi siempre |
216092 rows × 10 columns
De esta manera, si queremos tener acceso a la información sobre el uso de las redes sociales como medio de comunicación para informarse sobre el COVID-19, podemos usar el método loc
.
df_covid_19.loc[:, ('covid', 'redessociales')]
id
1000060.0 Siempre
1000734.0 Casi siempre
1000120.0 Nunca
1000235.0 Siempre
1000828.0 Casi siempre
...
17001614.0 Nunca
17017448.0 Siempre
17013032.0 Siempre
17014760.0 Siempre
17003990.0 Casi siempre
Name: (covid, redessociales), Length: 216092, dtype: object
2. Filtrar bases de datos utilizando la librería pandas
#
2.1. Filtrado de posiciones no consecutivas#
Si las posiciones que queremos seleccionar no son consecutivas, debemos representarlas en una lista. A continuación, vemos un ejemplo de esto.
Ejemplo 5#
Se nos solicita mostrar un DataFrame
que contenga únicamente las 10 primeras y las 10 últimas observaciones del DataFrame
df_covid_19
.
posiciones_filas = list(range(10)) + list(range(-10,0))
df_10_y_10 = df_covid_19.iloc[posiciones_filas, : ]
df_10_y_10
noti | covid | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
redessociales | chat | periodicos | tv | radio | redessociales | chat | periodicos | tv | radio | |
id | ||||||||||
1000060.0 | Siempre | Siempre | Casi siempre | A veces | Nunca | Siempre | Siempre | Siempre | A veces | Nunca |
1000734.0 | Casi siempre | A veces | A veces | Casi siempre | Casi siempre | Casi siempre | Casi siempre | A veces | Casi siempre | Casi siempre |
1000120.0 | Nunca | A veces | A veces | Siempre | Siempre | Nunca | A veces | A veces | Siempre | Siempre |
1000235.0 | Siempre | Siempre | A veces | A veces | A veces | Siempre | Siempre | Nunca | A veces | Nunca |
1000828.0 | Casi siempre | A veces | A veces | A veces | A veces | Casi siempre | Casi siempre | A veces | A veces | Nunca |
1000352.0 | A veces | A veces | Siempre | Casi siempre | Casi siempre | A veces | A veces | Siempre | Siempre | Siempre |
1000746.0 | Casi siempre | Casi siempre | Casi siempre | Casi siempre | Nunca | Casi siempre | Casi siempre | Casi siempre | Casi siempre | Nunca |
1000079.0 | Siempre | Siempre | Siempre | Nunca | A veces | Siempre | Siempre | Siempre | Nunca | A veces |
1000110.0 | Siempre | Siempre | Siempre | Siempre | Siempre | Siempre | Siempre | Siempre | Siempre | Siempre |
1000221.0 | Casi siempre | A veces | Siempre | Siempre | Nunca | Casi siempre | A veces | Siempre | Siempre | Nunca |
17000500.0 | Siempre | Siempre | Siempre | Siempre | Casi siempre | Casi siempre | Casi siempre | Casi siempre | Siempre | Casi siempre |
17000564.0 | Casi siempre | A veces | A veces | Nunca | A veces | Casi siempre | Casi siempre | A veces | Nunca | A veces |
17015108.0 | A veces | A veces | A veces | Nunca | Nunca | A veces | A veces | A veces | Nunca | Nunca |
17020582.0 | Siempre | Siempre | Siempre | Siempre | Siempre | Siempre | Siempre | Siempre | Siempre | Siempre |
17003368.0 | Siempre | Siempre | A veces | A veces | A veces | Siempre | Siempre | A veces | Siempre | A veces |
17001614.0 | A veces | A veces | Casi siempre | Siempre | Casi siempre | Nunca | A veces | Casi siempre | Siempre | Casi siempre |
17017448.0 | Siempre | Siempre | A veces | A veces | Nunca | Siempre | Siempre | A veces | A veces | Nunca |
17013032.0 | Siempre | Siempre | Siempre | Siempre | Siempre | Siempre | Casi siempre | Siempre | Siempre | Siempre |
17014760.0 | Siempre | Siempre | Siempre | Casi siempre | Casi siempre | Siempre | Siempre | Siempre | Casi siempre | Casi siempre |
17003990.0 | Casi siempre | Nunca | Nunca | Casi siempre | Casi siempre | Casi siempre | Nunca | A veces | Casi siempre | Casi siempre |
Ejemplo 6#
Se nos solicita mostrar un DataFrame
que contenga únicamente las 3 primeras columnas y de la columna 6 en adelante del DataFrame
df_covid_19
.
posiciones_columnas = list(range(3)) + list(range(5, len(df_covid_19.columns)))
df_3_y_6_en_adelante = df_covid_19.iloc[ : , posiciones_columnas]
df_3_y_6_en_adelante
noti | covid | |||||||
---|---|---|---|---|---|---|---|---|
redessociales | chat | periodicos | redessociales | chat | periodicos | tv | radio | |
id | ||||||||
1000060.0 | Siempre | Siempre | Casi siempre | Siempre | Siempre | Siempre | A veces | Nunca |
1000734.0 | Casi siempre | A veces | A veces | Casi siempre | Casi siempre | A veces | Casi siempre | Casi siempre |
1000120.0 | Nunca | A veces | A veces | Nunca | A veces | A veces | Siempre | Siempre |
1000235.0 | Siempre | Siempre | A veces | Siempre | Siempre | Nunca | A veces | Nunca |
1000828.0 | Casi siempre | A veces | A veces | Casi siempre | Casi siempre | A veces | A veces | Nunca |
... | ... | ... | ... | ... | ... | ... | ... | ... |
17001614.0 | A veces | A veces | Casi siempre | Nunca | A veces | Casi siempre | Siempre | Casi siempre |
17017448.0 | Siempre | Siempre | A veces | Siempre | Siempre | A veces | A veces | Nunca |
17013032.0 | Siempre | Siempre | Siempre | Siempre | Casi siempre | Siempre | Siempre | Siempre |
17014760.0 | Siempre | Siempre | Siempre | Siempre | Siempre | Siempre | Casi siempre | Casi siempre |
17003990.0 | Casi siempre | Nunca | Nunca | Casi siempre | Nunca | A veces | Casi siempre | Casi siempre |
216092 rows × 8 columns
2.2. Filtrado por niveles#
El objeto IndexSlice
nos permite indicar, para cada nivel de un objeto MultiIndex
, qué elementos queremos incluir.
Ejemplo 7#
A partir de la base de datos del BID, nos solicitan seleccionar las columnas que tengan información de noticias generales ("noti"
) sobre el uso del "chat"
o "tv"
:
df_covid_19.loc[:, pd.IndexSlice["noti", ('chat','tv')]]
noti | ||
---|---|---|
chat | tv | |
id | ||
1000060.0 | Siempre | A veces |
1000734.0 | A veces | Casi siempre |
1000120.0 | A veces | Siempre |
1000235.0 | Siempre | A veces |
1000828.0 | A veces | A veces |
... | ... | ... |
17001614.0 | A veces | Siempre |
17017448.0 | Siempre | A veces |
17013032.0 | Siempre | Siempre |
17014760.0 | Siempre | Casi siempre |
17003990.0 | Nunca | Casi siempre |
216092 rows × 2 columns
2.3. Filtrado condicional#
El filtrado condicional nos permite indicarle a un DataFrame
, mediante un arreglo de objetos tipo bool
, cuales elementos incluir. Podemos declarar un arreglo de objetos tipo bool
aplicando, término a término, los operadores relacionales (==
, !=
, >
, <
, >=
, <=
) o lógicos (&
, |
, ~
) que ya conoces.
A continuación, encuentras un ejemplo utilizando un operador relacional:
np.array([2, 2, 3, 4]) >= 3
>>> array([False, False, True, True])
A continuación, encuentras un ejemplo utilizando un operador lógico:
np.array([True, False, False, True]) | np.array([False, True, False, True])
>>> array([ True, True, False, True])
Veamos un ejemplo de cómo aplicar filtrado condicional a las filas de un DataFrame
.
Ejemplo 8#
Seleccionemos los registros correspondientes a personas menores de 35 años, oriundas de "Lima"
.
nombres[(nombres['Edad'] <= 35) & (nombres['Ciudad'] == "Lima")]
Nombre | Apellido | Edad | Ciudad | |
---|---|---|---|---|
(Pablo, Stecco) | Pablo | Stecco | 30 | Lima |
Veamos un ejemplo de cómo aplicar filtrado condicional a las columnas de un DataFrame
.
Ejemplo 9#
Seleccionemos los registros correspondientes a personas menores de 35 años, oriundas de "Lima"
, exluyendo las columnas cuyo nombre tenga menos de 6 caracteres.
Para esto, utilizamos el atributo loc
.
nombres.loc[(nombres['Edad'] <= 35) & (nombres['Ciudad'] == "Lima"), nombres.columns.str.len() > 5]
Nombre | Apellido | Ciudad | |
---|---|---|---|
(Pablo, Stecco) | Pablo | Stecco | Lima |
Referencias#
Pandas (2020). Documentación sobre el método .iloc(). Recuperado el 11 de febrero de 2020 de: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.iloc.html
Pandas (2020). Documentación sobre el método .loc(). Recuperado el 11 de febrero de 2020 de: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.loc.html
Pandas (2020). Documentación sobre el método .IndexSlice() . Recuperado el 11 de febrero de 2020 de: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.IndexSlice.html
BID (2020). Encuesta Coronavirus BID/Cornell. Recuperado el 11 de febrero de 2020 de: https://data.iadb.org/DataCatalog/Dataset#DataCatalogID=11319/28452
Créditos#
Autores: Juan David Reyes Jaimes, Jorge Esteban Camargo Forero, Alejandro Mantilla Redondo, Diego Alejandro Cely Gómez
Fecha última actualización: 12/07/2022