LoginSignup
6
3

More than 5 years have passed since last update.

Rのソートとlocaleについて

Last updated at Posted at 2018-12-04

Rのソートはデフォルトだとコードポイント順ではありません。何やら空気を読んでくれているようです。

## データの準備
d <- data.frame(
  kana = c("お", "エ", "う", "イ", "あ"),
  letter = c("A", "e", "C", "b", "Z"),
  stringsAsFactors = FALSE
)
## 空気を読んだソートをしてくれる
sort(d$kana)

#> [1] "あ" "イ" "う" "エ" "お"

?ComparisonR: Relational Operators)を確認すると書いてありますが、文字列同士の比較はデフォルトだとlocaleに依存した辞書順でソートされます。つまり、今の例のように日本語ロケールではカタカナとひらがなの違いは吸収されるわけです。

この挙動は分かって使う分には良いのですが、空気を読んでほしくない場合もあります。具体的に言えばコードポイント順にソートしてほしい場合もあるわけです。それにはいくつかやり方があります。

localeを変更する

素朴な方法としてSys.setlocale()でlocaleを変更してしまうというやり方があります。しかし、locale = "C"のままだと表示に難があります。

Sys.setlocale(locale = "C")
sort(d$kana)

#> [1] "\343\201\202" "\343\201\206" "\343\201\212" "\343\202\244"
#> [5] "\343\202\250"

ソートしてからlocaleを戻してやれば良いわけですが、面倒です。

sorted <- sort(d$kana)
Sys.setlocale(locale = "ja_JP")
sorted

#> [1] "あ" "う" "お" "イ" "エ"

withrを使う

とかなんとかボヤいていたら @yutannihilation さんが教えてくれました。ありがたや。

これなら既存のロケールに影響することはありませんし、printはもとのロケールを反映するので表示にも問題ありません。

withr::with_locale(c("LC_COLLATE" = "C"), sort(d$kana))

#> [1] "あ" "う" "お" "イ" "エ"

※追記: ソートを変更するだけならwhth_collate()で良さそうです。

withr::with_collate("C", sort(d$kana))

#> [1] "あ" "う" "お" "イ" "エ"

stringiについて

ところで、stringiの方は日本語だとうまくいきませんでした。

stringi::stri_sort(d$kana, locale = "C")

#> [1] "あ" "イ" "う" "エ" "お"

locale = "C"の指定が通っていないというわけではなく、アルファベットだとそれっぽく動きます。

## 通常の空気読んだソート
stringi::stri_sort(d$letter, locale = "en_EN")

#> [1] "A" "b" "C" "e" "Z"
## locale = "C"っぽさがあるソート
stringi::stri_sort(d$letter, locale = "C")

#> [1] "A" "C" "Z" "b" "e"

どうもstringiはライブラリとしてICUを使用しているため、システムのロケールを変更した場合とは挙動が異なるみたいです。ICUの場合、Cは実際にはen_US_POSIXを指定したのと同じことになるようです(cf. Locale - ICU User Guide)。

すなわちICUでできることはできる、ということなので、こんな感じで細かいソートの制御をすることもできます。これはこれで使いみちがありそうです。

## locale="en"のデフォルトのソートは小文字が前に出る
str <- c("a", "A", "a", "A")
stringi::stri_sort(str, locale = "en")

#> [1] "a" "a" "A" "A"
## colCaseFirstの指定で大文字を前にできる
stringi::stri_sort(str, locale = "en@colCaseFirst=upper")

#> [1] "A" "A" "a" "a"
## 通常のソートは辞書順
str2 <- c("A1", "A12", "A112")
stringi::stri_sort(str2, locale = "en")

#> [1] "A1"   "A112" "A12"
## 数字の大きさを考慮したソート(自然順ソート)ができる
stringi::stri_sort(str2, locale = "en@colNumeric=yes") 

#> [1] "A1"   "A12"  "A112"
6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3