You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

按特定约束合并多个pandas DataFrame的实现问询

Solution for Merging Three Pandas DataFrames with Custom Rules

I see you've tried basic merge/join/concat and manual approaches without hitting your exact requirements—let's build a solution that handles the custom value differentiation, row/column additions, and readability you need. We'll break this into two core parts: merging df1 and df2 with your value-splitting rule, then incorporating df3.

Step 1: Prep df1 and df2 for Merging

First, we need to align their ID column names since df1 uses ID and df2 uses PERSID—this ensures we can properly match rows:

import pandas as pd
import numpy as np

# Recreate your original DataFrames (fixed the typo in df2's index setup)
df1 = pd.DataFrame({'ID': [20016, 50048, 13478, 68493, 57483], 
                    'Sex': ['F', 'M', 'F', 'F', 'M'], 
                    'Var1': [3, 3, 3, 3, 2], 
                    'Var2': [2, 3, np.nan, 3, 2], 
                    'Var3': [-0.25, 0, 4, np.nan, 0.14]}).set_index('ID').reset_index()
df2 = pd.DataFrame({'PERSID': [20016, 50048, 13478, 68493, 57483, 45623], 
                    'Sex': ['F', 'M', 'F', 'F', 'M', 'M'], 
                    'Var1': [3, 1, 3, 3, 2, np.nan], 
                    'Var2': [3, 3, np.nan, 3, 2, 0], 
                    'Var3': [-0.25, 0, 4, np.nan, 0.14, 0.28]}).rename(columns={'PERSID': 'ID'}).reset_index(drop=True)

Step 2: Merge df1 and df2 with Value Differentiation

We'll use an outer merge to keep all rows, then process each column to split values into _x (from df1) and _y (from df2) only when they differ. We'll also keep a single column for values that match across both DataFrames:

# Outer merge on ID, add suffixes to distinguish df1/df2 columns
merged = pd.merge(df1, df2, on='ID', how='outer', suffixes=('_x', '_y'), indicator=True)

# Helper function to clean up columns: retain single column if values match, else keep _x/_y
def process_column(base_col):
    col_x = f"{base_col}_x"
    col_y = f"{base_col}_y"
    
    # Compare values, treating NaNs as equal (since pandas sees NaN != NaN)
    values_match = merged[col_x].fillna('TEMP_NAN') == merged[col_y].fillna('TEMP_NAN')
    
    # Create a base column for matching values
    merged[base_col] = np.where(values_match, merged[col_x], pd.NA)
    
    # Drop the base column if all values are missing (meaning no matches)
    if merged[base_col].isna().all():
        merged.drop(base_col, axis=1, inplace=True)
    else:
        # Clear _x/_y values where they match the base column
        merged.loc[values_match, [col_x, col_y]] = pd.NA

# Apply the helper to all shared columns
for col in ['Sex', 'Var1', 'Var2', 'Var3']:
    process_column(col)

# Clean up the merge indicator and reorder columns to keep _x/_y pairs adjacent
merged.drop('_merge', axis=1, inplace=True)

# Build a readable column order: ID first, then grouped base/_x/_y columns
column_order = ['ID']
for base_col in ['Sex', 'Var1', 'Var2', 'Var3']:
    if f"{base_col}_x" in merged.columns:
        column_order.append(f"{base_col}_x")
    if base_col in merged.columns:
        column_order.append(base_col)
    if f"{base_col}_y" in merged.columns:
        column_order.append(f"{base_col}_y")
# Remove duplicate entries (in case base column was kept)
column_order = list(dict.fromkeys(column_order))
merged = merged[column_order]

This will produce exactly the expected output you shared for df1 + df2, with matching values in a single column and differing values split into _x/_y pairs.

Step 3: Incorporate df3

Now we'll merge the combined df1/df2 result with df3. Since df3 has a different structure, we'll treat its SUBJECT column as the ID (adjust this if SUBJECT isn't meant to map to ID—use pd.concat([merged, df3], axis=1) if they're entirely unrelated):

# Recreate df3
df3 = pd.DataFrame({
    'SUBJECT': [200, 6700, 6702, '1330__201805', '4786__201805'],
    'Var4': [1640.345, 14236.430, 1345.400, 345.750, np.nan],
    'Var5': [345.0, 1713.0, np.nan, 335.0, 0.0],
    'Var6': [-0.250000, -0.050735, 0.034450, 0.140000, np.nan]
}).rename(columns={'SUBJECT': 'ID'})

# Full outer merge to preserve all rows and columns, fill gaps with NaN
final_merged = pd.merge(merged, df3, on='ID', how='outer')

# Reorder columns to keep the original grouped pairs first, then df3's columns
final_columns = merged.columns.tolist() + [col for col in df3.columns if col != 'ID']
final_merged = final_merged[final_columns]

Key Details

  • We use fillna('TEMP_NAN') to correctly compare NaN values (since pandas doesn't recognize NaN == NaN as True)
  • Columns are intentionally ordered to keep _x and _y pairs next to each other for readability
  • The final merge with df3 adds all its rows and columns without losing any existing data, filling missing cells with NaN as required

内容的提问来源于stack exchange,提问作者mExitus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:35:15