2017-06-22 35 views
1

假设我有存储在数据帧中的水果超市股票调查的结果:转换二进制调查结果字符向量中的R数据帧

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 

我的目标是:

我想将上述二元调查结果列转换为每个超级马尔的股票摘要的字符向量KET如下:

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

任务需要从广泛的重塑输入数据中的长格式。

虽然问题是明确地与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到类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函数的引用是明确的,以避免由于名称空间混乱而引起的名称冲突。

+0

非常感谢您对我的问题提供两种解决方案,同时注意我的问题标签,并为您的额外编码注释清楚地解释事情!虽然我最喜欢的'tidyverse'解决方案非常简洁,但我也对'data.table'替代方案的简洁感到惊讶。 – elarry

+0

@elarry说实话,我更喜欢'data.table'不仅仅是因为它的简洁的代码,而且也是为了解决更大问题的性能原因。例如,看[我的另一个问题的基准](https://stackoverflow.com/a/44755588/3817004)。 – Uwe

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

非常感谢共享一个用于将二进制数据转换为字符矢量的'paste()'替代方法 - 对于像我这样不知道'toString()'函数的人来说,这非常有用。 :) – elarry