NumPy 高性能技巧:基于多列条件查找最近邻行索引的向量化实现


NumPy 高性能技巧:基于多列条件查找最近邻行索引的向量化实现

本文深入探讨如何在numpy中高效解决一个复杂的数据查询问题:为大型数组中的每一行,查找满足特定多列(第二列和第四列)值完全匹配,且在另一列(第一列)上距离最近的n个行的原始索引。通过摒弃低效的python for循环,本教程将详细展示如何利用numpy的向量化操作,包括数据预处理、巧妙的分块策略、广播机制进行距离计算与排序,以及结果整合与还原,从而实现显著的性能提升和代码的简洁性,为处理大规模数据集提供一个专业且可复用的解决方案。

问题背景与挑战

在数据分析和科学计算中,我们经常需要从大型数据集中检索满足特定条件的记录。一个常见的挑战是,当查询条件涉及多列,并且需要根据某一列的“距离”来找出最接近的N个记录时,传统的逐行迭代(例如使用Python的for循环)会带来严重的性能问题,尤其是在处理百万级别甚至更大规模的NumPy数组时。

具体而言,我们的目标是:给定一个具有8列的NumPy数组,对于数组中的每一行,我们需要找出其他满足以下两个条件的N行:

  1. 它们的第二列(索引为1)的值与当前行的第二列值完全相同。
  2. 它们的第四列(索引为3)的值与当前行的第四列值完全相同。
  3. 在满足上述两个条件的所有行中,其第一列(索引为0)的值与当前行的第一列值最接近。

最终输出应是一个与原始数组行数相同、列数为N的数组,其中每一行包含对应原始行的N个最近邻行的原始索引。

NumPy 向量化解决方案概览

为了克服for循环的性能瓶颈,我们将采用NumPy的向量化能力。核心思路是:

  1. 添加原始索引:在对数组进行任何排序或分块操作之前,保留每行的原始位置信息。
  2. 按条件列分组:将原始数组根据其中一个等值条件列(例如第二列)进行排序,然后分块。这样,每个子块内部的所有行的该条件列值都是相同的。
  3. 在组内过滤与计算:对于每个子块,再根据另一个等值条件列(第四列)进行过滤。在过滤后的数据中,使用NumPy的广播机制高效计算第一列的绝对差值,并找出N个最小差值的索引。
  4. 整合结果:将所有子块计算出的索引合并,并根据原始索引恢复数组的初始顺序。

详细实现步骤

1. 准备数据:添加原始索引

在进行任何可能改变行顺序的操作之前,我们需要为原始数组的每一行添加一个唯一的标识符,即其在原始数组中的索引。这确保了即使数据经过排序、过滤和分块,我们也能最终追溯到其在原始数据集中的位置。

Tripo AI Tripo AI

AI驱动的3D建模平台

Tripo AI 970 查看详情 Tripo AI
import numpy as np

# 假设 arr 是从 "data.txt" 加载的原始大型数组
# arr = np.loadtxt("data.txt")

# 示例数据(简化版,模拟原始数据结构)
# 假设原始数据有8列,这里只用4列进行演示
arr = np.array([
    [1.0, 0.0, -1.6, 2.3],  # idx 0
    [1.0, 1.0, -1.6, 2.3],  # idx 1
    [1.0, 0.0, -1.6, 2.3],  # idx 2 (col1=0, col4=2.3, col0=1.0)
    [2.0, 0.0, -1.6, 2.3],  # idx 3
    [1.1, 0.0, -1.6, 2.3],  # idx 4 (col1=0, col4=2.3, col0=1.1)
    [1.2, 0.0, -1.6, 2.3],  # idx 5 (col1=0, col4=2.3, col0=1.2)
    [1.0, 1.0, -1.6, 2.3],  # idx 6 (col1=1, col4=2.3, col0=1.0)
    [1.1, 1.0, -1.6, 2.3],  # idx 7 (col1=1, col4=2.3, col0=1.1)
    [1.0, 0.0, -1.6, 7.3],  # idx 8 (col1=0, col4=7.3, col0=1.0)
    [1.1, 0.0, -1.6, 7.3]   # idx 9 (col1=0, col4=7.3, col0=1.1)
])

# 添加原始索引作为最后一列
# np.c_ 用于按列堆叠数组
arr_indexed = np.c_[arr, np.arange(len(arr))]
print("原始数组(带索引):\n", arr_indexed)

在上述代码中,np.arange(len(arr)) 生成一个从0到 len(arr)-1 的整数序列,然后 np.c_ 将其作为新的一列添加到原始数组 arr 的右侧。

2. 按条件列排序与分块

为了高效处理“第二列和第四列值相同”的条件,我们可以首先对数组进行排序。选择第二列(索引为1)作为主要的排序键,因为它的值在问题描述中是离散的。排序后,所有第二列值相同的行将聚集在一起,这为后续的分块操作提供了便利。

# 按第二列的值进行排序
arr_sorted_by_col1 = arr_indexed[arr_indexed[:, 1].argsort()]
print("\n按第二列排序后的数组:\n", arr_sorted_by_col1)

# 根据第二列的唯一值进行分块
# np.unique(..., return_index=True) 返回唯一值及其首次出现的索引
unique_col1_values, split_indices = np.unique(arr_sorted_by_col1[:, 1], return_index=True)
# split_indices[1:] 提供了除第一个块之外的所有分界点
splitted_array = np.split(arr_sorted_by_col1, split_indices[1:])

print("\n按第二列分块后的数组(列表形式):")
for i, block in enumerate(splitted_array):
    print(f"--- 块 {i} (第二列值为 {block[0, 1]}) ---\n", block)

np.unique(arr_sorted_by_col1[:, 1], return_index=True)[1][1:] 能够找到第二列值发生变化的所有位置,这些位置作为 np.split 的参数,可以将排序后的数组精确地分割成多个子数组,每个子数组内部的第二列值都是相同的。

3. 核心逻辑:query_trks 函数

这个函数负责在每个分块(即第二列值已确定的子数组)内部,进一步根据第四列的值进行过滤,然后计算第一列的距离并找出N个最近邻的原始索引。

def query_trks(FULL_block, target_col4_value, N):
    """
    在给定的数据块中,查找满足第四列条件且第一列最接近的N个行的原始索引。

    参数:
    FULL_block (np.ndarray): 一个NumPy数组,其中第二列的值是恒定的。
                            最后一列包含原始索引。
    target_col4_value (float): 要匹配的第四列值。
    N (int): 需要查找的最近邻行数。

    返回:
    np.ndarray: 一个数组,包含N个最近邻行的原始索引。
                如果符合条件的行数不足N,则返回所有符合条件的行的索引。
    """
    # 过滤出第四列值与目标值相等的行
    # 注意:这里假设第四列是索引为4,但根据问题描述,它是索引3(0-based)
    # 在示例数据中,我们假设是索引3
    # 根据问题描述,是第四列(索引3)
    filt_mask = (FULL_block[:, 3] == target_col4_value)

    # 获取过滤后的行的索引(在FULL_block内部的相对索引)
    internal_filt_indices = np.where(filt_mask)[0]

    if len(internal_filt_indices) == 0:
        return np.array([], dtype=int) # 没有匹配的行

    # 提取过滤后的行的第一列值
    trks_col0 = FULL_block[internal_filt_indices, 0]

    # 使用广播机制计算所有过滤后的行之间第一列值的绝对差值
    # trks_col0[:, None] 将一维数组转换为列向量,与 trks_col0 进行元素级减法
    # 得到一个 (len(trks_col0), len(trks_col0)) 的差值矩阵
    diff_matrix = np.abs(trks_col0[:, None] - trks_col0)

    # 对差值矩阵按列排序,获取排序后的索引
    # argsort(axis=0) 对每一列进行排序,返回排序后的索引
    # [:N] 获取前N个最小差值的索引
    # 注意:这里需要处理自身与自身的距离为0的情况,通常我们希望排除自身
    # 一种方法是先将对角线元素设为无穷大,或者在获取N个索引后排除自身
    # 考虑到实际需求,通常N个最近邻会包含自身(距离为0),如果需要排除,需额外处理。
    # 这里的argsort会把0距离的自身排在第一位。

    # 为了排除自身,我们可以将对角线元素设置为一个大值
    np.fill_diagonal(diff_matrix, np.inf)

    # 重新排序获取前N个最小差值的索引
    # argsort(axis=0) 会对每一列进行排序,返回排序后的索引
    # [:N] 获取前N个最小差值的索引
    closest_internal_indices = diff_matrix.argsort(axis=0)[:N]

    # 将内部索引转换回 FULL_block 中的实际索引
    # 然后再通过 FULL_block 找到其原始索引

    # 最终结果需要针对每个原始行返回 N 个索引。
    # 这里的 diff_matrix 是针对 trks_col0 的,所以 closest_internal_indices 也是 trks_col0 的索引。
    # 我们需要的是 FULL_block 中对应行的原始索引。

    # 创建一个空的列表来存储每个查询行的N个最近邻原始索引
    result_indices = np.empty((len(internal_filt_indices), N), dtype=int)

    for i, current_row_internal_idx in enumerate(internal_filt_indices):
        # 对于当前行 (FULL_block[current_row_internal_idx]),
        # 找到其在 trks_col0 中的位置 (j)
        # diff_matrix 的第 j 列包含了当前行与其他行的距离

        # 找到当前行在 trks_col0 内部的索引
        j = np.where(internal_filt_indices == current_row_internal_idx)[0][0]

        # 获取第 j 列中 N 个最近邻的内部索引
        n_closest_in_trks_col0 = closest_internal_indices[:, j]

        # 将这些内部索引映射回 FULL_block 的内部索引
        n_closest_in_full_block = internal_filt_indices[n_closest_in_trks_col0]

        # 提取这些行对应的原始索引(最后一列)
        result_indices[i] = FULL_block[n_closest_in_full_block, -1].astype(int)

    return result_indices

# 假设 N = 2
N = 2

# 创建一个列表来存储每个分块计算出的结果
all_computed_indices = []

# 遍历每个分块并调用 query_trks
for block in splitted_array:
    # 在每个block中,第二列的值是相同的,我们只需获取其值
    # 并且对于block中的每一行,我们都需要找到N个最近邻

    # 假设我们为block中的每一行查找最近邻
    # 我们需要迭代block中的每一行作为“当前行”来查询

    # 获取 block 中所有行的原始索引,这些是需要填充结果的行
    original_indices_in_block = block[:, -1].astype(int)

    # 为当前 block 创建一个临时存储结果的数组
    block_results = np.empty((len(block), N), dtype=int)

    for i, row in enumerate(block):
        # 当前行的第二列值和第四列值
        current_col1_val = row[1]
        current_col4_val = row[3]
        current_col0_val = row[0]

        # 过滤出与当前行第四列值相同的行
        # 这里的 FULL_block 实际上就是当前的 block,因为我们已经在外部按 col1 分割了
        # 并且 query_trks 应该只返回与 target_col4_value 匹配的行的最近邻

        # 优化:query_trks 应该接收一个 block 和一个 target_col4_value
        # 并且它应该为 block 中所有满足 target_col4_value 的行计算最近邻

        # 重新设计 query_trks,使其能够处理一个 block 中的所有行
        # 原始的 query_trks 假设 `trk` 是一个值,而不是整个列
        # 让我们回到原始答案的逻辑,它通过 `a[:, 0]` 传入了整个第一列,
        # 这意味着 `query_trks` 内部会为 `trks` 中的每个元素找到最近邻。
        # 这里的 `trks` 是 `FULL[filt, 0]`,即过滤后的第一列。

        # 让我们按照答案的逻辑重新构建 query_trks
        # `query_trks(FULL, trk, v, N)`
        # `FULL` 是当前的 `block`
        # `trk` 在答案中被移除,因为 `trks = FULL[filt, 0]` 已经包含了所有需要比较的第一列值
        # `v` 是 `target_col4_value`

以上就是NumPy 高性能技巧:基于多列条件查找最近邻行索引的向量化实现的详细内容,更多请关注其它相关文章!


# 几种  # 孟州附近网站推广店地址  # 松溪网页seo  # 百度网站优化首选软件  # 日用品网站如何推广  # 正定响应式网站建设费用  # 江津微信营销推广  # 铜山区个人课题网站建设  # 天津网站建设策略  # 网站建设财务分析内容  # 高邑个人网站推广教程  # 符合条件  # python  # 行数  # 我们可以  # 组中  # 浮点  # 让我们  # 创建一个  # 高性能  # 都是  # 性能瓶颈  # go 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 优化推广96088 】 【 技术知识133117 】 【 IDC资讯59369 】 【 网络运营7196 】 【 IT资讯61894


相关推荐: 《真我》申请退款方法  解决VS Code中Python版本冲突与输出异常的指南  iCloud官方网站 iCloud网页版在线登录入口  抖音号怎么解除企业认证改成个人?改成个人有影响吗?  Yandex无需登录畅游 俄罗斯搜索引擎最新官网指南  阿里旺旺电脑网页版入口 阿里旺旺电脑版网页登录入口  快递查询,一键速查  《大润发优鲜》充值方法介绍  《战地6》反作弊已成功拦截240万次作弊 发售第一周98%比赛没有作弊  Golang如何使用log记录日志信息_Golang log日志记录方法总结  J*a实现任务清单管理_集合框架综合入门练手  批改网网页版登录 批改网电脑版学生登录入口  4399小游戏下装链接 4399小游戏下载链接入口  VS Code的时间线(Timeline)视图:您的代码时光机  漫蛙漫画官方版直通入口 2025漫蛙漫画免注册访问说明  阿里云共享相册入口在哪  Yandex世界探索 最新官方免登录入口全知道  《地下城堡4:骑士与破碎编年史》墓穴挑战125攻略  告别繁琐SEO!如何使用SyliusSitemap插件自动化生成网站地图,提升搜索引擎排名  WooCommerce 购物车:始终显示所有交叉销售商品  如何在mysql中使用索引提示_mysql索引提示优化方法  纯CSS实现自适应宽度与响应式布局的水平按钮组  AffinityDesigner图层蒙版怎么用_AffinityDesigner图层蒙版设计应用  飞飞漫画漫画阅读官网_飞飞漫画漫画阅读官网进入阅读  优化2xN网格最大路径和的动态规划算法实践  抖音号已注销怎么解绑企业认证?不解绑企业认证会怎样?  J*aScript桌面应用_Electron多进程架构实战  英雄联盟争者留名活动介绍  VS Code如何设置默认配置  泰拉瑞亚网页版在线登录入口 泰拉瑞亚官方正版入口  iphone16系列配置参数介绍  抖音号升级成企业资质怎么弄?有什么好处?  申通快递物流信息查询 申通快递包裹状态追踪  优酷官网登录入口电脑版 优酷官网网址入口  汽水音乐车机版 汽水音乐车机版官方入口  Google Drive API 认证:服务账户与OAuth 2.0的选择与实践  苹果手机怎么合并照片_苹果手机合并多张照片的操作方法  视频号视频怎么免费保存到相册?保存到相册需要注意什么?  mysql离线安装后如何启动_mysql离线安装完成后启动服务的方法  手机耗电快是什么原因 延长手机电池续航时间的设置方法【详解】  J*aScript深度克隆:实现高效、健壮与安全的复杂对象复制  Python项目中的条件导入:解决跨模块依赖问题  繁花漫画使用教程  电子白板帮助菜单使用指南  广州地铁app准妈咪徽章领取方法  Yandex浏览器官方入口_Yandex搜索引擎中文版  Linux如何优化系统启动流程_Linux启动项优化方案  动漫岛在线动漫网 动漫岛动漫在线观看官方入口  《荔枝fm》导出文件教程  J*aScript实现网页表单实时输入字段比较与验证教程 

 2025-11-29

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

运城市盐湖区信雨科技有限公司


运城市盐湖区信雨科技有限公司

运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。

 8156699

 13765294890

 8156699@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.