Python 数据分析:学生画像匹配与相似度计算
适合人群:Python 初学者 / 数据分析入门 / 推荐系统基础学习者 / 教学案例分享
在数据分析和机器学习中,我们经常会遇到这样的问题:
- 如何判断两个学生的学习习惯是否相似?
- 如何衡量两个商品是不是"同类竞品"?
- 为什么推荐系统能给你推送"你可能喜欢"的内容?
- 两段文本内容相似,应该怎么用数据来表示?
这些问题,归根到底,都指向一个核心概念:
相似性度量
本文将通过"学生画像匹配"和"课程评价文本分析"两个小案例,带你理解下面几个非常常用的概念:
- 欧氏距离(Euclidean Distance)
- 曼哈顿距离(Manhattan Distance)
- 余弦相似度(Cosine Similarity)
并结合 Python 完成简单实战。
一、案例引入:谁和你最像?
假设我们想根据学生的学习数据,寻找"和你最相似的同学"。
比如现在有三位学生的成绩数据:
| 学生 | 数学 | 英语 |
|---|---|---|
| A | 80 | 85 |
| B | 82 | 88 |
| C | 60 | 70 |
问题来了:
- A 和 B 谁更像?
- A 和 C 谁更像?
- 我们能不能不用"感觉",而是用"计算"来判断?
答案是可以的。
在数据世界里,"相似"是可以被量化的 。
一种最直接的想法就是:
谁和 A 的"距离"更近,谁就更相似。
二、什么是相似性?什么是距离?
在数据分析里,经常会把"相似性"和"距离"放在一起讲。
你可以简单理解为:
- 距离越小,两个对象越相似
- 距离越大,两个对象差异越大
这种思路在很多应用里都非常常见,比如:
- 推荐系统
- 用户画像匹配
- 聚类分析
- 离群点分析
数据对象的相似性度量正是这些分析任务的重要基础[1]。
三、欧氏距离:最常见的"直线距离"
欧氏距离,就是我们在几何中最熟悉的"两点之间的直线距离"[1]。
如果两个学生用两个特征表示:
- 数学成绩
- 英语成绩
那么就可以把每个学生看成二维平面上的一个点。
例如:
- A = (80, 85)
- B = (82, 88)
- C = (60, 70)
欧氏距离公式
对于两个点:
python
A = (x1, y1)
B = (x2, y2)
欧氏距离为:
python
d = sqrt((x2 - x1)^2 + (y2 - y1)^2)
如果扩展到多个特征,也是同样思路:各维度差值平方后求和,再开方[1]。
四、手工算一遍:A 和谁更相似?
1)A 和 B 的欧氏距离
python
A = (80, 85)
B = (82, 88)
计算过程:
python
d(A,B) = sqrt((82-80)^2 + (88-85)^2)
= sqrt(2^2 + 3^2)
= sqrt(4 + 9)
= sqrt(13)
≈ 3.61
2)A 和 C 的欧氏距离
python
A = (80, 85)
C = (60, 70)
计算过程:
python
d(A,C) = sqrt((60-80)^2 + (70-85)^2)
= sqrt((-20)^2 + (-15)^2)
= sqrt(400 + 225)
= sqrt(625)
= 25
3)结论
因为:
python
d(A,B) = 3.61 < d(A,C) = 25
所以:
学生 B 与学生 A 更相似。
这就是"距离越小,相似度越高"的最直观体现。
五、用 Python 计算欧氏距离
下面我们用 Python 实现刚才的计算。
python
import math
A = (80, 85)
B = (82, 88)
C = (60, 70)
def euclidean_distance(p1, p2):
return math.sqrt((p1[0] - p2[0])**2 + (p1[1] - p2[1])**2)
dist_ab = euclidean_distance(A, B)
dist_ac = euclidean_distance(A, C)
print("A与B的欧氏距离:", dist_ab)
print("A与C的欧氏距离:", dist_ac)
输出结果:
python
A与B的欧氏距离: 3.605551275463989
A与C的欧氏距离: 25.0
如果你想让结果更美观一点,可以保留两位小数:
python
print("A与B的欧氏距离:{:.2f}".format(dist_ab))
print("A与C的欧氏距离:{:.2f}".format(dist_ac))
六、曼哈顿距离:不是走直线,而是"走格子"
除了欧氏距离,还有一种很常见的距离叫 曼哈顿距离[1]。
它的名字来源于美国曼哈顿的街区布局:
如果你只能沿着街道走,而不能斜着穿过去,那么你的路径就不是直线,而是"横着走 + 竖着走"。
曼哈顿距离公式
python
d = |x2 - x1| + |y2 - y1|
例子:A 和 B 的曼哈顿距离
python
A = (80, 85)
B = (82, 88)
d(A,B) = |82-80| + |88-85|
= 2 + 3
= 5
Python 实现
python
def manhattan_distance(p1, p2):
return abs(p1[0] - p2[0]) + abs(p1[1] - p2[1])
dist_ab_manhattan = manhattan_distance(A, B)
dist_ac_manhattan = manhattan_distance(A, C)
print("A与B的曼哈顿距离:", dist_ab_manhattan)
print("A与C的曼哈顿距离:", dist_ac_manhattan)
如何理解?
- 欧氏距离:看"直线有多远"
- 曼哈顿距离:看"按坐标轴走要走多远"
两者都能衡量差异,只是方式不同。
七、余弦相似度:比较的不是远近,而是方向
前面的欧氏距离和曼哈顿距离,主要适合数值型特征。
但如果面对的是文本数据,比如两段课程评价、两篇文章、两个商品描述,该怎么办?
这时候,一个很常用的方法就是:
余弦相似度(Cosine Similarity)
余弦相似性常用于文档数据的相似度测量,一般通过关键词向量来表示文档特征[1]。
八、为什么文本可以变成向量?
举个例子,假设我们有两段课程评价:
评价1
"老师讲课清晰,案例很多,课堂有趣"
评价2
"讲课清晰,案例丰富,课堂生动"
我们可以先提取关键词,比如:
- 讲课
- 清晰
- 案例
- 课堂
- 有趣
- 丰富
- 生动
然后统计每个词出现的次数,把它变成一个向量。
例如:
python
评价1 = [1, 1, 1, 1, 1, 0, 0]
评价2 = [1, 1, 1, 1, 0, 1, 1]
这样,文本相似度问题就变成了"两个向量是否相似"的问题。
九、余弦相似度公式
余弦相似度的核心思想是:
比较两个向量的方向是否接近,而不是比较长度是否相同。
公式如下:
python
cos(theta) = (A · B) / (||A|| * ||B||)
你可以简单理解为:
- 结果越接近
1:越相似 - 结果越接近
0:越不相似 - 结果越接近
-1:方向相反
在文本分析场景里,我们通常最关注:
余弦相似度越接近 1,文本内容越相似[1]。
十、用 Python 计算余弦相似度
python
import numpy as np
vec1 = np.array([1, 1, 1, 1, 1, 0, 0])
vec2 = np.array([1, 1, 1, 1, 0, 1, 1])
cos_sim = np.dot(vec1, vec2) / (np.linalg.norm(vec1) * np.linalg.norm(vec2))
print("余弦相似度:", cos_sim)
如果结果接近 1,说明两段评价内容比较相似。
十一、完整代码:欧氏距离 + 曼哈顿距离 + 余弦相似度
下面给出一份完整代码,复制即可运行。
python
import math
import numpy as np
# ======================
# 1. 欧氏距离与曼哈顿距离
# ======================
A = (80, 85)
B = (82, 88)
C = (60, 70)
def euclidean_distance(p1, p2):
return math.sqrt(sum((a - b) ** 2 for a, b in zip(p1, p2)))
def manhattan_distance(p1, p2):
return sum(abs(a - b) for a, b in zip(p1, p2))
print("=== 数值数据相似性分析 ===")
print("A与B的欧氏距离:{:.2f}".format(euclidean_distance(A, B)))
print("A与C的欧氏距离:{:.2f}".format(euclidean_distance(A, C)))
print("A与B的曼哈顿距离:", manhattan_distance(A, B))
print("A与C的曼哈顿距离:", manhattan_distance(A, C))
# ======================
# 2. 余弦相似度
# ======================
vec1 = np.array([1, 1, 1, 1, 1, 0, 0])
vec2 = np.array([1, 1, 1, 1, 0, 1, 1])
cos_sim = np.dot(vec1, vec2) / (np.linalg.norm(vec1) * np.linalg.norm(vec2))
print("\n=== 文本相似性分析 ===")
print("两段评价的余弦相似度:{:.4f}".format(cos_sim))
十二、这三种方法分别适合什么场景?
1. 欧氏距离
适合:
- 学生成绩比较
- 用户数值特征匹配
- 商品数值属性比较
特点:
- 最直观
- 最常用
- 适合连续数值型数据[1]
2. 曼哈顿距离
适合:
- 网格路径问题
- 某些高维特征场景
- 与欧氏距离做对比分析
特点:
- 强调各维度差值之和
- 对坐标轴变化更敏感[1]
3. 余弦相似度
适合:
- 文本相似度分析
- 推荐系统
- 关键词向量比较
特点:
- 比较方向
- 不强调绝对大小
- 文档数据很常用[1]
十三、最容易踩的坑
坑1:把"相似度"和"距离"混为一谈
要记住:
- 距离越小,相似度越高
- 相似度越大,对象越接近
两者方向相反,但本质都在衡量"像不像"。
坑2:以为余弦相似度比较的是"距离远近"
不是。
余弦相似度比较的是:
方向是否一致
即使两个向量长度不一样,只要方向接近,余弦相似度也可能很高。
坑3:所有数据都直接算欧氏距离
不一定合适。
如果是文本数据,欧氏距离通常不是首选,余弦相似度更常见[1]。
坑4:忽略特征维度的含义
比如学生画像中,如果一个特征是"成绩",另一个特征是"消费金额",量纲差异很大,直接计算距离可能不合理。
实际应用中,往往还需要做标准化处理。
十四、这部分知识有什么实际用途?
相似性度量在很多数据分析任务中都非常重要,例如:
- 聚类分析
- 推荐系统
- 用户画像匹配
- 文本相似度分析
- 离群点分析
数据对象的相似性度量正是这些应用的重要基础[1]。
也就是说:
只要你需要比较"两个对象像不像",相似性度量就一定用得上。
十五、给初学者的记忆口诀
这部分内容你可以先记住这 4 句话:
- 欧氏距离:看直线距离,越小越相似[1]。
- 曼哈顿距离:看网格路径距离。
- 余弦相似度:看方向相似,越接近 1 越相似[1]。
- 数值数据常看距离,文本数据常看余弦相似度[1]。
十六、课后练习
练习 1:基础题
已知三位学生成绩如下:
| 学生 | 数学 | 英语 |
|---|---|---|
| A | 75 | 80 |
| B | 78 | 82 |
| C | 60 | 65 |
请完成:
- 计算 A 与 B 的欧氏距离
- 计算 A 与 C 的欧氏距离
- 判断谁与 A 更相似
练习 2:提高题
请继续完成:
- 计算 A 与 B 的曼哈顿距离
- 计算 A 与 C 的曼哈顿距离
- 对比欧氏距离和曼哈顿距离的结果,有什么共同点?
练习 3:迁移题
假设两段课程评价转成关键词向量后如下:
python
vec1 = [1, 1, 0, 1, 0]
vec2 = [1, 0, 1, 1, 0]
请完成:
- 使用 Python 计算余弦相似度
- 判断两段评价是否相似
- 思考:为什么文本分析中常使用余弦相似度,而不是欧氏距离?
十七、总结
这篇文章主要解决了一个非常核心的问题:
在数据世界里,如何判断两个对象是否相似?
我们通过"学生画像匹配"和"课程评价文本分析"两个案例,学习了:
- 欧氏距离
- 曼哈顿距离
- 余弦相似度
其中最重要的结论是:
- 数值特征之间,常用距离来衡量差异
- 文本特征之间,常用余弦相似度来衡量方向相似性
- 相似性度量是推荐系统、聚类分析和文本分析的重要基础[1]
十八、写在最后
如果这篇文章对你有帮助,欢迎点赞、收藏、评论支持一下。
如果你也在学习 Python 数据分析,建议把欧氏距离、曼哈顿距离和余弦相似度这三个概念先彻底搞懂,因为它们在后续学习中会反复出现。
你在学习"相似性计算"时,最容易混淆的是哪个概念?
欢迎在评论区交流。