处理MovieLens大矩阵时,如何用Pandas高效计算皮尔逊相关?
基于Pandas/Numpy解决大规模用户-物品矩阵皮尔逊相关系数内存问题
针对MovieLens 10M数据集(69878个用户、10677个物品)计算用户间皮尔逊相关系数时的内存不足问题,以下是几个基于Pandas和Numpy的通用解决方案:
方案1:分块计算+磁盘存储(避免全相关矩阵驻留内存)
直接生成全量用户相关矩阵(69878×69878)需要约37GB内存,远超16GB限制。分块计算将用户矩阵拆分成小批次,分别计算块内、块间的相关系数并写入磁盘,仅保留当前计算的块数据在内存中。
代码实现
import pandas as pd import numpy as np # 假设user_item_matrix是你的用户-物品矩阵(行=用户,列=物品) chunk_size = 1000 # 可根据内存调整,16GB内存可设为1500-2000 user_chunks = [user_item_matrix[i:i+chunk_size] for i in range(0, len(user_item_matrix), chunk_size)] # 计算并保存块内用户相关系数 for chunk_idx, chunk in enumerate(user_chunks): chunk_corr = chunk.corr(method='pearson') chunk_corr.to_hdf(f'user_corr_chunk_{chunk_idx}.h5', key='corr', mode='w') # 计算并保存跨块用户相关系数 for i in range(len(user_chunks)): for j in range(i+1, len(user_chunks)): # 用numpy计算两个块的全相关矩阵,提取交叉部分 full_corr = np.corrcoef(user_chunks[i].values, user_chunks[j].values) # 截取块j用户与块i用户的相关系数矩阵 cross_corr = full_corr[len(user_chunks[i]):, :len(user_chunks[i])] # 转换为带索引的DataFrame并保存 cross_corr_df = pd.DataFrame( cross_corr, index=user_chunks[j].index, columns=user_chunks[i].index ) cross_corr_df.to_hdf(f'user_corr_cross_{i}_{j}.h5', key='corr', mode='w')
后续使用
推荐时,只需读取目标用户所在块的相关系数文件,以及该块与其他块的交叉相关文件,无需加载全量数据。
方案2:按需计算单用户相关系数(避免全矩阵生成)
实际推荐场景中,通常不需要所有用户的两两相关系数,只需针对目标用户计算其与其他用户的相关性。手动实现皮尔逊系数计算,仅处理共同评分的物品,大幅降低内存占用。
代码实现
import pandas as pd import numpy as np # 预处理:提前计算每个用户的评分均值、方差和标准差 user_means = user_item_matrix.mean(axis=1) user_sq_dev = ((user_item_matrix - user_means[:, np.newaxis]) ** 2).sum(axis=1) user_stds = np.sqrt(user_sq_dev) # 定义单对用户皮尔逊系数计算函数 def calc_pearson(x_vals, y_vals, x_mean, y_mean, x_std, y_std): # 过滤出两个用户都有评分的物品 common_mask = ~np.isnan(x_vals) & ~np.isnan(y_vals) if common_mask.sum() < 2: # 至少2个共同评分才有统计意义 return np.nan # 计算标准化后的协方差 x_centered = x_vals[common_mask] - x_mean y_centered = y_vals[common_mask] - y_mean covariance = np.sum(x_centered * y_centered) return covariance / (x_std * y_std) # 示例:计算目标用户与其他所有用户的相关系数 target_user_id = user_item_matrix.index[0] target_vals = user_item_matrix.loc[target_user_id].values target_mean = user_means[target_user_id] target_std = user_stds[target_user_id] corr_results = [] for user_id in user_item_matrix.index[1:]: curr_vals = user_item_matrix.loc[user_id].values curr_mean = user_means[user_id] curr_std = user_stds[user_id] corr = calc_pearson(target_vals, curr_vals, target_mean, curr_mean, target_std, curr_std) corr_results.append((user_id, corr)) # 整理为DataFrame,过滤无意义的NaN值 target_corr_df = pd.DataFrame(corr_results, columns=['user_id', 'correlation']).dropna()
优势
- 仅在需要推荐时计算目标用户的相关系数,内存占用仅为单个用户的评分数据+临时变量
- 跳过无共同评分的用户对,减少无效计算
关键说明
- 关于
float16的尝试:Pandas的corr()方法内部会强制转换为float64计算,因为皮尔逊系数对精度有要求,所以类型压缩无法解决核心内存问题。 - 稀疏矩阵补充:如果允许引入Scipy,可将用户-物品矩阵转换为稀疏矩阵进一步降低内存占用,但上述方案已完全基于Pandas/Numpy实现。
内容的提问来源于stack exchange,提问作者Can Demir
相关产品推荐
相关产品推荐

