将二元调查结果转换为R数据框中的字符向量

时间:2017-06-22 15:23:11

标签: r dataframe character binary-data tidyverse

假设我将超市水果库调查结果存储在数据框中:

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", "")

虽然这可以让我得到我想要的结果,但我发现我的解决方案相当笨拙,尤其是循环部分。有人可以和我分享一个更有效,更直接的解决方案吗?非常感谢提前!

2 个答案:

答案 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请求的tidyrdplyr包中的函数,可以实现相同的目标:

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))