📌 写在前面
大家好!
欢迎来到**【一起学Python】**的第85天!🎉
前84天,我们从环境安装一路学到统计函数,已经掌握了NumPy的“基本功”。但在实际数据分析中,我们经常遇到这样的需求:
- 📊 找出排名前10的数据
- 🔍 筛选出符合条件的样本
- 📉 把乱序的实验数据整理好
- 替换异常值或缺失标记
今天,我们就来解锁NumPy的排序与选择功能!掌握它们,你的数据清洗效率将提升10倍!🚀
一、数组排序:让数据“各就各位” 📊
NumPy提供了灵活高效的排序工具,既能返回新数组,也能原地修改,还能获取排序索引。
1.1 np.sort() vs arr.sort()
import numpy as npscores = np.array([85, 92, 78, 95, 60])# 方式1:np.sort() → 返回新数组,原数组不变 ✅ 推荐用于需保留原始数据的场景sorted_new = np.sort(scores)print("新数组:", sorted_new) # [60 78 85 92 95]print("原数组:", scores) # [85 92 78 95 60](未变)# 方式2:arr.sort() → 原地排序,节省内存 适合超大数据集scores.sort()print("原地排序后:", scores) # [60 78 85 92 95]
方法 | 是否修改原数组 | 内存开销 | 适用场景 |
|---|
np.sort(arr)
| ❌ 否 | 额外分配新数组 | 需保留原始数据、链式调用 |
arr.sort()
| ✅ 是 | 原地操作,省内存 | 大数据集、明确不需要原数据 |
1.2 np.argsort():排序索引(数据分析神器!)
不直接排序数据,而是返回排序后的位置索引。这在“按A列排序,同时重排B列”时极其有用!
ids = np.array([101, 102, 103, 104, 105])scores = np.array([85, 92, 78, 95, 60])# 获取从小到大排序的索引sorted_idx = np.argsort(scores)print("排序索引:", sorted_idx) # [4 2 0 1 3]# 用索引同时重排学号和成绩print("按成绩排序后的学号:", ids[sorted_idx]) # [105 103 101 102 104]print("按成绩排序后的分数:", scores[sorted_idx]) # [60 78 85 92 95]
1.3 多维数组排序:axis 参数
matrix = np.array([[3, 1, 2], [9, 7, 8], [6, 4, 5]])# 按行排序(axis=1)print("按行排序:\n", np.sort(matrix, axis=1))# 按列排序(axis=0)print("按列排序:\n", np.sort(matrix, axis=0))
二、条件选择与过滤:精准定位目标数据 🎯
当需要按条件提取或替换数据时,where 和 nonzero 是你的最佳搭档。
2.1 np.where():条件选择的“瑞士军刀”
np.where() 有两种常用模式,千万别混淆!
🔹 模式1:返回满足条件的索引
data = np.array([10, 20, 30, 40, 50])condition = data > 30indices = np.where(condition)print("满足条件的索引:", indices) # (array([3, 4]),)print("满足条件的元素:", data[indices]) # [40 50]
🔹 模式2:类似Excel的IF函数(条件替换)
# 格式:np.where(条件, 条件为真时的值, 条件为假时的值)grades = np.array([85, 55, 92, 48, 76])result = np.where(grades >= 60, "及格", "不及格")print("评级结果:", result) # ['及格' '不及格' '及格' '不及格' '及格']# 数值替换:小于0的补0,大于100的截断为100clipped = np.where(grades < 0, 0, np.where(grades > 100, 100, grades))
2.2 np.nonzero():提取非零/True元素的索引
arr = np.array([0, 5, 0, 8, 0, 3])# 返回非零元素的索引nz_idx = np.nonzero(arr)print("非零索引:", nz_idx) # (array([1, 3, 5]),)print("非零元素:", arr[nz_idx]) # [5 8 3]# 💡 等价于布尔索引:arr[arr != 0]# 但 nonzero() 返回的是索引,适合需要定位原始位置的场景
2.3 布尔索引 vs where vs nonzero
data = np.array([15, 25, 35, 45, 55])# 1. 布尔索引(最简洁,推荐日常使用)print(data[data > 30]) # [35 45 55]# 2. np.where(condition)(返回索引元组)print(np.where(data > 30)) # (array([2, 3, 4]),)# 3. np.nonzero(condition)(与where返回索引等价)print(np.nonzero(data > 30)) # (array([2, 3, 4]),)
选择建议:
- 只需提取数据 → 直接用
data[condition] - 需要知道原始位置 → 用
np.where() 或 np.nonzero() - 需要按条件赋值 → 用
np.where(cond, x, y)
三、实战演练:排序+选择的组合拳 🥊
场景:学生成绩分析系统
import numpy as np# 模拟数据:5名学生,3门课成绩students = np.array(["张三", "李四", "王五", "赵六", "孙七"])scores = np.array([ [85, 92, 78], # 张三 [60, 55, 70], # 李四 [95, 98, 96], # 王五 [45, 50, 48], # 赵六 [88, 90, 85] # 孙七])# 1. 计算总分total_scores = np.sum(scores, axis=1)print("总分:", total_scores) # [255 185 289 143 263]# 2. 找出总分前3的学生(降序排序索引)top3_idx = np.argsort(total_scores)[::-1][:3]print("前3名索引:", top3_idx) # [2 4 0]print("前3名学生:", students[top3_idx]) # ['王五' '孙七' '张三']# 3. 标记不及格(任意一科<60)fail_mask = np.any(scores < 60, axis=1)print("需补考学生:", students[fail_mask]) # ['李四' '赵六']# 4. 将低于60的成绩替换为60(平时分保护政策)protected_scores = np.where(scores < 60, 60, scores)print("保护后成绩:\n", protected_scores)
📝 核心技巧总结:
np.argsort()[::-1] → 快速实现降序排名np.any()/np.all() + 布尔索引 → 多条件组合筛选np.where() 嵌套 → 复杂条件赋值
今日作业
基础题 ⭐
- 创建数组
[3, 1, 4, 1, 5, 9, 2, 6],分别用 np.sort() 和 arr.sort() 排序,观察区别。 - 使用
np.argsort() 找出原数组中最大值的索引。 - 用
np.where() 将数组中小于3的元素替换为0,其余保持不变。
进阶题 ⭐⭐
- 创建一个4×5的随机整数矩阵(0~100),按列降序排序。
- 使用
np.nonzero() 提取矩阵中大于80的元素的行列坐标。 - 模拟体温数据,将
<36.0 或 >37.5 的值标记为“异常”,其余标记为“正常”。
挑战题 ⭐⭐⭐
- Top-K高效解法:生成100万个随机数,对比
np.sort()[-10:] 和 np.partition()[-10:] 的耗时(提示:partition 适合找前K大/小)。 - 多条件筛选:从二维数组中筛选出“第一列>50 且 第二列<30”的行。
- 成绩等级映射:将分数数组按
[0,60,75,85,100] 分段,映射为 ['D','C','B','A'](提示:用 np.digitize 或多次 where)。
💡 参考代码(挑战题-Top-K对比):
import numpy as npimport timedata = np.random.rand(1_000_000)# 方法1:全排序start = time.time()top10_sort = np.sort(data)[-10:]print(f"sort耗时: {time.time()-start:.4f}s")# 方法2:局部排序(更高效)start = time.time()top10_part = np.partition(data, -10)[-10:]print(f"partition耗时: {time.time()-start:.4f}s")
🎓 明日预告
第86天:NumPy文件读写与数据持久化
你将学到:
- 🔸
np.save / np.load 高效保存二进制数组 - 🔸
np.savetxt / np.loadtxt 读写CSV/文本文件 - 🔸 二进制格式 vs 文本格式的优劣对比
- 🔸 实战:数据预处理后的持久化存储与跨平台共享
敬请期待! 📂
学习小贴士
- 排序性能:
- 小数组:
sort() 足够快 - 找Top-K:优先用
np.partition()(时间复杂度 O(n) vs O(n log n)) - 需保留原数据:用
np.sort() 或先 .copy()
- 选择函数避坑:
np.where(cond) 返回的是元组,取索引用 indices[0]- 布尔索引
arr[mask] 返回的是副本,修改不影响原数组 - 多维条件筛选时,注意
&(与)、|(或)要用括号包裹:arr[(arr>0) & (arr<10)]
- 调试技巧:
- 排序后对不上?检查是否混用了
axis=0 和 axis=1 - 筛选结果为空?打印
condition.sum() 看有多少True - 内存爆满?大数据集优先用原地操作
arr.sort() 和视图切片
📚 学习路线图
第74天:NumPy安装 ✓第75天:ndarray对象 ✓第76天:数据类型 ✓第77天:数组属性 ✓第78天:创建数组 ✓第79天:切片和索引 ✓第80天:高级索引 ✓第81天:数组运算 ✓第82天:数组操作 ✓第83天:数学函数 ✓第84天:统计函数 ✓第85天:排序与选择 ✓ ← 你今天在这里第86天:文件读写第87天:与pandas结合...
💬 写在最后
排序与选择是数据分析的**“导航仪”和“过滤器”**。掌握它们,你就能:
✅ 快速定位关键数据,告别手动翻找✅ 灵活处理排名、筛选、替换等高频需求✅ 写出简洁、高效、可读性强的数据清洗代码
今天重点掌握:
- ✅
sort() 与 argsort() 的场景选择 - ✅
np.where() 的两种模式(索引返回 vs 条件赋值) - ✅ 布尔索引与
nonzero() 的配合使用
如果觉得有用,记得:
- 👍 点赞支持一下
- ⭐ 收藏方便复习
- 📤 分享给更多小伙伴
完成作业的同学,欢迎在评论区打卡! 💪
【一起学Python】每天进步一点点,365天后遇见更优秀的自己!
👉 关注公众号,不错过每天的学习内容!
🎯 今日金句:
"排序让数据有序,选择让价值显现。掌握它们,混乱的数据也将为你让路!" ️✨
明天见!