stats
パッケージにreshape
という関数があるのをご存知でしょうか。 これはdata frameの行と列を入れ替えることができる関数です。さっそく使ってみましょう。
行方向から列方向へ(wide)
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
の引数の名前がidvar
とtimevar
なのはそのためです。すなわち同じid
を持つデータを時系列time
で観測したときのようなものが想定されているのですね。
CO2
のデータに当てはめて考えると、Plant
からTreatment
までをid
とみなして、conc
ごとに値uptake
を得たと見ることができます。 列方向への展開はdirection="wide"
と書きます。続けてidvar
とtimevar
をセットしましょう。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
にセットします。timevar
とsep
は今度は展開後の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個のアヤメのデータです。
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_wider
とpivot_longer
、 data.table
パッケージならdcast
とmelt
があります。それらをお使いの方が多いのではないかと思います。 pivot_longer
は使い方に少しクセがあるなと思っていたのですが、今回紹介したreshape
に近い発想で作られていることに気が付きました。 基本パッケージを学ぶとtidyverseにも詳しくなれる(?)