
本教程详细阐述了如何利用NumPy的`argsort`函数,实现一个Pandas DataFrame根据另一个DataFrame的列序进行关联排序。通过将第二个DataFrame转换为NumPy数组并获取其列排序索引,然后利用NumPy高级索引技术,高效地重新排列第一个DataFrame的列,确保两个DataFrame之间保持逻辑一致的列顺序。
引言:关联DataFrame列序排序的需求
在数据分析和处理中,我们经常会遇到需要根据一个数据集的某种度量标准,来重新排列另一个相关数据集的情况。例如,当我们有两个结构相同的Pandas DataFrame,一个存储实体(如单词),另一个存储这些实体对应的某种评分(如相似度分数)。此时,我们可能希望根据评分DataFrame中每行的分数高低,来同步调整实体DataFrame中对应行的列顺序,以保持数据的一致性和可读性。本文将深入探讨如何利用Pandas和NumPy的强大功能,高效且专业地解决这一问题。
场景描述与数据准备
假设我们有两个具有相同索引和列数的Pandas DataFrame:
- df1 (实体DataFrame): 包含需要被排序的实体数据(例如,相关的单词)。
- df2 (评分DataFrame): 包含用于排序的数值评分,其结构与df1完全对应。
我们的目标是根据df2中每行(即每个索引项)的评分进行列向(axis=1)排序,并将这个排序结果应用到df1上,使得df1的列顺序与df2排序后的列顺序保持一致。
以下是示例数据:
import pandas as pd
import numpy as np
# DataFrame #1 (实体数据,例如单词)
data_words = {
'Col 0': ['Rockets', 'Canvases', 'Infections'],
'Col 1': ['Cars', 'Paint', 'Dirt'],
'Col 2': ['Ships', 'Ink', 'Dust']
}
df1 = pd.DataFrame(data_words, index=['Trains', 'Paintings', 'Germs'])
print("原始 df1 (实体数据):")
print(df1)
# 输出:
# Col 0 Col 1 Col 2
# Trains Rockets Cars Ships
# Paintings Canvases Paint Ink
# Germs Infections Dirt Dust
# DataFrame #2 (相似度评分)
data_scores = {
'Col 0': [47, 22, 77],
'Col 1': [80, 90, 40],
'Col 2': [33, 30, 52]
}
df2 = pd.DataFrame(data_scores, index=['Trains', 'Paintings', 'Germs'])
print("\n原始 df2 (相似度评分):")
print(df2)
# 输出:
# Col 0 Col 1 Col 2
# Trains 47 80 33
# Paintings 22 90 30
# Germs 77 40 52我们期望df1根据df2的列序(例如,降序)进行调整。以Trains行为例:df2中[47, 80, 33]降序排列后,其原始索引顺序是[1, 0, 2](即80在Col 1,47在Col 0,33在Col 2)。因此,df1中Trains行对应的['Rockets', 'Cars', 'Ships']也应按此顺序变为['Cars', 'Rockets', 'Ships']。
核心原理:numpy.argsort与高级索引
解决此问题的关键在于利用NumPy的两个强大特性:
- numpy.argsort(): 此函数返回对数组进行排序的索引。例如,np.array([3, 1, 2]).argsort()会返回[1, 2, 0],表示原数组中索引为1的元素最小,其次是索引为2的元素,最后是索引为0的元素。对于二维数组,argsort(axis=1)会按行返回每行元素的排序索引。
- NumPy高级索引: NumPy允许使用数组作为索引来访问和重排另一个数组的元素。通过结合行索引数组和列索引数组,我们可以实现复杂的元素选择和重排。
实现步骤与代码示例
我们将通过以下步骤实现关联排序:
- 获取排序索引: 对df2进行列向排序(通常是降序),并获取其排序后的列索引。
- 应用索引重排df1: 使用获取到的排序索引,结合NumPy的高级索引功能,重新排列df1的列。
# 步骤1: 获取 df2 的列向排序索引
# 为了实现降序排序,我们对 -df2 进行 argsort。
# np.argsort 默认沿最后一个轴(对于二维数组是列)进行排序,返回排序后的元素在原始数组中的索引。
# np.argsort(-df2) 会返回一个与 df2 形状相同的二维数组,其中每个元素是该行排序后的列索引。
sort_indices = np.argsort(-df2.to_numpy(), axis=1)
print("\ndf2 降序排序后的列索引 (每行):")
print(sort_indices)
# 输出示例:
# [[1 0 2] # Trains行: Col 1(80) > Col 0(47) > Col 2(33)
# [1 2 0] # Paintings行: Col 1(90) > Col 2(30) > Col 0(22)
# [0 2 1]] # Germs行: Col 0(77) > Col 2(52) > Col 1(40)
# 步骤2: 应用索引重排 df1
# 首先将 df1 转换为 NumPy 数组
df1_np = df1.to_numpy()
# 构建行索引数组。np.arange(len(df1)) 生成 [0, 1, 2, ...]
# [:, None] 将其转换为列向量 [[0], [1], [2], ...]
# 这使得在高级索引时,每行都能独立地使用 sort_indices 中的对应行。
row_indices = np.arange(len(df1))[:, None]
# 使用高级索引进行重排
# df1_np[row_indices, sort_indices] 会根据 row_indices 和 sort_indices
# 逐行选择 df1_np 中的元素,从而实现列的重排。
# 最后,将重排后的 NumPy 数组赋值回 df1,使用 df1[:] 确保原地修改。
df1[:] = df1_np[row_indices, sort_indices]
print("\n排序后的 df1 (实体数据):")
print(df1)
# 期望输出:
# Col 0 Col 1 Col 2
# Trains Cars Rockets Ships
# Paintings Paint Ink Canvases
# Germs Infections Dust Dirt代码解析
-
sort_indices = np.argsort(-df2.to_numpy(), axis=1)
- df2.to_numpy(): 将Pandas DataFrame df2转换为底层的NumPy数组。这是因为np.argsort直接作用于NumPy数组更高效和直观。
- -df2.to_numpy(): 为了实现降序排序,我们对所有数值取负。argsort默认是升序,对负数进行升序排序,其结果相当于对原正数进行降序排序。
- axis=1: 指定排序操作沿行方向进行,即对每一行的列进行排序。这将返回一个二维数组,其中每个元素是该行中排序后元素在原行中的列索引。
- sort_indices: 存储了一个二维数组,其形状与df2相同,内容是每一行中元素按降序排列后的原始列索引。
-
row_indices = np.arange(len(df1))[:, None]
- len(df1): 获取df1的行数。
- np.arange(len(df1)): 生成一个从0到len(df1)-1的一维整数数组,表示df1的行索引。
- [:, None]: 这是NumPy的一个技巧,用于增加数组的维度。它将一维数组[0, 1, 2]转换为一个列向量[[0], [1], [2]]。这个列向量在高级索引中至关重要,它确保了sort_indices中的每一行(代表一个原始行)都能独立地应用于df1_np的相应行。
-
df1[:] = df1_np[row_indices, sort_indices]
- df1_np = df1.to_numpy(): 再次将df1转换为NumPy数组,因为NumPy的高级索引操作直接作用于NumPy数组更高效。
- df1_np[row_indices, sort_indices]: 这是NumPy的高级索引核心。
- row_indices ([[0], [1], [2]]) 广播到 sort_indices 的所有列。
- 这有效地告诉NumPy:“对于第0行,使用sort_indices的第0行作为列索引来重排df1_np的第0行;对于第1行,使用sort_indices的第1行作为列索引来重排df1_np的第1行,依此类推。”
- 结果是一个新的NumPy数组,其列已根据df2的排序规则进行了重排。
- df1[:] = ...: 将重排后的NumPy数组赋值回df1。使用[:]进行赋值是Pandas中一种常见的原地修改DataFrame底层数据的方式,它会保留DataFrame原有的索引和列名,只更新其值。
注意事项
- 维度一致性: 确保df1和df2具有完全相同的行数和列数。如果维度不匹配,np.argsort或高级索引操作可能会报错或产生意外结果。
-
排序方向:
- 要实现降序排序,需要对评分DataFrame取负数(例如,-df2)后再进行argsort。
- 要实现升序排序,直接对评分DataFrame进行argsort即可(例如,np.argsort(df2.to_numpy(), axis=1))。
- 性能: 这种方法通过将DataFrame转换为NumPy数组,并利用NumPy优化的数组操作,在处理大型数据集时通常具有非常高的性能。
- 原地修改与新DataFrame: df1[:] = ... 这种写法会原地修改df1。如果需要保留原始df1并创建一个新的排序后的DataFrame,可以这样操作:df_sorted = df1_np[row_indices, sort_indices],然后将df_sorted转换为新的DataFrame。
总结
本教程提供了一种高效且专业的解决方案,用于根据一个Pandas DataFrame的列序来关联排序另一个DataFrame。通过巧妙地结合numpy.argsort获取排序索引和NumPy的高级索引功能,我们能够实现复杂的逐行列重排逻辑,同时保持代码的简洁性和执行效率。掌握这种技术,将有助于您在数据处理和分析工作中更灵活地处理关联数据集的排序需求。










