Ir al contenido
wannacode

Unir tablas: merge

En resumen: merge en pandas une dos tablas por una clave común, como un JOIN en SQL, y how decide qué filas se quedan.

Problema

«¿Qué categoría aporta más dinero?»

El gerente pregunta qué categoría vende más. Las líneas de pedido dicen cuántas unidades de qué producto se compraron, y a qué precio:

Pythondatos: las tablas de la tienda

Pero ahí no hay categoría. Solo hay product_id, el número de producto. El nombre y la categoría están en products.csv, y el nombre de la categoría está en una tercera tabla, categories.csv.

Los datos de una tienda casi siempre están divididos así: cada cosa se anota en un solo sitio, y las otras tablas apuntan a ella con un número. Para responder tienes que unir las tablas en una.

La idea

Unir por una clave: merge

merge toma cada fila de la tabla izquierda, busca en la tabla derecha la fila con el mismo valor en la columna clave y pega las dos filas en una. Eso se llama join (unión), y la columna por la que busca es la clave. En SQL lo mismo es JOIN.

Aquí la clave tiene un nombre distinto en cada tabla: product_id en las líneas, id en los productos. left_on y right_on dicen qué columna está en cada lado:

Pythondatos: las tablas de la tienda

Las filas siguen siendo 152, y las columnas pasan a 10: las cuatro de items y las seis de products. Las columnas product_id y id tienen el mismo número, ahora dos veces.

Por eso, lo más habitual es renombrar primero la clave para que tenga el mismo nombre y unir con on. Además, tomas solo las columnas que necesitas:

Pythondatos: las tablas de la tienda

Míralo

Cómo encuentra merge los pares

Aquí lo mismo con tablas pequeñas: 5 líneas de pedido y una lista de precios de 4 filas. En la lista de precios el café (17) aparece dos veces, con el precio antiguo y el nuevo, y el producto 26 todavía no está. Con how="left" se queda cada línea de pedido:

Pythondatos: las tablas de la tienda

import pandas as pd
lines = pd.DataFrame({
'order_id': [7, 7, 8, 8, 9],
'product_id': [1, 17, 5, 1, 26],
'quantity': [2, 1, 3, 1, 2],
})
catalog = pd.DataFrame({
'product_id': [1, 5, 17, 17],
'name': ['Pan blanco', 'Leche fresca', 'Café en grano', 'Café en grano'],
'price': [1.2, 1.6, 13.9, 14.9],
})
both = lines.merge(catalog, on='product_id', how='left')
print(len(lines), len(both))
both
Paso a paso, en palabras
  1. lines son 5 líneas de pedido. product_id dice qué producto se compró.
  2. catalog es la lista de precios: 4 filas. El producto 17 está dos veces, con el precio antiguo y el nuevo.
  3. Línea 1: product_id 1. merge busca todas las filas con 1 en catalog: Pan blanco. El par se convierte en una fila de both.
  4. Línea 2: product_id 17 está dos veces en catalog, con 13,90 € y 14,90 €. Los dos pares se convierten en dos filas.
  5. Línea 3: product_id 5, es Leche fresca.
  6. Línea 4: product_id 1, otra vez Pan blanco. Una fila de catalog puede ser el par de muchas líneas.
  7. Línea 5: el producto 26 no está en catalog. Con how='left' la línea se queda, y name y price son NaN.
  8. Entraron 5 líneas, salieron 6 filas: la clave repetida duplicó una línea. Por eso cuentas len antes y después.
  • Cada línea de pedido busca todas las filas de la lista de precios con el mismo product_id.
  • La línea del café encuentra dos y se convierte en dos filas. Cinco líneas pasan a 6.
  • El producto 26 no encuentra nada. Con how="left" la línea se queda, y las columnas de la lista de precios son NaN.

Pruébalo

¿Cuántas filas?

1. Si el café (17) estuviera tres veces en catalog, con tres precios, ¿cuántas filas tendría both para las mismas 5 líneas de pedido?

La idea

Qué filas se quedan: how

No todas las filas tienen pareja. Dos de los 20 clientes todavía no han pedido nada, y hay 60 pedidos. El parámetro how decide qué pasa con las filas sin pareja:

Pythondatos: las tablas de la tienda
  • inner (el valor por defecto): solo los pares. 60 filas, una por cada pedido. Los clientes sin pedidos desaparecen.
  • left: cada fila de la izquierda, con NaN donde no hay pareja. 62 filas: 60 pedidos y los dos clientes sin pedidos.
  • right: cada fila de la derecha. Aquí son 60, porque cada pedido tiene un cliente.
  • outer: cada fila de los dos lados.

indicator=True te dice de dónde vino cada fila. Añade una columna _merge con los valores both, left_only y right_only:

Pythondatos: las tablas de la tienda

Los dos clientes sin pedidos tienen los id 19 y 20. right_only es 0: ningún pedido pertenece a un cliente que falte en la tabla. Si lo hubiera, sería un error en los datos, y indicator lo mostraría.

Más precisamente, para curiosos

indicator es la comprobación más rápida después de un join. value_counts() sobre _merge te dice enseguida cuántas filas no encontraron pareja. Si esperas 0 y hay 300, las claves no coinciden: por ejemplo, una es el texto '17' y la otra el número 17.

Pruébalo

Productos sin reseña

¿Qué productos todavía no tienen ninguna reseña? Une products con las reseñas de modo que cada producto se quede, y guarda en no_review solo las filas sin pareja.

Pythondatos: las tablas de la tienda

La idea

Cuando la clave se repite

Ahora el gerente quiere la valoración del producto junto a cada línea de pedido. Las reseñas están en reviews.csv, y parece natural unirlas por product_id:

Pythondatos: las tablas de la tienda

Las filas pasaron a ser 167 en lugar de 152, y el total de los pedidos saltó de 1963,50 € a 2485,60 €. El café tiene 3 reseñas, así que cada línea del café formó pareja con las tres y se convirtió en tres filas. La sartén (22) tiene 2 reseñas. Nada falla, pero los números ya son incorrectos.

Por eso cuentas las filas antes y después de cada join. Si la tabla derecha debe tener una fila por clave, díselo a pandas con validate:

Pythondatos: las tablas de la tienda

validate="many_to_one" significa «a la izquierda la clave puede repetirse, a la derecha debe ser única». Si no lo es, merge se detiene con MergeError y te dice qué clave se repite. También existen "one_to_one" y "one_to_many".

Pruébalo

Una valoración en cada línea

Pon la valoración media del producto junto a cada línea de pedido. Las filas deben seguir siendo las que son (152), y los productos sin reseña deben recibir NaN.

Pythondatos: las tablas de la tienda

La idea

Dos columnas de nombre: suffixes

Ahora viene la categoría. Tanto los productos como las categorías tienen las columnas id y name. Tras el join pandas conserva las dos y añade las terminaciones _x (izquierda) y _y (derecha). Con suffixes las eliges tú:

Pythondatos: las tablas de la tienda

suffixes=("", "_category") deja las columnas del producto como están y añade _category a las de la categoría. La tarjeta de regalo no tiene categoría, así que name_category es NaN.

Aquí está la trampa de la lección. Si olvidas how="left", el inner join tira la tarjeta de regalo sin una sola advertencia:

Pythondatos: las tablas de la tienda

152 filas pasan a 148. Cuatro ventas de la tarjeta de regalo, 240 € en total, desaparecen del informe por categoría. Con how="left" las filas siguen siendo 152, y la categoría que falta aparece como NaN.

La idea

Una debajo de otra: concat

A veces las tablas no deben pegarse una al lado de otra, sino una debajo de otra. La tienda cambió de sistema: los pedidos hasta finales de 2025 se exportaron del antiguo, y los de 2026 del nuevo, con las mismas columnas. pd.concat las apila en una tabla:

Pythondatos: las tablas de la tienda

21 + 39 = 60 filas. ignore_index=True vuelve a numerar las filas desde 0. Sin él se conservan los índices de las dos tablas y pueden repetirse.

Las fechas aquí son texto como «2025-09-01», así que comparar < '2026-01-01' en orden alfabético coincide con el orden en el tiempo. Las fechas de verdad vienen en la lección «Fechas y series temporales».

Más precisamente, para curiosos

merge busca pares por una clave y pone las columnas una al lado de otra. concat no busca nada: apila las filas (o, con axis=1, las columnas) en el orden de las tablas. Si las columnas no coinciden, concat funciona igualmente, pero las celdas que faltan se vuelven NaN, así que compara antes df.columns de las dos tablas.

Ejercicio

El informe por categoría

Responde al gerente: ¿cuánto es el ingreso de cada categoría, sin los pedidos cancelados? Une las líneas con los pedidos (para status), con los productos (para category_id) y con las categorías, de modo que después de cada join sigan siendo 152 filas. Los productos sin categoría van bajo «Sin categoría». Guarda el resultado en by_category.

Pythondatos: las tablas de la tienda

Test

Ponte a prueba

1. ¿Qué pasa con una fila de la tabla izquierda que no tiene en la derecha una fila con la misma clave, si no pones how?
2. 152 líneas de pedido unidas con las reseñas por product_id pasaron a ser 167. ¿Por qué?
3. ¿Qué hace validate="many_to_one"?
4. products y categories tienen las dos una columna name. ¿Qué columnas da unirlas?
5. Tienes los pedidos del sistema antiguo y del nuevo, con las mismas columnas. ¿Cómo haces una sola tabla?

Creado por Ivo Gergov como parte de la campaña de Evo Tech por una educación accesible en programación ·