2017-06-22 2 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 

のように見えました私の目標:

上記の2分の調査結果の列を各スーパーマーケットの在庫要約の文字ベクトルに変換したい以下のようにKET:

store  fruits 
1 Asda apple, banana 
2 Booths  banana 
3 Co-op    

私のソリューション:

ステップ1:私は、対応する列名とバイナリ列のすべて1を交換するforループを使用:

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:3

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 

ステップを与える:私は文字ベクトル列に、個々の果物の列を連結するtidyr::unite()を使用して、私はstringrを使用していた :: str_replace_all()を使用して、不要な0とコンマ区切りをすべて削除します。

library(stringr) 
stock$fruits <- str_replace_all(stock$fruits, "0, |, 0|0", "") 

これは私が望む結果を得ることができましたが、私の解決策はやや不器用で、特にループ部分が見つかりました。誰かが親切に私とより効率的で簡単な解決策を共有できますか?事前に多くの感謝!ユニークな店舗名、唯一の1と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に変換し、それをワイドフォーマットからロングフォーマットに再構成します。次に、少なくとも1つの果物を有する行だけが、結果がstoreによってグループ化される後続の集約において考慮される。 は、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

私の質問タグに注意深い注意を払いながら、物事をはっきりと説明する余分なコーディングコメントのために、親切にも私の質問に2つの解決策を提供してくれてありがとう!私の推奨する 'tidyverse'の解決策はかなりきちんとしていますが、私は' data.table'代替手段の簡潔さにも驚いています。 – elarry

+0

@elarry正直言って、私は 'data.table'のほうが簡潔なコードだけでなく、より大きな問題のパフォーマンス上の理由からも好んでいます。たとえば、[別の質問のベンチマーク](https://stackoverflow.com/a/44755588/3817004)を参照してください。 – Uwe

1

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

関連する問題