NumPy 详细笔记

NumPy 详细笔记

目标:让完全零基础的小白也能看懂 NumPy,并能用它完成数组计算、数据统计、矩阵运算、条件筛选和简单数据分析。

这是一份 numpy 笔记:先讲清楚为什么,再讲怎么写,遇到复杂计算会拆开计算流程,最后给练习和答案。

你会学到什么

  1. NumPy 是什么,为什么比 Python 列表更适合数值计算。
  2. ndarrayshapedtypeaxis 这些核心概念。
  3. 如何创建数组、取数、筛选、修改、变形。
  4. 如何理解广播机制和向量化。
  5. 如何做统计计算、矩阵运算、随机数模拟。
  6. 方差、标准差、分位数、矩阵乘法、标准化等复杂计算的完整过程。
  7. 常见错误和排查思路。
  8. 有练习,有答案,有手算过程。

学习建议

NumPy 不要靠死记硬背函数名。你只要抓住四个关键词:

数组 shape dtype axis

多数 NumPy 问题,本质都是:

我有什么形状的数据,要沿着哪个方向,用什么规则批量计算。

1. NumPy 是什么

NumPy 是 Python 中最重要的数值计算库。它的核心对象是 ndarray,也就是“多维数组”。

做数据分析、机器学习、科学计算、图像处理、金融计算,几乎绕不开 NumPy。

Python 列表的问题

Python 列表很灵活,可以放数字、字符串、对象,但这种灵活有代价:

  1. 每个元素都是 Python 对象,计算开销大。
  2. 列表相加是拼接,不是数学加法。
  3. 大量循环计算时速度慢。

NumPy 的优势

NumPy 数组通常存储同一种类型的数据,内存更紧凑,底层使用 C 语言做批量计算,所以更快。

一句话:

NumPy 擅长把一堆数字作为整体一次性计算,而不是让 Python 一个个循环处理。

import numpy as np

python_list_a = [1, 2, 3]
python_list_b = [10, 20, 30]
print("Python 列表相加:", python_list_a + python_list_b)

numpy_arr_a = np.array([1, 2, 3])
numpy_arr_b = np.array([10, 20, 30])
print("NumPy 数组相加:", numpy_arr_a + numpy_arr_b)

2. ndarray:NumPy 的核心对象

ndarray 是 N-dimensional array 的缩写,意思是 N 维数组。

你可以这样理解:

维度例子直观理解
0 维np.array(5)一个数
1 维[1, 2, 3]一排数
2 维[[1,2,3],[4,5,6]]表格或矩阵
3 维多个二维矩阵叠起来一摞表格

ndarray 最重要的属性

属性含义
ndim维度数量
shape每个维度有多长
size总元素个数
dtype元素类型
itemsize每个元素占多少字节
nbytes整个数组占多少字节

最先要会看的就是 shape。很多错误都来自 shape 没看清。

arr0 = np.array(5)
arr1 = np.array([1, 2, 3])
arr2 = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.int32)

for name, arr in [("0维", arr0), ("1维", arr1), ("2维", arr2)]:
    print(name)
    print(arr)
    print("ndim:", arr.ndim)
    print("shape:", arr.shape)
    print("size:", arr.size)
    print("dtype:", arr.dtype)
    print("-" * 30)

3. shape:数组形状怎么读

shape 是 NumPy 最重要的概念之一。

一维数组

arr = [1, 2, 3]
shape = (3,)

意思是:一维,里面有 3 个元素。

二维数组

arr = [[1, 2, 3],
       [4, 5, 6]]
shape = (2, 3)

意思是:2 行 3 列。

三维数组

shape = (2, 3, 4)

意思可以理解为:

2 块,每块 3 行,每行 4 个数

size 的计算过程

如果:

shape = (2, 3, 4)

那么:

size = 2 * 3 * 4 = 24

如果 dtype=int32,每个元素占 4 字节:

nbytes = 24 * 4 = 96 字节
arr = np.arange(24, dtype=np.int32).reshape(2, 3, 4)

print("数组 shape:", arr.shape)
print("数组 size:", arr.size)
print("单个元素字节 itemsize:", arr.itemsize)
print("总字节 nbytes:", arr.nbytes)
print("手算 size:", 2 * 3 * 4)
print("手算 nbytes:", 2 * 3 * 4 * 4)

4. dtype:为什么 NumPy 要求同一种类型

NumPy 数组通常要求元素类型统一,这叫同质性。

这不是缺点,而是 NumPy 速度快的重要原因。

类型提升

如果你把不同类型的数据放进一个数组,NumPy 会自动找一个能兼容所有元素的类型。

常见情况:

输入结果 dtype
[1, 2, 3]整数
[1, 2.5, 3]浮点数
[1, "hello"]字符串

实用建议

做数值计算时,尽量保持数组是纯数字。
如果混入字符串,很多数学函数会不能正常使用。

print(np.array([1, 2, 3]).dtype)
print(np.array([1, 2.5, 3]).dtype)
print(np.array([1, "hello"]).dtype)

arr = np.array([1, 2, 3], dtype=np.float64)
print(arr)
print(arr.dtype)

5. 创建数组

NumPy 创建数组的方法很多。不要全部死记,按用途记就行。

用途函数
从列表创建np.array()
全 0np.zeros()
全 1np.ones()
固定值np.full()
空数组np.empty()
等差序列np.arange()
指定个数等间隔np.linspace()
单位矩阵np.eye()
对角矩阵np.diag()
按已有数组形状创建zeros_likeones_likefull_like

arangelinspace

arange 看步长:

np.arange(0, 10, 2)

表示从 0 到 10,不包含 10,步长是 2。

linspace 看数量:

np.linspace(0, 10, 6)

表示从 0 到 10,生成 6 个等间隔的数。

print("array:", np.array([1, 2, 3]))
print("zeros:\n", np.zeros((2, 3)))
print("ones:\n", np.ones((2, 3)))
print("full:\n", np.full((2, 3), 7))
print("arange:", np.arange(0, 10, 2))
print("linspace:", np.linspace(0, 10, 6))
print("eye:\n", np.eye(3))
print("diag:\n", np.diag([1, 2, 3]))

6. 随机数:生成模拟数据

随机数常用于:

  1. 生成练习数据。
  2. 模拟实验。
  3. 初始化机器学习模型参数。
  4. 抽样。

推荐使用:

rng = np.random.default_rng(种子)

种子的作用是让结果可复现。
同一个种子,每次生成的随机数相同,方便调试和写教程。

rng = np.random.default_rng(42)

print("随机整数:")
print(rng.integers(1, 10, size=(2, 3)))

print("0 到 1 的随机小数:")
print(rng.random((2, 3)))

print("正态分布:")
print(rng.normal(loc=0, scale=1, size=5))

7. 索引和切片:取出你想要的数据

一维数组

arr[0]      # 第 1 个
arr[-1]     # 最后 1 个
arr[1:4]    # 第 2 个到第 4 个
arr[::2]    # 每隔 1 个取

二维数组

二维数组写法:

arr[,]

例如:

arr[1, 2]

表示第 2 行第 3 列。

计算过程

给定:

arr = [[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]]

arr[1, 2]

第 1 个索引 1 -> 第 2 行 [5, 6, 7, 8]
第 2 个索引 2 -> 第 3 个元素 7
结果 = 7

arr[:, 1]

: 表示所有行
1 表示第 2 列
结果 = [2, 6, 10]
arr = np.arange(1, 13).reshape(3, 4)
print(arr)

print("第 2 行第 3 列:", arr[1, 2])
print("所有行第 2 列:", arr[:, 1])
print("前 2 行后 2 列:")
print(arr[:2, 2:])

8. 布尔索引:按条件筛选

布尔索引就是用条件筛选数组。

给定:

arr = [10, 25, 8, 30]
条件 arr >= 20

计算过程:

10 >= 20 -> False
25 >= 20 -> True
8 >= 20  -> False
30 >= 20 -> True

得到:

mask = [False, True, False, True]

再用 arr[mask]

只保留 True 的位置 -> [25, 30]

多条件注意:

(arr > 10) & (arr < 30)

不要写成:

arr > 10 and arr < 30
arr = np.array([10, 25, 8, 30])
mask = arr >= 20

print("条件结果:", mask)
print("筛选结果:", arr[mask])
print("大于 10 且小于 30:", arr[(arr > 10) & (arr < 30)])

9. 修改数据:直接赋值和条件赋值

NumPy 可以直接修改数组。

常见写法:

arr[0] = 100
arr[arr < 0] = 0

条件替换常用 np.where

np.where(条件, 条件为 True 的值, 条件为 False 的值)

np.where 计算过程

arr = [55, 80, 90]
条件 arr >= 60 -> [False, True, True]
np.where(arr >= 60, "及格", "不及格")

结果:

["不及格", "及格", "及格"]
score = np.array([55, 80, 90])

print(np.where(score >= 60, "及格", "不及格"))

arr = np.array([-2, 5, -1, 8])
arr[arr < 0] = 0
print(arr)

10. 视图和拷贝:为什么改一个,另一个也变

NumPy 中很重要的一点:

切片通常返回视图,不是新数组。

视图和原数组共享同一份数据。
改视图,原数组可能一起变。

如果你想要完全独立的一份数据,要使用:

copy()

直白理解

  • 视图:同一份数据的另一个看法。
  • 拷贝:重新复制一份数据。

数据清洗时,如果不希望污染原数据,记得 copy()

arr = np.array([10, 20, 30, 40])
view = arr[1:3]
view[0] = 999
print("修改 view 后原数组:", arr)

arr = np.array([10, 20, 30, 40])
copy_arr = arr[1:3].copy()
copy_arr[0] = 999
print("修改 copy 后原数组:", arr)
print("copy_arr:", copy_arr)

11. axis:统计方向

axis 是 NumPy 最容易卡住的概念。

对二维数组:

arr = [[1, 2, 3],
       [4, 5, 6]]

整体求和

1 + 2 + 3 + 4 + 5 + 6 = 21

axis=0

axis=0 表示沿着行的方向往下压,按列得到结果:

第 1 列: 1 + 4 = 5
第 2 列: 2 + 5 = 7
第 3 列: 3 + 6 = 9
结果: [5, 7, 9]

axis=1

axis=1 表示沿着列的方向往右压,按行得到结果:

第 1 行: 1 + 2 + 3 = 6
第 2 行: 4 + 5 + 6 = 15
结果: [6, 15]

记法:

  • axis=0:每列一个结果。
  • axis=1:每行一个结果。
arr = np.array([[1, 2, 3],
                [4, 5, 6]])

print("整体求和:", arr.sum())
print("axis=0 每列求和:", arr.sum(axis=0))
print("axis=1 每行求和:", arr.sum(axis=1))
print("axis=0 每列平均:", arr.mean(axis=0))
print("axis=1 每行平均:", arr.mean(axis=1))

12. 数组运算:逐元素计算

NumPy 的普通运算通常是逐元素计算。

a = [1, 2, 3]
b = [10, 20, 30]
a + b = [1+10, 2+20, 3+30] = [11, 22, 33]
a * b = [1*10, 2*20, 3*30] = [10, 40, 90]

这和 Python 列表完全不同。

Python 列表 + 是拼接,NumPy 数组 + 是数学加法。

a = np.array([1, 2, 3])
b = np.array([10, 20, 30])

print("a + b:", a + b)
print("a - b:", a - b)
print("a * b:", a * b)
print("b / a:", b / a)
print("a ** 2:", a ** 2)

13. 广播机制:不同形状也能计算

广播是 NumPy 的核心能力之一。

它允许小数组自动扩展成大数组的形状,再进行逐元素计算。

广播规则

从右往左对齐维度。每一维要满足:

  1. 两个维度相等。
  2. 或者其中一个维度是 1。

计算过程

a.shape = (2, 3)
b.shape = (3,)

对齐:

a: 2, 3
b:    3

最后一维都是 3,可以匹配。
于是 b 会被复制成:

[[10, 20, 30],
 [10, 20, 30]]

然后逐元素相加:

[[1+10, 2+20, 3+30],
 [4+10, 5+20, 6+30]]
a = np.array([[1, 2, 3],
              [4, 5, 6]])
b = np.array([10, 20, 30])

print("a shape:", a.shape)
print("b shape:", b.shape)
print("b 广播后的样子:")
print(np.broadcast_to(b, a.shape))
print("a + b:")
print(a + b)

14. reshape、ravel、flatten、转置

数组变形是高频操作。

操作作用
reshape改变形状,元素总数必须不变
ravel展平成一维,尽量返回视图
flatten展平成一维,一定返回拷贝
.T转置,二维中就是行列互换

reshape 计算过程

arr = [1, 2, 3, 4, 5, 6]
reshape(2, 3)

按原顺序填入:

第 1 行: [1, 2, 3]
第 2 行: [4, 5, 6]

结果:

[[1, 2, 3],
 [4, 5, 6]]

-1 表示让 NumPy 自动推断:

arr.reshape(2, -1)

6 个元素,第一维是 2,所以第二维自动算出 3。

arr = np.arange(1, 7)
matrix = arr.reshape(2, 3)

print("原数组:", arr)
print("reshape(2, 3):")
print(matrix)
print("reshape(2, -1):")
print(arr.reshape(2, -1))
print("转置:")
print(matrix.T)
print("ravel:", matrix.ravel())
print("flatten:", matrix.flatten())

15. 常用数学函数

NumPy 提供了大量数学函数,而且都可以直接作用在数组上。

函数作用
np.sqrt平方根
np.exp指数
np.log自然对数
np.sin / np.cos三角函数
np.abs绝对值
np.round四舍五入
np.ceil向上取整
np.floor向下取整

这些函数通常也是逐元素计算。

arr = np.array([1, 4, 9, 16])

print("sqrt:", np.sqrt(arr))
print("power:", np.power(arr, 2))
print("abs:", np.abs(np.array([-3, -1, 2])))
print("round:", np.round(np.array([1.2, 2.6, 3.5])))
print("ceil:", np.ceil(np.array([1.2, 2.6])))
print("floor:", np.floor(np.array([1.2, 2.6])))

16. 统计函数:从数据中提取信息

常见统计函数:

函数含义
sum求和
mean平均值
median中位数
var方差
std标准差
min / max最小值 / 最大值
argmin / argmax最小值 / 最大值的位置
percentile分位数
cumsum累积和
cumprod累积积

平均值、方差、标准差计算过程

[1, 2, 3] 为例:

平均值 = (1 + 2 + 3) / 3 = 2

每个数减平均值:

1 - 2 = -1
2 - 2 = 0
3 - 2 = 1

平方:

(-1)^2 = 1
0^2 = 0
1^2 = 1

总体方差:

(1 + 0 + 1) / 3 = 0.666666...

标准差:

sqrt(0.666666...) = 0.816496...
arr = np.array([1, 2, 3])

mean = arr.mean()
deviation = arr - mean
square_deviation = deviation ** 2
var = square_deviation.mean()
std = np.sqrt(var)

print("平均值:", mean)
print("偏差:", deviation)
print("偏差平方:", square_deviation)
print("手算方差:", var)
print("手算标准差:", std)
print("NumPy 方差:", np.var(arr))
print("NumPy 标准差:", np.std(arr))

17. 中位数和分位数

中位数

先排序,再找中间。

奇数个:

[1, 3, 8] -> 中位数 3

偶数个:

[1, 3, 8, 10] -> (3 + 8) / 2 = 5.5

分位数计算过程

[44, 47, 64, 67] 的 25 分位数为例。

  1. 排序:[44, 47, 64, 67]
  2. 位置范围:0 到 3
  3. 25% 位置:0.25 * (4 - 1) = 0.75
  4. 位置 0 是 44,位置 1 是 47
  5. 0.75 表示从 44 到 47 走 75%
44 + (47 - 44) * 0.75
= 44 + 3 * 0.75
= 46.25
arr = np.array([44, 47, 64, 67])

p = 25
position = p / 100 * (len(arr) - 1)
left = int(np.floor(position))
right = int(np.ceil(position))
ratio = position - left
manual = arr[left] + (arr[right] - arr[left]) * ratio

print("位置:", position)
print("左值:", arr[left])
print("右值:", arr[right])
print("比例:", ratio)
print("手算 25 分位数:", manual)
print("NumPy 25 分位数:", np.percentile(arr, 25))
print("中位数:", np.median(arr))

18. 缺失值 NaN

NaN 表示 Not a Number,常用来表示缺失值或非法结果。

注意:

np.nan == np.nan

结果是 False

判断 NaN 要使用:

np.isnan()

如果数组里有 NaN,普通统计函数会得到 NaN。
可以使用忽略 NaN 的版本:

普通函数忽略 NaN
sumnansum
meannanmean
stdnanstd
maxnanmax
minnanmin
data = np.array([10, 20, np.nan, 40])

print("是否缺失:", np.isnan(data))
print("普通平均:", np.mean(data))
print("忽略 NaN 平均:", np.nanmean(data))

filled = np.where(np.isnan(data), np.nanmean(data), data)
print("用均值填充:", filled)

19. 排序、去重、计数

常用函数:

函数作用
np.sort排序,返回新数组
arr.sort()原地排序,修改原数组
np.argsort返回排序索引
np.unique去重并排序
np.unique(return_counts=True)去重并统计次数

argsort 的计算过程

scores = [82, 95, 78]

从小到大排序应该是:

78, 82, 95

它们在原数组的位置是:

78 -> 位置 2
82 -> 位置 0
95 -> 位置 1

所以:

argsort = [2, 0, 1]
scores = np.array([82, 95, 78])
names = np.array(["张三", "李四", "王五"])

order = np.argsort(scores)
print("排序索引:", order)
print("从低到高分数:", scores[order])
print("从低到高姓名:", names[order])
print("从高到低姓名:", names[order[::-1]])

arr = np.array([2, 1, 2, 3, 1, 4, 3])
values, counts = np.unique(arr, return_counts=True)
print("唯一值:", values)
print("次数:", counts)

20. 拼接和拆分

常用函数:

函数作用
concatenate沿已有维度拼接
stack增加新维度后拼接
hstack水平方向拼接
vstack垂直方向拼接
split拆分数组

concatenate 和 stack 的区别

a = [1, 2, 3]
b = [4, 5, 6]

concatenate

[1, 2, 3, 4, 5, 6]

stack

[[1, 2, 3],
 [4, 5, 6]]

stack 会多出一个维度。

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

print("concatenate:", np.concatenate([a, b]))
print("stack:")
print(np.stack([a, b]))
print("vstack:")
print(np.vstack([a, b]))
print("hstack:", np.hstack([a, b]))

arr = np.arange(10)
print("split:", np.split(arr, [3, 7]))

21. 矩阵运算

NumPy 里要区分:

写法含义
A * B逐元素乘法
A @ B矩阵乘法
np.dot(A, B)点积或矩阵乘法

矩阵乘法要求

A.shape = (m, n)
B.shape = (n, k)

中间的 n 必须相等。

结果 shape:

(m, k)

矩阵乘法计算过程

A = [[1, 2],
     [3, 4]]

B = [[5, 6],
     [7, 8]]

第 1 行第 1 列:

1*5 + 2*7 = 19

第 1 行第 2 列:

1*6 + 2*8 = 22

第 2 行第 1 列:

3*5 + 4*7 = 43

第 2 行第 2 列:

3*6 + 4*8 = 50

结果:

[[19, 22],
 [43, 50]]
A = np.array([[1, 2],
              [3, 4]])
B = np.array([[5, 6],
              [7, 8]])

manual = np.array([
    [1 * 5 + 2 * 7, 1 * 6 + 2 * 8],
    [3 * 5 + 4 * 7, 3 * 6 + 4 * 8],
])

print("逐元素乘法:")
print(A * B)
print("手算矩阵乘法:")
print(manual)
print("NumPy 矩阵乘法:")
print(A @ B)

22. 线性代数入门

线性代数相关函数在 np.linalg 中。

常用函数:

函数作用
np.linalg.solve解线性方程组
np.linalg.inv求逆矩阵
np.linalg.det求行列式
np.linalg.eig特征值和特征向量

解方程组

方程:

2x + y = 8
x + 3y = 13

写成矩阵:

[[2, 1],
 [1, 3]] @ [x, y] = [8, 13]

np.linalg.solve 可以直接求 [x, y]

coef = np.array([[2, 1],
                 [1, 3]])
target = np.array([8, 13])

solution = np.linalg.solve(coef, target)
print("x, y:", solution)
print("验证:")
print(coef @ solution)

23. 标准化:机器学习常见预处理

标准化公式:

z = (x - mean) / std

作用:

  1. 减去均值,让数据中心变成 0 附近。
  2. 除以标准差,让波动尺度变成 1 附近。

计算过程

[10, 20, 30] 为例:

mean = (10 + 20 + 30) / 3 = 20

偏差:

10 - 20 = -10
20 - 20 = 0
30 - 20 = 10

方差:

((-10)^2 + 0^2 + 10^2) / 3 = 200 / 3 = 66.666...

标准差:

sqrt(66.666...) = 8.1649

标准化:

10 -> -10 / 8.1649 = -1.2247
20 -> 0 / 8.1649 = 0
30 -> 10 / 8.1649 = 1.2247
data = np.array([[10, 100],
                 [20, 200],
                 [30, 300]], dtype=float)

mean = data.mean(axis=0)
std = data.std(axis=0)
standardized = (data - mean) / std

print("每列均值:", mean)
print("每列标准差:", std)
print("标准化后:")
print(np.round(standardized, 4))
print("标准化后每列均值:", np.round(standardized.mean(axis=0), 10))
print("标准化后每列标准差:", np.round(standardized.std(axis=0), 10))

24. 综合案例:销售利润分析

给定 5 天销售额和成本:

销售额: [20, 25, 22, 30, 28]
成本:   [15, 18, 16, 22, 20]

要求:

  1. 计算每天利润。
  2. 计算平均利润和标准差。
  3. 找出利润最高的天数。
  4. 计算利润率。

计算过程

每天利润:

第 1 天: 20 - 15 = 5
第 2 天: 25 - 18 = 7
第 3 天: 22 - 16 = 6
第 4 天: 30 - 22 = 8
第 5 天: 28 - 20 = 8

profit = [5, 7, 6, 8, 8]

平均利润:

(5 + 7 + 6 + 8 + 8) / 5 = 34 / 5 = 6.8

标准差:

偏差 = [-1.8, 0.2, -0.8, 1.2, 1.2]
偏差平方 = [3.24, 0.04, 0.64, 1.44, 1.44]
方差 = 6.8 / 5 = 1.36
标准差 = sqrt(1.36) = 1.166

利润最高是 8,出现在第 4 天和第 5 天。

sales = np.array([20, 25, 22, 30, 28])
cost = np.array([15, 18, 16, 22, 20])

profit = sales - cost
profit_rate = profit / sales

print("每天利润:", profit)
print("利润率:", np.round(profit_rate, 4))
print("平均利润:", profit.mean())
print("利润标准差:", profit.std())
print("最高利润:", profit.max())
print("最高利润天数:", np.where(profit == profit.max())[0] + 1)

25. 综合案例:成绩分析

有 5 名学生、3 门课成绩:

[[80, 85, 90],
 [70, 75, 78],
 [95, 88, 92],
 [60, 65, 70],
 [88, 90, 86]]

每行是一个学生,每列是一门课。

要求:

  1. 计算每个学生平均分。
  2. 计算每门课平均分。
  3. 找出总分最高的学生。
  4. 按平均分划分等级。

计算流程

每个学生平均分:按行算,axis=1
每门课平均分:按列算,axis=0
总分最高:先按行求和,再找最大值位置。

score = np.array([
    [80, 85, 90],
    [70, 75, 78],
    [95, 88, 92],
    [60, 65, 70],
    [88, 90, 86],
])

student_mean = score.mean(axis=1)
course_mean = score.mean(axis=0)
student_total = score.sum(axis=1)
best_student = np.argmax(student_total) + 1
level = np.select(
    [student_mean >= 90, student_mean >= 80, student_mean >= 60],
    ["优秀", "良好", "及格"],
    default="不及格",
)

print("每个学生平均分:", np.round(student_mean, 2))
print("每门课平均分:", np.round(course_mean, 2))
print("每个学生总分:", student_total)
print("总分最高学生编号:", best_student)
print("等级:", level)

26. 常见错误和排查方法

错误 1:shape 不匹配

常见报错:

operands could not be broadcast together

解决:

print(a.shape)
print(b.shape)

从右往左检查维度是否相等或是否有 1。

错误 2:多条件筛选忘记括号

错误:

arr[arr > 3 & arr < 8]

正确:

arr[(arr > 3) & (arr < 8)]

错误 3:把 * 当矩阵乘法

A * B

是逐元素乘法。

矩阵乘法要写:

A @ B

错误 4:切片修改影响原数组

如果不想影响原数组,用:

arr.copy()

错误 5:整数数组里放 NaN

np.nan 是浮点概念。需要缺失值时,数组通常要是浮点类型。

27. NumPy 总结

一句话总结

NumPy 的本质是:用多维数组表达数据,用向量化运算批量处理数据。

必须掌握的 12 个点

  1. ndarray 是 NumPy 的核心。
  2. shape 决定数组结构。
  3. dtype 决定类型和内存。
  4. axis 决定统计方向。
  5. 数组普通运算通常是逐元素运算。
  6. 布尔索引用于按条件筛选。
  7. np.where 用于条件替换。
  8. 切片通常是视图,copy() 才是独立副本。
  9. 广播机制让不同形状的数组可以一起算。
  10. reshape 改形状,元素总数不能变。
  11. A * B 是逐元素乘法,A @ B 是矩阵乘法。
  12. 少写 Python 循环,多用向量化。

解题思路

遇到 NumPy 问题,先问:

1. 数据 shape 是什么?
2. 要按行算还是按列算?
3. 是筛选、替换、统计,还是矩阵运算?
4. 是否需要保留原数组?
5. 结果 shape 应该是什么?

28. 扩展学习路线

第一阶段:数组基础

  • ndarray
  • shape
  • dtype
  • 索引切片
  • 布尔筛选

第二阶段:数组计算

  • 向量化
  • 广播
  • 统计函数
  • 缺失值处理
  • 排序去重

第三阶段:数学能力

  • 矩阵乘法
  • 线性方程组
  • 标准化
  • 随机模拟
  • 简单概率统计

第四阶段:连接真实应用

  • Pandas:表格数据分析
  • Matplotlib / Seaborn:数据可视化
  • SciPy:科学计算
  • scikit-learn:机器学习
  • PyTorch:深度学习张量计算

NumPy 学得越扎实,后面学 Pandas、机器学习、深度学习越轻松。

练习题

下面练习都有答案和计算过程。建议你先自己做,再看答案。

练习 1:温度分析

某城市一周最高气温:

[26, 30, 29, 31, 30, 32, 29]

要求:

  1. 计算平均气温。
  2. 找出最高气温和最低气温。
  3. 统计超过 30 度的天数。
  4. 找出超过 30 度的具体温度。
# 练习 1 答案
temps = np.array([26, 30, 29, 31, 30, 32, 29])

print("平均气温:", temps.mean())
print("最高气温:", temps.max())
print("最低气温:", temps.min())
print("超过 30 度天数:", np.count_nonzero(temps > 30))
print("超过 30 度温度:", temps[temps > 30])

练习 1 计算过程

平均气温:

(26 + 30 + 29 + 31 + 30 + 32 + 29) / 7
= 207 / 7
= 29.5714

超过 30 度:

26 > 30 -> False
30 > 30 -> False
29 > 30 -> False
31 > 30 -> True
30 > 30 -> False
32 > 30 -> True
29 > 30 -> False

所以超过 30 度的是 [31, 32],共 2 天。

练习 2:成绩统计

成绩:

[85, 90, 78, 92, 88]

要求:

  1. 计算平均分、中位数、标准差。
  2. 找出最高分的位置。
  3. 将成绩按规则分类:>=90 优秀>=80 良好,否则 继续努力
# 练习 2 答案
score = np.array([85, 90, 78, 92, 88])

level = np.select(
    [score >= 90, score >= 80],
    ["优秀", "良好"],
    default="继续努力",
)

print("平均分:", score.mean())
print("中位数:", np.median(score))
print("标准差:", score.std())
print("最高分位置:", score.argmax())
print("等级:", level)

练习 2 计算过程

平均分:

(85 + 90 + 78 + 92 + 88) / 5 = 433 / 5 = 86.6

中位数:

排序后: [78, 85, 88, 90, 92]
中间位置是 88

最高分:

最高分是 92,位置是 3

注意:Python 和 NumPy 的位置从 0 开始,所以第 4 个元素的位置是 3。

练习 3:矩阵运算

给定:

A = [[1, 2],
     [3, 4]]

B = [[5, 6],
     [7, 8]]

要求:

  1. 计算 A + B
  2. 计算逐元素乘法 A * B
  3. 计算矩阵乘法 A @ B
# 练习 3 答案
A = np.array([[1, 2],
              [3, 4]])
B = np.array([[5, 6],
              [7, 8]])

print("A + B:")
print(A + B)
print("A * B:")
print(A * B)
print("A @ B:")
print(A @ B)

练习 3 计算过程

逐元素加法:

[[1+5, 2+6],
 [3+7, 4+8]]
= [[6, 8],
   [10, 12]]

逐元素乘法:

[[1*5, 2*6],
 [3*7, 4*8]]
= [[5, 12],
   [21, 32]]

矩阵乘法:

第 1 行第 1 列 = 1*5 + 2*7 = 19
第 1 行第 2 列 = 1*6 + 2*8 = 22
第 2 行第 1 列 = 3*5 + 4*7 = 43
第 2 行第 2 列 = 3*6 + 4*8 = 50

练习 4:数组变形和 axis

创建 1 到 12 的数组,并变成 3 行 4 列。

要求:

  1. 计算每行的和。
  2. 计算每列的平均值。
  3. 展平成一维数组。
# 练习 4 答案
arr = np.arange(1, 13).reshape(3, 4)

print("数组:")
print(arr)
print("每行的和:", arr.sum(axis=1))
print("每列的平均值:", arr.mean(axis=0))
print("展平:", arr.ravel())

练习 4 计算过程

数组:

[[ 1,  2,  3,  4],
 [ 5,  6,  7,  8],
 [ 9, 10, 11, 12]]

每行求和:

第 1 行: 1 + 2 + 3 + 4 = 10
第 2 行: 5 + 6 + 7 + 8 = 26
第 3 行: 9 + 10 + 11 + 12 = 42

每列平均:

第 1 列: (1 + 5 + 9) / 3 = 5
第 2 列: (2 + 6 + 10) / 3 = 6
第 3 列: (3 + 7 + 11) / 3 = 7
第 4 列: (4 + 8 + 12) / 3 = 8

练习 5:标准化

给定二维数据:

[[10, 100],
 [20, 200],
 [30, 300]]

要求:

  1. 计算每列均值。
  2. 计算每列标准差。
  3. 对每列做标准化。
  4. 验证标准化后每列均值接近 0,标准差接近 1。
# 练习 5 答案
data = np.array([[10, 100],
                 [20, 200],
                 [30, 300]], dtype=float)

mean = data.mean(axis=0)
std = data.std(axis=0)
z = (data - mean) / std

print("每列均值:", mean)
print("每列标准差:", std)
print("标准化结果:")
print(np.round(z, 4))
print("标准化后均值:", np.round(z.mean(axis=0), 10))
print("标准化后标准差:", np.round(z.std(axis=0), 10))

练习 5 计算过程

第 1 列 [10, 20, 30]

均值 = 20
标准差 = 8.1649
标准化结果 = [-1.2247, 0, 1.2247]

第 2 列 [100, 200, 300]

均值 = 200
标准差 = 81.6497
标准化结果 = [-1.2247, 0, 1.2247]

虽然两列数值尺度不同,但标准化后形状一样。
这就是标准化的作用:消除量纲和尺度差异。

练习 6:综合销售分析

给定 6 天销售额和成本:

销售额 = [100, 120, 90, 150, 130, 160]
成本   = [60,  80,  50, 100, 85,  110]

要求:

  1. 计算每天利润。
  2. 计算每天利润率。
  3. 找出利润最高的一天。
  4. 计算总销售额、总成本、总利润。
  5. 计算整体利润率。
  6. 找出利润率高于整体利润率的天数。
# 练习 6 答案
sales = np.array([100, 120, 90, 150, 130, 160])
cost = np.array([60, 80, 50, 100, 85, 110])

profit = sales - cost
profit_rate = profit / sales

total_sales = sales.sum()
total_cost = cost.sum()
total_profit = profit.sum()
overall_profit_rate = total_profit / total_sales
high_days = np.where(profit_rate > overall_profit_rate)[0] + 1

print("每天利润:", profit)
print("每天利润率:", np.round(profit_rate, 4))
print("利润最高的一天:", profit.argmax() + 1)
print("总销售额:", total_sales)
print("总成本:", total_cost)
print("总利润:", total_profit)
print("整体利润率:", round(overall_profit_rate, 4))
print("利润率高于整体利润率的天数:", high_days)

练习 6 计算过程

每天利润:

第 1 天: 100 - 60 = 40
第 2 天: 120 - 80 = 40
第 3 天: 90 - 50 = 40
第 4 天: 150 - 100 = 50
第 5 天: 130 - 85 = 45
第 6 天: 160 - 110 = 50

总销售额:

100 + 120 + 90 + 150 + 130 + 160 = 750

总成本:

60 + 80 + 50 + 100 + 85 + 110 = 485

总利润:

750 - 485 = 265

整体利润率:

265 / 750 = 0.3533 = 35.33%

注意:整体利润率不是每天利润率的简单平均。
正确方式是:

总利润 / 总销售额

最后

NumPy 的学习重点不是“背多少函数”,而是形成数组思维。

把问题翻译成 NumPy 时,先说清楚:

数据是什么 shape?
我要沿哪个 axis 算?
是逐元素、筛选、聚合、广播,还是矩阵运算?

当你能稳定回答这三个问题,NumPy 就不再是零散函数,而是一套清晰的数值计算语言。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值