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
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
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
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
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:
renamedevuelve una copia. Si no guardás el resultado, el DataFrame original sigue igual y parece que la línea no hizo nada.- Sin
columns=,renamecree que le estás renombrando el índice. Como el índice no tiene ninguna etiqueta con ese nombre, no renombra nada y tampoco se queja.
Dónde se rompe
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.
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.
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.
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.
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).
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
| Expresión | Qué devuelve | Cuándo |
|---|---|---|
df["c"] | Series | Para calcular sobre una columna |
df[["c"]] | DataFrame de 1 columna | Cuando lo que sigue espera una tabla |
df[["a", "b"]] | DataFrame con esas dos, en ese orden | Elegir y ordenar de una |
df[COLUMNAS] | DataFrame con el orden de la lista | Al final, para cumplir el contrato de salida |
df.rename(columns={...}) | Copia con los nombres cambiados | Después de leer, y guardando el resultado |
df.rename(columns=lambda c: c.strip().lower()) | Copia con los encabezados normalizados | Primera línea, siempre |
df.drop(columns=["a"]) | Copia sin esa columna | Cuando es más corto decir qué sacar |
list(df.columns) | Los nombres como lista | Antes de un KeyError |
df.select_dtypes("number") | Solo las numéricas | Para aplicar algo a todas las de un tipo |