reshapeを使って行列を入れ替える

reshapeでdata frameをtidyにできる?
R
公開

2022年12月27日

statsパッケージにreshapeという関数があるのをご存知でしょうか。 これはdata frameの行と列を入れ替えることができる関数です。さっそく使ってみましょう。

行方向から列方向へ(wide)

datasets::CO2を例に使います。

head(datasets::CO2)
  Plant   Type  Treatment conc uptake
1   Qn1 Quebec nonchilled   95   16.0
2   Qn1 Quebec nonchilled  175   30.4
3   Qn1 Quebec nonchilled  250   34.8
4   Qn1 Quebec nonchilled  350   37.2
5   Qn1 Quebec nonchilled  500   35.3
6   Qn1 Quebec nonchilled  675   39.2

列方向に伸ばすときの基本的な発想は、同じ対象についての複数データを横に並べるという考え方です。reshapeの引数の名前がidvartimevarなのはそのためです。すなわち同じidを持つデータを時系列timeで観測したときのようなものが想定されているのですね。

CO2のデータに当てはめて考えると、PlantからTreatmentまでをidとみなして、concごとに値uptakeを得たと見ることができます。 列方向への展開はdirection="wide"と書きます。続けてidvartimevarをセットしましょう。sepは新しい列名に使われる記号になります。

reshape(datasets::CO2, direction="wide", idvar=c("Plant", "Type", "Treatment"), timevar="conc", sep="|")
   Plant        Type  Treatment uptake|95 uptake|175 uptake|250 uptake|350 uptake|500 uptake|675 uptake|1000
1    Qn1      Quebec nonchilled      16.0       30.4       34.8       37.2       35.3       39.2        39.7
8    Qn2      Quebec nonchilled      13.6       27.3       37.1       41.8       40.6       41.4        44.3
15   Qn3      Quebec nonchilled      16.2       32.4       40.3       42.1       42.9       43.9        45.5
22   Qc1      Quebec    chilled      14.2       24.1       30.3       34.6       32.5       35.4        38.7
29   Qc2      Quebec    chilled       9.3       27.3       35.0       38.8       38.6       37.5        42.4
36   Qc3      Quebec    chilled      15.1       21.0       38.1       34.0       38.9       39.6        41.4
43   Mn1 Mississippi nonchilled      10.6       19.2       26.2       30.0       30.9       32.4        35.5
50   Mn2 Mississippi nonchilled      12.0       22.0       30.6       31.8       32.4       31.1        31.5
57   Mn3 Mississippi nonchilled      11.3       19.4       25.8       27.9       28.5       28.1        27.8
64   Mc1 Mississippi    chilled      10.5       14.9       18.1       18.9       19.5       22.2        21.9
71   Mc2 Mississippi    chilled       7.7       11.4       12.3       13.0       12.5       13.7        14.4
78   Mc3 Mississippi    chilled      10.6       18.0       17.9       17.9       17.9       18.9        19.9

列方向から行方向へ(long)

列から行にするのに適当なデータを探すのが面倒だったので、いま作ったデータを元に戻してみます。

# いったんCO2_reに保存
CO2_re <- reshape(datasets::CO2, direction="wide", idvar=c("Plant", "Type", "Treatment"), timevar="conc", sep="|")

今度はdirection="long"と書いて行方向に展開します。 対象とする列をvaryingにセットします。timevarsepは今度は展開後のdata frameの列名を決めるために使います。 sepで列名を分割して値を取り出すのですが、きちんと分割できるような列名になっている必要があります。

reshape(CO2_re, direction="long", idvar=c("Plant", "Type", "Treatment"), varying=4:10, timevar="conc", sep="|") |> head(10)
                              Plant        Type  Treatment conc uptake
Qn1.Quebec.nonchilled.95        Qn1      Quebec nonchilled   95   16.0
Qn2.Quebec.nonchilled.95        Qn2      Quebec nonchilled   95   13.6
Qn3.Quebec.nonchilled.95        Qn3      Quebec nonchilled   95   16.2
Qc1.Quebec.chilled.95           Qc1      Quebec    chilled   95   14.2
Qc2.Quebec.chilled.95           Qc2      Quebec    chilled   95    9.3
Qc3.Quebec.chilled.95           Qc3      Quebec    chilled   95   15.1
Mn1.Mississippi.nonchilled.95   Mn1 Mississippi nonchilled   95   10.6
Mn2.Mississippi.nonchilled.95   Mn2 Mississippi nonchilled   95   12.0
Mn3.Mississippi.nonchilled.95   Mn3 Mississippi nonchilled   95   11.3
Mc1.Mississippi.chilled.95      Mc1 Mississippi    chilled   95   10.5

元には戻りましたがrownamesのデフォルト値がなかなかひどい。

時系列的な解釈ができないデータの場合

例えばdatasets::irisは、異なる150個のアヤメのデータです。

head(datasets::iris)
  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
6          5.4         3.9          1.7         0.4  setosa

3種類のSpeciesがあり、それぞれ50個ずつデータが縦に並んでいるのですが、これを横に並べたいという特殊な事情が生じたとします。つまりいま150×5のテータを50×12にしようということです。これをreshapeで行えるでしょうか。

実はこのデータのままではできなくて、ダミーのidを加える必要があります。1行目のデータの隣に51行目及び101行目のデータを持って来ようとしていますが、そのためにはそれらの3データが共通のidを持っている必要があります。そこで、Speciesごとに1~50の連番でidを付与することにします。

transform(iris, id=rep(1:50, 3)) |> head()
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species id
1          5.1         3.5          1.4         0.2  setosa  1
2          4.9         3.0          1.4         0.2  setosa  2
3          4.7         3.2          1.3         0.2  setosa  3
4          4.6         3.1          1.5         0.2  setosa  4
5          5.0         3.6          1.4         0.2  setosa  5
6          5.4         3.9          1.7         0.4  setosa  6

長いのでheadで切ってしまいましたが51行目と101行目でidが1になっているはずです。これを軸にして列方向に展開してみましょう。

transform(iris, id=rep(1:50, 3)) |> 
  reshape(direction="wide", idvar="id", timevar="Species", sep="|") |> head()
  id Sepal.Length|setosa Sepal.Width|setosa Petal.Length|setosa Petal.Width|setosa Sepal.Length|versicolor Sepal.Width|versicolor Petal.Length|versicolor Petal.Width|versicolor Sepal.Length|virginica Sepal.Width|virginica Petal.Length|virginica Petal.Width|virginica
1  1                 5.1                3.5                 1.4                0.2                     7.0                    3.2                     4.7                    1.4                    6.3                   3.3                    6.0                   2.5
2  2                 4.9                3.0                 1.4                0.2                     6.4                    3.2                     4.5                    1.5                    5.8                   2.7                    5.1                   1.9
3  3                 4.7                3.2                 1.3                0.2                     6.9                    3.1                     4.9                    1.5                    7.1                   3.0                    5.9                   2.1
4  4                 4.6                3.1                 1.5                0.2                     5.5                    2.3                     4.0                    1.3                    6.3                   2.9                    5.6                   1.8
5  5                 5.0                3.6                 1.4                0.2                     6.5                    2.8                     4.6                    1.5                    6.5                   3.0                    5.8                   2.2
6  6                 5.4                3.9                 1.7                0.4                     5.7                    2.8                     4.5                    1.3                    7.6                   3.0                    6.6                   2.1

このように列方向にデータを持って来ることができました。

statsパッケージ以外の方法

行列の入れ替えは、tidyrパッケージならpivot_widerpivot_longerdata.tableパッケージならdcastmeltがあります。それらをお使いの方が多いのではないかと思います。 pivot_longerは使い方に少しクセがあるなと思っていたのですが、今回紹介したreshapeに近い発想で作られていることに気が付きました。 基本パッケージを学ぶとtidyverseにも詳しくなれる(?)