tutoriales.com

Uniendo el Universo de Datos: Guía Completa de `merge()`, `join()` y `concat()` en Pandas ✨

Este tutorial te guiará a través de las poderosas funciones de combinación de DataFrames en Pandas: `merge()`, `join()` y `concat()`. Aprenderás cuándo y cómo usar cada una, explorando ejemplos prácticos y consideraciones importantes para la manipulación de tus datos. Al finalizar, tendrás las habilidades para integrar diversas fuentes de datos con confianza y eficiencia.

Intermedio15 min de lectura4 views
Reportar error

La capacidad de combinar conjuntos de datos es una habilidad fundamental en la ciencia de datos. Rara vez trabajamos con una única fuente de información; lo más común es que nuestros datos provengan de múltiples tablas, archivos o bases de datos que necesitan ser unidos para un análisis coherente. Pandas, la biblioteca por excelencia para la manipulación de datos en Python, nos ofrece herramientas robustas para esta tarea: merge(), join() y concat().

Entender las diferencias y aplicaciones de cada una es crucial para construir pipelines de datos eficientes y evitar errores comunes. En este tutorial, desglosaremos estas funciones, proporcionando una guía práctica y ejemplos claros para que puedas dominarlas por completo.

🚀 Introducción: La Necesidad de Unir Datos

Imagina que tienes información de clientes en una tabla y sus pedidos en otra. Para saber qué clientes realizaron qué pedidos, necesitas unir estas dos piezas de información. Aquí es donde entran en juego las funciones de combinación de Pandas. Cada una tiene su propósito y escenarios de uso óptimos:

  • pd.merge(): Para unir DataFrames basándose en columnas clave, similar a las operaciones JOIN de SQL.
  • DataFrame.join(): Un método conveniente para unir DataFrames en base a sus índices o a una columna clave en el DataFrame de la derecha.
  • pd.concat(): Para apilar DataFrames vertical u horizontalmente.
💡 **Consejo:** Una buena comprensión de la teoría de conjuntos y las operaciones de bases de datos (especialmente SQL JOINs) te ayudará enormemente a entender cómo funcionan estas funciones en Pandas.

🤝 pd.merge(): El Poder de los JOINs de SQL

pd.merge() es la función más flexible y potente para combinar DataFrames basándose en una o más claves. Su funcionamiento es análogo a las operaciones JOIN de SQL. Es ideal cuando necesitas combinar dos DataFrames por valores comunes en una o varias columnas.

Sintaxis Básica de merge()

pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None, 
         left_index=False, right_index=False, sort=False, 
         suffixes=('_x', '_y'), copy=True, indicator=False, 
         validate=None)

Los argumentos clave son:

  • left: El DataFrame de la izquierda.
  • right: El DataFrame de la derecha.
  • how: Tipo de merge a realizar (veremos los detalles a continuación).
  • on: Nombre de la columna o lista de columnas en la que se unen los DataFrames. Estas columnas deben existir en ambos DataFrames.
  • left_on, right_on: Nombres de las columnas en left y right respectivamente si tienen nombres diferentes para la misma clave.
  • left_index, right_index: Usar el índice como clave de unión.

Tipos de merge() (how)

El parámetro how define cómo se manejan las filas que no tienen una coincidencia en el otro DataFrame. Aquí están los cuatro tipos principales:

  1. 'inner' (valor por defecto): Retorna solo las filas donde las claves tienen coincidencias en ambos DataFrames. Es el tipo de JOIN más común.
  2. 'outer': Retorna todas las filas cuando hay una coincidencia en una de las claves. Si una clave no tiene coincidencia, los valores se rellenan con NaN.
  3. 'left': Retorna todas las filas del DataFrame left, y las filas coincidentes del DataFrame right. Si no hay coincidencia, los valores del right se rellenan con NaN.
  4. 'right': Retorna todas las filas del DataFrame right, y las filas coincidentes del DataFrame left. Si no hay coincidencia, los valores del left se rellenan con NaN.
LEFT JOIN Tabla A + Intersección RIGHT JOIN Tabla B + Intersección INNER JOIN Solo Intersección OUTER JOIN Unión Completa

Ejemplo Práctico de merge()

Vamos a crear dos DataFrames de ejemplo:

import pandas as pd
import numpy as np

# DataFrame de empleados
df_empleados = pd.DataFrame({
    'ID_Empleado': [1, 2, 3, 4, 5],
    'Nombre': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
    'Departamento': ['RRHH', 'IT', 'Ventas', 'IT', 'RRHH']
})

# DataFrame de salarios
df_salarios = pd.DataFrame({
    'ID_Empleado': [1, 2, 3, 6],
    'Salario': [60000, 75000, 80000, 50000] # El empleado 6 no existe en df_empleados
})

print("\nDataFrame de Empleados:")
print(df_empleados)
print("\nDataFrame de Salarios:")
print(df_salarios)

Merge 'inner' (por defecto):

df_inner_merge = pd.merge(df_empleados, df_salarios, on='ID_Empleado', how='inner')
print("\nInner Merge (solo coincidencias):")
print(df_inner_merge)

Merge 'left':

df_left_merge = pd.merge(df_empleados, df_salarios, on='ID_Empleado', how='left')
print("\nLeft Merge (todas las filas de empleados, salarios coincidentes):")
print(df_left_merge)

Merge 'right':

df_right_merge = pd.merge(df_empleados, df_salarios, on='ID_Empleado', how='right')
print("\nRight Merge (todas las filas de salarios, empleados coincidentes):")
print(df_right_merge)

Merge 'outer':

df_outer_merge = pd.merge(df_empleados, df_salarios, on='ID_Empleado', how='outer')
print("\nOuter Merge (todas las filas de ambos, NaN si no hay coincidencia):")
print(df_outer_merge)
🔥 **Importante:** Cuando las columnas clave tienen nombres diferentes en los dos DataFrames, usa `left_on` y `right_on`. Por ejemplo: `pd.merge(df1, df2, left_on='ID_Cliente', right_on='Cliente_ID')`.

Manejo de Sufijos (suffixes)

Si ambos DataFrames tienen columnas con el mismo nombre pero que no son las claves de unión, Pandas añade sufijos para distinguirlas por defecto (_x y _y). Puedes personalizar estos sufijos con el parámetro suffixes.

df_info_personal = pd.DataFrame({
    'ID_Empleado': [1, 2, 3, 4],
    'Edad': [25, 30, 35, 28],
    'Nivel': ['Junior', 'Senior', 'Manager', 'Junior']
})

df_info_laboral = pd.DataFrame({
    'ID_Empleado': [1, 2, 3, 5],
    'Experiencia': [2, 7, 10, 1],
    'Nivel': ['Básico', 'Intermedio', 'Avanzado', 'Básico'] # Columna 'Nivel' también existe aquí
})

merged_df_suffixes = pd.merge(df_info_personal, df_info_laboral, on='ID_Empleado', suffixes=('_personal', '_laboral'))
print("\nMerge con Sufijos Personalizados:")
print(merged_df_suffixes)

🔗 DataFrame.join(): Uniones Basadas en Índices

El método DataFrame.join() es muy similar a merge(), pero está optimizado para unir DataFrames en función de sus índices. Aunque puede unirse también en una columna clave del DataFrame de la derecha, su uso principal es para uniones basadas en índices. Es más conciso para este tipo de operaciones.

Sintaxis Básica de join()

df_left.join(other, on=None, how='left', lsuffix='', rsuffix='', 
             sort=False)

Los argumentos clave son:

  • other: El DataFrame o lista de DataFrames a unir con el DataFrame left.
  • on: Columna(s) en left a usar como clave, que se unirá con el índice de other.
  • how: Tipo de join ('left', 'right', 'inner', 'outer'). Por defecto es 'left'.
  • lsuffix, rsuffix: Sufijos para nombres de columnas duplicados.

Ejemplo Práctico de join()

Vamos a usar los mismos DataFrames que antes, pero modificaremos uno para que el ID sea el índice.

# DataFrame de empleados con ID como índice
df_empleados_idx = df_empleados.set_index('ID_Empleado')

# DataFrame de salarios (manteniendo ID como columna para uniones on=)
df_salarios_idx = df_salarios.set_index('ID_Empleado')

print("\nDataFrame de Empleados (Índice):")
print(df_empleados_idx)
print("\nDataFrame de Salarios (Índice):")
print(df_salarios_idx)

Join en el índice:

df_joined_idx = df_empleados_idx.join(df_salarios_idx, how='left')
print("\nJoin en Índices (Left Join):")
print(df_joined_idx)

Join de una columna a un índice:

Si df_salarios no tuviera el ID_Empleado como índice, podrías hacer:

df_salarios_no_idx = pd.DataFrame({
    'ID_Empleado_Salario': [1, 2, 3, 6],
    'Salario': [60000, 75000, 80000, 50000] 
})

# El ID_Empleado_Salario de df_salarios_no_idx se unirá con el índice de df_empleados_idx
df_joined_col_idx = df_empleados_idx.join(df_salarios_no_idx.set_index('ID_Empleado_Salario'), how='left')
print("\nJoin de Columna a Índice (Left Join):")
print(df_joined_col_idx)
📌 Nota: `join()` por defecto realiza un 'left join' y espera que la clave de unión del DataFrame de la derecha sea su índice. `merge()` por defecto es 'inner' y espera columnas.

¿Cuándo usar join() frente a merge()?

  • Usa join() cuando quieras unir por el índice de uno o ambos DataFrames.
  • Usa merge() cuando quieras unir por columnas específicas, especialmente si son varias o si los nombres de las columnas clave son diferentes.

En esencia, df1.join(df2) es un atajo para pd.merge(df1, df2, left_index=True, right_index=True, how='left') (o sus variaciones con on).


pd.concat(): Apilando DataFrames

pd.concat() se utiliza para concatenar objetos de Pandas (Series o DataFrames) a lo largo de un eje particular, ya sea apilándolos uno encima del otro (verticalmente) o uno al lado del otro (horizontalmente). No se basa en claves para encontrar coincidencias, sino en la posición o en los índices de los objetos.

Sintaxis Básica de concat()

pd.concat(objs, axis=0, join='outer', ignore_index=False, keys=None, 
          levels=None, names=None, verify_integrity=False, copy=True)

Los argumentos clave son:

  • objs: Una secuencia o mapeo de objetos Series o DataFrame. Por ejemplo, una lista [df1, df2, df3].
  • axis: El eje a lo largo del cual se concatenan los DataFrames:
    • axis=0 (por defecto): Concatena por filas (apila verticalmente).
    • axis=1: Concatena por columnas (apila horizontalmente).
  • join: Cómo manejar los índices/columnas en el otro eje cuando no coinciden:
    • 'outer' (por defecto): Unión de los índices/columnas (mantener todos).
    • 'inner': Intersección de los índices/columnas (mantener solo los comunes).
  • ignore_index: Si es True, el nuevo índice resultante no contendrá los valores de índice de los objetos concatenados. Útil si no te importa el índice original.
  • keys: Permite crear un índice jerárquico para identificar de qué DataFrame proviene cada fila/columna.
Pandas Concatenación (pd.concat) axis=0 (Vertical) DataFrame 1 DataFrame 2 Resultado axis=1 (Horizontal) DF 1 DF 2 Resultado Se añaden filas debajo Se añaden columnas al lado

Ejemplo Práctico de concat()

Vamos a crear algunos DataFrames para concatenar.

# DataFrame de ventas del primer trimestre
df_q1 = pd.DataFrame({
    'Producto': ['A', 'B', 'C'],
    'Ventas_Enero': [100, 150, 200],
    'Ventas_Febrero': [120, 140, 180]
})

# DataFrame de ventas del segundo trimestre
df_q2 = pd.DataFrame({
    'Producto': ['A', 'D', 'C'],
    'Ventas_Marzo': [130, 90, 210],
    'Ventas_Abril': [110, 100, 190]
})

print("\nDataFrame Q1:")
print(df_q1)
print("\nDataFrame Q2:")
print(df_q2)

Concatenación Vertical (axis=0):

df_ventas_vertical = pd.concat([df_q1, df_q2], axis=0, ignore_index=True)
print("\nConcatenación Vertical (por filas, ignorando índice):")
print(df_ventas_vertical)

Si no ignoramos el índice, los índices originales se mantienen, lo que podría generar índices duplicados. ignore_index=True es muy útil para resetear el índice.

Concatenación Horizontal (axis=1):

df_ventas_horizontal = pd.concat([df_q1, df_q2], axis=1)
print("\nConcatenación Horizontal (por columnas):")
print(df_ventas_horizontal)

Fíjate que df_q1 y df_q2 no tienen las mismas filas, y sus índices son por defecto [0, 1, 2]. Al concatenar horizontalmente, Pandas alinea por índice. Si los índices no coinciden, se rellenarán con NaN.

Concatenación con keys:

df_ventas_con_keys = pd.concat([df_q1, df_q2], keys=['Q1', 'Q2'])
print("\nConcatenación con Keys (índice jerárquico):")
print(df_ventas_con_keys)
print("\nAccediendo a datos del Q1:")
print(df_ventas_con_keys.loc['Q1'])
¿Cuándo usar `join='inner'` en `concat()`?Cuando concatenas horizontalmente (`axis=1`) y solo quieres mantener las filas donde los índices son comunes en *todos* los DataFrames a concatenar, usa `join='inner'`. Por ejemplo, `pd.concat([df_q1, df_q2], axis=1, join='inner')` solo incluiría los índices `0, 1, 2` de ambos DataFrames si los tuvieran en común. En el ejemplo anterior, `df_q1` y `df_q2` tienen los mismos índices por defecto `[0, 1, 2]`, por lo que el `inner` o `outer` darían el mismo resultado si los DataFrames tienen la misma cantidad de filas. Pero si tuvieran diferentes números de filas y, por tanto, diferentes rangos de índices, `inner` sería útil para solo ver la intersección de filas.

🆚 Comparativa: merge(), join() y concat()

Es fundamental saber cuándo usar cada una. Aquí una tabla resumen:

Característicapd.merge()DataFrame.join()pd.concat()
------------
PropósitoUnir DataFrames por columnas clave (SQL JOIN)Unir DataFrames por índices (o columna con índice)Apilar DataFrames vertical u horizontalmente
Claves de UniónColumnas especificadas (on, left_on, right_on) o índices (left_index, right_index)Por defecto el índice del DataFrame base y el índice de otherNo se basa en claves, sino en la posición o el índice del eje opuesto
------------
how / join'inner' (por defecto), 'outer', 'left', 'right''left' (por defecto), 'outer', 'inner', 'right''outer' (por defecto), 'inner' (para el eje opuesto)
ArgumentosAmplia gama de opciones para claves, sufijos, etc.Más conciso, enfocado en uniones de índice.axis es clave para dirección, ignore_index, keys
------------
Uso ComúnCombinar datos relacionales (e.g., clientes y pedidos)Añadir columnas a un DataFrame basándose en su índiceAñadir filas (nuevos registros) o columnas (nuevas variables)
⚠️ **Advertencia:** Aunque `DataFrame.join()` puede aceptar una columna con `on`, la práctica recomendada para uniones basadas en columnas es `pd.merge()`, ya que es más explícito y flexible en esos casos.

🛠️ Estrategias Avanzadas y Consejos

Unir con Múltiples Claves

Puedes especificar una lista de columnas para on en merge() si necesitas unir por varias claves a la vez. Esto es común para asegurar la unicidad de las filas.

df_productos = pd.DataFrame({
    'ID_Categoria': [1, 1, 2, 2],
    'ID_Producto': [101, 102, 201, 202],
    'Nombre_Producto': ['Laptop', 'Mouse', 'Teclado', 'Monitor']
})

df_ventas = pd.DataFrame({
    'ID_Categoria': [1, 1, 2, 1, 2],
    'ID_Producto': [101, 102, 201, 101, 202],
    'Cantidad': [5, 10, 8, 3, 12],
    'Fecha': ['2023-01-01', '2023-01-02', '2023-01-01', '2023-01-03', '2023-01-04']
})

merged_multi_key = pd.merge(df_productos, df_ventas, on=['ID_Categoria', 'ID_Producto'])
print("\nMerge con Múltiples Claves:")
print(merged_multi_key)

Verificación de Uniones (validate)

El argumento validate en pd.merge() es muy útil para asegurar la integridad de tu unión y detectar problemas de datos. Puede tomar los valores:

  • 'one_to_one': Verifica que la clave de unión sea única en ambos DataFrames. Si no lo es, lanza un MergeError.
  • 'one_to_many': Verifica que la clave de unión sea única en el DataFrame izquierdo.
  • 'many_to_one': Verifica que la clave de unión sea única en el DataFrame derecho.
  • 'many_to_many': Permite múltiples coincidencias en ambos lados (por defecto).
# Ejemplo de merge one-to-one (esperamos que ID_Empleado sea único en ambos)
try:
    pd.merge(df_empleados, df_salarios, on='ID_Empleado', validate='one_to_one')
    print("\nMerge one-to-one exitoso (los IDs son únicos en ambos lados)")
except pd.errors.MergeError as e:
    print(f"\nError de Merge one-to-one: {e}")

# Creamos un escenario para un error de one-to-one
df_salarios_duplicado = pd.DataFrame({
    'ID_Empleado': [1, 1, 2],
    'Salario': [60000, 65000, 75000]
})

try:
    pd.merge(df_empleados, df_salarios_duplicado, on='ID_Empleado', validate='one_to_one')
except pd.errors.MergeError as e:
    print(f"\nError esperado de Merge one-to-one con duplicados: {e}")

Mejorando el Rendimiento con Índices

Cuando realizas operaciones de merge() o join() en DataFrames muy grandes, establecer índices en las columnas clave puede mejorar significativamente el rendimiento, ya que Pandas puede usar algoritmos de búsqueda más rápidos.

# Antes del merge, establecer el índice en la columna clave
df_empleados_indexed = df_empleados.set_index('ID_Empleado')
df_salarios_indexed = df_salarios.set_index('ID_Empleado')

# Luego, usar merge con left_index=True y right_index=True
# O directamente usar join, que está diseñado para índices
merged_indexed = pd.merge(df_empleados_indexed, df_salarios_indexed, left_index=True, right_index=True, how='left')
print("\nMerge usando índices para mejor rendimiento:")
print(merged_indexed)
90% Dominio

Cajas de Información Avanzadas

<div class="callout tip">
    💡 <strong>Consideración de Memoria:</strong> Para DataFrames muy grandes, las operaciones de unión pueden consumir mucha memoria. Asegúrate de tener suficiente RAM o considera estrategias de procesamiento en bloques si trabajas con datasets enormes que no caben en memoria.
</div>
<div class="callout important">
    🔥 <strong>Cuidado con los Duplicados:</strong> Antes de realizar un `merge()`, especialmente con un `how='inner'`, asegúrate de entender cómo los valores duplicados en tus claves afectarán el resultado. Pueden generar filas adicionales no deseadas (producto cartesiano limitado). Usa `df.drop_duplicates()` si es necesario.
</div>
<div class="callout warning">
    ⚠️ <strong>Tipos de Datos:</strong> Asegúrate de que las columnas que estás utilizando como claves de unión tengan el mismo tipo de dato en ambos DataFrames. Pandas es inteligente, pero las inconsistencias pueden llevar a resultados inesperados o errores.
</div>

Diagrama de Flujo para Decidir

Inicio ¿Unir por filas o columnas? pd.concat() No ¿Unir por índices? df.join() No ¿Unir por columnas clave? pd.merge() Diagrama de selección Pandas • Merge, Join & Concat

Este diagrama te ayuda a decidir qué función usar basándote en la naturaleza de tu tarea de combinación de datos.


✅ Conclusión

Dominar pd.merge(), DataFrame.join() y pd.concat() es esencial para cualquier analista de datos o científico de datos que trabaje con Pandas. Cada función tiene su nicho y, cuando se usan correctamente, permiten integrar y preparar datos de manera eficiente para el análisis.

Recuerda:

  • merge() para uniones tipo SQL en columnas clave.
  • join() para uniones basadas en índices, más conciso.
  • concat() para apilar DataFrames vertical u horizontalmente.

Practica con diferentes conjuntos de datos y tipos de how o axis para solidificar tu comprensión. ¡Tus habilidades de manipulación de datos te lo agradecerán!

Tutoriales relacionados

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!