Tiza Paso a paso
Practicar →

Pensamiento Computacional · Unidad 6 — Bibliotecas de Python

Pandas y DataFrames

Un DataFrame es una tabla con nombres de columna. Sirve para trabajar con datos reales: leer un CSV, filtrar filas, sacar promedios.

Practicar este tema →7 ejercicios que te corrigen paso a paso · gratis, sin cuenta

La explicación

La tabla

import pandas as pd

df = pd.DataFrame({
    "nombre": ["ana", "luis", "sol"],
    "nota": [9, 4, 7]
})
    nombre   nota
0    ana       9
1    luis      4
2    sol       7

Cada columna tiene nombre y todas sus celdas son del mismo tipo. Las filas se numeran solas.

Mirar

df.head()       las primeras 5 filas
df.shape        (filas, columnas)
len(df)         cuántas filas
df.columns      los nombres de las columnas
df.describe()   estadísticas de las columnas numéricas

df.head() es lo primero que se hace siempre con un archivo nuevo: mirar qué tiene antes de programar sobre él.

Una columna

df["nota"]           la columna entera
df["nota"].mean()   el promedio
df["nota"].max()    el máximo
df["nota"].sum()    la suma

Una columna funciona como un array de NumPy: df["nota"] * 2 duplica todas las notas de una.

Filtrar filas

aprobados = df[df["nota"] >= 7]
Se lee de adentro hacia afuera. df["nota"] >= 7 no devuelve las notas: devuelve una columna de True/False, una por fila. Y df[...] se queda con las filas donde dio True.

Para combinar condiciones se usa & y | en vez de and y or, y cada condición va entre paréntesis:
df[(df["nota"] >= 7) & (df["nota"] < 10)]
Olvidar esos paréntesis es un error muy común y el mensaje que da no ayuda nada.

Una fila: iloc y loc

Con df["nota"] se saca una columna. Para sacar una fila hay dos formas, y la diferencia entre ellas es el error más común del tema:

df.iloc[0]             la fila que está en la posición 0 (la primera)
df.iloc[[0, 3]]        las filas de las posiciones 0 y 3, como tabla
df.iloc[:2]            las dos primeras (lo mismo que df.head(2))
df.loc[3]              la fila cuyo índice es 3
df.loc[3, "nota"]      una celda: la nota de la fila de índice 3

iloc cuenta posiciones, como los corchetes de una lista: la i es de «integer». En cambio loc busca por el índice, la columna de números que pandas escribe a la izquierda de la tabla. En una tabla recién leída los dos coinciden (el índice va 0, 1, 2…), y por eso la diferencia pasa desapercibida hasta que se filtra o se ordena.

import pandas as pd

df = pd.DataFrame({
    "nombre": ["ana", "luis", "sol", "teo", "eva"],
    "comision": ["A", "B", "A", "B", "A"],
    "nota": [6, 9, 4, 7, 8]
})

aprobados = df[df["nota"] >= 7]
print(aprobados)
print(aprobados.iloc[0]["nombre"])
print(aprobados.loc[3, "nombre"])
  nombre comision  nota
1   luis        B     9
3    teo        B     7
4    eva        A     8
luis
teo
Filtrar no renumera. Las filas que quedaron conservan su índice de la tabla original: 1, 3 y 4. Por eso aprobados.iloc[0] es luis (el primero que quedó) y aprobados.loc[3] es teo (el que tenía índice 3). Y aprobados.loc[0] da KeyError: 0: la fila de índice 0 era ana, que no aprobó. Regla práctica: si querés «el primero», «el último», «los tres de arriba», es iloc; si querés una fila por su etiqueta, es loc.

loc también sirve para cambiar una celda: df.loc[2, "nota"] = 5 le pone 5 a la nota de la fila de índice 2 en la misma tabla.

Ordenar: sort_values

import pandas as pd

df = pd.DataFrame({
    "nombre": ["ana", "luis", "sol", "teo", "eva"],
    "comision": ["A", "B", "A", "B", "A"],
    "nota": [6, 9, 4, 7, 8]
})

orden = df.sort_values("nota", ascending=False)
print(orden)
print(orden.iloc[0]["nombre"], orden.loc[0, "nombre"])
  nombre comision  nota
1   luis        B     9
4    eva        A     8
3    teo        B     7
0    ana        A     6
2    sol        A     4
luis ana

sort_values(columna) ordena las filas por esa columna, de menor a mayor; con ascending=False, de mayor a menor. Igual que sorted en el tema 4.3, devuelve una tabla nueva y deja df como estaba: si no se guarda el resultado, el orden se pierde.

Las filas viajan con su índice. Después de ordenar, la primera fila es luis, pero su índice sigue siendo 1. Por eso orden.iloc[0] es luis (la nota más alta) y orden.loc[0] es ana (la que tenía índice 0, que ahora está cuarta). «El de nota más alta» se pide con iloc[0].

Para ordenar por dos columnas se pasan listas: df.sort_values(by=["comision", "nota"], ascending=[True, False]) agrupa por comisión (A antes que B) y, adentro de cada una, pone las notas de mayor a menor: eva, ana, sol, luis, teo.

Contar: value_counts

df["comision"].value_counts()
comision
A    3
B    2
Name: count, dtype: int64

Cuenta cuántas veces aparece cada valor de una columna, de la más frecuente a la menos. Es el diccionario contador del tema 4.5 en una sola línea, y es lo primero que se mira en una columna de categorías: cuántos por comisión, por carrera, por producto.

Archivos CSV

df = pd.read_csv("notas.csv")        leer
df.to_csv("salida.csv", index=False)  guardar

Un CSV es un archivo de texto con valores separados por comas, y es el formato estándar para datos. Compará esto con la unidad 5: lo que allá eran diez líneas con open, strip y split, acá es una sola — y encima convierte los tipos solo.

El index=False al guardar evita que agregue una columna extra con los números de fila, que casi nunca se quiere.

Un ejercicio resuelto, paso a paso

Un archivo notas.csv tiene columnas nombre y nota. Escribir el código que muestre cuántos aprobaron y el promedio de los aprobados.

import pandas as pd

df = pd.read_csv("notas.csv")
print(df.head())                # mirar antes de programar

aprobados = df[df["nota"] >= 7]
print("Aprobaron:", len(aprobados))
print("Promedio:", aprobados["nota"].mean())

Con la tabla de ana (9), luis (4) y sol (7): aprobaron 2, y el promedio de ellos es (9 + 7) / 2 = 8.0.

Por qué el head() antes de todo. Los nombres de las columnas de un archivo real casi nunca son los que uno imagina: pueden tener mayúsculas, acentos o espacios ("Nota Final" en vez de "nota"). Si programás a ciegas, el KeyError aparece recién al correr. Mirar primero cuesta dos segundos.

Compará con hacer lo mismo a mano, como en la unidad 5: abrir el archivo, saltear el encabezado, separar cada línea por comas, convertir la nota, acumular... quince líneas, y todas con posibilidad de error. Para eso existen las bibliotecas.

Control: len(aprobados) nunca puede ser mayor que len(df), y el promedio de los aprobados tiene que ser mayor o igual que 7. Si no, el filtro está mal.

Y uno del nivel del parcial

Un archivo ventas.csv tiene las columnas producto, mes y monto. Calcular el total facturado por producto, el total por mes, cuál fue el mes de mayor facturación y el total general.

El archivo:

producto,mes,monto
lapicera,enero,1500
cuaderno,enero,2400
lapicera,febrero,900
cuaderno,febrero,3000
goma,enero,1000

Leerlo es una línea:

import pandas as pd

df = pd.read_csv("ventas.csv")

read_csv hace solo lo que en la unidad 5 costaba diez líneas: abre el archivo, usa la primera fila como nombres de columna, separa por comas, y convierte los números a números. Que monto quede como número y no como texto es lo que permite sumarlo después.

Conviene mirar los tipos antes de calcular nada:

df.dtypes
# producto object (texto)
# mes object
# monto int64 (numero: se puede sumar)

El total por producto, con groupby:

por_producto = df.groupby("producto")["monto"].sum()

groupby se lee literal: agrupá por producto, tomá la columna monto, sumala. Es el reemplazo del diccionario acumulador de la unidad 4, en una línea.

El resultado:

cuaderno   5400
goma        1000
lapicera   2400

El que más facturó, sin ordenar ni recorrer:

por_producto.idxmax() # 'cuaderno' -> QUIEN
por_producto.max() # 5400 -> CUANTO

idxmax() devuelve el índice del máximo, que acá es el nombre del producto. Es distinto de max(), que devolvería el valor 5400. Confundirlos es el error más común de esta unidad: uno da "quién" y el otro da "cuánto".

El total por mes es el mismo patrón cambiando la columna:

por_mes = df.groupby("mes")["monto"].sum()
por_mes.idxmax() # 'enero'
enero     4900   (1500 + 2400 + 1000)
febrero   3900   (900 + 3000)
mejor mes: enero

El total general:

df["monto"].sum() # 8800

El control que hay que hacer siempre con un groupby: los subtotales tienen que sumar el total.

por producto: 5400 + 1000 + 2400 = 8800 ✓
por mes:      4900 + 3900 = 8800 ✓
total general: 8800 ✓

Si no cierra, alguna fila se perdió o se contó dos veces. Pasa, por ejemplo, cuando hay nombres con espacios sobrantes: "goma" y "goma " son dos productos distintos para pandas, y el groupby los separa sin avisar. Conviene revisar con df["producto"].unique() cuántos productos distintos encontró.

La comparación con hacerlo a mano, que es lo que este ejercicio muestra: con open, split y un diccionario son unas quince líneas y hay que manejar el encabezado, las conversiones y las líneas vacías. Con pandas son cuatro, y los casos raros ya están resueltos.

Dónde se pierde el punto: contestar el valor cuando piden el nombre. "¿Cuál fue el mes de mayor facturación?" se contesta "enero", no "4900"; y para eso va idxmax().

Dónde se cae la mayoría

Hasta acá la explicación. La práctica es la otra mitad: 7 ejercicios de este tema, en cuatro niveles, que no te dicen sólo si está bien al final sino que te corrigen en cada paso y te explican por qué. Además trae una animación que muestra la idea en movimiento.

Practicar Pandas y DataFrames →

Dónde entra este tema en el parcial

Los otros temas de la unidad 6

Bibliotecas de Python

Ver todos los temas de Pensamiento Computacional