侧边栏壁纸
  • 累计撰写 18 篇文章
  • 累计创建 1 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

如何判断 k 个长度相同的数组是否“总体相似”?

詹迪佳
2025-07-17 / 0 评论 / 0 点赞 / 3 阅读 / 22586 字

关键词:数组相似、欧氏距离、z-score、Python 模板、异常值鲁棒

0. 场景回顾

假设你有 k 份长度为 n 的浮点数组:

a[1], a[2], …, a[k]   # 每个 a[i] 为一个数组,长度为 n,即a[i][1...n]

你想一句话回答:

“这 k 条曲线,整体看起来到底有多像?”

本文给出一条完整路径

1. 先把“相似”拆成两层

维度

关心什么?

例子

处理方式

形状 (Shape)

曲线走势

心电图波形

z-score 标准化后比较

幅度 (Magnitude)

绝对数值

温度数值大小

直接用原始值比较

通常先算形状,再按需要融合幅度。

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. 常见坑与对策清单

问题

快速解法

n 很大(>1e4)

PCA 先降到 50 维

k 很大(>1e4)

MiniBatch KMeans → 算簇间相似

异常值

用 MAD 代替 std,或 Spearman 相关系数

时间序列相位偏移

把欧氏距离换成 DTW

5. 一句话总结

先标准化,再算两两距离,最后用 1/(1+mean(D)) 把“像不像”变成 0~1 分,就这么简单。

其他计算相似度文章:

https://blog.51cto.com/u_16213586/8248791

0

评论区