假设我将超市水果库调查结果存储在数据框中:
stock <- data.frame(
store = c("Asda", "Booths", "Co-op"),
apple = c(1, 0, 0),
banana = c(1, 1, 0),
coconut = c(0, 0, 0)
)
看起来像
store apple banana coconut
1 Asda 1 1 0
2 Booths 0 1 0
3 Co-op 0 0 0
我的目标:
我想将上面的二元调查结果列转换为每个超市的股票摘要字符向量,如下所示:
store fruits
1 Asda apple, banana
2 Booths banana
3 Co-op
我的解决方案:
第1步:我使用for
循环将二进制列中的所有1替换为相应的列名:
for(i in names(stock)[2:4]) {
stock[which(stock[[i]] == 1), i] <- i
}
得到了
store apple banana coconut
1 Asda apple banana 0
2 Booths 0 banana 0
3 Co-op 0 0 0
第2步:我使用tidyr::unite()
将各个水果列连接成一个字符向量列:
library(tidyverse)
stock <- unite(stock, fruits, apple:coconut, sep = ", ")
给我
store fruits
1 Asda apple, banana, 0
2 Booths 0, banana, 0
3 Co-op 0, 0, 0
第3步:我必须使用stringr :: str_replace_all()删除所有不需要的0和逗号分隔符:
library(stringr)
stock$fruits <- str_replace_all(stock$fruits, "0, |, 0|0", "")
虽然这可以让我得到我想要的结果,但我发现我的解决方案相当笨拙,尤其是循环部分。有人可以和我分享一个更有效,更直接的解决方案吗?非常感谢提前!
答案 0 :(得分:2)
该任务需要将输入数据从宽格式转换为长格式。
虽然问题已明确标记为tidyverse
,但我想从使用data.table
的简明melt()
解决方案开始,我对此更熟悉:
library(data.table)
melt(setDT(stock), id.vars = "store")[
value > 0, .(fruits = toString(variable)), keyby = store][.(stock$store)]
store fruits 1: Asda apple, banana 2: Booths banana 3: Co-op NA
它强制stock
到类data.table
并将其从宽到长格式重塑。然后,在后续聚合中仅考虑具有至少一个水果的行,其中结果按store
分组。 toString()
用于聚合,这是paste()
的简明替代。为了包含所有商店,即使那些没有任何成果的商店,也需要最终的正确加入。
通过使用OP请求的tidyr
和dplyr
包中的函数,可以实现相同的目标:
library(magrittr)
stock %>%
tidyr::gather(fruits, , -store) %>%
dplyr::filter(value > 0) %>%
dplyr::group_by(store) %>%
dplyr::summarise(toString(fruits)) %>%
dplyr::right_join(stock %>% dplyr::select(store))
# A tibble: 3 x 2 store `toString(fruits)` <fctr> <chr> 1 Asda apple, banana 2 Booths banana 3 Co-op <NA>
两个结果都是等效的。
请注意,tidyverse
函数的引用是明确的,以避免由于名称空间混乱而导致名称冲突。
答案 1 :(得分:1)
假设唯一商店名称,只有1和0,并且没有缺失值:
library(dplyr)
library(tidyr)
result <- stock %>%
gather(fruit, binary, -store) %>%
mutate(fruit = if_else(binary == 1, fruit, NA_character_)) %>%
select(-binary) %>%
filter(!is.na(fruit)) %>%
group_by(store) %>%
summarize(fruits = paste(fruit, collapse = ", ")) %>%
ungroup() %>%
right_join(stock %>% select(store)) %>%
mutate(fruits = if_else(is.na(fruits), "", fruits))