Indice / pandas: fundamentos / 4.2
Leer y mirar
Cuatro métodos y una advertencia. Los métodos se aprenden en cinco
minutos; la advertencia - que head() no te muestra cómo está el
archivo - es la que evita la mitad de los errores del curso.
Para qué sirve
Antes de escribir la primera transformación hay que contestar tres preguntas: cuántas filas y columnas hay, de qué tipo quedó cada columna y cuántos datos faltan. Las tres se contestan con una línea cada una, y las tres se saltean todo el tiempo.
Saltearlas cuesta caro porque los errores que producen no dan error: dan un promedio que parece razonable calculado sobre la mitad de las filas.
ELI5Explicado facil
Es mirar la caja antes de armar el mueble. Contás las piezas, mirás si hay tornillos y si falta algo. Nadie empieza a atornillar de una… y sin embargo con los datos casi todo el mundo empieza a atornillar de una.
El ejemplo mínimo
import pandas as pd
clientes = pd.read_csv("clientes_crudos.csv")
print("forma:", clientes.shape)
print("columnas:", list(clientes.columns))
print()
print(clientes.head(3).to_string())
print()
print(clientes.tail(2).to_string())
forma: (120, 6)
columnas: ['id', 'nombre', 'correo', 'edad', 'ciudad', 'fecha_alta']
id nombre correo edad ciudad fecha_alta
0 C0001 José Paz jose.paz@example.com 62 Salta 2025-02-20
1 C0002 Lucía Peña lucia.pena@example.com 48 Salta 2025-01-05
2 C0003 Santiago Herrera santiago.herrera@example.com 40 Salta 2024-05-12
id nombre correo edad ciudad fecha_alta
118 C0119 carla suárez carla.suarez@example.com 17 Neuquén 2025-10-01
119 C0120 Walter Silva walter.silva@example.com 41 Bahía Blanca 2026-03-18
to_string() en los ejemplos
Verás .to_string() en casi todos los ejemplos del curso. Sirve para
que pandas imprima la tabla entera sin cortarla con puntos suspensivos. En tu consola
no hace falta: es para que la salida de esta página sea siempre la misma.
Qué contesta cada uno
| Expresión | La pregunta que contesta | Lo que NO contesta |
|---|---|---|
df.shape | ¿Cuántas filas y columnas? | Si las filas sirven |
df.head() | ¿Qué forma tienen los datos? | Cómo están los datos |
df.dtypes | ¿De qué tipo quedó cada columna? | Por qué quedó de ese tipo |
df.info() | Tipo y cuántos no faltan, por columna | Qué valores son los que faltan |
df.describe() | Extremos y cuartiles de las numéricas | Nada de las columnas de texto, salvo que se pidan |
info(), que es el que más rinde
import pandas as pd
clientes = pd.read_csv("clientes_crudos.csv")
clientes.info()
print()
print("Leelo asi:")
print("- 120 entries: el archivo tiene 120 filas.")
print("- 'non-null' es cuantas TIENEN dato, no cuantas faltan.")
print("- edad quedo str y deberia ser un numero: eso es un hallazgo.")
<class 'pandas.DataFrame'>
RangeIndex: 120 entries, 0 to 119
Data columns (total 6 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 id 120 non-null str
1 nombre 118 non-null str
2 correo 118 non-null str
3 edad 117 non-null str
4 ciudad 119 non-null str
5 fecha_alta 120 non-null str
dtypes: str(6)
memory usage: 5.8 KB
Leelo asi:
- 120 entries: el archivo tiene 120 filas.
- 'non-null' es cuantas TIENEN dato, no cuantas faltan.
- edad quedo str y deberia ser un numero: eso es un hallazgo.
Dos trampas de lectura en esa salida, y las dos se cobran seguido:
non-nulles lo que hay, no lo que falta.117 non-nullsobre 120 filas significa 3 ausentes. Hay que restar.- Una columna que debería ser un número y dice
stres un hallazgo, no un detalle. Significa que adentro hay algo que no es un número, y que cualquier cuenta sobre ella va a fallar o a mentir.
describe()
import pandas as pd
inventario = pd.read_csv("inventario_productos.csv")
print("columnas numericas:")
print(inventario.describe().round(2).to_string())
print()
print("columnas de texto (hay que pedirlas):")
print(inventario.describe(include="str").to_string())
print()
print("Sobre una numerica da minimo, maximo y cuartiles.")
print("Sobre una de texto da conteo, distintos y el mas frecuente.")
print("Y el maximo de cantidad, 999999, ya es una pregunta para hacerle a alguien.")
columnas numericas:
cantidad precio_unitario
count 34.00 33.00
mean 29480.68 518.41
std 171486.23 307.25
min -5.00 -12.30
25% 50.25 326.49
50% 80.50 542.80
75% 97.00 781.78
max 999999.00 918.40
columnas de texto (hay que pedirlas):
codigo producto categoria
count 35 35 35
unique 35 35 11
top P001 Teclado mecánico Accesorios
freq 1 1 5
Sobre una numerica da minimo, maximo y cuartiles.
Sobre una de texto da conteo, distintos y el mas frecuente.
Y el maximo de cantidad, 999999, ya es una pregunta para hacerle a alguien.
Mirá los extremos antes que las medias. cantidad tiene un mínimo de
-5 y un máximo de 999999: los dos son imposibles para un
inventario, y ya aparecieron sin buscarlos. La media de 29480.68 con una
mediana de 80.50 es la firma de un valor extremo tirando del promedio.
describe() ignora las columnas de texto
Por defecto solo mira las numéricas. Para las de texto hay que pedirlo con
include="str", y entonces contesta otras preguntas: cuántos valores
distintos hay y cuál es el más frecuente.
El dtype que te va a confundir
import pandas as pd
print("pandas", pd.__version__)
clientes = pd.read_csv("clientes_crudos.csv")
print()
print(clientes.dtypes)
print()
print("Dos cosas que hay que saber de esta salida:")
print("1) En pandas 3 una columna de texto es 'str'. Casi todo lo que")
print(" encuentres buscando dice 'object': es pandas 2 o anterior.")
print("2) edad tambien dice str, y eso NO es normal: deberia ser un numero.")
edad = clientes["edad"]
print()
print("los valores de edad que no son un numero:")
print(edad[~edad.str.fullmatch(r"-?\d+")].value_counts(dropna=False).to_string())
pandas 3.0.5
id str
nombre str
correo str
edad str
ciudad str
fecha_alta str
dtype: object
Dos cosas que hay que saber de esta salida:
1) En pandas 3 una columna de texto es 'str'. Casi todo lo que
encuentres buscando dice 'object': es pandas 2 o anterior.
2) edad tambien dice str, y eso NO es normal: deberia ser un numero.
los valores de edad que no son un numero:
edad
NaN 3
treinta 3
- 1
str, no object
Casi todo lo que encuentres buscando en internet dice que una columna de texto es
object: eso es pandas 2 o anterior. En la versión que usamos dice
str. Es el mismo concepto con otro nombre, pero conviene saberlo antes
de pasar media hora convencido de que algo anda mal.
Dónde se rompe
import pandas as pd
clientes = pd.read_csv("clientes_crudos.csv")
print("Las 5 primeras filas, que es lo unico que mira casi todo el mundo:")
print(clientes.head().to_string())
print()
print("Y ahora las mismas columnas, contadas sobre las 120:")
edad = clientes["edad"]
print("edades que no son un numero :", int((~edad.str.fullmatch(r"-?\d+")).sum()))
print("correos sin arroba :",
int((~clientes["correo"].fillna("").str.contains("@", regex=False)).sum()))
print("nombres con espacios de mas :",
int((clientes["nombre"].fillna("") != clientes["nombre"].fillna("").str.strip()).sum()))
print()
print("Ninguno de esos problemas aparece en las 5 primeras filas.")
print("head() sirve para ver COMO es el archivo, no COMO ESTA.")
Las 5 primeras filas, que es lo unico que mira casi todo el mundo:
id nombre correo edad ciudad fecha_alta
0 C0001 José Paz jose.paz@example.com 62 Salta 2025-02-20
1 C0002 Lucía Peña lucia.pena@example.com 48 Salta 2025-01-05
2 C0003 Santiago Herrera santiago.herrera@example.com 40 Salta 2024-05-12
3 C0004 José Rodríguez jose.rodriguez@example.com 67 Paraná 2024-08-11
4 C0005 Walter Gutiérrez walter.gutierrez@example.com 34 Rosario 2026-01-01
Y ahora las mismas columnas, contadas sobre las 120:
edades que no son un numero : 7
correos sin arroba : 11
nombres con espacios de mas : 16
Ninguno de esos problemas aparece en las 5 primeras filas.
head() sirve para ver COMO es el archivo, no COMO ESTA.
Las cinco primeras filas están impecables. En las 120 hay 7 edades que no son
números, 11 correos sin arroba y 16 nombres con espacios de más. Ninguno de
esos problemas es visible en head(), y no es casualidad: los
datos sucios suelen entrar después, cuando el sistema ya estaba en producción y
alguien empezó a cargar a mano.
Causa: el programa se está ejecutando desde otra carpeta. El nombre
pelado "clientes_crudos.csv" se busca en el directorio de trabajo, no
al lado del .py.
Arreglo: correr desde la carpeta de los datos, o mirar la lección 0.1: con el depurador es el error número uno.
Causa: el separador no es la coma. Un CSV «europeo» usa
;.
Arreglo: read_csv(..., sep=";"). Si
df.shape devuelve (180, 1), es esto y no otra cosa.
Causa: el archivo no está en UTF-8, típicamente porque lo guardó Excel en Windows.
Arreglo: read_csv(..., encoding="latin-1") o
"cp1252". Los archivos de este curso están todos en UTF-8.
Causa: los ausentes están escritos con un token que pandas no reconoce
- el clásico es -. Para pandas son texto, no ausencias.
Arreglo: declararlos: na_values=["-", "N/A", "null"]. Es
la lección 5.4.
Acá hay que elegir
- Con inferencia (
read_csvpelado) - lo normal. Es cómodo y te dice algo: si una columna que debería ser numérica quedastr, ya sabés que hay basura adentro. - Todo como texto (
dtype=str, keep_default_na=False) - cuando vas a producir un informe de rechazos que muestre el valor tal cual venía. Es lo que hacen casi todas las prácticas del curso, porque unNaNya perdió si era un guion, unN/Ao un campo vacío.
La regla: si vas a informar lo que estaba mal, leé como texto. Si solo vas a calcular, dejá que infiera.
Recetario
| Expresión | Qué hace | Cuándo |
|---|---|---|
pd.read_csv("a.csv") | Lee el archivo infiriendo tipos | Siempre, para empezar |
df.shape | (filas, columnas) | La primera línea de todo |
df.head(n) / df.tail(n) | Primeras / últimas n filas | Ver la forma, no el estado |
df.columns | Los nombres de las columnas | Antes de escribir uno mal |
df.dtypes | El tipo de cada columna | La pregunta que más resuelve |
df.info() | Tipo + no nulos, por columna | Diagnóstico de una línea |
df.describe() | Extremos y cuartiles | Buscar lo imposible |
df.describe(include="str") | Conteo, distintos y más frecuente | Columnas de texto |
df.isna().sum() | Ausentes por columna | Más directo que restar de info() |
df["c"].value_counts(dropna=False) | Qué valores hay y cuántos | Cazar el valor raro |
df.sample(5) | 5 filas al azar | Cuando head() te está mintiendo |