在python中加速numpy / scipy中的矢量化相关函数?

时间:2012-08-13 03:58:17

标签: python numpy scipy

我编写了一个函数,它接受矩阵中列的成对关联(如pdist中的内置scipy.stats),但可以处理参数na_values指定的缺失值。即:

def my_pdist(X, dist_func, na_values=["NA"]):
    X = array(X, dtype=object)
    num_rows, num_cols = X.shape
    dist_matrix = []
    for col1 in range(num_cols):
        pdist_row = []
        for col2 in range(num_cols):
            pairs = array([[x, y] for x, y in zip(X[:, col1], X[:, col2]) \
                           if (x not in na_values) and (y not in na_values)])
            if len(pairs) == 0:
                continue
            dist = dist_func(pairs[:, 0],
                             pairs[:, 1])
            pdist_row.append(dist)
        dist_matrix.append(pdist_row)
    dist_matrix = array(dist_matrix)
    return dist_matrix

其中dist_func是指定距离度量的函数。有没有办法加快这个功能?使用它的一个例子是:

def spearman_dist(u, v, na_vals=["NA"]):
    matrix = [[x, y] for x, y in zip(u, v) \
              if (u not in na_vals) and (v not in na_vals)]
    matrix = array(matrix)
    spearman = scipy.stats.spearmanr(matrix[:, 0], matrix[:, 1])[0]
    return 1 - spearman

my_pdist(X, spearman_dist, na_values=["NA"])

如何更快地进行矢量化/制作?

2 个答案:

答案 0 :(得分:3)

我有一些建议:

  1. 不要使用类型为'object'的数组。这可以防止numpy使用它的任何内置优化,因为它被强制操作python对象而不是原始值。如果使用float数组,则可以使用np.nan而不是'NA'。对于整数数组,最好只在一个单独的数组中存储好/坏值的掩码(你也可以使用掩码数组,但我发现它们有点笨拙)。

  2. 我敢打赌,这条线占据了大部分时间:

    pairs = array([[x, y] for x, y in zip(X[:, col1], X[:, col2]) \
                       if (x not in na_values) and (y not in na_values)])
    

    所以你可以像这样加速内循环:

    x1 = X[:, col1]
    x2 = X[:, col2]
    mask = ~np.isnan(x1) * ~np.isnan(x2)
    if mask.sum() == 0:
        continue
    dist = dist_func(x1[mask], x2[mask])
    
  3. 不是使用list.append构建dist_matrix,而是从空数组开始并随时填充元素:

    dist_matrix = np.empty((num_cols, num_cols))
    for col1 in range(num_cols):
        for col2 in range(num_cols):
            ...
            dist_matrix[col1, col2] = dist
    
  4. 由于您在范围(num_cols)上迭代两次,因此您实际上计算了两次距离值。这可以优化:

    dist_matrix = np.empty((num_cols, num_cols))
    for col1 in range(num_cols):
        for col2 in range(col1, num_cols):
            ...
            dist_matrix[col1, col2] = dist
            dist_matrix[col2, col1] = dist
    
  5. 可以在没有任何for循环的情况下进行整个计算,但这取决于dist_func的细节。

答案 1 :(得分:0)

你可以尝试用numpy的Masked Arrays替换你的na_vals。