Python · Fundamentos · 6 min de lectura

Cómo funciona un DataFrame

Antes de la primera línea de pandas: qué es de verdad un DataFrame, por qué cada columna tiene un tipo y por qué una sola expresión sobre una columna reemplaza un bucle sobre miles de filas.

La idea

Un DataFrame es una tabla que vive en la memoria de Python. Tiene filas hacia abajo, columnas hacia el lado y una etiqueta en cada fila llamada índice. orders, order_items y customers de Harbor & Pine son DataFrames, cargados para ti antes del primer nivel de los capítulos de pandas.

Figura 1

Las partes de un DataFrame: índice, columnas y valores

orders
indexorder_idstatuschannelshipping_fee
01001completedweb0.00
11002completedweb0.00
21003completedmarketplace4.99
31004completedmarketplace4.99
orders['channel'] (una Series)
indexchannel
0web
1web
2marketplace
3marketplace

Paso 1 de 4 Este es el comienzo del DataFrame orders. Parece una tabla porque lo es: filas hacia abajo, columnas hacia el lado.

  1. Este es el comienzo del DataFrame orders. Parece una tabla porque lo es: filas hacia abajo, columnas hacia el lado.
  2. La primera columna no son datos. Es el índice: una etiqueta para cada fila, aquí los números 0, 1, 2, 3. pandas lo añade solo y lo usa para seguir la pista de las filas.
  3. Cada una de las otras columnas tiene un nombre y guarda un único tipo de valor de arriba abajo. shipping_fee son todo números, channel es todo texto.
  4. Si eliges una columna con orders['channel'] obtienes una Series: una sola columna que lleva consigo el mismo índice.

Lo importante es cómo funcionan las columnas. Un DataFrame no es una cuadrícula de celdas independientes, como una hoja de cálculo. Es un conjunto de columnas, y cada columna es un arreglo con tipo: un nombre, un tipo de valor, un valor por fila. Todo lo que harás en pandas es una operación sobre columnas enteras: sumar dos, comparar una con un número, agrupar por una, sumar otra.

Por qué existe

Python a secas te da listas. Una lista de 30.000 precios funciona, pero para duplicarlos escribes un bucle, y un bucle que recorre 30.000 elementos en Python es lento y ruidoso. Para mantener una segunda lista de cantidades alineada con los precios, tienes que recordar que la posición 4.711 de una significa la posición 4.711 de la otra. Nada lo garantiza.

Un DataFrame resuelve los dos problemas. Las columnas se guardan una al lado de otra y siempre tienen la misma longitud, así que la fila 4.711 es la misma fila en todas las columnas. Y las operaciones actúan sobre la columna entera de una vez, en código compilado rápido, de modo que una pregunta sobre 30.000 líneas de pedido tarda un parpadeo y no un café.

Cómo funciona por dentro

Filas, columnas, índice

orders.shape da (12000, 7): 12.000 filas y 7 columnas. El índice es aparte de las 7 columnas. Por defecto son los números de 0 a 11999, y su trabajo es etiquetar filas para que pandas pueda alinear cosas. Cuando filtras u ordenas, las filas conservan sus etiquetas originales, por eso un resultado puede empezar en la fila 3.418 y no en la 0. reset_index() las renumera cuando quieres eso.

Columnas con tipo

Figura 2

Cada columna tiene un solo dtype

orders.dtypes
columndtypeexample
order_idint641001
statusobjectcompleted
shipping_feefloat644.99
order_dateobject2023-01-01 20:51:19

Paso 1 de 4 orders.info() u orders.dtypes muestra el dtype de cada columna: el tipo de valor que guarda.

  1. orders.info() u orders.dtypes muestra el dtype de cada columna: el tipo de valor que guarda.
  2. order_id es int64, números enteros. shipping_fee es float64, números con decimales. Con ambos se puede hacer aritmética.
  3. status es object, que por ahora significa texto. Puedes compararlo con 'completed', pero no sumarlo.
  4. order_date también es object: el archivo guarda las fechas como texto, así que pandas todavía no las trata como fechas. Un dtype equivocado es la razón habitual de que un cálculo falle o ordene raro.

Una columna guarda un dtype, y el dtype decide qué puedes hacer. Los números se suman y se promedian. El texto se compara y se corta. Las fechas se restan. Si un número llega como texto ("$1,249.00" en un archivo de proveedor, por ejemplo), ninguna aritmética funciona hasta que lo conviertes. Por eso los capítulos de pandas empiezan con info(): muestra el dtype de cada columna y cuántos valores no faltan. Una columna como coupon_code tiene huecos, y pandas los muestra como NaN.

Operaciones vectorizadas

Una operación sobre una columna se aplica a cada valor, fila a fila, y devuelve una columna.

Figura 3

Una expresión sobre una columna, sin bucle

order_items
quantityunit_pricediscount_pct
931.990.0
311.990.0
411.990.0
1149.000.0
quantity * unit_price
line_total
287.91
35.97
47.96
149.00

Paso 1 de 3 Cuatro líneas de pedido de order_items. Queremos el dinero de cada línea: cantidad por precio.

  1. Cuatro líneas de pedido de order_items. Queremos el dinero de cada línea: cantidad por precio.
  2. order_items['quantity'] * order_items['unit_price'] multiplica las dos columnas fila a fila: la primera con la primera, la segunda con la segunda, y así sucesivamente.
  3. El resultado es una columna nueva con un valor por fila. No hubo for, ni contador, ni una primera fila tratada de forma especial.

De ahí se siguen tres cosas. El resultado tiene la misma longitud y el mismo índice que los datos de entrada, así que encaja con la tabla de la que salió. Puedes guardarlo como columna nueva: order_items['line_total'] = order_items['quantity'] * order_items['unit_price']. Y es rápido, porque pandas entrega la columna entera a código compilado en vez de ejecutar Python una vez por fila. Lo mismo ocurre con una comparación: orders['shipping_fee'] > 0 da una columna de True y False, una por pedido. Esa es la base del filtrado.

Un DataFrame, una tabla SQL y una hoja de cálculo

Los tres guardan filas y columnas, y se distinguen en cómo trabajas con ellos.

  • Hoja de cálculo. Cada celda es independiente, y una fórmula se escribe una vez por celda. Los tipos son laxos: una celda puede tener un número y la siguiente texto. Ves los datos y los editas a mano.
  • Tabla SQL. Las columnas tienen tipo y las filas no tienen orden salvo que lo pidas. Los datos viven en una base de datos; envías una consulta y recibes un resultado. Un cambio es una sentencia como UPDATE.
  • DataFrame. Las columnas tienen tipo como en SQL, pero la tabla está en la memoria de tu sesión y tiene un índice que conserva el orden de las filas. Ejecutas una operación tras otra, y cada paso devuelve un DataFrame o una Series nuevos a los que puedes poner nombre y guardar.

El mismo trabajo en cada uno: "total de envío por canal" es una fórmula SUM por grupo en una hoja de cálculo, un GROUP BY en SQL y orders.groupby('channel')['shipping_fee'].sum() en pandas. La lectura es la misma; solo cambia el vocabulario.

Errores comunes

"Necesito un bucle para hacer algo con cada fila"

Casi nunca. Los bucles son el hábito de Python a secas, y funcionan sobre un DataFrame, pero son lentos y largos. orders['shipping_fee'] * 2 es una línea y duplica las 12.000 tarifas. Recurre a un bucle solo cuando el resultado de una fila dependa de verdad del de la anterior. Si te ves escribiendo for i in range(len(df)), busca primero la operación sobre columnas.

"El índice es simplemente la primera columna"

El índice no es una de las 7 columnas de orders. Etiqueta filas y no cuenta en shape. Como viaja con los datos, un resultado filtrado conserva las etiquetas viejas: la tercera fila del resultado puede llevar la etiqueta 2.041. Usa .iloc para "la tercera fila" y .loc para "la fila con etiqueta 2.041", como verás en el capítulo 5.

"Un DataFrame se comporta como una hoja de cálculo: cada celda es independiente"

En pandas una celda nunca se edita aislada con una fórmula que vive en ella. Las operaciones actúan sobre columnas, y una columna tiene un dtype. Por eso un solo valor de texto suelto como "n/a" en una columna de precios convierte toda la columna en object, y entonces sum() no puede sumarla. Corrige el valor o convierte la columna, y la aritmética vuelve.

Ponte a prueba

Una pregunta. Al responderla, la lección queda marcada en el mapa; nunca bloquea un nivel.

En orders, la columna shipping_fee guarda números y status guarda texto. ¿Qué pasa con orders['shipping_fee'] * 2?

Elige qué puede ejecutarse en este sitio. Puedes cambiarlo cuando quieras desde "Configurar cookies", al pie de cada página.