Módulo 07 — Pandas: Series, DataFrames y cargar datos
Aquí empieza lo que viniste a buscar. Pandas es la librería estándar para trabajar con datos tabulares en Python: lo que harías en Excel, pero programado, repetible y sin límite de filas. Este módulo te presenta sus dos estructuras y cómo meter datos dentro.
Instalar e importar
pip install pandas
import pandas as pd # el alias pd es universal
import numpy as np # casi siempre van juntos
La Series: una columna con etiquetas
Una Series es una secuencia de valores, como un array de NumPy, pero con un índice: una etiqueta para cada valor. Piensa en ella como una sola columna de una tabla.
import pandas as pd
notas = pd.Series([7, 5, 9, 6])
print(notas)
# 0 7
# 1 5
# 2 9
# 3 6
# dtype: int64
Esa columna de la izquierda (0, 1, 2, 3) es el índice. Por defecto son números, pero puedes ponerle etiquetas tuyas:
notas = pd.Series([7, 5, 9], index=["Ada", "Luis", "Marta"])
print(notas["Ada"]) # 7 accedes por etiqueta
print(notas.mean()) # 7.0 los métodos de NumPy funcionan igual
El DataFrame: la tabla completa
Un DataFrame es un conjunto de Series que comparten el mismo índice: una tabla con filas y columnas, cada columna con su nombre. Es la estructura de Pandas.
La forma más clara de crear uno a mano es con un diccionario, donde cada clave es una columna:
df = pd.DataFrame({
"nombre": ["Ada", "Luis", "Marta", "Juan"],
"edad": [20, 22, 21, 23],
"ciclo": ["DAW", "DAW", "ASIR", "DAW"],
"nota": [9, 5, 7, 4],
})
print(df)
# nombre edad ciclo nota
# 0 Ada 20 DAW 9
# 1 Luis 22 DAW 5
# 2 Marta 21 ASIR 7
# 3 Juan 23 DAW 4
¿Reconoces la estructura? Es la lista de diccionarios del módulo 03, pero con todas las herramientas de análisis encima.
Lo primero que haces con cualquier tabla: mirarla
Cuando cargas datos reales, no los imprimes enteros (pueden ser miles de filas). Los inspeccionas:
df.head() # las primeras 5 filas
df.head(3) # las primeras 3
df.tail() # las últimas 5
df.shape # (4, 4) -> filas, columnas
df.columns # nombres de columnas
df.index # el índice
df.dtypes # el tipo de dato de cada columna
df.info() # resumen: columnas, tipos, nulos, memoria
df.describe() # estadísticas de las columnas numéricas
df.info() y df.describe() son tus dos primeras paradas con cualquier dataset. info() te dice si hay valores faltantes y de qué tipo es cada columna. describe() te da media, mínimo, máximo y cuartiles de un vistazo.
print(df.describe())
# edad nota
# count 4.000000 4.000000
# mean 21.500000 6.250000
# std 1.290994 2.217356
# min 20.000000 4.000000
# 25% 20.750000 4.750000
# 50% 21.500000 6.000000
# 75% 22.250000 7.500000
# max 23.000000 9.000000
Acceder a columnas
Una columna es una Series. La sacas por su nombre:
df["nombre"] # la columna nombre
df[["nombre", "nota"]] # varias columnas (lista dentro de corchetes -> DataFrame)
Fíjate en los dobles corchetes para varias columnas: por dentro le pasas una lista de nombres. Un solo nombre devuelve una Series; una lista de nombres devuelve un DataFrame.
Sobre una columna numérica, todos los métodos de agregación funcionan:
df["nota"].mean() # 6.25
df["nota"].max() # 9
df["edad"].sum() # 86
df["ciclo"].unique() # array(['DAW', 'ASIR']) valores distintos
df["ciclo"].value_counts() # cuántos de cada uno
# DAW 3
# ASIR 1
value_counts() es oro puro: te cuenta cuántas veces aparece cada valor de una columna. Lo usarás sin parar.
Crear y modificar columnas
Una columna nueva se crea asignándola. Y como Pandas vectoriza (gracias a NumPy), operas sobre la columna entera de golpe:
# Columna calculada a partir de otra
df["nota_sobre_100"] = df["nota"] * 10
# Columna a partir de varias
df["aprobado"] = df["nota"] >= 5 # columna de True/False
print(df)
# nombre edad ciclo nota nota_sobre_100 aprobado
# 0 Ada 20 DAW 9 90 True
# 1 Luis 22 DAW 5 50 True
# 2 Marta 21 ASIR 7 70 True
# 3 Juan 23 DAW 4 40 False
Ni un bucle. Esta es la promesa de Pandas: pensar en columnas, no en filas una por una.
Cargar datos de verdad: leer un CSV
En la práctica no escribes los datos a mano: los lees de un archivo. El formato más común es el CSV (valores separados por comas):
df = pd.read_csv("alumnos.csv")
read_csv tiene parámetros para los casos que se tuercen, que son muchos:
df = pd.read_csv(
"alumnos.csv",
sep=";", # si el separador es ; en vez de , (típico en España)
encoding="utf-8", # o "latin-1" si las tildes salen raras
decimal=",", # si los decimales usan coma (1,5 en vez de 1.5)
)
Pandas también lee Excel (pd.read_excel("datos.xlsx")), JSON (pd.read_json) y bases de datos. La estructura que obtienes siempre es un DataFrame, así que todo lo que aprendas se aplica venga de donde venga el dato.
Guardar resultados
Cuando termines, exportas:
df.to_csv("resultado.csv", index=False) # index=False para no escribir el índice
df.to_excel("resultado.xlsx", index=False)
index=False evita que Pandas añada una columna extra con el número de fila. Casi siempre lo quieres así.
Un dataset de práctica para los ejercicios
Para los ejercicios usa este DataFrame. Cópialo tal cual:
import pandas as pd
ventas = pd.DataFrame({
"producto": ["Teclado", "Ratón", "Monitor", "Webcam", "Teclado", "Monitor", "Ratón", "Auriculares"],
"categoria": ["Periférico", "Periférico", "Pantalla", "Periférico", "Periférico", "Pantalla", "Periférico", "Audio"],
"precio": [25, 15, 150, 40, 25, 180, 18, 60],
"unidades": [4, 7, 2, 3, 5, 1, 9, 6],
"ciudad": ["Madrid", "Sevilla", "Madrid", "Bilbao", "Sevilla", "Madrid", "Bilbao", "Sevilla"],
})
Ejercicios
Usa el DataFrame ventas de arriba en todos.
07.1 — Muestra las primeras 3 filas, la forma de la tabla (filas y columnas) y el tipo de cada columna.
07.2 — Muestra el resumen estadístico de las columnas numéricas con describe(). ¿Cuál es el precio medio?
07.3 — Crea una columna nueva ingreso que sea precio * unidades. Muestra la tabla resultante.
07.4 — ¿Cuántos productos distintos hay en la columna producto? ¿Cuántas ventas hay por cada ciudad? (Pista: unique y value_counts.)
07.5 — Calcula el ingreso total de todas las ventas (suma de la columna ingreso que creaste).
07.6 — Crea una columna cara que valga True si el precio supera 50 y False si no. ¿Cuántos productos son “caros”?
07.7 — Crea un DataFrame con 5 películas (título, director, año, recaudación en millones). Añade una columna decada calculada a partir del año. Ordena por recaudación descendente.
07.8 — Dado un DataFrame de alumnos (nombre, nota_teoria, nota_practica), calcula nota_final = 40% teoría + 60% práctica, redondéala a un decimal y añade una columna aprobado (True si >= 5).
07.9 — Crea una pd.Series con ventas de los 7 días de la semana, con los días como índice. Calcula la media solo de los días laborables (lunes–viernes) sin escribir esos valores a mano.
Soluciones
07.1
print(ventas.head(3))
print(ventas.shape) # (8, 5)
print(ventas.dtypes)
07.2
print(ventas.describe())
# El precio medio (mean de 'precio') es 64.125
07.3
ventas["ingreso"] = ventas["precio"] * ventas["unidades"]
print(ventas)
07.4
print(ventas["producto"].unique()) # 5 productos distintos
print(ventas["producto"].nunique()) # 5
print(ventas["ciudad"].value_counts())
# Madrid 3
# Sevilla 3
# Bilbao 2
07.5
ventas["ingreso"] = ventas["precio"] * ventas["unidades"]
print(ventas["ingreso"].sum()) # 1452
07.6
ventas["cara"] = ventas["precio"] > 50
print(ventas["cara"].sum()) # 3 (True cuenta como 1)
07.7
import pandas as pd
peliculas = pd.DataFrame({
"titulo": ["El Padrino", "Titanic", "Avengers", "Matrix", "Inception"],
"director": ["Coppola", "Cameron", "Russo", "Wachowski", "Nolan"],
"anio": [1972, 1997, 2019, 1999, 2010],
"recaudacion": [245, 2187, 2798, 463, 836]
})
peliculas["decada"] = (peliculas["anio"] // 10) * 10
print(peliculas.sort_values("recaudacion", ascending=False))
07.8
import pandas as pd
alumnos = pd.DataFrame({
"nombre": ["Ada", "Leo", "Zara", "Iván", "Mía"],
"nota_teoria": [6, 4, 8, 5, 7],
"nota_practica": [7, 5, 9, 6, 4]
})
alumnos["nota_final"] = (
alumnos["nota_teoria"] * 0.4 + alumnos["nota_practica"] * 0.6
).round(1)
alumnos["aprobado"] = alumnos["nota_final"] >= 5
print(alumnos)
07.9
import pandas as pd
dias = ["Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado", "Domingo"]
ventas = pd.Series([120, 95, 140, 110, 130, 200, 85], index=dias)
laborables = ventas[:"Viernes"]
print(f"Media laborables: {laborables.mean():.1f}")