🐍 Curso Python · Nivel N3–N4 Intermedio / Avanzado

Bloque 6: Data Science

Python es el rey del análisis de datos. En este bloque instalas el trío de oro (NumPy, pandas, Matplotlib) y aprendes a leer un fichero CSV, limpiarlo, explorarlo, agruparlo y visualizarlo con gráficos. Rematas analizando un fichero de ventas como un/a analista de verdad.

⏱ ≈ 16 horas 📚 5 lecciones ✍️ 15 ejercicios guiados 📝 Examen del bloque + análisis de ventas
Lección 1 · Teoría

Instalación y NumPy

1.1 · Instalar el trío del Data Science

Crea un entorno virtual para este bloque y usa pip:

entorno + paquetes
$ python -m venv .venv
$ source .venv/bin/activate      # Windows: .venv\Scripts\activate
$ pip install numpy pandas matplotlib
$ python
>>> import pandas as pd   # si no lanza error, todo bien

1.2 · NumPy: arrays rápidos

NumPy es la base numérica. Su objeto estrella es el ndarray, una lista optimizada capaz de hacer operaciones vectorizadas en todo el array:

operaciones vectorizadas
import numpy as np

datos = np.array([1, 2, 3, 4])
print(datos * 2)              # [2 4 6 8] (¡en un paso!)
print(datos.mean(), datos.sum())  # 2.5 10
columna = np.array([1, 2])      # vector fila
matrix = np.random.rand(2, 3)  # matriz 2x3 aleatoria
💡 En datos grandes, un for en Python puro es lentísimo; una operación vectorizada de NumPy es 100× más rápida porque corre en C.
Lección 2 · Teoría

pandas: Series y DataFrame

pandas es la herramienta estrella del análisis de datos. Trae dos estructuras: Series (una columna, con índice) y DataFrame (una tabla, como una hoja de cálculo).

2.1 · Crear un DataFrame

dataframe desde un dict
import pandas as pd

datos = {
    "producto": ["ratón", "teclado", "pad"],
    "precio": [12, 45, 8],
    "stock": [20, 5, 50],
}
df = pd.DataFrame(datos)
print(df)
print(df.shape)        # (3 filas, 3 columnas)
print(df.columns)      # nombres de columnas

2.2 · Acceder a series y celdas

localizar datos
precio_raton = df.loc[0, "precio"]   # fila 0, col 'precio' → 12
precios = df["precio"]              # toda la columna (una Series)
print(precios.mean())               # media de precios
⚠️ Distingue: df["columna"] selecciona columna (Series); df.loc[fila, "columna"] selecciona celda; df.iloc[fila, col] selecciona por posición numérica.
Lección 3 · Teoría

Leer, filtrar y agrupar datos

3.1 · Leer un CSV de un solo golpe

leer CSV con pandas
import pandas as pd

df = pd.read_csv("ventas.csv")
print(df.head())        # primeras 5 filas
print(df.info())        # tipos de cada columna
print(df.describe())    # estadísticas de columnas numéricas

3.2 · Filtrar con condiciones booleanas

filtros encadenados
# ventas de la categoría 'periférico' con importe > 30
caras = df[(df["categoria"] == "periférico") & (df["importe"] > 30)]
print(caras)
⚠️ En pandas usa & (y) y | (o), no and/or. Y envuelve cada condición entre paréntesis: df[(a) & (b)].

3.3 · Agrupar y agregar

groupby agrupa por columna y luego aplicamos una agregación:

ventas por categoría
por_categoria = df.groupby("categoria")["importe"].sum()
print(por_categoria)          # total por categoría

resumen = df.groupby("mes").agg({"importe": ["sum", "mean"]})
print(resumen)                 # total y media por mes
Lección 4 · Teoría

Limpieza de datos

Los datos reales vienen con NaN (valores ausentes), cadenas con espacios y formatos mezclados. Limpiar es el 80 % del trabajo real del Data Science:

limpiar el dataframe
import pandas as pd
import numpy as np

df = pd.DataFrame({
    "nombre": [" ana ", "Luis", None],
    "edad": [25, np.nan, 30],
})

df["nombre"] = df["nombre"].str.strip()        # quitar espacios
df["edad"] = df["edad"].fillna(df["edad"].mean())  # rellenar NaN con la media
df = df.dropna(subset=["nombre"])                   # borrar filas sin nombre
df = df.drop_duplicates()                             # quitar duplicados
💡 Trucos clave: isna()/notna() detectan ausentes, fillna(x) los rellena, dropna() los elimina y astype(int/float/str) fuerza el tipo de una columna.
Lección 5 · Teoría

Visualización con Matplotlib

Matplotlib pinta los gráficos. pyplot da la API sencilla: creas figura, llamas al tipo de gráfico y muestras/guardas.

gráfico de barras
import matplotlib.pyplot as plt
import pandas as pd

df = pd.read_csv("ventas.csv")
totales = df.groupby("mes")["importe"].sum()

plt.bar(totales.index, totales.values, color="#7c5cff")
plt.title("Ventas por mes")
plt.xlabel("Mes")
plt.ylabel("Importe")
plt.show()                 # o plt.savefig("grafico.png")

5.1 · Tipos de gráfico que dominan

plt.bar() — comparar categorías (mes por mes).
plt.plot() — tendencia en el tiempo (línea).
plt.hist() — distribución de una columna (histograma).
plt.scatter() — relación entre dos columnas (puntos).
df.plot() — atajo de pandas sobre Matplotlib.
plt.savefig(nombre) — guarda el gráfico en un fichero.
💡 En Jupyter o VS Code los gráficos se muestran con %matplotlib inline (Jupyter) o rellenando un bloque de celdas de salida (VS Code). En un script normal usa plt.show().
🎉 Ya tienes el ciclo completo del análisis de datos: read_csv → limpieza → filtro → groupby → gráfico. Esto es exactamente lo que se pide en la mayoría de puestos de «analista de datos» junior.
Manos a la obra

✍️ Los 15 ejercicios del bloque

Crea la carpeta python-b6/ y un archivo .py por ejercicio. Acuérdate de activar el entorno virtual antes de ejecutarlos (source .venv/bin/activate).

Sección A · NumPy

1 Arrays vectorizados

Crea np.array([1, 2, 3, 4]) y muestra su doble, su suma, su raíz cuadrada (con np.sqrt) y la media.

Ver solución
import numpy as np

datos = np.array([1, 2, 3, 4])
print(datos * 2)
print(datos.sum())
print(np.sqrt(datos))
print(datos.mean())

2 Matriz 3×3 aleatoria

Con np.random.randint crea una matriz 3×3 de números del 1 al 10 y muestra su forma, su máximo y el máximo de cada columna (axis=0).

Ver solución
import numpy as np

m = np.random.randint(1, 11, size=(3, 3))
print(m)
print(m.shape)
print(m.max())
print(m.max(axis=0))

Sección B · pandas básico

3 Primer DataFrame

Crea un DataFrame a partir del dict {"ciudad": ["Madrid", "Valencia", "Bilbao"], "votos": [100, 80, 60]} y muestra las 2 primeras filas con head(2), el shape y las columnas.

Ver solución
import pandas as pd

df = pd.DataFrame({"ciudad": ["Madrid", "Valencia", "Bilbao"],
                   "votos": [100, 80, 60]})
print(df.head(2))
print(df.shape)
print(df.columns)

4 Acceder a celdas

Sobre el DataFrame anterior, muetra con loc la fila 1 y la celda [2, "votos"], y con iloc la primera fila por posición.

Ver solución
import pandas as pd

df = pd.DataFrame({"ciudad": ["Madrid", "Valencia", "Bilbao"],
                   "votos": [100, 80, 60]})
print(df.loc[1])
print(df.loc[2, "votos"])
print(df.iloc[0])

5 Describe tu tabla

Crear un DataFrame con {"alumno": [...5 nombres...], "nota": [...5 notas...]} y muestra describe() de la columna nota y su desviación típica con .std().

Ver solución
import pandas as pd

df = pd.DataFrame({"alumno": ["a", "b", "c", "d", "e"],
                   "nota": [7, 8, 9, 6, 10]})
print(df["nota"].describe())
print(df["nota"].std())

Sección C · Filtrar, agrupar y limpiar

6 Filtro de mayores

Sobre un DataFrame de {"nombre": [...], "edad": [...]}, muestra solo las filas con edad mayor de 25 usando la condición booleana entre paréntesis.

Ver solución
import pandas as pd

df = pd.DataFrame({"nombre": ["Ana", "Luis", "Rita", "Paz"],
                   "edad": [24, 30, 28, 19]})
mayores = df[df["edad"] > 25]
print(mayores)

7 Filtro con & y |

Del DataFrame anterior, muestra los mayores de 25 o los que empiezan por R (usa el módulo str de pandas: df["nombre"].str.startswith("R")) combinados con |.

Ver solución
import pandas as pd

df = pd.DataFrame({"nombre": ["Ana", "Luis", "Rita", "Paz"],
                   "edad": [24, 30, 28, 19]})
sel = df[(df["edad"] > 25) | df["nombre"].str.startswith("R")]
print(sel)

8 Groupby de ventas

Crea un DataFrame de ventas {"tienda": ["A", "A", "B", "B"], "importe": [100, 50, 200, 75]} y con groupby muestra la suma y la media por tienda.

Ver solución
import pandas as pd

df = pd.DataFrame({"tienda": ["A", "A", "B", "B"],
                   "importe": [100, 50, 200, 75]})
print(df.groupby("tienda")["importe"].sum())
print(df.groupby("tienda")["importe"].mean())

9 Leer CSV con pandas

Crea alumnos.csv (con las librerías csv) con nombre,nota para 4 alumnos, luego léelo con pd.read_csv y muestra head() y la nota media.

Ver solución
import csv
import pandas as pd

with open("alumnos.csv", "w", newline="", encoding="utf-8") as f:
    w = csv.writer(f)
    w.writerow(["nombre", "nota"])
    w.writerows([["Ana", 8], ["Luis", 7], ["Rita", 9], ["Paz", 5]])

df = pd.read_csv("alumnos.csv")
print(df.head())
print(df["nota"].mean())

10 Rellenar NaN

Crea un DataFrame con {"edad": [25, None, 30, None]} y rellena los ausentes con la media de la columna usando fillna. Muestra el resultado.

Ver solución
import pandas as pd

df = pd.DataFrame({"edad": [25, None, 30, None]})
df["edad"] = df["edad"].fillna(df["edad"].mean())
print(df)

11 Quitar duplicados y espacios

Crea un DataFrame con nombres con espacios y una fila duplicada. Limpia con str.strip() y drop_duplicates(). Muestra el resultado.

Ver solución
import pandas as pd

df = pd.DataFrame({"nombre": [" Ana ", "Luis", "Ana", "Luis"]})
df["nombre"] = df["nombre"].str.strip()
df = df.drop_duplicates()
print(df)

12 Gráfico de barras

Con un DataFrame de ventas por mes, pinta un plt.bar() con título y etiquetas, y guárdalo con plt.savefig("barras.png").

Ver solución
import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame({"mes": ["ene", "feb", "mar"],
                   "importe": [100, 150, 90]})

plt.bar(df["mes"], df["importe"], color="#7c5cff")
plt.title("Ventas por mes")
plt.xlabel("Mes")
plt.ylabel("Importe")
plt.savefig("barras.png")

13 Línea de tendencia

Pinta con plt.plot la serie [5, 8, 6, 10, 12] (días 1 a 5) con un marcador de puntos y título «Tendencia».

Ver solución
import matplotlib.pyplot as plt

dias = [1, 2, 3, 4, 5]
valores = [5, 8, 6, 10, 12]
plt.plot(dias, valores, marker="o")
plt.title("Tendencia")
plt.show()

14 Histograma estándar

Con plt.hist muestra la distribución de las notas de una lista de 20 notas aleatorias entre 0 y 10 (random.randint), con 10 contenedores (bins=10).

Ver solución
import random
import matplotlib.pyplot as plt

notas = [random.randint(0, 10) for _ in range(20)]
plt.hist(notas, bins=10)
plt.title("Distribución de notas")
plt.show()

15 🏆 Proyecto integrador: «análisis de ventas»

Crea analisis_ventas.py: un mini informe de un CSV de ventas usando pandas y un gráfico. Requisitos:

  • Genera ventas.csv (12 filas) con columnas mes, categoria, importe (categorías: periférico y pantalla; importes aleatorios).
  • Léelo con pd.read_csv.
  • Añade 2 filas con importe/valor ausente (NaN) y límpialas con dropna o fillna.
  • Filtra solo las ventas con importe mayor a un umbral (p. ej. 60).
  • Agrupa con groupby("categoria")["importe"].sum().
  • Pinta un plt.bar del total por categoría y guarda resumen_ventas.png.
  • Muestra por consola el total general y la media por categoría.
  • Define una función con docstring para una parte del análisis.
Ver una solución posible
import csv
import random
import pandas as pd
import matplotlib.pyplot as plt

def generar_csv(ruta):
    """Crea el fichero de ventas con 12 filas y 2 huecos a propósito."""
    filas = []
    for mes in ["ene", "feb", "mar"]:
        filas.append({"mes": mes, "categoria": "periférico", "importe": random.randint(30, 90)})
        filas.append({"mes": mes, "categoria": "pantalla", "importe": random.randint(80, 200)})
    filas.append({"mes": "abr", "categoria": "periférico", "importe": None})
    filas.append({"mes": "abr", "categoria": "pantalla", "importe": None})
    with open(ruta, "w", newline="", encoding="utf-8") as f:
        w = csv.DictWriter(f, fieldnames=["mes", "categoria", "importe"])
        w.writeheader()
        w.writerows(filas)

generar_csv("ventas.csv")

df = pd.read_csv("ventas.csv")
df = df.dropna(subset=["importe"])

caras = df[df["importe"] > 60]
totales = df.groupby("categoria")["importe"].sum()
medias = df.groupby("categoria")["importe"].mean()

print(f"Ventas altas: {len(caras)}")
print(totales)
print(medias)

plt.bar(totales.index, totales.values, color="#7c5cff")
plt.title("Ventas totales por categoría")
plt.savefig("resumen_ventas.png")
Evaluación del bloque

📝 Examen del Bloque 6

Dos partes: un test teórico autoevaluable (mínimo 70 %) y un reto práctico de autocorrección con laboratorio.

Parte A · Test teórico (aprueba con 11/15)

1 · ¿Cuál es la estructura principal de NumPy?
2 · ¿Qué es una operación vectorizada?
3 · ¿Qué estructura de pandas es UNA columna?
4 · ¿Cómo se accede a una celda concreta por nombre de fila y columna?
5 · ¿Qué combina condiciones en pandas?
6 · ¿Qué hace df.groupby("x")["y"].sum()?
7 · ¿Para qué sirve dropna()?
8 · ¿Qué rellena los huecos NaN con la media?
9 · ¿Qué hace plt.bar()?
10 · ¿Cuál es el atajo para leer un CSV con pandas?
11 · ¿Qué gráfico muestra la distribución de una columna?
12 · ¿Cómo se guarda un gráfico en un fichero?
13 · ¿Qué comando instala los paquetes de datos?
14 · ¿Qué hace df.describe()?
15 · ¿Cuál es el primer paso recomendado con un CSV sucio?

Parte B · Reto «tiny dataset»

Crea dataset.py donde practiques el ciclo pandas. Valida tú mismo/a estos criterios. Mínimo para aprobar: 6 de 8 casillas.

Cumplidos: 0 / 8

🎉 ¿Superaste las dos partes? Bloque 6 completado. Leer, limpiar, agrupar y graficar datos ya es tuyo. El bloque 7 da el siguiente gran salto: Python en la web, con APIs y Flask. 🐍🔥