github star gitee star atomgit star PyPI Downloads AI 编程 AI 交流群

大家好,我是正在实战各种AI项目的程序员晚枫。

欢迎来到数据分析课程第二课!

今天学习NumPy,这是Python科学计算的基础库。掌握它,你的数据处理速度能提升几十甚至上百倍。


为什么需要NumPy?

Python列表的问题

1
2
3
4
5
6
7
8
# 创建100万个数字的列表
import time

start = time.time()
python_list = list(range(1000000))
result = [x * 2 for x in python_list] # 每个元素乘2
print(f"Python列表耗时: {time.time() - start:.4f}秒")
# 耗时约0.15秒

NumPy数组的优势

1
2
3
4
5
6
7
import numpy as np

start = time.time()
numpy_array = np.arange(1000000)
result = numpy_array * 2 # 向量化运算
print(f"NumPy数组耗时: {time.time() - start:.4f}秒")
# 耗时约0.001秒,快150倍!

为什么这么快?

  • NumPy底层用C语言实现
  • 数组在内存中是连续的
  • 支持向量化运算(不用写循环)

创建数组

从列表创建

1
2
3
4
5
6
7
8
9
10
11
import numpy as np

# 一维数组
arr1 = np.array([1, 2, 3, 4, 5])
print(arr1) # [1 2 3 4 5]

# 二维数组(矩阵)
arr2 = np.array([[1, 2, 3], [4, 5, 6]])
print(arr2)
# [[1 2 3]
# [4 5 6]]

常用创建函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 等差数列
np.arange(0, 10, 2) # [0, 2, 4, 6, 8]
np.linspace(0, 1, 5) # [0, 0.25, 0.5, 0.75, 1]

# 特殊数组
np.zeros((3, 4)) # 3行4列的全0数组
np.ones((2, 3)) # 全1数组
np.eye(3) # 3x3单位矩阵
np.full((2, 2), 7) # 全部填充7

# 随机数组
np.random.rand(3, 3) # 0-1之间的随机数
np.random.randint(1, 10, (3, 3)) # 1-9的随机整数
np.random.randn(3, 3) # 标准正态分布

数组属性

1
2
3
4
5
6
7
arr = np.array([[1, 2, 3], [4, 5, 6]])

print(arr.shape) # (2, 3) - 形状:2行3列
print(arr.ndim) # 2 - 维度
print(arr.size) # 6 - 元素总数
print(arr.dtype) # int64 - 数据类型
print(arr.itemsize) # 8 - 每个元素占8字节

索引和切片

一维数组

1
2
3
4
5
6
7
arr = np.array([10, 20, 30, 40, 50])

print(arr[0]) # 10
print(arr[-1]) # 50
print(arr[1:4]) # [20 30 40]
print(arr[::2]) # [10 30 50] - 步长为2
print(arr[::-1]) # [50 40 30 20 10] - 反转

二维数组

1
2
3
4
5
6
7
8
9
arr = np.array([[1, 2, 3], 
[4, 5, 6],
[7, 8, 9]])

print(arr[0, 1]) # 2 - 第0行第1列
print(arr[0]) # [1 2 3] - 第0行
print(arr[:, 1]) # [2 5 8] - 第1列
print(arr[0:2, 1:3]) # [[2 3]
# [5 6]]

布尔索引(筛选)

1
2
3
4
5
6
7
8
9
10
arr = np.array([1, 2, 3, 4, 5, 6])

# 选出大于3的元素
print(arr[arr > 3]) # [4 5 6]

# 选出偶数
print(arr[arr % 2 == 0]) # [2 4 6]

# 多条件
print(arr[(arr > 2) & (arr < 6)]) # [3 4 5]

数组运算

基本运算

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

print(arr + 10) # [11 12 13 14 15]
print(arr * 2) # [2 4 6 8 10]
print(arr ** 2) # [1 4 9 16 25]
print(arr / 2) # [0.5 1. 1.5 2. 2.5]

数组间运算

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

print(a + b) # [5 7 9]
print(a * b) # [4 10 18] - 对应元素相乘
print(a @ b) # 32 - 点积(1*4 + 2*5 + 3*6)

通用函数(ufunc)

1
2
3
4
5
6
7
arr = np.array([0, np.pi/2, np.pi])

print(np.sin(arr)) # 正弦
print(np.cos(arr)) # 余弦
print(np.exp(arr)) # 指数
print(np.log(arr + 1)) # 对数
print(np.sqrt(arr)) # 开方

数组变形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
arr = np.arange(12)  # [0 1 2 ... 11]

# reshape改变形状
arr2d = arr.reshape(3, 4)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]

# flatten展平
arr_flat = arr2d.flatten()

# transpose转置
arr2d.T
# [[ 0 4 8]
# [ 1 5 9]
# [ 2 6 10]
# [ 3 7 11]]

实战:计算股票收益率

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import numpy as np

# 模拟5天的股价
prices = np.array([100, 102, 101, 105, 108])

# 计算日收益率
returns = (prices[1:] - prices[:-1]) / prices[:-1]
print("日收益率:", returns)
# [0.02 -0.00980392 0.03960396 0.02857143]

# 累计收益率
cumulative_return = (prices[-1] - prices[0]) / prices[0]
print(f"累计收益率: {cumulative_return:.2%}") # 8.00%

# 平均收益率
avg_return = np.mean(returns)
print(f"平均日收益率: {avg_return:.2%}")

# 收益率波动(标准差)
volatility = np.std(returns)
print(f"波动率: {volatility:.2%}")

性能对比:NumPy vs Python列表

实际跑一下看看差距有多大:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import numpy as np
import time

# 100万元素求和
size = 10_000_000

# Python列表
start = time.time()
py_list = list(range(size))
result = sum(py_list)
print(f"Python列表: {time.time()-start:.4f}秒")

# NumPy数组
start = time.time()
np_arr = np.arange(size)
result = np.sum(np_arr)
print(f"NumPy数组: {time.time()-start:.4f}秒")
# 典型结果:Python 0.8秒 vs NumPy 0.01秒,快80倍!

不同数据类型的内存对比

1
2
3
4
5
6
7
8
9
10
11
import sys

# Python列表 vs NumPy数组,同样存储100万个整数
py_list = list(range(1000000))
np_arr = np.arange(1000000, dtype=np.int32)

print(f"Python列表: {sys.getsizeof(py_list) / 1024 / 1024:.1f} MB")
# 约 38 MB

print(f"NumPy int32数组: {np_arr.nbytes / 1024 / 1024:.1f} MB")
# 约 3.8 MB,只有Python列表的1/10!

进阶用法

广播机制详解

广播是NumPy最强大也最容易困惑的特性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 规则1:标量和数组运算
arr = np.array([[1, 2, 3], [4, 5, 6]])
print(arr + 10)
# [[11 12 13]
# [14 15 16]]

# 规则2:不同形状的数组
row = np.array([1, 2, 3]) # shape (3,)
col = np.array([[10], [20]]) # shape (2, 1)
print(row + col)
# [[11 12 13]
# [21 22 23]]

# 实战:标准化每列数据
data = np.random.rand(100, 5) # 100行5列
mean = data.mean(axis=0) # 每列均值 shape (5,)
std = data.std(axis=0) # 每列标准差 shape (5,)
standardized = (data - mean) / std # 广播自动生效!

花式索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
arr = np.arange(20).reshape(4, 5)

# 选取指定行
print(arr[[0, 2, 3]]) # 第0、2、3行

# 选取指定位置的元素
rows = np.array([0, 1, 2, 3])
cols = np.array([0, 2, 4])
print(arr[rows, cols]) # [0 7 14 19]

# 用np.ix_选取矩形区域
print(arr[np.ix_([0, 2], [1, 3])])
# [[ 1 3]
# [11 13]]

避坑指南

❌ 坑1:视图vs副本搞混

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
arr = np.arange(6)

# 视图(共享内存,改一个另一个也变)
view = arr[1:4]
view[0] = 999
print(arr) # [0 999 3 4 5] ← arr也变了!

# 副本(独立内存,互不影响)
copy = arr[1:4].copy()
copy[0] = 100
print(arr) # 不受影响

# 判断是否是视图
print(view.base is not None) # True,是视图
print(copy.base is not None) # False,是副本

❌ 坑2:整数数组索引的陷阱

1
2
3
4
5
6
7
8
arr = np.arange(12).reshape(3, 4)

# 这样赋值不是你想要的
arr[[0, 1], [2, 3]] = 100
# 只修改了arr[0,2]和arr[1,3]两个元素

# 如果想修改第0行和第1行的所有元素
arr[[0, 1]] = 100 # 这样才对

❌ 坑3:数据类型自动提升

1
2
3
4
5
6
7
8
arr = np.array([1, 2, 3], dtype=np.int32)
arr[0] = 1.5 # 1.5会被截断为1!
print(arr) # [1 2 3]

# 需要显式转换类型
arr = arr.astype(np.float64)
arr[0] = 1.5
print(arr) # [1.5 2. 3. ]

实战案例:分析电商用户消费数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import numpy as np

# 模拟1000个用户的消费数据
np.random.seed(42)
user_ids = np.arange(1001, 2001) # 用户ID
spend = np.random.exponential(500, 1000) # 消费金额,指数分布
orders = np.random.poisson(5, 1000) # 订单数,泊松分布
age = np.random.randint(18, 65, 1000) # 年龄

# 1. 基本统计
print(f"平均消费: ¥{spend.mean():.0f}")
print(f"消费中位数: ¥{np.median(spend):.0f}")
print(f"消费标准差: ¥{spend.std():.0f}")

# 2. 用户分层
high_value = spend > np.percentile(spend, 80) # 前20%为高价值用户
print(f"高价值用户数: {high_value.sum()}")
print(f"高价值用户平均消费: ¥{spend[high_value].mean():.0f}")
print(f"普通用户平均消费: ¥{spend[~high_value].mean():.0f}")

# 3. 年龄与消费的关系
young = age < 30
middle = (age >= 30) & (age < 45)
senior = age >= 45
print(f"年轻人(<30)平均消费: ¥{spend[young].mean():.0f}")
print(f"中年人(30-45)平均消费: ¥{spend[middle].mean():.0f}")
print(f"年长者(>=45)平均消费: ¥{spend[senior].mean():.0f}")

# 4. 计算客单价
avg_order_value = spend / np.maximum(orders, 1) # 避免除0
print(f"平均客单价: ¥{avg_order_value.mean():.0f}")

下节预告

下一课我们将学习NumPy进阶-数学运算,包括统计函数、线性代数等内容。

👉 继续阅读:NumPy进阶-数学运算


💬 加入学习交流群

扫码加入Python学习交流群,和数千名同学一起进步:

👉 点击加入交流群

群里不定期分享:

  • 数据分析实战案例
  • Python学习资料
  • 求职面试经验
  • 行业最新动态

推荐:AI Python数据分析实战营

🎁 限时福利:送《利用Python进行数据分析》实体书

👉 点击了解详情


课程导航

上一篇: Anaconda安装与环境配置

下一篇: NumPy进阶-数学运算


PS:NumPy是数据分析的基石。花时间打好基础,后面的Pandas会学得更轻松。



📚 推荐教材

主教材《Excel+Python 飞速搞定数据分析与处理(图灵出品)》

💬 联系我

平台账号/链接
微信扫码加好友
微博@程序员晚枫
知乎@程序员晚枫
抖音@程序员晚枫
小红书@程序员晚枫
B 站Python 自动化办公社区

主营业务:AI 编程培训、企业内训、技术咨询

🎓 AI 编程实战课程

想系统学习 AI 编程?程序员晚枫的 AI 编程实战课 帮你从零上手!