获取行内最低值列名时忽略NA值的R语言技术问题
问题:获取对应最低价的零售商名称(忽略NA值)
现有零售商品价格数据,行对应商品,列对应零售商,部分零售商不售卖部分商品(NA值),已通过dplyr生成cheapestPRICE列,但生成cheapestSTORE列时遇到两个问题:
- 执行以下代码时报错:
Error in FUN(left) : invalid argument to unary operator,推测因包含itemName字符列导致:
retailPRICE$cheapestSTORE <- names(retailPRICE)[minCol(replace(retailPRICE, is.na(retailPRICE), 0), ties.method = "first")]
- 未报错时,
minCol会将NA识别为最低值,导致商店匹配错误,错误输出如下:
| itemName | Aldi | Walmart | Costco | cheapestPRICE | cheapestSTORE |
|---|---|---|---|---|---|
| Apples | NA | 0.75 | NA | 0.75 | Aldi |
| Bananas | 0.55 | 0.69 | NA | 0.55 | Costco |
| Milk | 1.95 | 2.15 | 0.16 | 0.16 | Costco |
| Eggs | 157.00 | 192.00 | 75.00 | 75.00 | Costco |
| Cereal | 1.10 | 1.10 | NA | 1.10 | Costco |
期望得到正确的cheapestSTORE列,结果如下:
| itemName | Aldi | Walmart | Costco | cheapestPRICE | cheapestSTORE |
|---|---|---|---|---|---|
| Apples | NA | 0.75 | NA | 0.75 | Walmart |
| Bananas | 0.55 | 0.69 | NA | 0.55 | Aldi |
| Milk | 1.95 | 2.15 | 0.16 | 0.16 | Costco |
| Eggs | 157.00 | 192.00 | 75.00 | 75.00 | Costco |
| Cereal | 1.10 | 1.10 | NA | 1.10 | Aldi |
解决方案
方法1:dplyr + apply逐行处理
直接针对价格列逐行计算,忽略NA值并处理价格相同的情况(取第一个出现的零售商):
library(dplyr) # 原始数据 itemName <- c('Apples', 'Bananas', 'Milk', 'Eggs', 'Cereal') Aldi <- c(NA, 0.55, 1.95, 157.00, 1.10) Walmart <- c(0.75, 0.69, 2.15, 192.00, 1.10) Costco <- c(NA, NA, 0.16, 75.00, NA) retailPRICE <- data.frame(itemName, Aldi, Walmart, Costco) %>% mutate(cheapestPRICE = pmin(Aldi, Walmart, Costco, na.rm = TRUE), # 仅处理价格列,避免字符列干扰 cheapestSTORE = apply(select(., Aldi, Walmart, Costco), 1, function(row) { # 找到等于该行最低价的第一个零售商名称(自动忽略NA) names(row)[which(row == min(row, na.rm = TRUE))[1]] }))
方法2:tidyr重塑数据(逻辑更清晰)
通过将宽表转为长表,更直观地筛选每个商品的最低价零售商:
library(dplyr) library(tidyr) # 原始数据 itemName <- c('Apples', 'Bananas', 'Milk', 'Eggs', 'Cereal') Aldi <- c(NA, 0.55, 1.95, 157.00, 1.10) Walmart <- c(0.75, 0.69, 2.15, 192.00, 1.10) Costco <- c(NA, NA, 0.16, 75.00, NA) retailPRICE <- data.frame(itemName, Aldi, Walmart, Costco) %>% # 先计算最低价 mutate(cheapestPRICE = pmin(Aldi, Walmart, Costco, na.rm = TRUE)) %>% # 转长表:每个商品-零售商组合为一行 pivot_longer(cols = c(Aldi, Walmart, Costco), names_to = "store", values_to = "price") %>% # 按商品分组,筛选价格等于最低价的行,取第一个(处理价格相同的情况) group_by(itemName) %>% filter(price == cheapestPRICE) %>% slice(1) %>% # 提取商店名称,合并回原表 select(itemName, cheapestSTORE = store) %>% right_join(retailPRICE, by = "itemName") %>% # 调整列顺序与期望一致 select(itemName, Aldi, Walmart, Costco, cheapestPRICE, cheapestSTORE)
内容的提问来源于stack exchange,提问作者Amanda Ballenger
相关产品推荐
相关产品推荐

