在Python的数值计算场景中,NumPy二维数组的重复行查找是常见需求,比如数据清洗时需要去除重复的样本记录,或者统计重复行的出现频率。NumPy内置的unique函数配合axis参数,能够以向量化的方式快速完成这个任务,避免低效的循环遍历。

unique函数axis参数基础用法
NumPy的np.unique函数默认会将数组展平后去重,当指定axis=0时,会按照行维度进行去重,返回去重后的唯一行数组。同时该函数还支持返回多个可选输出,帮助我们定位重复行的相关信息。
函数的基本语法如下:
import numpy as np
# 创建包含重复行的二维数组
arr = np.array([
[1, 2, 3],
[4, 5, 6],
[1, 2, 3],
[7, 8, 9],
[4, 5, 6]
])
# 使用axis=0参数去重,返回唯一行
unique_rows = np.unique(arr, axis=0)
print("去重后的唯一行:")
print(unique_rows)
上述代码的输出结果为:
去重后的唯一行: [[1 2 3] [4 5 6] [7 8 9]]
获取重复行的索引和出现次数
除了返回去重后的行,np.unique还可以通过设置返回参数,得到每个唯一行在原数组中的首次出现索引、所有出现索引以及出现次数,这些信息可以帮助我们进一步分析重复行的情况。
相关的参数说明:
- return_index:设为True时,返回每个唯一行在原数组中首次出现的索引
- return_inverse:设为True时,返回原数组中每个行对应的唯一行索引,可用于还原原数组
- return_counts:设为True时,返回每个唯一行的出现次数
下面的示例演示如何获取重复行的相关信息:
import numpy as np
arr = np.array([
[1, 2, 3],
[4, 5, 6],
[1, 2, 3],
[7, 8, 9],
[4, 5, 6]
])
# 获取唯一行、首次出现索引、逆映射索引、出现次数
unique_rows, first_indices, inverse_indices, counts = np.unique(
arr, axis=0, return_index=True, return_inverse=True, return_counts=True
)
print("唯一行:")
print(unique_rows)
print("每个唯一行首次出现的索引:", first_indices)
print("原数组每个行对应的唯一行索引:", inverse_indices)
print("每个唯一行的出现次数:", counts)
# 筛选出现次数大于1的重复行
duplicate_rows = unique_rows[counts > 1]
print("重复的行:")
print(duplicate_rows)
上述代码的输出结果为:
唯一行: [[1 2 3] [4 5 6] [7 8 9]] 每个唯一行首次出现的索引: [0 1 3] 原数组每个行对应的唯一行索引: [0 1 0 2 1] 每个唯一行的出现次数: [2 2 1] 重复的行: [[1 2 3] [4 5 6]]
自定义函数封装重复行查找逻辑
我们可以将上述逻辑封装成一个通用的函数,方便在不同场景中直接调用,快速获取数组中的重复行以及对应的重复次数。
import numpy as np
def find_duplicate_rows(arr):
"""
查找NumPy二维数组中的重复行
:param arr: 输入的二维NumPy数组
:return: 重复行数组、每个重复行的出现次数
"""
# 按照行维度去重,返回唯一行和出现次数
unique_rows, counts = np.unique(arr, axis=0, return_counts=True)
# 筛选出现次数大于1的行
duplicate_mask = counts > 1
duplicate_rows = unique_rows[duplicate_mask]
duplicate_counts = counts[duplicate_mask]
return duplicate_rows, duplicate_counts
# 测试封装的函数
test_arr = np.array([
[10, 20],
[30, 40],
[10, 20],
[50, 60],
[30, 40],
[30, 40]
])
dup_rows, dup_counts = find_duplicate_rows(test_arr)
print("测试数组的重复行:")
print(dup_rows)
print("对应重复次数:", dup_counts)
运行上述代码,输出结果如下:
测试数组的重复行: [[10 20] [30 40]] 对应重复次数: [2 3]
性能对比:unique axis参数 vs 循环遍历
对于大规模数组,使用np.unique的axis参数比手动循环遍历的效率高很多,因为NumPy的底层是C实现,向量化操作避免了Python层面的循环开销。下面的示例对比两种方式的性能差异:
import numpy as np
import time
# 生成包含1万行、5列的随机数组,人为添加部分重复行
np.random.seed(42)
base_arr = np.random.randint(0, 100, size=(5000, 5))
test_arr = np.vstack([base_arr, base_arr[:5000]])
# 方法1:使用np.unique axis参数
start = time.time()
unique_rows = np.unique(test_arr, axis=0)
end = time.time()
print("np.unique方法耗时:", end - start, "秒")
# 方法2:循环遍历去重
start = time.time()
seen = []
duplicate_rows_loop = []
for row in test_arr:
row_tuple = tuple(row)
if row_tuple in seen:
if row_tuple not in duplicate_rows_loop:
duplicate_rows_loop.append(row_tuple)
else:
seen.append(row_tuple)
end = time.time()
print("循环遍历方法耗时:", end - start, "秒")
在普通配置的电脑上运行,通常np.unique方法耗时在零点零几秒,而循环遍历方法可能需要数秒甚至更久,数据量越大差距越明显。
注意事项
使用np.unique的axis参数时需要注意几个问题:
- 输入的数组必须是二维及以上,否则指定
axis=0会报错 - 数组的数据类型需要一致,否则可能会触发类型转换,影响结果正确性
- 如果数组中包含NaN值,因为NaN不等于NaN,所以包含NaN的行不会被判定为重复行,需要提前处理NaN值
如果数组中存在NaN值,可以先使用np.isnan检测并替换,再进行重复行查找:
import numpy as np
arr_with_nan = np.array([
[1.0, 2.0],
[np.nan, 3.0],
[1.0, 2.0],
[np.nan, 3.0]
])
# 替换NaN为自定义值,这里用-999代替
arr_filled = np.where(np.isnan(arr_with_nan), -999, arr_with_nan)
unique_rows = np.unique(arr_filled, axis=0)
print("处理NaN后的去重行:")
print(unique_rows)
NumPyunique_axis数组重复行Python数据处理修改时间:2026-07-02 20:45:26