備忘ログ

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

Rのbaseのorder()関数がデータフレームを受け入れなくなっていた

baseorder()内部で仕様されいているxtfrm()の仕様の変更で、order()関数がデータフレームをうけつけなくなった(バージョン4.2.xまでは受け付けていた)。

どういうことかと言うと、以前はデータフレームを特定の列の昇順(または降順)で変えようと思ったときに次のようなコードも以前のバージョンのRでは実行可能だったが現在は実行できなくなっている。

# 例えば、irisデータをSepal.Lengthの昇順で並び替えてみようと思った場合(現在はエラーで実行できない)
iris[order(iris["Sepal.Length"]), ]
## Error in xtfrm.data.frame(x): cannot xtfrm data frames

もちろん、ベクトルとして$を使って列を呼び出せば従来どおり実行可能となる。

head(iris[order(iris$Sepal.Length), ])
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 14          4.3         3.0          1.1         0.1  setosa
## 9           4.4         2.9          1.4         0.2  setosa
## 39          4.4         3.0          1.3         0.2  setosa
## 43          4.4         3.2          1.3         0.2  setosa
## 42          4.5         2.3          1.3         0.3  setosa
## 4           4.6         3.1          1.5         0.2  setosa

どうしてもデータフレームの状態で呼び出したければdo.callや(unlist())などはさんでやるといい。

head(iris[do.call(order, iris["Sepal.Length"]), ])
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 14          4.3         3.0          1.1         0.1  setosa
## 9           4.4         2.9          1.4         0.2  setosa
## 39          4.4         3.0          1.3         0.2  setosa
## 43          4.4         3.2          1.3         0.2  setosa
## 42          4.5         2.3          1.3         0.3  setosa
## 4           4.6         3.1          1.5         0.2  setosa
head(iris[order(unlist(iris["Sepal.Length"])), ])
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 14          4.3         3.0          1.1         0.1  setosa
## 9           4.4         2.9          1.4         0.2  setosa
## 39          4.4         3.0          1.3         0.2  setosa
## 43          4.4         3.2          1.3         0.2  setosa
## 42          4.5         2.3          1.3         0.3  setosa
## 4           4.6         3.1          1.5         0.2  setosa

ここで問題になるのが、データの行を昇順に並び替えたいと思ったとき。例えば次のようなデータセットを考えてみる。

set.seed(123)
d <- data.frame(ID = 1:10, 
                TIEM_01 = rnorm(10),
                TIME_02 = rnorm(10),
                TIME_03 = c(rnorm(5), NA, rnorm(3), NA))
d
##    ID     TIEM_01    TIME_02    TIME_03
## 1   1 -0.56047565  1.2240818 -1.0678237
## 2   2 -0.23017749  0.3598138 -0.2179749
## 3   3  1.55870831  0.4007715 -1.0260044
## 4   4  0.07050839  0.1106827 -0.7288912
## 5   5  0.12928774 -0.5558411 -0.6250393
## 6   6  1.71506499  1.7869131         NA
## 7   7  0.46091621  0.4978505 -1.6866933
## 8   8 -1.26506123 -1.9666172  0.8377870
## 9   9 -0.68685285  0.7013559  0.1533731
## 10 10 -0.44566197 -0.4727914         NA

このデータセットは本来は各行ごとに昇順に並んでいるはずだが、データの取り方(webからのスクレイピングなど)の都合から昇順に並んでいないものと考える。ここで以前ならば、各行のデータを昇順に並び替える方法としては次のようにするとよかった。

for(i in 1:nrow(d)) d[i, 2:4] <- d[i, 2:4][order(d[i, 2:4])]
## Error in xtfrm.data.frame(x): cannot xtfrm data frames

今回は残念ながらdo.call()ではうまくいかない。ここではunlist()をつかって、取り出した行の値をベクトル化すると意図した通り動く。

for(i in 1:nrow(d)) d[i, 2:4] <- d[i, 2:4][order(unlist(d[i, 2:4]))]
d
##    ID    TIEM_01     TIME_02   TIME_03
## 1   1 -1.0678237 -0.56047565 1.2240818
## 2   2 -0.2301775 -0.21797491 0.3598138
## 3   3 -1.0260044  0.40077145 1.5587083
## 4   4 -0.7288912  0.07050839 0.1106827
## 5   5 -0.6250393 -0.55584113 0.1292877
## 6   6  1.7150650  1.78691314        NA
## 7   7 -1.6866933  0.46091621 0.4978505
## 8   8 -1.9666172 -1.26506123 0.8377870
## 9   9 -0.6868529  0.15337312 0.7013559
## 10 10 -0.4727914 -0.44566197        NA

ちょっとバッドノウハウ感があるのでもう少しいい解決策があるのかもしれない。

たしかにorder()関数が二列以上のデータフレームを指定した場合の挙動は謎挙動だったので、エラーになるのもやむを得ないかもしえれないが、前に書いたコードを久々に動かしたらエラーを吐いてびっくりした。結構大きな変更だと思うが、NEWSにそれっぽい記述が見つけられなかった。