Python 中的缺失值插补
Missing value imputation in Python
我有两个巨大的向量 item_clusters 和 beta。元素item_clusters [i ]是item i所属的cluster 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,而不需要分别遍历每个集群。
我怀疑
alpha_beta = beta.multiply(alpha)
是个糟糕的主意,因为您只需要行总和的第一个元素,所以如果我没记错的话,您白白做了几百万次乘加:
sum_row = alpha_beta.sum(1)[0, 0]
因此,写下 beta * alpha 的离散公式,然后选择您需要的行并推导出其总和的公式。
我不知道这是否意味着我是这里受欢迎的孩子,但我认为您可以通过以下方式向量化您的操作:
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
请注意,以上是我手动完成的。如果您使用 higher-level 工具会容易得多,例如 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
我有两个巨大的向量 item_clusters 和 beta。元素item_clusters [i ]是item i所属的cluster 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,而不需要分别遍历每个集群。
我怀疑
alpha_beta = beta.multiply(alpha)
是个糟糕的主意,因为您只需要行总和的第一个元素,所以如果我没记错的话,您白白做了几百万次乘加:
sum_row = alpha_beta.sum(1)[0, 0]
因此,写下 beta * alpha 的离散公式,然后选择您需要的行并推导出其总和的公式。
我不知道这是否意味着我是这里受欢迎的孩子,但我认为您可以通过以下方式向量化您的操作:
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
请注意,以上是我手动完成的。如果您使用 higher-level 工具会容易得多,例如 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