如何在R中基于分组从不同DataFrame取值实现数据归一化?
问题描述
我有如下结构的DataFrame:
Col1 = c("A1", "A1", "A2", "A2") Col2 = c("B1", "B1", "B2", "B2") Value = c(10, 20, 30, 40) df = data.frame(Col1, Col2, Value)
该DataFrame包含多组观测数据,有两个因子列(Col1、Col2)和一个数值列(Value),同一分组对应多行不同数值,且我有多个类似结构的DataFrame。
另外还有两个存储所有分组(Col1和Col2)全局最小、最大值的DataFrame:
MinCol1 = c("A1", "A2") MinCol2 = c("B1", "B2") MinValue = c(1, 1) mins = data.frame(MinCol1, MinCol2, MinValue) MaxCol1 = c("A1", "A2") MaxCol2 = c("B1", "B2") MaxValue = c(100, 100) maxes = data.frame(MaxCol1, MaxCol2, MaxValue)
我需要对这类DataFrame按分组进行数值归一化,将Value映射到[0,1]区间,且归一化的取值范围要从上述mins和maxes中获取对应分组的最小值和最大值。
我写了如下函数,但不确定range(...)里该填什么,才能实现按分组从mins和maxes中查找对应范围:
normalizeDataForAllBenchmarks = function(df, mins, maxes) { ### Normalize metrics [0,1] df_normal = df %>% group_by(Process, Category, Metric) %>% mutate(Value = rescale(Value, to = c(0,1), from = range(...))) return(df_normal) }
解决方案
注:你函数中的分组列Process, Category, Metric与示例中的Col1, Col2对应,以下示例将用Col1和Col2作为分组列,你可根据实际场景替换为自己的分组列名。
方法一:合并全局最值后计算归一化
这种方法逻辑直观,先合并全局最值表,再与目标DataFrame关联,最后直接用对应分组的全局极值计算归一化:
library(dplyr) library(scales) normalizeDataForAllBenchmarks = function(df, mins, maxes) { # 重命名最值表列名,方便后续合并 mins_renamed = mins %>% rename(Col1 = MinCol1, Col2 = MinCol2, global_min = MinValue) maxes_renamed = maxes %>% rename(Col1 = MaxCol1, Col2 = MaxCol2, global_max = MaxValue) # 合并全局最小值和最大值表 global_ranges = mins_renamed %>% inner_join(maxes_renamed, by = c("Col1", "Col2")) # 关联目标表并计算归一化,最后移除临时列 df_normal = df %>% inner_join(global_ranges, by = c("Col1", "Col2")) %>% mutate(Value = rescale(Value, to = c(0,1), from = c(global_min, global_max))) %>% select(-global_min, -global_max) return(df_normal) }
方法二:分组内匹配全局最值
如果想保留group_by的写法,可在分组内根据当前分组的标识,从最值表中提取对应极值:
library(dplyr) library(scales) normalizeDataForAllBenchmarks = function(df, mins, maxes) { df_normal = df %>% group_by(Col1, Col2) %>% # 替换为你的实际分组列:Process, Category, Metric mutate( # 提取当前分组的全局最小值 global_min = mins$MinValue[mins$MinCol1 == first(Col1) & mins$MinCol2 == first(Col2)], # 提取当前分组的全局最大值 global_max = maxes$MaxValue[maxes$MaxCol1 == first(Col1) & maxes$MaxCol2 == first(Col2)], # 计算归一化值 Value = rescale(Value, to = c(0,1), from = c(global_min, global_max)) ) %>% select(-global_min, -global_max) %>% ungroup() return(df_normal) }
测试验证
用你提供的示例数据调用函数:
result = normalizeDataForAllBenchmarks(df, mins, maxes) print(result)
输出结果符合预期:
Col1 Col2 Value 1 A1 B1 0.09090909 2 A1 B1 0.19191919 3 A2 B2 0.29292929 4 A2 B2 0.39393939
内容的提问来源于stack exchange,提问作者vic
相关产品推荐
相关产品推荐

