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

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

今天继续NumPy进阶内容,学习统计函数和线性代数运算

这些函数是数据分析的核心工具,掌握它们,你就能处理90%的数值计算需求。


统计函数

基础统计量

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
import numpy as np

data = np.array([85, 92, 78, 90, 88, 95, 82])

# 总和
print(np.sum(data)) # 610

# 均值
print(np.mean(data)) # 87.14

# 中位数
print(np.median(data)) # 88.0

# 标准差
print(np.std(data)) # 5.55

# 方差
print(np.var(data)) # 30.82

# 最值
print(np.min(data), np.max(data)) # 78 95
print(np.argmin(data), np.argmax(data)) # 最小/最大值的索引

# 极差
print(np.ptp(data)) # 17 (95-78)

百分位数

1
2
3
4
5
6
7
scores = np.array([65, 70, 75, 80, 85, 90, 95, 98, 100])

# 计算百分位数
print(np.percentile(scores, 25)) # 第一四分位数 Q1
print(np.percentile(scores, 50)) # 中位数 Q2
print(np.percentile(scores, 75)) # 第三四分位数 Q3
print(np.percentile(scores, 90)) # 前10%的分数线

多维数组统计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 学生成绩表:3个学生,4门课
scores = np.array([
[85, 92, 78, 90], # 学生A
[88, 85, 92, 87], # 学生B
[90, 88, 85, 95] # 学生C
])

# 每门课的平均分(按列计算)
print(np.mean(scores, axis=0)) # [87.67 88.33 85. 90.67]

# 每个学生的总分(按行计算)
print(np.sum(scores, axis=1)) # [345 352 358]

# 每门课的最高分
print(np.max(scores, axis=0)) # [90 92 92 95]

常用数学函数

四舍五入与取整

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

print(np.round(arr, 2)) # [3.14 2.72 1.41 4.67]
print(np.floor(arr)) # [3. 2. 1. 4.] - 向下取整
print(np.ceil(arr)) # [4. 3. 2. 5.] - 向上取整
print(np.trunc(arr)) # [3. 2. 1. 4.] - 截断小数

条件统计

1
2
3
4
5
6
7
8
9
10
11
sales = np.array([120, 150, 80, 200, 95, 180, 110])

# 统计大于100的天数
print(np.sum(sales > 100)) # 5

# 大于100的销售额总和
print(np.sum(sales[sales > 100])) # 760

# 平均值(忽略小于50的异常值)
filtered = sales[sales >= 50]
print(np.mean(filtered))

累积运算

1
2
3
4
5
revenue = np.array([100, 120, 90, 150, 130])

print(np.cumsum(revenue)) # [100 220 310 460 590] - 累积和
print(np.cumprod(revenue)) # 累积积
print(np.diff(revenue)) # [ 20 -30 60 -20] - 差分

线性代数运算

矩阵创建

1
2
3
4
5
6
7
# 创建矩阵
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])

print(A)
# [[1 2]
# [3 4]]

矩阵运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 矩阵乘法
print(A @ B) # 或 np.dot(A, B)
# [[19 22]
# [43 50]]

# 转置
print(A.T)
# [[1 3]
# [2 4]]

# 逆矩阵
print(np.linalg.inv(A))
# [[-2. 1. ]
# [ 1.5 -0.5]]

# 行列式
print(np.linalg.det(A)) # -2.0

# 特征值和特征向量
eigenvalues, eigenvectors = np.linalg.eig(A)

解线性方程组

1
2
3
4
5
6
7
8
9
# 解方程组:
# 2x + y = 5
# x - y = 1

A = np.array([[2, 1], [1, -1]])
b = np.array([5, 1])

solution = np.linalg.solve(A, b)
print(solution) # [2. 1.],即 x=2, y=1

随机数生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 设置随机种子(保证可重复)
np.random.seed(42)

# 均匀分布
np.random.rand(3, 3) # 0-1之间的随机数
np.random.uniform(1, 10, 5) # 1-10之间的5个随机数

# 正态分布
np.random.randn(1000) # 标准正态分布
np.random.normal(0, 1, 100) # 均值为0,标准差为1

# 整数随机数
np.random.randint(1, 7, 10) # 模拟掷骰子10次

# 随机选择
np.random.choice(['A', 'B', 'C'], size=5, p=[0.5, 0.3, 0.2])

# 打乱顺序
arr = np.arange(10)
np.random.shuffle(arr)

实战:销售数据分析

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

# 模拟30天的销售数据
np.random.seed(42)
daily_sales = np.random.normal(1000, 200, 30).astype(int)

print("=== 销售数据报告 ===")
print(f"总销售额: ¥{np.sum(daily_sales):,}")
print(f"日均销售: ¥{np.mean(daily_sales):.0f}")
print(f"最高单日: ¥{np.max(daily_sales)}")
print(f"最低单日: ¥{np.min(daily_sales)}")
print(f"标准差: ¥{np.std(daily_sales):.0f} (波动程度)")

# 找出销量前10%的天数
top_10_threshold = np.percentile(daily_sales, 90)
top_days = daily_sales[daily_sales >= top_10_threshold]
print(f"\n优秀天数(前10%): {len(top_days)}天")
print(f"优秀标准: ¥{top_10_threshold:.0f}以上")

# 周累计销售
weekly_sales = daily_sales.reshape(5, 6).sum(axis=1)
print(f"\n每周销售: {weekly_sales}")
print(f"最佳周: 第{np.argmax(weekly_sales)+1}周")

性能优化技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1. 使用向量化,避免循环
# 慢
result = []
for i in range(len(arr)):
result.append(arr[i] * 2)

# 快
result = arr * 2

# 2. 使用广播机制
# 标准化(减去均值除以标准差)
standardized = (arr - np.mean(arr)) / np.std(arr)

# 3. 预分配内存
result = np.zeros_like(arr) # 先创建空数组
for i in range(len(arr)):
result[i] = complex_operation(arr[i])

性能对比:NumPy vs 纯Python实现

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

# 计算100万个数的标准差
data = np.random.randn(1000000)
py_list = data.tolist()

# 纯Python实现
start = time.time()
mean_val = sum(py_list) / len(py_list)
var_val = sum((x - mean_val)**2 for x in py_list) / len(py_list)
std_val = math.sqrt(var_val)
py_time = time.time() - start

# NumPy实现
start = time.time()
np_std = np.std(data)
np_time = time.time() - start

print(f"纯Python: {py_time:.4f}秒, 结果={std_val:.6f}")
print(f"NumPy: {np_time:.4f}秒, 结果={np_std:.6f}")
print(f"NumPy快了{py_time/np_time:.0f}倍")
# 典型结果:NumPy快50-100倍

进阶用法

矩阵分解与应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# SVD分解(推荐系统、降维的核心算法)
A = np.random.randn(5, 3)
U, S, Vt = np.linalg.svd(A, full_matrices=False)
print("奇异值:", S)

# 用SVD还原
A_reconstructed = U @ np.diag(S) @ Vt
print("还原误差:", np.linalg.norm(A - A_reconstructed))

# 低秩近似(压缩数据)
k = 2 # 只保留前2个奇异值
A_low_rank = U[:, :k] @ np.diag(S[:k]) @ Vt[:k, :]
print("压缩后误差:", np.linalg.norm(A - A_low_rank))
print(f"压缩率: {A.size / (U[:,:k].size + S[:k].size + Vt[:k,:].size):.1f}倍")

多维数组的轴操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 3维数组(比如:30天 × 24小时 × 5个指标)
data = np.random.randn(30, 24, 5)

# 每天的平均指标(沿小时维度聚合)
daily_mean = data.mean(axis=1) # shape (30, 5)

# 每小时的全天平均(沿天维度聚合)
hourly_mean = data.mean(axis=0) # shape (24, 5)

# 每个指标的全局最大值
max_per_metric = data.max(axis=(0, 1)) # shape (5,)

# 找出每天表现最好的一小时
best_hour_per_day = data.mean(axis=2).argmax(axis=1) # shape (30,)

向量化实现移动平均

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 方法1:cumsum实现(超快)
def moving_avg_cumsum(arr, window):
cumsum = np.cumsum(arr)
cumsum[window:] = cumsum[window:] - cumsum[:-window]
return cumsum[window-1:] / window

# 方法2:convolve实现
def moving_avg_convolve(arr, window):
return np.convolve(arr, np.ones(window)/window, mode='valid')

# 方法3:循环实现(慢)
def moving_avg_loop(arr, window):
result = []
for i in range(len(arr) - window + 1):
result.append(arr[i:i+window].mean())
return np.array(result)

# 性能对比
data = np.random.randn(100000)
window = 20

%timeit moving_avg_cumsum(data, window) # ~200μs
%timeit moving_avg_convolve(data, window) # ~500μs
%timeit moving_avg_loop(data, window) # ~2s (慢10000倍!)

避坑指南

❌ 坑1:除以零的警告

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

# 直接除会报警告
result = 1 / arr # RuntimeWarning: divide by zero
print(result) # [1. 0.5 inf 0.25]

# 安全做法:用np.divide并处理
with np.errstate(divide='ignore', invalid='ignore'):
result = np.divide(1, arr)
result[arr == 0] = 0 # 把inf替换为0

❌ 坑2:浮点数精度问题

1
2
3
4
5
6
7
8
9
10
# 经典问题:0.1 + 0.2 != 0.3
a = np.array([0.1, 0.2])
print(a.sum() == 0.3) # False!

# 用np.isclose代替 ==
print(np.isclose(a.sum(), 0.3)) # True

# 比较两个数组是否近似相等
b = np.array([0.3])
print(np.allclose(a.sum(), b)) # True

❌ 坑3:随机数的可重复性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 不设随机种子,每次结果不同
print(np.random.rand(3)) # 每次运行不同

# 设置全局种子(影响所有后续随机操作)
np.random.seed(42)
print(np.random.rand(3)) # 每次相同

# 更好的方式:用RandomState(不影响全局)
rng = np.random.RandomState(42)
print(rng.rand(3)) # 这个rng实例是独立的

# Python 3.7+ 推荐用Generator
rng = np.random.default_rng(42)
print(rng.random(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
32
33
34
35
36
37
38
39
40
41
import numpy as np

# 模拟电商数据:10000个用户,5个特征
np.random.seed(42)
n_users = 10000

# 生成真实感的消费数据
user_id = np.arange(1, n_users + 1)
register_days = np.random.exponential(365, n_users).astype(int) # 注册天数
total_spend = np.random.exponential(2000, n_users) # 总消费
order_count = np.random.poisson(8, n_users) # 订单数
avg_order_value = total_spend / np.maximum(order_count, 1) # 客单价
last_purchase_days = np.random.exponential(30, n_users).astype(int) # 最后购买距今天数

# 1. 基本统计报告
print("=" * 50)
print("电商平台用户消费分析报告")
print("=" * 50)
print(f"总用户数: {n_users:,}")
print(f"平均消费: ¥{total_spend.mean():,.0f}")
print(f"消费中位数: ¥{np.median(total_spend):,.0f}")
print(f"消费标准差: ¥{total_spend.std():,.0f}")

# 2. 用户分层(RFM思路)
# R: 最近购买天数 < 30 = 活跃
is_active = last_purchase_days < 30
is_high_value = total_spend > np.percentile(total_spend, 80)

vip = is_active & is_high_value # 活跃且高消费
potential = is_active & ~is_high_value # 活跃但消费低
at_risk = ~is_active & is_high_value # 不活跃但曾经高消费
lost = ~is_active & ~is_high_value # 流失用户

print(f"\nVIP用户: {vip.sum()} ({vip.mean()*100:.1f}%)")
print(f"潜力用户: {potential.sum()} ({potential.mean()*100:.1f}%)")
print(f"风险用户: {at_risk.sum()} ({at_risk.mean()*100:.1f}%)")
print(f"流失用户: {lost.sum()} ({lost.mean()*100:.1f}%)")

# 3. 计算各层客单价
for name, mask in [("VIP", vip), ("潜力", potential), ("风险", at_risk), ("流失", lost)]:
print(f"{name}用户平均客单价: ¥{avg_order_value[mask].mean():.0f}")

下节预告

下一课我们将进入Pandas,这是数据分析最核心的库。

你将学会:

  • Series和DataFrame数据结构
  • 如何创建和操作表格数据

👉 继续阅读:Pandas入门-Series和DataFrame


💬 加入学习交流群

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

👉 点击加入交流群

群里不定期分享:

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

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

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

👉 点击了解详情


课程导航

上一篇: NumPy基础-数组操作

下一篇: Pandas入门-Series和DataFrame


PS:NumPy的统计和数学函数是数据分析的基础。熟练掌握,后面的学习会事半功倍。



📚 推荐教材

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

💬 联系我

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

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

🎓 AI 编程实战课程

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