将二进制调查结果转换为 R 数据框中的字符向量
Convert binary survey results to character vectors in an R data frame
假设我将超市水果库存调查的结果存储在数据框中:
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", "")
虽然这可以获得我想要的结果,但我发现我的解决方案相当笨拙,尤其是循环部分。请问有人可以与我分享一个更有效,更直接的解决方案吗?非常感谢!
假设商店名称唯一,只有 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))
该任务需要将输入数据从宽格式重塑为长格式。
虽然这个问题被明确地标记为 tidyverse
,但我想从使用我更熟悉的 melt()
的简洁 data.table
解决方案开始:
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
强制转换为 class 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
函数的引用是明确的,以避免由于名称混乱而导致的名称冲突 space。
假设我将超市水果库存调查的结果存储在数据框中:
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", "")
虽然这可以获得我想要的结果,但我发现我的解决方案相当笨拙,尤其是循环部分。请问有人可以与我分享一个更有效,更直接的解决方案吗?非常感谢!
假设商店名称唯一,只有 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))
该任务需要将输入数据从宽格式重塑为长格式。
虽然这个问题被明确地标记为 tidyverse
,但我想从使用我更熟悉的 melt()
的简洁 data.table
解决方案开始:
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
强制转换为 class 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
函数的引用是明确的,以避免由于名称混乱而导致的名称冲突 space。