【问题标题】:Missing value imputation in PythonPython中的缺失值插补
【发布时间】:2016-05-21 18:31:30
【问题描述】:

我有两个巨大的向量 item_clustersbeta。元素 item_clusters [ i ] 是项目 i 所属的集群 ID。元素 beta [ i ] 是给予项目 i 的分数。分数是 {-1, 0, 1, 2, 3}。

每当特定项目的分数为 0 时,我必须将其与属于同一集群的其他项目的平均非零分数进行估算。最快的方法是什么?

这是我迄今为止尝试过的。我将 item_clusters 转换为矩阵 clusters_to_items 使得元素 clusters_to_items [ i ][ j ] = 1 如果集群 i 包含项目 j,否则为 0。之后我运行以下代码。

# beta (1x1.3M) csr matrix
# num_clusters = 1000
# item_clusters (1x1.3M) numpy.array
# clust_to_items (1000x1.3M) csr_matrix

alpha_z = []
for clust in range(0, num_clusters):
    alpha = clust_to_items[clust, :]
    alpha_beta = beta.multiply(alpha)
    sum_row = alpha_beta.sum(1)[0, 0]
    num_nonzero = alpha_beta.nonzero()[1].__len__() + 0.001
    to_impute = sum_row / num_nonzero
    Z = np.repeat(to_impute, beta.shape[1])
    alpha_z = alpha.multiply(Z)
    idx = beta.nonzero()
    alpha_z[idx] = beta.data
interact_score = alpha_z.tolist()[0]

# The interact_score is the required modified beta 
# This is used to do some work that is very fast

问题是这段代码必须运行 150K 次,而且速度很慢。根据我的估计,运行需要 12 天。

编辑:我相信,我需要一些非常不同的想法,我可以直接使用 item_clusters,而不需要单独遍历每个集群。

【问题讨论】:

  • 好吧,我将成为这里不受欢迎的孩子:Python 是一门很棒的语言,但是如果性能和它在这里一样重要,特别是如果你正在处理大量的原始数据,这是您花费大部分时间的热点,用 C 实现它。
  • 你能找出哪一行代码花费的时间最多吗?
  • 提示:查看numpy的ufunc

标签: python-3.x numpy scipy missing-data


【解决方案1】:

我不知道这是否意味着我是这里受欢迎的孩子,但我认为您可以通过以下方式矢量化您的操作:

def fast_impute(num_clusters, item_clusters, beta):

    # get counts
    cluster_counts = np.zeros(num_clusters)
    np.add.at(cluster_counts, item_clusters, 1)

    # get complete totals
    totals = np.zeros(num_clusters)
    np.add.at(totals, item_clusters, beta)

    # get number of zeros
    zero_counts = np.zeros(num_clusters)
    z = beta == 0
    np.add.at(zero_counts, item_clusters, z)

    # non-zero means
    cluster_means = totals / (cluster_counts - zero_counts)

    # perform imputations
    imputed_beta = np.where(beta != 0, beta, cluster_means[item_clusters])

    return imputed_beta

这给了我

>>> N = 10**6
>>> num_clusters = 1000
>>> item_clusters = np.random.randint(0, num_clusters, N)
>>> beta = np.random.choice([-1, 0, 1, 2, 3], size=len(item_clusters))
>>> %time imputed = fast_impute(num_clusters, item_clusters, beta)
CPU times: user 652 ms, sys: 28 ms, total: 680 ms
Wall time: 679 ms

>>> imputed[:5]
array([ 1.27582017, -1.        , -1.        ,  1.        ,  3.        ])
>>> item_clusters[:5]
array([506, 968, 873, 179, 269])
>>> np.mean([b for b, i in zip(beta, item_clusters) if i == 506 and b != 0])
1.2758201701093561

请注意,我是手动完成上述操作的。如果您使用更高级别的工具会容易得多,比如pandas提供的那些:

>>> df = pd.DataFrame({"beta": beta, "cluster": item_clusters})
>>> df.head()
   beta  cluster
0     0      506
1    -1      968
2    -1      873
3     1      179
4     3      269
>>> df["beta"] = df["beta"].replace(0, np.nan)
>>> df["beta"] = df["beta"].fillna(df["beta"].groupby(df["cluster"]).transform("mean"))
>>> df.head()
      beta  cluster
0  1.27582      506
1 -1.00000      968
2 -1.00000      873
3  1.00000      179
4  3.00000      269

【讨论】:

    【解决方案2】:

    我的怀疑是

    alpha_beta = beta.multiply(alpha)
    

    是一个糟糕的主意,因为你只需要行和的第一个元素,所以如果我没记错的话,你会徒劳地做几百万次乘加:

    sum_row = alpha_beta.sum(1)[0, 0]
    

    所以,写下 beta * alpha 的离散公式,然后选择您需要的行并推导出其总和的公式。

    【讨论】:

    • 这个想法是 alpha 和 beta 都是向量。只有当它们属于集群集群时,我才需要获取 beta 元素的总和(即 alpha 值为 1)。这就是为什么我将 alpha 和 beta 元素相乘。 alpha_beta sum 是一个元素的矩阵。要获得实际的浮点值,我必须明确提及 [0][0]。
    • @SonuKMishra 提示:sum(element-wise product) 可以一步计算,即点积
    • 但我不需要总和。我需要平均值,我也需要这些元素的数量。
    • 另外,我还使用了一个乘法,所以去掉一个最多可以给我 2 倍的加速。我需要的远不止这些。请查看我在问题中提出的编辑说明。我相信,如果我能以某种方式摆脱 for 循环,那就太棒了!
    猜你喜欢
    • 2018-05-21
    • 2018-08-14
    • 2013-07-08
    • 1970-01-01
    • 2020-02-05
    • 1970-01-01
    • 2017-05-04
    • 2021-05-21
    相关资源
    最近更新 更多