Introducción
Las tablas de datos son una forma común de almacenar información en bases de datos relacionales. En este cuaderno, aprenderemos a usar la biblioteca Pandas para trabajar con tablas de datos en Python.
Matemáticamente, se puede ver una tabla de datos como un conjunto de tuplas, donde cada tupla es un vector de datos. En este sentido, una tabla de datos es un conjunto de vectores de datos que comparten el mismo dominio. Por ejemplo, la siguiente tabla de datos tiene 3 vectores de datos:
Nombre | Edad | Sexo |
---|---|---|
Juan | 25 | M |
María | 30 | F |
Pedro | 28 | M |
Desde un punto de vista matemático, esta tabla puede ser vista como un conjunto de 3 elementos, donde cada elemento es una terna ordenada:
$$ \{ (\text{Juan}, 25, M), (\text{María}, 30, F), (\text{Pedro}, 28, M) \}. $$
Este es un subconjunto del producto cartesiano de los conjuntos de nombres, edades y sexos, es decir, es una relación ternaria.
Así, de manera general, una tabla de datos ($R$) es una relación entre conjuntos de datos ($A_1,\ldots,A_n$), es decir, $R\subseteq A_1\times\ldots\times A_n$. A cada columna de la tabla ($A_1,\ldots,A_n$) se le llama atributo; y a cada fila (elemento de $R$) se le llama registro y a cada componente de un registro se lo llama campo.
Para trabajar con tablas de datos en Python, se puede utilizar la librería pandas
; es usual importarla con el alias pd
. Adicionalmente, usaremos la librería numpy
para trabajar con arreglos (tuplas) de datos.
import pandas as pd
import numpy as np
Iniciaremos creando la tabla de datos anterior con Pandas, esto se lo puede realizar de varias formas. La primera forma, que es menos común, es mediante una arreglo datos de numpy, esta refleja la estructura matemática de una tabla de datos. Primero definimos el arreglo de datos y luego creamos la tabla de datos con Pandas, usando la función pd.DataFrame
. Para una mejor visualización, usaremos la función display
de la librería IPython.display
.
# Creación del arreglo de datos
data = np.array( [('Juan', 25, 'M'), ('Maria', 30, 'F'), ('Pedro', 28, 'M')] )
# Visualización del arreglo de datos
print(data)
# Generación del DataFrame
df = pd.DataFrame(data, columns=['Nombre', 'Edad', 'Sexo'])
# Visualización del DataFrame
display(df)
[['Juan' '25' 'M'] ['Maria' '30' 'F'] ['Pedro' '28' 'M']]
Nombre | Edad | Sexo | |
---|---|---|---|
0 | Juan | 25 | M |
1 | Maria | 30 | F |
2 | Pedro | 28 | M |
# Generación de un DataFrame a partir de un diccionario
df = pd.DataFrame({'Nombre': ['Juan', 'Maria', 'Pedro'], 'Edad': [25, 30, 28], 'Sexo': ['M', 'F', 'M']})
# Visualización del DataFrame
display(df)
Nombre | Edad | Sexo | |
---|---|---|---|
0 | Juan | 25 | M |
1 | Maria | 30 | F |
2 | Pedro | 28 | M |
Adicionalmente, existen funciones para crear tablas de datos a partir de archivos de texto, como pd.read_csv
para archivos CSV, pd.read_excel
para archivos Excel, entre otros.
Operaciones básicas con tablas de datos
Las principales operaciones que se pueden realizar con tablas de datos son:
- Selección (SELECT)
- Proyección (PROJECT)
- Reunión (JOIN)
Para presentar la definición de estas operaciones, usaremos la siguiente tabla de datos:
Tabla de datos $R$
Id_alumno | Nombre | Edad | Sexo |
---|---|---|---|
1 | Juan | 25 | M |
2 | María | 30 | F |
3 | Pedro | 28 | M |
4 | Ana | 22 | F |
Tabla de datos $S$
Id_alumno | Asignatura | Calificación |
---|---|---|
1 | Matemática | 8 |
2 | Matemática | 9 |
1 | Física | 7 |
3 | Física | 5 |
5 | Física | 6 |
# Definimos el primer DataFrame R
dfR = pd.DataFrame({"Id_alumno": [1, 2, 3, 4], "Nombre": ["Juan", "Maria", "Pedro", "Ana"], "Edad": [25, 30, 28, 22], "Sexo": ["M", "F", "M", "F"]})
# Definimos el segundo DataFrame S
dfS = pd.DataFrame({'Id_alumno': [1, 2, 1, 3, 5], 'Asignatura': ['Matemáticas', 'Matemáticas', 'Física', 'Física', 'Matemática'], 'Calificación': [8, 9, 7, 5, 6]})
# Visualización del primer DataFrame
print('Tabla de datos R')
display(dfR)
# Visualización del segundo DataFrame
print('Tabla de datos S')
display(dfS)
Tabla de datos R
Id_alumno | Nombre | Edad | Sexo | |
---|---|---|---|---|
0 | 1 | Juan | 25 | M |
1 | 2 | Maria | 30 | F |
2 | 3 | Pedro | 28 | M |
3 | 4 | Ana | 22 | F |
Tabla de datos S
Id_alumno | Asignatura | Calificación | |
---|---|---|---|
0 | 1 | Matemáticas | 8 |
1 | 2 | Matemáticas | 9 |
2 | 1 | Física | 7 |
3 | 3 | Física | 5 |
4 | 5 | Matemática | 6 |
Selección (SELECT)
El operador selección toma los registros (filas) de una tabla de datos que satisfacen una condición dada. De manera general, si $R$ es una relación (tabla de datos) y $p$ es una condición, entonces la selección de $R$ con respecto a $p$ es: $$ \{ (x_1, x_2,\ldots,x_n)\in R : p(x_1, x_2,\ldots,x_n) \}. $$ Por ejemplo, de la tabla de datos $R$, queremos seleccionar los registros mayores de 25 años. El resultado sería: $$ \{ (x_1, x_2,x_3,x_4)\in R : x_3>25 \}, $$ es decir,
Id_alumno | Nombre | Edad | Sexo |
---|---|---|---|
2 | María | 30 | F |
3 | Pedro | 28 | M |
Existen varias formas de realizar esto con Pandas, una de ellas es mediante el método query
de un DataFrame. Este método toma una cadena de texto que representa la condición. Por ejemplo, para seleccionar los registros mayores de 25 años, se puede usar el siguiente código:
dfR.query('Edad > 25')
Id_alumno | Nombre | Edad | Sexo | |
---|---|---|---|---|
1 | 2 | Maria | 30 | F |
2 | 3 | Pedro | 28 | M |
Otra opción es mediante el método loc
, que toma una condición booleana:
dfR.loc[dfR['Edad'] > 25]
Id_alumno | Nombre | Edad | Sexo | |
---|---|---|---|---|
1 | 2 | Maria | 30 | F |
2 | 3 | Pedro | 28 | M |
Una tercera opción es seleccionando directamente los registro (esta opción suele ser más eficiente en tablas de datos grandes):
dfR[dfR['Edad'] > 25]
Id_alumno | Nombre | Edad | Sexo | |
---|---|---|---|---|
1 | 2 | Maria | 30 | F |
2 | 3 | Pedro | 28 | M |
Se pueden combinar condiciones con operadores lógicos, como &
para el operador AND
y |
para el operador OR
. Por ejemplo, para seleccionar los registros mayores de 25 años y de sexo masculino, se pueden usar los siguientes códigos:
dfR.query('Edad > 25 & Sexo == "M"')
Id_alumno | Nombre | Edad | Sexo | |
---|---|---|---|---|
2 | 3 | Pedro | 28 | M |
dfR.loc[(dfR['Edad']>25) & (dfR['Sexo']=='M')]
Id_alumno | Nombre | Edad | Sexo | |
---|---|---|---|---|
2 | 3 | Pedro | 28 | M |
dfR[(dfR['Edad']>25) & (dfR['Sexo']=='M')]
Id_alumno | Nombre | Edad | Sexo | |
---|---|---|---|---|
2 | 3 | Pedro | 28 | M |
En lenguaje SQL, el operador selección se representa con la cláusula WHERE
. Por ejemplo, la selección de los registros mayores de 25 años se puede escribir como:
SELECT *
FROM R
WHERE Edad>25;
Proyección (PROJECT)
El operador proyección toma los atributos (columnas) de una tabla de datos que se desean. De manera general, si $R$ es una relación (tabla de datos) y $A$ es un subconjunto de los atributos de $R$, entonces la proyección de $R$ con respecto a $A$ es: $$ \{ (x_\alpha, x_\beta,\ldots)\in R : (x_1,\ldots,x_\alpha,\ldots, x_\beta,\ldots) \in R\}, $$ donde $x_\alpha, x_\beta,\ldots$ son atributos pertenecientes a $A$.
Por ejemplo, de la tabla de datos $R$, queremos seleccionar los atributos Nombre
y Edad
(que son los atributos 2 y 3). El resultado sería:
$$
\{ (x_2, x_3)\in R : (x_1, x_2, x_3, x_4) \in R\},
$$
es decir,
Nombre | Edad |
---|---|
Juan | 25 |
María | 30 |
Pedro | 28 |
Ana | 22 |
En esta caso, también existen varias formas de realizar esto con Pandas, una de ellas es seleccionando directamente la lista de columnas. Por ejemplo, para seleccionar los atributos Nombre
y Edad
, se puede usar el siguiente código:
dfR[['Nombre', 'Edad']]
Nombre | Edad | |
---|---|---|
0 | Juan | 25 |
1 | Maria | 30 |
2 | Pedro | 28 |
3 | Ana | 22 |
También se puede usar el método loc
para seleccionar los atributos:
dfR.loc[:, ['Nombre', 'Edad']]
Nombre | Edad | |
---|---|---|
0 | Juan | 25 |
1 | Maria | 30 |
2 | Pedro | 28 |
3 | Ana | 22 |
Con esta última opción, se pueden combinar las operaciones de selección y proyección en una sola línea de código. Por ejemplo, para seleccionar los registros mayores de 25 años y de sexo masculino, y proyectar los atributos Nombre
y Edad
, se puede usar el siguiente código:
dfR.loc[dfR['Edad']>25, ['Nombre', 'Edad']]
Nombre | Edad | |
---|---|---|
1 | Maria | 30 |
2 | Pedro | 28 |
En el lenguaje SQL, el operador proyección se representa con la cláusula SELECT
. Por ejemplo, la proyección de los atributos Nombre
y Edad
se puede escribir como:
SELECT Nombre, Edad
FROM R;
Reunión (JOIN)
El operador reunión toma los registros de dos tablas de datos que tienen un valor común en un atributo. De manera general, si $R$ y $S$ son dos relaciones (tablas de datos) y $A$ es un atributo común a ambas, entonces la reunión de $R$ y $S$ con respecto a $A$ es:
$$
\{ (x_1, x_2,\ldots,x_n, y_1, y_2,\ldots,y_m)\in R\times S : x_i=y_j \text{ para algún } i,j\}.
$$
Por ejemplo, de las tablas de datos $R$ y $S$, queremos reunir los registros que tienen el mismo Id_alumno
. El resultado sería:
$$
\{ (x_1, x_2,x_3,x_4, y_1, y_2, y_3)\in R\times S : x_1=y_1\},
$$
es decir,
Id_alumno | Nombre | Edad | Sexo | Id_alumno | Asignatura | Calificación |
---|---|---|---|---|---|---|
1 | Juan | 25 | M | 1 | Matemática | 8 |
1 | Juan | 25 | M | 1 | Física | 7 |
2 | María | 30 | F | 2 | Matemática | 9 |
3 | Pedro | 28 | M | 3 | Física | 5 |
En Pandas, la reunión de dos tablas de datos se puede realizar con la función merge
. Esta función toma dos DataFrames y el nombre del atributo común. Por ejemplo, para reunir las tablas de datos $R$ y $S$ con respecto al atributo Id_alumno
, se puede usar el siguiente código:
pd.merge(dfR, dfS, on='Id_alumno')
Id_alumno | Nombre | Edad | Sexo | Asignatura | Calificación | |
---|---|---|---|---|---|---|
0 | 1 | Juan | 25 | M | Matemáticas | 8 |
1 | 1 | Juan | 25 | M | Física | 7 |
2 | 2 | Maria | 30 | F | Matemáticas | 9 |
3 | 3 | Pedro | 28 | M | Física | 5 |
Otra opción es mediante el método join
, para esto, es necesario que el atributo común sea el índice de los DataFrames. Esto último se puede lograr con el método set_index
. Por ejemplo, para reunir las tablas de datos $R$ y $S$ con respecto al atributo Id_alumno
, se puede usar el siguiente código:
dfR.join(dfS.set_index('Id_alumno'), on='Id_alumno', how='inner')
Id_alumno | Nombre | Edad | Sexo | Asignatura | Calificación | |
---|---|---|---|---|---|---|
0 | 1 | Juan | 25 | M | Matemáticas | 8 |
0 | 1 | Juan | 25 | M | Física | 7 |
1 | 2 | Maria | 30 | F | Matemáticas | 9 |
2 | 3 | Pedro | 28 | M | Física | 5 |
En este último código, hemos visto la opción how='inner'
(también disponible en la función join
), que indica que se tomarán los registros que tienen el mismo Id_alumno
en ambas tablas. Existen otras opciones, como how='left'
para tomar todos los registros de la tabla izquierda y los registros que tienen el mismo Id_alumno
en la tabla derecha, y how='right'
para hacer lo contrario. Para ejemplificar esto, observemos los siguientes resultados:
pd.merge(dfR, dfS, on='Id_alumno', how='left')
Id_alumno | Nombre | Edad | Sexo | Asignatura | Calificación | |
---|---|---|---|---|---|---|
0 | 1 | Juan | 25 | M | Matemáticas | 8.0 |
1 | 1 | Juan | 25 | M | Física | 7.0 |
2 | 2 | Maria | 30 | F | Matemáticas | 9.0 |
3 | 3 | Pedro | 28 | M | Física | 5.0 |
4 | 4 | Ana | 22 | F | NaN | NaN |
pd.merge(dfR, dfS, on='Id_alumno', how='right')
Id_alumno | Nombre | Edad | Sexo | Asignatura | Calificación | |
---|---|---|---|---|---|---|
0 | 1 | Juan | 25.0 | M | Matemáticas | 8 |
1 | 2 | Maria | 30.0 | F | Matemáticas | 9 |
2 | 1 | Juan | 25.0 | M | Física | 7 |
3 | 3 | Pedro | 28.0 | M | Física | 5 |
4 | 5 | NaN | NaN | NaN | Matemática | 6 |
Se tiene otras dos opciones, how='outer'
para tomar todos los registros de ambas tablas, y how='inner'
(opción por defecto) para tomar los registros que tienen el mismo Id_alumno
en ambas tablas.
pd.merge(dfR, dfS, on='Id_alumno', how='outer')
Id_alumno | Nombre | Edad | Sexo | Asignatura | Calificación | |
---|---|---|---|---|---|---|
0 | 1 | Juan | 25.0 | M | Matemáticas | 8.0 |
1 | 1 | Juan | 25.0 | M | Física | 7.0 |
2 | 2 | Maria | 30.0 | F | Matemáticas | 9.0 |
3 | 3 | Pedro | 28.0 | M | Física | 5.0 |
4 | 4 | Ana | 22.0 | F | NaN | NaN |
5 | 5 | NaN | NaN | NaN | Matemática | 6.0 |
En el lenguaje SQL, el operador reunión se representa con la cláusula JOIN
. Por ejemplo, la reunión de las tablas de datos $R$ y $S$ con respecto al atributo Id_alumno
se puede escribir como:
SELECT *
FROM R
JOIN S
ON R.Id_alumno=S.Id_alumno;