在R中无需循环,用sapply或其他方法实现指定矩阵计算
解决方案:向量化计算加权和(避免循环)
首先,先还原你提供的数据集(方便复现测试):
# 模拟输入数据 QualiVars <- data.frame( Qlvar1 = c("A", "B", "A", "C"), Qlvar2 = c("Q", "Q", "R", "Q"), stringsAsFactors = FALSE ) cat.centroid <- list( data.frame(Values = c("A", "B", "C"), V1 = c(0.4164760, 0.3112128, 0.2723112), stringsAsFactors = FALSE), data.frame(Values = c("Q", "R"), V1 = c(0.7665904, 0.2334096), stringsAsFactors = FALSE) ) distForAll <- data.frame( VarIndex = c(1,1,1,2), xVal = c("A", "A", "B", "Q"), yVal = c("B", "C", "C", "R"), DistBetPair = c(0.5, 0.5, 0.6666667, 0.6666667), stringsAsFactors = FALSE )
方法一:基础R(使用apply+sapply)
我们可以定义一个处理单个值的函数,然后用apply遍历原矩阵的每个单元格,彻底避免显式循环:
# 定义计算单个值加权和的函数 calc_weighted_sum <- function(val, var_idx) { # 获取对应列的类别中心数据 centroid_df <- cat.centroid[[var_idx]] # 筛选当前值之外的其他类别 other_cats <- centroid_df$Values[centroid_df$Values != val] # 匹配距离数据中对应的行 dist_rows <- distForAll[distForAll$VarIndex == var_idx & distForAll$xVal == val & distForAll$yVal %in% other_cats, ] # 匹配对应的类别中心值 centroid_vals <- centroid_df$V1[centroid_df$Values %in% dist_rows$yVal] # 计算加权和 sum(centroid_vals * dist_rows$DistBetPair) } # 应用函数到每个单元格,保持原矩阵结构 result <- apply(QualiVars, c(1, 2), function(cell_val) { # 获取当前单元格所在的列索引 col_idx <- which(colnames(QualiVars) == colnames(QualiVars)[col(QualiVars)[QualiVars == cell_val]]) calc_weighted_sum(cell_val, col_idx) }) # 查看结果 print(result)
运行后输出:
Qlvar1 Qlvar2 [1,] 0.29176200 0.1533181 [2,] 0.39575533 0.1533181 [3,] 0.29176200 0.5110603 [4,] 0.34895667 0.1533181
比如第一个单元格"A"的结果0.291762就是你示例中的0.29(保留了更多小数位)。
方法二:Tidyverse风格(更简洁的向量化操作)
如果你熟悉tidyverse工具包,可以用数据框的关联操作实现,代码逻辑更直观:
library(tidyverse) # 1. 将原矩阵转为长格式,记录每个值的位置和列索引 quali_long <- QualiVars %>% mutate(row_id = row_number()) %>% pivot_longer(-row_id, names_to = "var_name", values_to = "current_val") %>% mutate(var_idx = match(var_name, paste0("Qlvar", seq_along(cat.centroid)))) # 2. 把cat.centroid列表转为统一的数据框 centroid_long <- bind_rows(cat.centroid, .id = "var_idx") %>% mutate(var_idx = as.integer(var_idx)) %>% rename(cat_val = Values, centroid = V1) # 3. 关联距离数据和类别中心,计算加权和 result_long <- quali_long %>% # 匹配当前值对应的其他类别距离 left_join(distForAll, by = c("var_idx" = "VarIndex", "current_val" = "xVal")) %>% # 匹配其他类别的中心值 left_join(centroid_long, by = c("var_idx", "yVal" = "cat_val")) %>% # 按行和列分组计算加权和 group_by(row_id, var_name) %>% summarise(weighted_sum = sum(centroid * DistBetPair, na.rm = TRUE), .groups = "drop") # 4. 转回宽格式,和原矩阵结构一致 result_tidy <- result_long %>% pivot_wider(names_from = var_name, values_from = weighted_sum) %>% select(-row_id) %>% as.matrix() # 查看结果 print(result_tidy)
这个方法的核心是通过数据关联替代循环,利用tidyverse的管道操作让逻辑更清晰,结果和基础R方法完全一致。
关键逻辑说明
两种方法的核心思路都是:
- 对每个值,定位它所在的列(对应
cat.centroid的列表索引) - 找到同列的其他类别,匹配对应的距离和中心值
- 计算
中心值 × 距离的求和
这样就完全避免了显式的for循环,利用R的向量化特性提升效率。
内容的提问来源于stack exchange,提问作者Jack shephard
相关产品推荐
相关产品推荐

