
👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕NumPy这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获!
文章目录
- Python NumPy – 数组的去重 unique 函数的使用 🧮
-
- 什么是 NumPy unique 函数? 🤔
- unique 函数的基本语法 🔧
-
- 参数详解:
- 基础用法示例 💡
-
- 简单去重操作
- 处理包含 NaN 的数组
- 高级功能探索 🔍
-
- 返回索引信息
- 获取逆向映射
- 统计元素出现次数
- 组合多个参数
- 多维数组的去重操作 📊
-
- 不指定 axis 参数
- 指定 axis=0(按行去重)
- 指定 axis=1(按列去重)
- 实际应用场景 🎯
-
- 数据清洗和预处理
- 性能测试对比
- 在机器学习中的应用
- 处理特殊数据类型 ⚙️
-
- 结构化数组去重
- 时间序列数据去重
- 错误处理和边界情况 ⚠️
-
- 处理空数组
- 处理全相同元素的数组
- 处理包含无穷大的数组
- 性能优化技巧 ⚡
-
- 内存效率考虑
- 并行处理大数组
- 与其他库的集成 🔄
-
- 与 Pandas 的结合使用
- 与 Matplotlib 的结合使用
- 最佳实践建议 ✅
-
- 1. 根据需求选择合适的参数组合
- 2. 处理大数据集的策略
- 3. 错误预防和调试
- 总结 🎉
Python NumPy – 数组的去重 unique 函数的使用 🧮
在数据科学和数值计算的世界中,处理重复数据是一个常见的挑战。无论是清理原始数据集、优化存储空间,还是准备数据分析的输入,去除重复元素都是一个基础而重要的操作。NumPy 作为 Python 中最强大的科学计算库之一,提供了 unique 函数来优雅地解决这个问题。
今天,我们将深入探讨 NumPy 的 unique 函数,从基础概念到高级应用,通过丰富的代码示例和实际场景,帮助你全面掌握这个强大的工具。
什么是 NumPy unique 函数? 🤔
NumPy 的 unique 函数是专门用于查找数组中唯一元素的强大工具。它不仅可以简单地去除重复元素,还能提供额外的信息,如每个唯一元素的索引位置、返回数组中每个元素对应的唯一元素索引等。
import numpy as np
# 基础用法示例
arr = np.array([1, 2, 2, 3, 3, 3, 4, 5])
unique_elements = np.unique(arr)
print("原数组:", arr)
print("去重后:", unique_elements)
输出结果:
原数组: [1 2 2 3 3 3 4 5]
去重后: [1 2 3 4 5]
unique 函数的基本语法 🔧
让我们先来看看 unique 函数的完整语法:
numpy.unique(ar, return_index=False, return_inverse=False,
return_counts=False, axis=None, equal_nan=True)
参数详解:
- ar: 输入数组
- return_index: 如果为 True,返回唯一元素在原数组中的索引
- return_inverse: 如果为 True,返回原数组中每个元素对应唯一数组的索引
- return_counts: 如果为 True,返回每个唯一元素在原数组中出现的次数
- axis: 指定沿着哪个轴进行去重操作(对于多维数组)
- equal_nan: 是否将 NaN 值视为相等
基础用法示例 💡
简单去重操作
import numpy as np
# 一维数组去重
arr1d = np.array([5, 2, 8, 2, 1, 8, 9, 5])
result = np.unique(arr1d)
print("一维数组去重:", result)
# 字符串数组去重
str_arr = np.array(['apple', 'banana', 'apple', 'cherry', 'banana'])
unique_str = np.unique(str_arr)
print("字符串数组去重:", unique_str)
# 浮点数数组去重
float_arr = np.array([1.1, 2.2, 1.1, 3.3, 2.2])
unique_float = np.unique(float_arr)
print("浮点数数组去重:", unique_float)
输出结果:
一维数组去重: [1 2 5 8 9]
字符串数组去重: ['apple' 'banana' 'cherry']
浮点数数组去重: [1.1 2.2 3.3]
处理包含 NaN 的数组
# 包含 NaN 的数组
nan_arr = np.array([1, 2, np.nan, 2, 3, np.nan, 4])
unique_with_nan = np.unique(nan_arr)
print("包含 NaN 的数组去重:", unique_with_nan)
# 使用 equal_nan=False
unique_no_nan_equal = np.unique(nan_arr, equal_nan=False)
print("不将 NaN 视为相等:", unique_no_nan_equal)
输出结果:
包含 NaN 的数组去重: [ 1. 2. 3. 4. nan]
不将 NaN 视为相等: [ 1. 2. nan nan 3. 4.]
高级功能探索 🔍
返回索引信息
当设置 return_index=True 时,函数会返回唯一元素在原数组中第一次出现的位置:
arr = np.array([3, 1, 2, 1, 3, 4, 2, 5])
unique_elements, indices = np.unique(arr, return_index=True)
print("原数组:", arr)
print("唯一元素:", unique_elements)
print("首次出现的索引:", indices)
# 创建可视化映射
for i, (element, index) in enumerate(zip(unique_elements, indices)):
print(f"元素 {element} 首次出现在索引 {index}")
输出结果:
原数组: [3 1 2 1 3 4 2 5]
唯一元素: [1 2 3 4 5]
首次出现的索引: [1 2 0 5 7]
元素 1 首次出现在索引 1
元素 2 首次出现在索引 2
元素 3 首次出现在索引 0
元素 4 首次出现在索引 5
元素 5 首次出现在索引 7
获取逆向映射
return_inverse=True 参数非常有用,它可以告诉我们原数组中的每个元素对应去重后的数组中的哪个位置:
arr = np.array([3, 1, 2, 1, 3, 4, 2])
unique_elements, inverse_indices = np.unique(arr, return_inverse=True)
print("原数组:", arr)
print("唯一元素:", unique_elements)
print("逆向索引:", inverse_indices)
# 验证逆向映射的正确性
print("\\n验证逆向映射:")
for i, idx in enumerate(inverse_indices):
print(f"arr[{i}] = {arr[i]} -> unique_elements[{idx}] = {unique_elements[idx]}")
输出结果:
原数组: [3 1 2 1 3 4 2]
唯一元素: [1 2 3 4]
逆向索引: [2 0 1 0 2 3 1]
验证逆向映射:
arr[0] = 3 -> unique_elements[2] = 3
arr[1] = 1 -> unique_elements[0] = 1
arr[2] = 2 -> unique_elements[1] = 2
arr[3] = 1 -> unique_elements[0] = 1
arr[4] = 3 -> unique_elements[2] = 3
arr[5] = 4 -> unique_elements[3] = 4
arr[6] = 2 -> unique_elements[1] = 2
统计元素出现次数
return_counts=True 可以帮助我们了解每个唯一元素在原数组中出现了多少次:
arr = np.array([1, 2, 2, 3, 3, 3, 4, 5, 5])
unique_elements, counts = np.unique(arr, return_counts=True)
print("原数组:", arr)
print("唯一元素:", unique_elements)
print("出现次数:", counts)
# 创建统计表
print("\\n元素出现次数统计:")
for element, count in zip(unique_elements, counts):
print(f"元素 {element}: 出现 {count} 次")
输出结果:
原数组: [1 2 2 3 3 3 4 5 5]
唯一元素: [1 2 3 4 5]
出现次数: [1 2 3 1 2]
元素出现次数统计:
元素 1: 出现 1 次
元素 2: 出现 2 次
元素 3: 出现 3 次
元素 4: 出现 1 次
元素 5: 出现 2 次
组合多个参数
我们可以同时使用多个参数来获取更全面的信息:
arr = np.array([5, 2, 8, 2, 1, 8, 9, 5, 1])
unique_elements, indices, inverse_indices, counts = np.unique(
arr,
return_index=True,
return_inverse=True,
return_counts=True
)
print("原数组:", arr)
print("唯一元素:", unique_elements)
print("首次索引:", indices)
print("逆向索引:", inverse_indices)
print("出现次数:", counts)
# 创建综合分析报告
print("\\n=== 综合分析报告 ===")
for i, (element, first_idx, count) in enumerate(zip(unique_elements, indices, counts)):
print(f"元素 {element}: 首次出现于索引 {first_idx}, 总共出现 {count} 次")
输出结果:
原数组: [5 2 8 2 1 8 9 5 1]
唯一元素: [1 2 5 8 9]
首次索引: [4 1 0 2 6]
逆向索引: [2 1 3 1 0 3 4 2 0]
出现次数: [2 2 2 2 1]
=== 综合分析报告 ===
元素 1: 首次出现于索引 4, 总共出现 2 次
元素 2: 首次出现于索引 1, 总共出现 2 次
元素 5: 首次出现于索引 0, 总共出现 2 次
元素 8: 首次出现于索引 2, 总共出现 2 次
元素 9: 首次出现于索引 6, 总共出现 1 次
多维数组的去重操作 📊
对于多维数组,unique 函数的行为取决于 axis 参数的设置。
不指定 axis 参数
当不指定 axis 参数时,多维数组会被展平后再进行去重:
# 二维数组示例
arr_2d = np.array([[1, 2, 3],
[2, 3, 4],
[1, 2, 3]])
print("原二维数组:")
print(arr_2d)
# 默认行为:展平后去重
unique_flat = np.unique(arr_2d)
print("\\n展平后去重:", unique_flat)
输出结果:
原二维数组:
[[1 2 3]
[2 3 4]
[1 2 3]]
展平后去重: [1 2 3 4]
指定 axis=0(按行去重)
当我们设置 axis=0 时,会按照每一行作为一个整体来进行去重:
arr_2d = np.array([[1, 2, 3],
[2, 3, 4],
[1, 2, 3],
[5, 6, 7],
[2, 3, 4]])
print("原二维数组:")
print(arr_2d)
# 按行去重
unique_rows = np.unique(arr_2d, axis=0)
print("\\n按行去重后的结果:")
print(unique_rows)
# 获取更多信息
unique_rows, row_indices, inverse_row_indices, row_counts = np.unique(
arr_2d,
axis=0,
return_index=True,
return_inverse=True,
return_counts=True
)
print("\\n=== 行去重详细信息 ===")
print("唯一行:", unique_rows)
print("首次出现的行索引:", row_indices)
print("每行的逆向索引:", inverse_row_indices)
print("每行出现次数:", row_counts)
输出结果:
原二维数组:
[[1 2 3]
[2 3 4]
[1 2 3]
[5 6 7]
[2 3 4]]
按行去重后的结果:
[[1 2 3]
[2 3 4]
[5 6 7]]
=== 行去重详细信息 ===
唯一行: [[1 2 3]
[2 3 4]
[5 6 7]]
首次出现的行索引: [0 1 3]
每行的逆向索引: [0 1 0 2 1]
每行出现次数: [2 2 1]
指定 axis=1(按列去重)
类似地,设置 axis=1 会对每一列进行去重操作:
arr_2d = np.array([[1, 2, 1, 3],
[4, 5, 4, 6],
[7, 8, 7, 9]])
print("原二维数组:")
print(arr_2d)
# 按列去重
unique_cols = np.unique(arr_2d, axis=1)
print("\\n按列去重后的结果:")
print(unique_cols)
# 获取详细信息
unique_cols, col_indices, inverse_col_indices, col_counts = np.unique(
arr_2d,
axis=1,
return_index=True,
return_inverse=True,
return_counts=True
)
print("\\n=== 列去重详细信息 ===")
print("唯一列索引:", col_indices)
print("每列的逆向索引:", inverse_col_indices)
print("每列出现次数:", col_counts)
输出结果:
原二维数组:
[[1 2 1 3]
[4 5 4 6]
[7 8 7 9]]
按列去重后的结果:
[[1 2 3]
[4 5 6]
[7 8 9]]
=== 列去重详细信息 ===
唯一列索引: [0 1 3]
每列的逆向索引: [0 1 0 2]
每列出现次数: [2 1 1]
实际应用场景 🎯
数据清洗和预处理
在数据分析项目中,经常需要对原始数据进行清洗。unique 函数在这方面表现出色:
# 模拟用户ID数据,可能存在重复
user_ids = np.array([1001, 1002, 1003, 1001, 1004, 1002, 1005, 1003])
print("原始用户ID数量:", len(user_ids))
print("原始用户ID:", user_ids)
# 去除重复的用户ID
unique_user_ids = np.unique(user_ids)
print("\\n去重后用户ID数量:", len(unique_user_ids))
print("去重后用户ID:", unique_user_ids)
# 计算重复率
duplicate_rate = (len(user_ids) – len(unique_user_ids)) / len(user_ids) * 100
print(f"\\n重复率: {duplicate_rate:.2f}%")
输出结果:
原始用户ID数量: 8
原始用户ID: [1001 1002 1003 1001 1004 1002 1005 1003]
去重后用户ID数量: 5
去重后用户ID: [1001 1002 1003 1004 1005]
重复率: 37.50%
性能测试对比
让我们比较一下不同方法的性能差异:
import time
# 创建大型数组进行性能测试
large_array = np.random.randint(0, 1000, size=100000)
# 方法1: 使用 NumPy unique
start_time = time.time()
unique_np = np.unique(large_array)
np_time = time.time() – start_time
# 方法2: 使用 Python set
start_time = time.time()
unique_set = list(set(large_array))
set_time = time.time() – start_time
# 方法3: 使用 Python list comprehension (较慢,仅作演示)
start_time = time.time()
unique_list = []
for item in large_array:
if item not in unique_list:
unique_list.append(item)
list_time = time.time() – start_time
print(f"数组大小: {len(large_array)}")
print(f"NumPy unique 耗时: {np_time:.6f} 秒")
print(f"Python set 耗时: {set_time:.6f} 秒")
print(f"List comprehension 耗时: {list_time:.6f} 秒")
print(f"\\n结果一致性检查:")
print(f"NumPy 和 Set 结果长度相同: {len(unique_np) == len(unique_set)}")
#mermaid-svg-Ob5J6f7qePt5JN4Y{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Ob5J6f7qePt5JN4Y .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Ob5J6f7qePt5JN4Y .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Ob5J6f7qePt5JN4Y .error-icon{fill:#552222;}#mermaid-svg-Ob5J6f7qePt5JN4Y .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Ob5J6f7qePt5JN4Y .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Ob5J6f7qePt5JN4Y .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Ob5J6f7qePt5JN4Y .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Ob5J6f7qePt5JN4Y .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Ob5J6f7qePt5JN4Y .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Ob5J6f7qePt5JN4Y .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Ob5J6f7qePt5JN4Y .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Ob5J6f7qePt5JN4Y .marker.cross{stroke:#333333;}#mermaid-svg-Ob5J6f7qePt5JN4Y svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Ob5J6f7qePt5JN4Y p{margin:0;}#mermaid-svg-Ob5J6f7qePt5JN4Y .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-Ob5J6f7qePt5JN4Y .cluster-label text{fill:#333;}#mermaid-svg-Ob5J6f7qePt5JN4Y .cluster-label span{color:#333;}#mermaid-svg-Ob5J6f7qePt5JN4Y .cluster-label span p{background-color:transparent;}#mermaid-svg-Ob5J6f7qePt5JN4Y .label text,#mermaid-svg-Ob5J6f7qePt5JN4Y span{fill:#333;color:#333;}#mermaid-svg-Ob5J6f7qePt5JN4Y .node rect,#mermaid-svg-Ob5J6f7qePt5JN4Y .node circle,#mermaid-svg-Ob5J6f7qePt5JN4Y .node ellipse,#mermaid-svg-Ob5J6f7qePt5JN4Y .node polygon,#mermaid-svg-Ob5J6f7qePt5JN4Y .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Ob5J6f7qePt5JN4Y .rough-node .label text,#mermaid-svg-Ob5J6f7qePt5JN4Y .node .label text,#mermaid-svg-Ob5J6f7qePt5JN4Y .image-shape .label,#mermaid-svg-Ob5J6f7qePt5JN4Y .icon-shape .label{text-anchor:middle;}#mermaid-svg-Ob5J6f7qePt5JN4Y .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Ob5J6f7qePt5JN4Y .rough-node .label,#mermaid-svg-Ob5J6f7qePt5JN4Y .node .label,#mermaid-svg-Ob5J6f7qePt5JN4Y .image-shape .label,#mermaid-svg-Ob5J6f7qePt5JN4Y .icon-shape .label{text-align:center;}#mermaid-svg-Ob5J6f7qePt5JN4Y .node.clickable{cursor:pointer;}#mermaid-svg-Ob5J6f7qePt5JN4Y .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Ob5J6f7qePt5JN4Y .arrowheadPath{fill:#333333;}#mermaid-svg-Ob5J6f7qePt5JN4Y .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Ob5J6f7qePt5JN4Y .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Ob5J6f7qePt5JN4Y .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ob5J6f7qePt5JN4Y .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Ob5J6f7qePt5JN4Y .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ob5J6f7qePt5JN4Y .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Ob5J6f7qePt5JN4Y .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Ob5J6f7qePt5JN4Y .cluster text{fill:#333;}#mermaid-svg-Ob5J6f7qePt5JN4Y .cluster span{color:#333;}#mermaid-svg-Ob5J6f7qePt5JN4Y div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Ob5J6f7qePt5JN4Y .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Ob5J6f7qePt5JN4Y rect.text{fill:none;stroke-width:0;}#mermaid-svg-Ob5J6f7qePt5JN4Y .icon-shape,#mermaid-svg-Ob5J6f7qePt5JN4Y .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ob5J6f7qePt5JN4Y .icon-shape p,#mermaid-svg-Ob5J6f7qePt5JN4Y .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Ob5J6f7qePt5JN4Y .icon-shape .label rect,#mermaid-svg-Ob5J6f7qePt5JN4Y .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ob5J6f7qePt5JN4Y .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Ob5J6f7qePt5JN4Y .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Ob5J6f7qePt5JN4Y :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
大数据集去重
选择方法
NumPy unique
Python set
List comprehension
高性能保持排序丰富功能
中等性能无序结果简单快速
低性能保持顺序内存密集
推荐用于科学计算
适合简单去重
不推荐大数据
在机器学习中的应用
在机器学习项目中,特征工程阶段经常需要用到去重操作:
# 模拟分类问题中的标签数据
labels = np.array(['cat', 'dog', 'bird', 'cat', 'fish', 'dog', 'bird', 'cat'])
print("原始标签:", labels)
print("样本数量:", len(labels))
# 获取唯一的类别标签
unique_labels, label_indices, label_counts = np.unique(
labels,
return_index=True,
return_counts=True
)
print("\\n唯一标签:", unique_labels)
print("各类别样本数量:", dict(zip(unique_labels, label_counts)))
# 计算类别分布
total_samples = len(labels)
class_distribution = {label: count/total_samples for label, count in zip(unique_labels, label_counts)}
print("\\n类别分布:")
for label, ratio in class_distribution.items():
print(f"{label}: {ratio:.2%}")
# 检查是否存在类别不平衡问题
imbalance_threshold = 0.3
is_imbalanced = any(ratio < imbalance_threshold for ratio in class_distribution.values())
print(f"\\n是否存在类别不平衡: {'是' if is_imbalanced else '否'}")
输出结果:
原始标签: ['cat' 'dog' 'bird' 'cat' 'fish' 'dog' 'bird' 'cat']
样本数量: 8
唯一标签: ['bird' 'cat' 'dog' 'fish']
各类别样本数量: {'bird': 2, 'cat': 3, 'dog': 2, 'fish': 1}
类别分布:
bird: 25.00%
cat: 37.50%
dog: 25.00%
fish: 12.50%
是否存在类别不平衡: 是
处理特殊数据类型 ⚙️
结构化数组去重
NumPy 支持结构化数组,unique 函数也能很好地处理这类复杂数据:
# 定义结构化数组的数据类型
dtype = [('name', 'U10'), ('age', 'i4'), ('score', 'f4')]
# 创建结构化数组
structured_data = np.array([
('Alice', 25, 85.5),
('Bob', 30, 92.0),
('Charlie', 25, 85.5), # 与 Alice 相同
('David', 35, 78.0),
('Alice', 25, 85.5), # 重复 Alice
], dtype=dtype)
print("原始结构化数据:")
for record in structured_data:
print(record)
# 对整个记录进行去重
unique_records = np.unique(structured_data)
print("\\n去重后的结构化数据:")
for record in unique_records:
print(record)
# 统计重复情况
_, counts = np.unique(structured_data, return_counts=True)
print(f"\\n重复记录数量: {len(structured_data) – len(unique_records)}")
print(f"各记录出现次数: {counts}")
输出结果:
原始结构化数据:
('Alice', 25, 85.5)
('Bob', 30, 92.0)
('Charlie', 25, 85.5)
('David', 35, 78.0)
('Alice', 25, 85.5)
去重后的结构化数据:
('Alice', 25, 85.5)
('Bob', 30, 92.0)
('Charlie', 25, 85.5)
('David', 35, 78.0)
重复记录数量: 1
各记录出现次数: [2 1 1 1]
时间序列数据去重
在处理时间序列数据时,去重操作也很常见:
# 创建时间戳数组(模拟重复的时间戳)
timestamps = np.array([
'2023-01-01T10:00:00',
'2023-01-01T10:01:00',
'2023-01-01T10:00:00', # 重复
'2023-01-01T10:02:00',
'2023-01-01T10:01:00', # 重复
'2023-01-01T10:03:00'
], dtype='datetime64[s]')
print("原始时间戳:")
for ts in timestamps:
print(ts)
# 去重时间戳
unique_timestamps = np.unique(timestamps)
print("\\n去重后时间戳:")
for ts in unique_timestamps:
print(ts)
# 分析时间间隔
time_diffs = np.diff(unique_timestamps)
print("\\n相邻时间间隔:")
for diff in time_diffs:
print(f"{diff} 秒")
输出结果:
原始时间戳:
2023-01-01T10:00:00
2023-01-01T10:01:00
2023-01-01T10:00:00
2023-01-01T10:02:00
2023-01-01T10:01:00
2023-01-01T10:03:00
去重后时间戳:
2023-01-01T10:00:00
2023-01-01T10:01:00
2023-01-01T10:02:00
2023-01-01T10:03:00
相邻时间间隔:
60 seconds
60 seconds
60 seconds
错误处理和边界情况 ⚠️
处理空数组
# 空数组的情况
empty_array = np.array([])
try:
unique_empty = np.unique(empty_array)
print("空数组去重结果:", unique_empty)
print("结果类型:", type(unique_empty))
print("结果形状:", unique_empty.shape)
except Exception as e:
print(f"错误: {e}")
输出结果:
空数组去重结果: []
结果类型: <class 'numpy.ndarray'>
结果形状: (0,)
处理全相同元素的数组
# 全相同元素的数组
same_elements = np.array([5, 5, 5, 5, 5])
unique_same = np.unique(same_elements)
print("全相同元素数组去重:", unique_same)
# 获取详细信息
unique_elements, indices, inverse_indices, counts = np.unique(
same_elements,
return_index=True,
return_inverse=True,
return_counts=True
)
print("唯一元素:", unique_elements)
print("首次索引:", indices)
print("逆向索引:", inverse_indices)
print("出现次数:", counts)
输出结果:
全相同元素数组去重: [5]
唯一元素: [5]
首次索引: [0]
逆向索引: [0 0 0 0 0]
出现次数: [5]
处理包含无穷大的数组
# 包含无穷大的数组
inf_array = np.array([1, np.inf, 2, np.inf, 3, –np.inf, 1])
unique_inf = np.unique(inf_array)
print("包含无穷大的数组去重:", unique_inf)
# 检查特殊值
print("是否包含正无穷:", np.isinf(unique_inf).any())
print("是否包含负无穷:", np.isneginf(unique_inf).any())
print("是否包含 NaN:", np.isnan(unique_inf).any())
输出结果:
包含无穷大的数组去重: [-inf 1. 2. 3. inf]
是否包含正无穷: True
是否包含负无穷: True
是否包含 NaN: False
性能优化技巧 ⚡
内存效率考虑
当处理大型数组时,内存使用是一个重要考虑因素:
import sys
# 创建不同大小的数组进行内存测试
sizes = [1000, 10000, 100000]
for size in sizes:
# 原始数组
original_array = np.random.randint(0, size//10, size=size)
# 去重后的数组
unique_array = np.unique(original_array)
# 计算内存使用
original_memory = sys.getsizeof(original_array)
unique_memory = sys.getsizeof(unique_array)
print(f"数组大小: {size}")
print(f"原始数组内存: {original_memory:,} bytes")
print(f"去重后内存: {unique_memory:,} bytes")
print(f"内存节省: {(original_memory – unique_memory)/original_memory*100:.2f}%")
print("-" * 40)
并行处理大数组
对于超大数组,可以考虑分块处理:
def chunk_unique(arr, chunk_size=10000):
"""
分块处理大数组的去重操作
"""
chunks = []
for i in range(0, len(arr), chunk_size):
chunk = arr[i:i+chunk_size]
chunk_unique_vals = np.unique(chunk)
chunks.append(chunk_unique_vals)
# 合并所有块的结果
combined = np.concatenate(chunks)
final_unique = np.unique(combined)
return final_unique
# 测试分块处理
large_array = np.random.randint(0, 10000, size=100000)
regular_unique = np.unique(large_array)
chunked_unique = chunk_unique(large_array)
print("常规去重结果长度:", len(regular_unique))
print("分块去重结果长度:", len(chunked_unique))
print("结果一致性:", np.array_equal(np.sort(regular_unique), np.sort(chunked_unique)))
与其他库的集成 🔄
与 Pandas 的结合使用
虽然 Pandas 有自己的去重方法,但有时仍需要与 NumPy 的 unique 函数配合使用:
import pandas as pd
# 创建 DataFrame
df = pd.DataFrame({
'A': [1, 2, 2, 3, 3, 3],
'B': ['x', 'y', 'y', 'z', 'z', 'z'],
'C': [10, 20, 20, 30, 30, 30]
})
print("原始 DataFrame:")
print(df)
# 使用 Pandas 的 drop_duplicates
pandas_dedup = df.drop_duplicates()
print("\\nPandas 去重结果:")
print(pandas_dedup)
# 使用 NumPy unique 处理特定列
unique_values_A, counts_A = np.unique(df['A'], return_counts=True)
print(f"\\n列 A 的唯一值及出现次数:")
for val, count in zip(unique_values_A, counts_A):
print(f" {val}: {count} 次")
与 Matplotlib 的结合使用
去重后的数据常用于可视化:
import matplotlib.pyplot as plt
# 生成带有重复值的数据
data = np.random.choice(['A', 'B', 'C', 'D'], size=1000, p=[0.1, 0.3, 0.4, 0.2])
# 添加一些重复项
data = np.concatenate([data, data[:100]]) # 人为增加重复
# 去重并统计
unique_items, counts = np.unique(data, return_counts=True)
# 创建条形图
plt.figure(figsize=(10, 6))
plt.bar(unique_items, counts)
plt.title('去重后的数据分布')
plt.xlabel('类别')
plt.ylabel('频次')
plt.show()
print("数据分布统计:")
for item, count in zip(unique_items, counts):
percentage = count / len(data) * 100
print(f"{item}: {count} ({percentage:.1f}%)")
最佳实践建议 ✅
1. 根据需求选择合适的参数组合
# 示例:根据不同的业务需求选择不同的参数
def smart_unique_analysis(data, analysis_type='basic'):
"""
智能去重分析函数
"""
if analysis_type == 'basic':
return np.unique(data)
elif analysis_type == 'with_indices':
return np.unique(data, return_index=True, return_inverse=True)
elif analysis_type == 'full':
return np.unique(data, return_index=True, return_inverse=True, return_counts=True)
else:
raise ValueError("Invalid analysis_type")
# 测试不同类型的分析
test_data = np.array([1, 2, 2, 3, 3, 3, 4, 5])
print("基础去重:", smart_unique_analysis(test_data, 'basic'))
print("带索引去重:", smart_unique_analysis(test_data, 'with_indices'))
print("完整分析:", smart_unique_analysis(test_data, 'full'))
2. 处理大数据集的策略
def efficient_unique_large_dataset(data, threshold=100000):
"""
高效处理大数据集的去重函数
"""
if len(data) < threshold:
# 小数据集直接处理
return np.unique(data)
else:
# 大数据集采用分块处理
print(f"处理大数据集 (大小: {len(data)})…")
chunk_size = threshold // 10
chunks = []
for i in range(0, len(data), chunk_size):
chunk = data[i:i+chunk_size]
chunk_unique = np.unique(chunk)
chunks.append(chunk_unique)
# 合并并最终去重
combined = np.concatenate(chunks)
return np.unique(combined)
# 测试大数据集处理
large_data = np.random.randint(0, 1000, size=200000)
result = efficient_unique_large_dataset(large_data)
print(f"大数据集去重结果长度: {len(result)}")
3. 错误预防和调试
def safe_unique(data, **kwargs):
"""
安全的去重函数,包含错误处理
"""
try:
# 检查输入数据
if data is None:
raise ValueError("输入数据不能为 None")
if len(data) == 0:
print("警告: 输入为空数组")
return np.array([]), *[np.array([]) for _ in range(sum(kwargs.values()))]
# 执行去重操作
result = np.unique(data, **kwargs)
return result
except Exception as e:
print(f"去重操作出错: {e}")
return None
# 测试安全去重函数
print("正常数据测试:")
normal_data = np.array([1, 2, 2, 3, 3, 3])
result = safe_unique(normal_data)
print("结果:", result)
print("\\n空数组测试:")
empty_result = safe_unique(np.array([]))
print("结果:", empty_result)
print("\\nNone 输入测试:")
none_result = safe_unique(None)
print("结果:", none_result)
总结 🎉
NumPy 的 unique 函数是一个功能强大且灵活的工具,在数据科学和数值计算中发挥着重要作用。通过本文的学习,我们掌握了:
无论你是数据科学家、机器学习工程师,还是普通的 Python 开发者,掌握 unique 函数都能让你的数据处理工作更加高效和优雅。
记住,选择合适的方法取决于你的具体需求。对于简单的去重任务,基本的 np.unique() 就足够了;而对于复杂的分析任务,合理利用其丰富的参数选项可以大大提升工作效率。
希望这篇详细的指南能够帮助你在日常工作中更好地使用 NumPy 的 unique 函数!如果你想要了解更多关于 NumPy 或其他 Python 数据科学工具的内容,建议查看 NumPy 官方文档 获取最新和最权威的信息。
Happy coding! 🐍✨
🙌 感谢你读到这里! 🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近你与目标的距离。 💡 如果本文对你有帮助,不妨 👍 点赞、📌 收藏、📤 分享 给更多需要的朋友! 💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿 🔔 关注我,不错过下一篇干货!我们下期再见!✨




