Semana 6: Fundamentos de Series y DataFrame
🎯 Objetivos de aprendizaje
Al final de esta semana podrás:
- Explicar qué son una
Seriesy unDataFrame, y cómo se relacionan con un vector y una matriz. - Construir un
DataFramea partir de estructuras de datos de Python, y cargar uno desde un CSV conpd.read_csv. - Inspeccionar la forma, columnas, dtypes, índice y estadísticas resumen de un
DataFrame. - Seleccionar una sola columna como una
Series, y conocer la diferencia entre las dos formas principales de hacerlo.
Lección
De diccionario de listas a DataFrame
El mini-proyecto de la Semana 5 de Python 101 calculaba promedios por estudiante usando un dict. Un DataFrame es la estructura de pandas diseñada exactamente para este tipo de datos tabulares — filas y columnas, con etiquetas en ambos ejes:
import pandas as pd
df = pd.DataFrame({
"name": ["Amina", "Youssef", "Sara"],
"score": [88, 74, 95],
})
df
También puedes construir uno a partir de una lista de diccionarios — exactamente la forma de "lista de registros" del ejemplo de colecciones anidadas de la Semana 3 del track Normal de Python 101:
records = [
{"name": "Amina", "score": 88},
{"name": "Youssef", "score": 74},
]
pd.DataFrame(records)
Ambas formas producen el mismo tipo de objeto; cuál es más conveniente depende solo de cómo ya estén estructurados tus datos — diccionario de listas cuando piensas "columna por columna", lista de diccionarios cuando piensas "registro por registro".
Una Series es una sola columna etiquetada — piénsala como un vector , salvo que cada entrada también tiene una etiqueta (su índice), no solo una posición:
df["score"] # una Series
type(df["score"]) # pandas.core.series.Series
Un DataFrame es una tabla 2D de esas columnas que comparten un índice de fila — el análogo tabular de una matriz , salvo que las columnas pueden tener distintos dtypes y ambos ejes llevan etiquetas, no solo posiciones numéricas.
El índice
Todo DataFrame (y Series) tiene un índice de fila — las etiquetas a lo largo del borde izquierdo — visible cada vez que imprimes uno. Por defecto es simplemente 0, 1, 2, ..., pero no tiene que serlo:
df.index # RangeIndex(start=0, stop=3, step=1) por defecto
df_named = df.set_index("name") # usa la columna "name" como índice en su lugar
df_named.loc["Amina"] # ahora puedes buscar una fila por nombre directamente
set_index no modifica df in situ por defecto — devuelve un DataFrame nuevo con el cambio, el mismo patrón de "devuelve un valor nuevo, no muta" que ya viste con sorted() en Python 101.
Leer un CSV
El mismo students-normal.csv de Python 101 se carga en una sola llamada — sin bucle manual con csv.DictReader, sin conversiones manuales con int(...):
df = pd.read_csv("students-normal.csv")
df.head() # las primeras 5 filas
df.tail(3) # las últimas 3 filas
pd.read_csv infiere automáticamente el dtype de cada columna (los números se vuelven int64/float64, el texto se queda como object), que es la mayor parte de lo que la Semana 5 del track Normal de Python 101 hizo a mano, resuelto para ti en una línea.
Inspeccionar un DataFrame
Un puñado de métodos responden "¿cómo se ve realmente este dataset?" antes de que hagas cualquier otra cosa con él:
df.shape # (filas, columnas) — p. ej. (10, 4)
df.columns # nombres de las columnas
df.dtypes # el tipo de dato de cada columna
df.info() # forma + dtypes + conteos de no nulos, todo a la vez
df.describe() # count, mean, std, min, cuartiles, max — para columnas numéricas
Vale la pena detenerse en df.describe(): la media y la desviación estándar son exactamente las estadísticas que ya conoces de un curso de estadística, calculadas instantáneamente sobre una columna entera en lugar de a mano. Puedes renombrar columnas después del hecho si los nombres del archivo de origen no son convenientes para trabajar:
df = df.rename(columns={"quiz1": "quiz_1"})
⚠️ Errores comunes
- Olvidar que la mayoría de las operaciones de DataFrame devuelven un objeto nuevo.
df.rename(...),df.set_index(...), y muchas otras no cambiandfen sí a menos que reasignes (df = df.rename(...)) o pasesinplace=True. - Confundir
df.shape(sin paréntesis) con una llamada a método..shapees un atributo, no una función —df.shape()lanza unTypeError. - Asumir que
.describe()cubre todas las columnas. Por defecto solo resume columnas numéricas; las columnas de texto necesitan una mirada diferente (la Semana 8 cubre limpiarlas e inspeccionarlas).
🧩 Retos
Carga students-normal.csv (de la Semana 5 de Python 101 — reutiliza el mismo archivo) en un DataFrame e imprime cuántas filas y columnas tiene.
Selecciona solo la columna quiz1 como una Series. ¿Cuáles son dos sintaxis distintas para hacer esto?
Calcula la media de la columna quiz1 usando un método de Series (no sum()/len() a mano).
Ejecuta df.describe() en el DataFrame de estudiantes. ¿Incluye la columna name? ¿Por qué sí o por qué no?
Establece name como el índice del DataFrame de estudiantes, y luego busca la fila de Amina directamente por su nombre en lugar de por número de fila.
Construye un pequeño DataFrame a mano (no desde un CSV) con dos columnas, city y population, para 3 ciudades de tu elección — usando el estilo de diccionario de listas o el de lista de diccionarios.
🤔 Preguntas socráticas
- Una
Seriesa menudo se compara con unalistde Python con etiquetas. ¿Qué puedes hacer con el.mean()/.std()de una Series en una sola llamada que no podrías hacer con unalistsimple sin escribir tu propia función? df.dtypesmuestra el tipo inferido de cada columna. ¿Qué podría salir mal si una columna que parece numérica (comoquiz1) en realidad tuviera una fila con texto, como"absent"? ¿Qué dtype probablemente inferiría pandas para toda la columna?- Pasaste 5 semanas de Python 101 construyendo a mano la lógica de lectura de CSV y promediado. ¿Qué líneas específicas de esa lógica reemplaza
pd.read_csv(...).describe()? ¿Se pierde realmente algo al usar el atajo, o solo se ahorra tiempo? df.set_index("name")cambia el índice numérico por defecto por uno con significado. ¿Qué saldría mal si la columnanametuviera un valor duplicado — se podría seguir distinguiendo a dos estudiantes distintos después?