Saltar al contenido

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

EjemploLas cuatro lineas que se escriben siempre
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

Un método, una pregunta
ExpresiónLa pregunta que contestaLo 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 columnaQué valores son los que faltan
df.describe()Extremos y cuartiles de las numéricasNada de las columnas de texto, salvo que se pidan

info(), que es el que más rinde

Ejemploinfo() dice dos cosas por columna: el tipo y cuantos NO faltan
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:

describe()

Ejemplodescribe() contesta cosas distintas segun el tipo de la columna
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

EjemploEl dtype que te va a confundir buscando en internet
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
En pandas 3 el texto es 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

Ejemplohead() no es una muestra: las primeras filas casi siempre estan limpias
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.

FileNotFoundError: [Errno 2] No such file or directory

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.

(sin error) el archivo tiene una sola columna

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.

UnicodeDecodeError: 'utf-8' codec can't decode byte

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.

(sin error) info() dice 120 non-null y hay campos vacíos

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

¿Leer con los tipos que pandas infiera, o leer todo como texto?
  • Con inferencia (read_csv pelado) - lo normal. Es cómodo y te dice algo: si una columna que debería ser numérica queda str, 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 un NaN ya perdió si era un guion, un N/A o 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

Leer y mirar
ExpresiónQué haceCuándo
pd.read_csv("a.csv")Lee el archivo infiriendo tiposSiempre, para empezar
df.shape(filas, columnas)La primera línea de todo
df.head(n) / df.tail(n)Primeras / últimas n filasVer la forma, no el estado
df.columnsLos nombres de las columnasAntes de escribir uno mal
df.dtypesEl tipo de cada columnaLa pregunta que más resuelve
df.info()Tipo + no nulos, por columnaDiagnóstico de una línea
df.describe()Extremos y cuartilesBuscar lo imposible
df.describe(include="str")Conteo, distintos y más frecuenteColumnas de texto
df.isna().sum()Ausentes por columnaMás directo que restar de info()
df["c"].value_counts(dropna=False)Qué valores hay y cuántosCazar el valor raro
df.sample(5)5 filas al azarCuando head() te está mintiendo

Ponete a prueba

1. df.shape devuelve (120, 6). ¿Que significa?Nivel 1 · Recordar
2. info() dice 117 non-null en una columna de un archivo de 120 filas. ¿Cuantos datos faltan?Nivel 1 · Recordar
3. Una columna que deberia tener edades queda con dtype igual a str. ¿Que dice eso?Nivel 2 · Predecir la salida
4. describe() de una columna da mean 29480.68 y 50% 80.50. ¿Que pasa?Nivel 2 · Predecir la salida
5. Mirás head() de un archivo y las 5 primeras filas estan impecables. ¿Que podes concluir?Nivel 3 · Elegir entre dos casi correctas
6. Vas a producir un informe que muestre cada valor rechazado tal cual venia en el archivo. ¿Como conviene leerlo?Nivel 3 · Elegir entre dos casi correctas
7. Este codigo corre y promedio queda en 46.5, un valor perfectamente plausible. Igual esta mal. ¿Por que?
clientes = pd.read_csv("clientes_crudos.csv", na_values=["NaN", "N/A"])
edad = pd.to_numeric(clientes["edad"], errors="coerce")
promedio = edad.mean()
Nivel 4 · Encontrar el defecto