关键词:数组相似、欧氏距离、z-score、Python 模板、异常值鲁棒
0. 场景回顾
假设你有 k 份长度为 n 的浮点数组:
a[1], a[2], …, a[k] # 每个 a[i] 为一个数组,长度为 n,即a[i][1...n]
你想一句话回答:
“这 k 条曲线,整体看起来到底有多像?”
本文给出一条完整路径
1. 先把“相似”拆成两层
通常先算形状,再按需要融合幅度。
2. 三步算出总体相似度
Step 1 统一尺度(可选但强烈建议)
对每个数组做 z-score 标准化
μ_i = mean(a[i])
σ_i = std(a[i])
a_norm[i] = (a[i] - μ_i) / σ_i
σ_i ≈ 0 时直接设 0,防止除零。
Step 2 两两距离矩阵
选一个距离:
- 欧氏距离(L2):最直观
- 曼哈顿距离(L1):更抗异常
- 余弦距离:只看形状
复杂度 O(k²·n)。k 很大时用近似最近邻 (Faiss/Annoy)。
Step 3 压成一个相似度
把距离矩阵 D 变成 0~1 的标量:
similarity = 1 / (1 + mean(D)) # 距离越小越接近 1
如需更严格,可用 max(D)
而非 mean(D)
。
3. 完整 Python 代码(可直接跑)
import numpy as np
from scipy.spatial.distance import pdist, squareform
def overall_similarity(arrays, mode='shape', metric='euclidean'):
"""
arrays: list or 2-D array of shape (k, n)
mode : 'shape' | 'magnitude' | 'both'
metric: 'euclidean', 'cosine', 'cityblock', ...
return: similarity in [0, 1]
"""
arrays = np.asarray(arrays, dtype=float)
k, n = arrays.shape
# 标准化
if mode in ('shape', 'both'):
mu = arrays.mean(axis=1, keepdims=True)
sigma = arrays.std(axis=1, keepdims=True)
sigma[sigma == 0] = 1
z = (arrays - mu) / sigma
else:
z = arrays
# 距离矩阵
dists = squareform(pdist(z, metric=metric))
mask = ~np.eye(k, dtype=bool)
avg_dist = dists[mask].mean()
sim_shape = 1 / (1 + avg_dist)
if mode == 'both':
dist_raw = squareform(pdist(arrays, metric=metric))
sim_raw = 1 / (1 + dist_raw[mask].mean())
sim_shape = (sim_shape + sim_raw) / 2 # 可改权重
return sim_shape
# Demo
if name == "__main__":
k, n = 10, 200
arrays = [np.sin(np.linspace(0, 2*np.pi, n)) + 0.1*np.random.randn(n) for _ in range(k)]
print("总体相似度:", overall_similarity(arrays, mode='shape'))
new_arrays = [arrays[0], arrays[0] + 0.1]
print("总体相似度:", overall_similarity(new_arrays, mode='shape'))
print("总体相似度:", overall_similarity(new_arrays, mode='magnitude'))
print("总体相似度:", overall_similarity(new_arrays, mode='both'))
4. 常见坑与对策清单
5. 一句话总结
先标准化,再算两两距离,最后用 1/(1+mean(D))
把“像不像”变成 0~1 分,就这么简单。
其他计算相似度文章:
评论区