備忘ログ

チラシの裏的備忘録&メモ

Rでデータフレーム ⇔ リスト の変換

qiita.com

こちらでRで各行を要素としたリストを作成する方法が自分で関数を定義して紹介されているが、今なら{purrr}pmap()を使うと簡単にできそう。

今回は例としてirisデータを使うが、全部を使うと見にくいので冒頭5行だけ使用するために、iris.headとしてオブジェクトを持っておく。

iris.head <- head(iris, n = 5)

ここで、{purrr}pmap()でデータフレームでlist化してやる。

library(purrr)
iris.list <- pmap(iris.head, list)
iris.list
## [[1]]
## [[1]]$Sepal.Length
## [1] 5.1
## 
## [[1]]$Sepal.Width
## [1] 3.5
## 
## [[1]]$Petal.Length
## [1] 1.4
## 
## [[1]]$Petal.Width
## [1] 0.2
## 
## [[1]]$Species
## [1] setosa
## Levels: setosa versicolor virginica
## 
## 
## [[2]]
## [[2]]$Sepal.Length
## [1] 4.9
## 
## [[2]]$Sepal.Width
## [1] 3
## 
## [[2]]$Petal.Length
## [1] 1.4
## 
## [[2]]$Petal.Width
## [1] 0.2
## 
## [[2]]$Species
## [1] setosa
## Levels: setosa versicolor virginica
## 
## 
## [[3]]
## [[3]]$Sepal.Length
## [1] 4.7
## 
## [[3]]$Sepal.Width
## [1] 3.2
## 
## [[3]]$Petal.Length
## [1] 1.3
## 
## [[3]]$Petal.Width
## [1] 0.2
## 
## [[3]]$Species
## [1] setosa
## Levels: setosa versicolor virginica
## 
## 
## [[4]]
## [[4]]$Sepal.Length
## [1] 4.6
## 
## [[4]]$Sepal.Width
## [1] 3.1
## 
## [[4]]$Petal.Length
## [1] 1.5
## 
## [[4]]$Petal.Width
## [1] 0.2
## 
## [[4]]$Species
## [1] setosa
## Levels: setosa versicolor virginica
## 
## 
## [[5]]
## [[5]]$Sepal.Length
## [1] 5
## 
## [[5]]$Sepal.Width
## [1] 3.6
## 
## [[5]]$Petal.Length
## [1] 1.4
## 
## [[5]]$Petal.Width
## [1] 0.2
## 
## [[5]]$Species
## [1] setosa
## Levels: setosa versicolor virginica

簡単に各行を要素としたリストができる。

これをデータフレームに戻すには、要素にキーとなる名前がついているので{dplyr}bind_row()を使うと簡単にできる。

library(dplyr)
## 
##  次のパッケージを付け加えます: 'dplyr'

##  以下のオブジェクトは 'package:stats' からマスクされています: 
## 
##      filter, lag

##  以下のオブジェクトは 'package:base' からマスクされています: 
## 
##      intersect, setdiff, setequal, union
iris.dataframe <- bind_rows(iris.head)
iris.dataframe
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          5.1         3.5          1.4         0.2  setosa
## 2          4.9         3.0          1.4         0.2  setosa
## 3          4.7         3.2          1.3         0.2  setosa
## 4          4.6         3.1          1.5         0.2  setosa
## 5          5.0         3.6          1.4         0.2  setosa

また、このbind_row()は要素の名前をキーにしてちゃんとデータフレームにしてくれるので、リストごとに一部欠損値があっても(長さが異なっていても)、そこの値はNAで処理してくれる。

# 適当なリストを作ってみる
a <- list(
  A = list("ID" = 1234, "SEX" = "F", "AGE" = 45),
  B = list("ID" = 4567, "AGE" = 65),
  C = list("SEX" = "M", "AGE" = 35)
)
a
## $A
## $A$ID
## [1] 1234
## 
## $A$SEX
## [1] "F"
## 
## $A$AGE
## [1] 45
## 
## 
## $B
## $B$ID
## [1] 4567
## 
## $B$AGE
## [1] 65
## 
## 
## $C
## $C$SEX
## [1] "M"
## 
## $C$AGE
## [1] 35
# aをデータフレーム化(この場合tibbleになる)する
a.tibble <- bind_rows(a)
a.tibble
## # A tibble: 3 x 3
##      ID SEX     AGE
##   <dbl> <chr> <dbl>
## 1  1234 F        45
## 2  4567 <NA>     65
## 3    NA M        35

このとき、そのままだとリストの名前が落ちてしまうので、必要ならばこれを取り出して、列名つけたり新たな列を作って処理すると良いと思う。

# リストの名前を取り出して行名にする
# a.tibbleを行名をつけられるdata.frameにしておく
a.dataframe <- as.data.frame(a.tibble)
rownames(a.dataframe) <- names(a)
a.dataframe
##     ID  SEX AGE
## A 1234    F  45
## B 4567 <NA>  65
## C   NA    M  35
# リストの名前を取り出して新たなNAME列を作る
# ついでにNAME列を先頭もってくる
a.tibble <- a.tibble %>% 
  mutate(NAME = names(a)) %>% 
  select(NAME, everything())
a.tibble
## # A tibble: 3 x 4
##   NAME     ID SEX     AGE
##   <chr> <dbl> <chr> <dbl>
## 1 A      1234 F        45
## 2 B      4567 <NA>     65
## 3 C        NA M        35