虽然 pd.merge 为根据列或索引组合DataFrame提供了强大的、类似SQL的灵活性,但Pandas还提供了另一个便捷方法 .join(),它专门为根据其索引标签组合DataFrame,或将索引与列合并而优化。可将其视为执行特定类型合并的简化方式,尤其是索引上的左连接,这非常普遍。基本的基于索引的合并默认情况下,DataFrame.join() 尝试使用其索引与另一个DataFrame(或多个DataFrame)合并。除非另行指定,它会执行左连接,这意味着它保留了调用DataFrame(“左侧”的那个)的所有行,并根据索引包含“右侧”DataFrame的匹配数据。我们来设置两个简单的DataFrame以作说明:import pandas as pd # 左侧DataFrame left_df = pd.DataFrame({'A': ['A0', 'A1', 'A2'], 'B': ['B0', 'B1', 'B2']}, index=['K0', 'K1', 'K2']) # 右侧DataFrame right_df = pd.DataFrame({'C': ['C0', 'C2', 'C3'], 'D': ['D0', 'D2', 'D3']}, index=['K0', 'K2', 'K3']) print("左侧DataFrame:") print(left_df) print("\n右侧DataFrame:") print(right_df) # 将 left_df 与 right_df 根据它们的索引合并(默认:左连接) joined_df = left_df.join(right_df) print("\n合并后的DataFrame(索引上的左连接):") print(joined_df)Output:Left DataFrame: A B K0 A0 B0 K1 A1 B1 K2 A2 B2 Right DataFrame: C D K0 C0 D0 K2 C2 D2 K3 C3 D3 Joined DataFrame (left join on index): A B C D K0 A0 B0 C0 D0 K1 A1 B1 NaN NaN K2 A2 B2 C2 D2请注意,结果 joined_df 保留了 left_df 的所有索引标签(K0、K1、K2)。对于 K0 和 K2,它们存在于两个DataFrame的索引中,right_df 的相应值(C0、D0、C2、D2)被包含进来。对于仅在 left_df 中的 K1,right_df 的列(C 和 D)被 NaN(非数字)填充,表示缺失数据。来自 right_df 的索引 K3 未被包含,因为它不在 left_df 中,并且我们执行了左连接。列上的索引合并.join() 方法不限于索引对索引的合并。您可以使用 on 参数将调用DataFrame(左侧)的索引与传入DataFrame(右侧)中的一列或多列进行合并。# 右侧DataFrame,使用列而非索引 right_df_on_col = pd.DataFrame({'key': ['K0', 'K2', 'K3'], 'C': ['C0', 'C2', 'C3'], 'D': ['D0', 'D2', 'D3']}) print("左侧DataFrame:") print(left_df) print("\n右侧DataFrame:") print(right_df_on_col) # 将 left_df 的索引与 right_df_on_col 的列合并 joined_on_col = left_df.join(right_df_on_col.set_index('key'), on=None) # 或者,更直接地使用 .join 的 'on' 参数(作用于*另一个*DataFrame): # 这在 .join() 的使用中不太常见,pd.merge 在这里通常更清晰。 # 但是,如果您在 right_df_on_col 上调用 join: # joined_on_col = right_df_on_col.join(left_df, on='key') # 将右侧的 'key' 列与左侧的索引合并 # 使用 .join 实现此目的的最常见模式是首先设置索引: joined_on_col_idiom = left_df.join(right_df_on_col.set_index('key')) print("\n合并后的DataFrame(左侧索引与右侧列合并):") print(joined_on_col_idiom)Output:Left DataFrame: A B K0 A0 B0 K1 A1 B1 K2 A2 B2 Right DataFrame (with column): key C D 0 K0 C0 D0 1 K2 C2 D2 2 K3 C3 D3 Joined DataFrame (left index on right column): A B C D K0 A0 B0 C0 D0 K1 A1 B1 NaN NaN K2 A2 B2 C2 D2在此示例中,我们首先在 right_df_on_col 上使用了 set_index('key'),将 key 列设为其索引,从而允许与 left_df 进行标准的索引对索引合并。这是使用 .join() 时一个很常见的模式。结果与第一个示例相同,因为逻辑变得一致:基于匹配的索引值(left_df 中的 K0、K1、K2 与 right_df_on_col 的新索引)进行合并。虽然 left_df.join(other, on='col_in_other') 是可能的,但它通常不如使用 pd.merge(left_df, other, left_index=True, right_on='col_in_other') 或上面所示的 set_index 方法易读。.join() 的主要优点在于其在基于索引的操作方面的简洁性。使用 how 指定合并类型正如 pd.merge 一样,.join() 方法接受 how 参数来控制连接类型。选项有:'left': (默认) 保留左侧DataFrame的所有键。'right': 保留右侧DataFrame的所有键。'outer': 保留两个DataFrame的所有键。'inner': 只保留在两个DataFrame中都找到的键。我们来看看这些如何影响使用原始 left_df 和 right_df 的结果:# 内连接 inner_join = left_df.join(right_df, how='inner') print("\n内连接(两个DataFrame中都有的键):") print(inner_join) # 外连接 outer_join = left_df.join(right_df, how='outer') print("\n外连接(任一DataFrame中有的键):") print(outer_join) # 右连接 right_join = left_df.join(right_df, how='right') print("\n右连接(右侧DataFrame中有的键):") print(right_join)Output:Inner Join (keys in both): A B C D K0 A0 B0 C0 D0 K2 A2 B2 C2 D2 Outer Join (keys in either): A B C D K0 A0 B0 C0 D0 K1 A1 B1 NaN NaN K2 A2 B2 C2 D2 K3 NaN NaN C3 D3 Right Join (keys in right): A B C D K0 A0 B0 C0 D0 K2 A2 B2 C2 D2 K3 NaN NaN C3 D3请看差异:inner:只有 K0 和 K2 出现,因为它们是 left_df 和 right_df 中都存在的唯一索引标签。outer:两个DataFrame中的所有唯一索引标签(K0、K1、K2、K3)都存在。原始DataFrame中缺少数据的地方用 NaN 填充。right:right_df 的所有索引标签(K0、K2、K3)都被保留。left_df 的数据在索引匹配时被包含;否则,使用 NaN(如 K3 所示)。这是说明左连接的图示:digraph G { rankdir=LR; node [shape=plaintext]; splines=false; edge [color="#adb5bd"]; A [ label=< <TABLE BORDER="1" CELLBORDER="1" CELLSPACING="0" BGCOLOR="#e9ecef"> <TR><TD BGCOLOR="#74c0fc">索引</TD><TD>A</TD><TD>B</TD></TR> <TR><TD BGCOLOR="#ced4da">K0</TD><TD>A0</TD><TD>B0</TD></TR> <TR><TD BGCOLOR="#ced4da">K1</TD><TD>A1</TD><TD>B1</TD></TR> <TR><TD BGCOLOR="#ced4da">K2</TD><TD>A2</TD><TD>B2</TD></TR> </TABLE> >]; B [ label=< <TABLE BORDER="1" CELLBORDER="1" CELLSPACING="0" BGCOLOR="#e9ecef"> <TR><TD BGCOLOR="#74c0fc">索引</TD><TD>C</TD><TD>D</TD></TR> <TR><TD BGCOLOR="#ced4da">K0</TD><TD>C0</TD><TD>D0</TD></TR> <TR><TD BGCOLOR="#ced4da">K2</TD><TD>C2</TD><TD>D2</TD></TR> <TR><TD BGCOLOR="#ced4da">K3</TD><TD>C3</TD><TD>D3</TD></TR> </TABLE> >]; C [ label=< <TABLE BORDER="1" CELLBORDER="1" CELLSPACING="0" BGCOLOR="#e9ecef"> <TR><TD BGCOLOR="#74c0fc">索引</TD><TD>A</TD><TD>B</TD><TD>C</TD><TD>D</TD></TR> <TR><TD BGCOLOR="#ced4da">K0</TD><TD>A0</TD><TD>B0</TD><TD>C0</TD><TD>D0</TD></TR> <TR><TD BGCOLOR="#ced4da">K1</TD><TD>A1</TD><TD>B1</TD><TD BGCOLOR="#f8f9fa">NaN</TD><TD BGCOLOR="#f8f9fa">NaN</TD></TR> <TR><TD BGCOLOR="#ced4da">K2</TD><TD>A2</TD><TD>B2</TD><TD>C2</TD><TD>D2</TD></TR> </TABLE> >]; {rank=source; A; B;} {rank=sink; C;} A:s -> C:n [label=" 左侧.合并(右侧) ", color="#4263eb", fontcolor="#4263eb", style=dashed]; B:s -> C:n [color="#4263eb", style=dashed]; }表示一个 left_df.join(right_df) 操作。左侧表的所有索引键都被保留。右侧表的匹配键会带入其数据;不匹配的键则导致右侧表的列出现 NaN 值。处理重叠的列名如果您要合并的DataFrame拥有同名列(除了索引或正在合并的列之外),.join() 会引发错误。为解决此问题,您可以使用 lsuffix 和 rsuffix 参数,分别在左侧和右侧DataFrame的重叠列名后添加区分后缀。# 另一个右侧DataFrame,但有一个冲突的列名 'B' right_overlap = pd.DataFrame({'B': ['B_r0', 'B_r2', 'B_r3'], # 重叠列 'B' 'D': ['D0', 'D2', 'D3']}, index=['K0', 'K2', 'K3']) print("左侧DataFrame:") print(left_df) print("\n带重叠的右侧DataFrame:") print(right_overlap) # 尝试不使用后缀进行合并(会引发错误) # joined_overlap_error = left_df.join(right_overlap) # 引发 ValueError # 使用后缀解决重叠 joined_overlap_fixed = left_df.join(right_overlap, lsuffix='_left', rsuffix='_right') print("\n带后缀的合并DataFrame:") print(joined_overlap_fixed)Output:Left DataFrame: A B K0 A0 B0 K1 A1 B1 K2 A2 B2 Right DataFrame with Overlap: B D K0 B_r0 D0 K2 B_r2 D2 K3 B_r3 D3 Joined DataFrame with Suffixes: A B_left B_right D K0 A0 B0 B_r0 D0 K1 A1 B1 NaN NaN K2 A2 B2 B_r2 D2正如您所见,left_df 中原始的 B 列现在变为 B_left,而 right_overlap 中的 B 列在最终结果中现在变为 B_right。合并多个DataFrame您可以通过向 .join() 传递一个DataFrame列表,一次性合并两个以上的DataFrame。合并操作根据索引从左到右依次进行。# 第三个DataFrame other_df = pd.DataFrame({'E': ['E1', 'E2', 'E4']}, index=['K1', 'K2', 'K4']) print("左侧DF:") print(left_df) print("\n右侧DF:") print(right_df) print("\n其他DF:") print(other_df) # 将 left_df 与 right_df 和 other_df 合并(默认:左连接) multi_join = left_df.join([right_df, other_df]) print("\n多DataFrame合并结果(左连接):") print(multi_join) # 外连接示例 multi_join_outer = left_df.join([right_df, other_df], how='outer') print("\n多DataFrame合并结果(外连接):") print(multi_join_outer) Output:Left DF: A B K0 A0 B0 K1 A1 B1 K2 A2 B2 Right DF: C D K0 C0 D0 K2 C2 D2 K3 C3 D3 Other DF: E K1 E1 K2 E2 K4 E4 Multi-Join Result (left joins): A B C D E K0 A0 B0 C0 D0 NaN K1 A1 B1 NaN NaN E1 K2 A2 B2 C2 D2 E2 Multi-Join Result (outer joins): A B C D E K0 A0 B0 C0 D0 NaN K1 A1 B1 NaN NaN E1 K2 A2 B2 C2 D2 E2 K3 NaN NaN C3 D3 NaN K4 NaN NaN NaN NaN E4默认的左连接只保留初始DataFrame(left_df)的索引。外连接组合所有涉及的DataFrame的所有索引。总而言之,DataFrame.join() 提供了一种简洁的语法,主要用于基于索引的合并。虽然 pd.merge 提供了更通用的合并能力,但当您的组合逻辑主要依赖于DataFrame索引时,.join() 通常更快、更易读,特别是对于执行左连接的常见情况。请记住,.join() 内部使用 pd.merge,所以底层逻辑是一致的。