2016-07-07 4 views
5

dplyrのmutate_if()関数を使用してリスト列をデータフレーム列に変換したいが、試してみると困惑するエラーに遭遇するそうする。私は、dplyr 0.5.0、purrr 0.2.2、R 3.3.0を使用しています。述語関数(dplyr :: mutate_if)に基づいてデータフレームの列を変更する

基本的な設定は次のようになりますに

d <- dplyr::data_frame(
    A = list(
    list(list(x = "a", y = 1), list(x = "b", y = 2)), 
    list(list(x = "c", y = 3), list(x = "d", y = 4)) 
), 
    B = LETTERS[1:2] 
) 

私は(この場合にはd$A)をリストの列を変換したい:私はデータフレームd、その列があるリストのいくつかを持っています次の関数を使用して、データフレームの列:

tblfy <- function(x) { 
    x %>% 
    purrr::transpose() %>% 
    purrr::simplify_all() %>% 
    dplyr::as_data_frame() 
} 

私はリストの列d$A

あるリスト lapply(d$A, tblfy)で置き換えることがしたい、です
[[1]] 
# A tibble: 2 x 2 
     x  y 
    <chr> <dbl> 
1  a  1 
2  b  2 

[[2]] 
# A tibble: 2 x 2 
     x  y 
    <chr> <dbl> 
1  c  3 
2  d  4 

もちろん、この単純なケースでは、私はちょうど簡単な再割り当てを行うことができます。しかし、理想的にはdplyrを使って、任意の数のリスト列を扱うことができる、一般的に適用可能な方法で、これをプログラムで行うことがポイントです。私がつまずくところ

はここにあります:私は、次のアプリケーション

d %>% dplyr::mutate_if(is.list, funs(tblfy)) 

を使用して、データ・フレームの列にリストの列を変換しようとすると私は解釈する方法がわからないエラーメッセージが表示されます。

Error: Each variable must be named. 
Problem variables: 1, 2 

なぜmutate_if()はできませんの?希望の結果を得るために、どうすればそれを適切に適用できますか?

備考

コメンターは機能tblfy()がベクトル化されるべきであると指摘しています。それは合理的な提案です。しかし、私が間違ってベクター化していない限り、それは問題の根源には達していないようです。更新がpurrrといくつかの経験を積む後

mutate_if()tblfy()

tblfy_vec <- Vectorize(tblfy) 

のベクトル化バージョンでエラー

Error: wrong result size (4), expected 2 or 1 

で失敗したプラグ、私は今、自然、次のアプローチを見つけ、やや長めの場合:

d %>% 
    map_if(is.list, ~ map(., ~ map_df(., identity))) %>% 
    as_data_frame() 

これは以下の@ alistaireの解決策とほぼ同じですが、map_if()を使用しています。 の代わりに、mutate_if()の代わりに、map()を使用してください。 Vectorize()

+2

期待される出力はなんですか? AをリストのリストからTibbleのリストに変更したいのですか? – MrFlick

+1

関数はベクトル化されておらず、1つのリストのみを受け入れます。 'tblfy(d $ A)'を見てください。 'd $ A'には2つのリストがあるので、エラーがあります。あなたはリンゴとリンゴを比較していません。あなたの 'lapply(d $ A、tblfy)'では、一度に1つのリストをあなたの関数に与えているので、それはうまくいきます。 'tblfy(d $ A [[1]])'と 'tblfy(d $ A [[2]])のようになります。 dplyr関数では、2つのリストを提供しています。 'tblfy'を変更して複数のリストを受け入れるか、dplyr呼び出しを変更してください。 MrFlickが尋ねるように、あなたが構築しているものについてもっと広く考える。 –

+0

@MrFlick希望の出力を明示的にするために質問を編集しました。今は明らかですか? – egnha

答えて

5

tblfy機能エラーアウト私のために(その要素が直接連鎖されている場合でも)、それでは、それを少し再構築してみましょう、だけでなくベクトル化を追加して、どの私たちは、そうでない場合は、必要に応じて事前rowwise()コールを避けることができます:

tblfy <- Vectorize(function(x){x %>% purrr::map_df(identity) %>% list()}) 

は、今、私たちはうまくmutate_ifを使用することができます。

d %>% mutate_if(purrr::is_list, tblfy) 
## Source: local data frame [2 x 2] 
## 
##    A  B 
##   <list> <chr> 
## 1 <tbl_df [2,2]>  A 
## 2 <tbl_df [2,2]>  B 

...と、私たちはそこに何があるか確認するためにネスト解除場合、

d %>% mutate_if(purrr::is_list, tblfy) %>% tidyr::unnest() 
## Source: local data frame [4 x 3] 
## 
##  B  x  y 
## <chr> <chr> <dbl> 
## 1  A  a  1 
## 2  A  b  2 
## 3  B  c  3 
## 4  B  d  4 

カップルノート:

  • map_df(identity)は、代替製剤のいずれかよりtibbleの構築をより効率的であるように思われます。私はidentityコールが不要だと知っていますが、ほとんどのものは壊れています。
  • 私はリストの列のリストの構造に多少依存するので、非常に有用であると確信しています。同じような構造のものがたくさんあるなら、それは便利だと思います。
  • Vectorizeの代わりにpmapでこれを行う方法があるかもしれませんが、私はそれをいくつかの大雑把な試行で動作させることはできません。
+1

ありがとうございました! 'map_df()'を使った 'tblfy()のあなたのバージョンは私のものよりも簡潔です。そういうことを考えなかったのです。実際、 'map_df()'のソースコードを見ると、あなたのソリューションがなぜ動作するのか、特に 'list()'が必要な理由(最初は困惑していた)が説明されています。 'bind_rows'は、' list() 'を除いて、次元4のデータフレームになります。 – egnha

6

いかなるコピーせずにインプレース変換:

library(data.table) 

for (col in d) if (is.list(col)) lapply(col, setDF) 

d 
#Source: local data frame [2 x 2] 
# 
#    A B 
#1 <S3:data.frame> A 
#2 <S3:data.frame> B 
+0

' mutate_if'についての元の質問には答えませんが、基本的な問題に対する非常に良い代替案です。 data.tableは私にとって初めてのものです。ありがとう! – egnha

関連する問題