Saltar al contenido

Indice / pandas: fundamentos / 4.3

Seleccionar columnas

Un corchete o dos. Parece una cuestión de estilo y no lo es: cambia el tipo de lo que obtenés, y con eso cambia todo lo que podés hacer después.

Para qué sirve

Casi nunca se trabaja con el archivo entero. Se eligen las columnas que importan, se les pone un nombre usable y se las deja en el orden que pide la salida. Son tres operaciones chicas que aparecen en absolutamente todos los programas del curso.

Y hay una sola idea que hay que tener clara: el corchete simple devuelve una Series y el doble devuelve un DataFrame. No es un detalle de sintaxis; es la diferencia entre que el método que buscás exista o no.

ELI5Explicado facil

Pedir una columna con un corchete es como sacar una hoja de la carpeta: te queda una hoja en la mano.

Pedirla con dos corchetes es sacar una carpeta con esa única hoja adentro. Tenés lo mismo, pero envuelto distinto, y hay cosas que se le pueden hacer a una carpeta y no a una hoja suelta.

El ejemplo mínimo

EjemploUn corchete devuelve una Series; dos, un DataFrame
import pandas as pd

inventario = pd.read_csv("inventario_productos.csv")

una = inventario["producto"]
varias = inventario[["codigo", "producto"]]

print("inventario['producto']   ->", type(una).__name__)
print("inventario[['codigo',...]] ->", type(varias).__name__)

print()
print(una.head(3).to_string())
print()
print(varias.head(3).to_string(index=False))

print()
# Y una lista de UN solo nombre tambien devuelve DataFrame.
print("inventario[['producto']] ->", type(inventario[["producto"]]).__name__)
inventario['producto']   -> Series
inventario[['codigo',...]] -> DataFrame

0       Teclado mecánico
1      Ratón inalámbrico
2    Monitor 24 pulgadas

codigo            producto
  P001    Teclado mecánico
  P002   Ratón inalámbrico
  P003 Monitor 24 pulgadas

inventario[['producto']] -> DataFrame
Cuál conviene

Series cuando vas a calcular: .mean(), .value_counts(), una comparación para armar una máscara. DataFrame cuando lo que sigue espera una tabla: escribir un CSV, un merge, mostrar varias columnas juntas.

La lista también fija el orden

EjemploLa lista tambien fija el ORDEN de las columnas
import pandas as pd

inventario = pd.read_csv("inventario_productos.csv")
print("orden del archivo:", list(inventario.columns))

reordenado = inventario[["precio_unitario", "codigo", "cantidad"]]
print("orden que pediste:", list(reordenado.columns))

print()
print(reordenado.head(3).to_string(index=False))

print()
print("Sirve para armar el archivo de salida: el contrato de una practica")
print("fija el orden de las columnas, y esta es la forma de cumplirlo.")
orden del archivo: ['codigo', 'producto', 'categoria', 'cantidad', 'precio_unitario']
orden que pediste: ['precio_unitario', 'codigo', 'cantidad']

 precio_unitario codigo  cantidad
            4.53   P001     112.0
          781.78   P002      82.0
          872.23   P003     108.0

Sirve para armar el archivo de salida: el contrato de una practica
fija el orden de las columnas, y esta es la forma de cumplirlo.

Esto deja de ser cosmético en cuanto hay que producir un archivo: los contratos de las prácticas fijan el orden de las columnas de salida, y esta es la forma de cumplirlo. Un df[COLUMNAS] al final del programa, con la lista escrita en una constante, y no hay manera de equivocarse.

Renombrar

Ejemplorename devuelve una copia, y hay que decirle que son columnas
import pandas as pd

inventario = pd.read_csv("inventario_productos.csv")

renombrado = inventario.rename(columns={
    "precio_unitario": "precio",
    "cantidad": "stock",
})

print("antes  :", list(inventario.columns))
print("despues:", list(renombrado.columns))

print()
print("El original no cambio: rename devuelve una tabla nueva.")
print("Si no guardas el resultado, no paso nada.")

print()
# Sin columns=, rename cree que le estas renombrando el INDICE.
confundido = inventario.rename({"precio_unitario": "precio"})
print("sin columns= ->", list(confundido.columns))
print("No dio error y no renombro nada: renombro el indice, que no tiene")
print("ninguna etiqueta que se llame precio_unitario.")
antes  : ['codigo', 'producto', 'categoria', 'cantidad', 'precio_unitario']
despues: ['codigo', 'producto', 'categoria', 'stock', 'precio']

El original no cambio: rename devuelve una tabla nueva.
Si no guardas el resultado, no paso nada.

sin columns= -> ['codigo', 'producto', 'categoria', 'cantidad', 'precio_unitario']
No dio error y no renombro nada: renombro el indice, que no tiene
ninguna etiqueta que se llame precio_unitario.

Dos cosas que sorprenden, y las dos sin dar error:

Dónde se rompe

EjemploEl KeyError que se ve imposible, y como se caza
import pandas as pd

# Un CSV exportado de una planilla suele traer espacios en los encabezados.
crudo = pd.DataFrame({
    "codigo": ["P001", "P002"],
    " precio ": [4.53, 781.78],
    "Stock": [112, 82],
})

print("columnas, tal cual:", list(crudo.columns))
print("y con los limites marcados:")
for nombre in crudo.columns:
    print(f"  |{nombre}|")

try:
    crudo["precio"]
except KeyError as error:
    print()
    print("KeyError:", error)
    print("El nombre existe... pero con un espacio de cada lado.")

limpio = crudo.rename(columns=lambda c: c.strip().lower())
print()
print("despues de normalizar los encabezados:", list(limpio.columns))
print(limpio.to_string(index=False))
columnas, tal cual: ['codigo', ' precio ', 'Stock']
y con los limites marcados:
  |codigo|
  | precio |
  |Stock|

KeyError: 'precio'
El nombre existe... pero con un espacio de cada lado.

despues de normalizar los encabezados: ['codigo', 'precio', 'stock']
codigo  precio  stock
  P001    4.53    112
  P002  781.78     82

Un KeyError con un nombre que está a la vista en la tabla es casi siempre esto: un espacio invisible, una mayúscula, o un carácter raro que quedó del archivo original. La forma de cazarlo es imprimir los nombres con delimitadores alrededor, como en el ejemplo.

KeyError: 'precio'

Causa: el nombre real tiene un espacio (' precio '), una mayúscula distinta, o un carácter invisible.

Arreglo: normalizar los encabezados apenas se lee el archivo: df.rename(columns=lambda c: c.strip().lower()). Una línea, y el problema no vuelve.

KeyError: "['precio', 'stock'] not in index"

Causa: el mismo problema pero con selección múltiple. El mensaje es más útil: dice cuáles de los nombres no encontró.

Arreglo: compará con list(df.columns) antes de pelearte.

(sin error) rename no renombró nada

Causa: faltó columns=, así que pandas intentó renombrar el índice, o no se guardó el resultado.

Arreglo: df = df.rename(columns={...}), con las dos cosas.

AttributeError: 'DataFrame' object has no attribute 'mean'... o al revés

Causa: confundiste Series con DataFrame. Muchos métodos existen en los dos pero devuelven cosas distintas, y algunos existen en uno solo.

Arreglo: mirá type(x). En el panel de Variables del depurador se ve sin escribir nada (lección 0.1).

(sin error) df.precio funciona hasta que deja de funcionar

Causa: el acceso con punto solo anda si el nombre es un identificador válido de Python y no choca con un método. df.count devuelve el método count, no tu columna llamada count.

Arreglo: usá siempre corchetes. Es dos caracteres más y nunca sorprende.

Recetario

Seleccionar y renombrar columnas
ExpresiónQué devuelveCuándo
df["c"]SeriesPara calcular sobre una columna
df[["c"]]DataFrame de 1 columnaCuando lo que sigue espera una tabla
df[["a", "b"]]DataFrame con esas dos, en ese ordenElegir y ordenar de una
df[COLUMNAS]DataFrame con el orden de la listaAl final, para cumplir el contrato de salida
df.rename(columns={...})Copia con los nombres cambiadosDespués de leer, y guardando el resultado
df.rename(columns=lambda c: c.strip().lower())Copia con los encabezados normalizadosPrimera línea, siempre
df.drop(columns=["a"])Copia sin esa columnaCuando es más corto decir qué sacar
list(df.columns)Los nombres como listaAntes de un KeyError
df.select_dtypes("number")Solo las numéricasPara aplicar algo a todas las de un tipo

Ponete a prueba

1. ¿Que diferencia hay entre df["precio"] y df[["precio"]]?Nivel 1 · Recordar
2. ¿Que hace df[["precio", "codigo", "stock"]] si en el archivo las columnas estan en otro orden?Nivel 1 · Recordar
3. Corres df.rename(columns={"cantidad": "stock"}) y despues df.columns sigue diciendo cantidad. ¿Por que?Nivel 2 · Predecir la salida
4. df.rename({"cantidad": "stock"}), sin columns=, corre sin error y no cambia nada. ¿Que renombró?Nivel 2 · Predecir la salida
5. df["precio"] tira KeyError: 'precio', pero al imprimir el DataFrame la columna precio se ve. ¿Que conviene hacer primero?Nivel 3 · Elegir entre dos casi correctas
6. Queres normalizar TODOS los encabezados de un archivo apenas lo leas. ¿Cual lo hace?Nivel 3 · Elegir entre dos casi correctas
7. Este codigo tiene que escribir un CSV con las columnas en el orden codigo, stock, precio. Corre sin error y el archivo sale con otro orden. ¿Por que?
SALIDA = ["codigo", "stock", "precio"]
inv = pd.read_csv("inventario_productos.csv")
inv.rename(columns={"cantidad": "stock", "precio_unitario": "precio"})
inv[["codigo", "cantidad", "precio_unitario"]].to_csv("salida.csv", index=False)
Nivel 4 · Encontrar el defecto