cjk_sort() returns x ordered by an ICU collation; cjk_order() returns
the permutation that would do it, for reordering a data frame.
Usage
cjk_sort(x, locale = NULL, decreasing = FALSE)
cjk_order(x, locale = NULL, decreasing = FALSE)Arguments
- x
A character vector. Anything else is coerced with
as.character(). That coercion is R's, not this package's, so a numeric vector is measured as R chooses to write it – which moves withoptions(scipen)andoptions(OutDec), and can therefore differ between sessions. Convert deliberately if you mean to measure numbers; these verbs are for text. A list is not coerced –as.character()deparses one rather than coercing it, so the text measured would be the R code that builds the list – so a list, a data frame or a function is an error naming what to do instead.- locale
ICU locale naming the collation, e.g.
"zh".NULL, the default, uses the session default – which is unlikely to order Han usefully, so name one.- decreasing
Reverse the order.
Details
sort() reads LC_COLLATE, so the order it gives Han text is a property
of the session rather than of the data. Under C or en_US.UTF-8 it
falls back to code point order; under zh_CN.utf8 glibc supplies a pinyin
collation and the answer changes; under ja_JP.utf8 it changes again.
None of those is wrong in itself, and that is the difficulty: the same
script sorts a name column differently on two machines and says nothing.
The fallback order is worth knowing, because it is not arbitrary. The unified ideographs are laid out in KangXi radical-stroke order, so within the base block sorting by code point sorts by radical. What it is not is phonetic, which is what someone sorting names is after. It also stops being radical order across blocks: every Extension A character (U+3400-U+4DBF) sorts ahead of every base-block one, so a rare character lands nowhere near the common characters sharing its radical.
ICU carries real collations. With locale = "zh" Han sorts by pinyin, so a
column of Chinese surnames comes out in the order a Chinese reader expects.
Choosing the order
The collation is selected by the locale string, including the BCP 47 variants:
"zh"– Mandarin pinyin."zh-u-co-stroke"– stroke count, the convention in many indexes."zh-u-co-zhuyin"– bopomofo order."ja"and"ko"– Japanese and Korean.
A locale whose language ICU has no collation for is an error here rather
than the silent fallback to root collation stringi would otherwise give
you, because that fallback produces a plausible-looking wrong order. An
unrecognised region is not an error: "zh-CH", a typo for "zh-CN",
resolves to "zh" and still sorts by pinyin, which is the right answer.
The language is matched without regard to case, as BCP 47 specifies, so
"ZH" is "zh"; and "root" asks for ICU's root collation by name,
which is a choice rather than a fallback.
Missing values
NA sorts last and is kept, so the result is always the same length as
x. stringi::stri_sort() drops missing values by default, which would
silently shorten a column.
See also
cjk_romanize(), whose ascii = TRUE output gives a sort key you
can use where no ICU locale is available.
Examples
# Chinese surnames: Zhang, Wang, Li
x <- c("\u5f35", "\u738b", "\u674e")
sort(x) # depends on LC_COLLATE
#> [1] "張" "李" "王"
cjk_sort(x, locale = "zh") # pinyin: Li, Wang, Zhang -- on any machine
#> [1] "李" "王" "張"
# stroke order instead
cjk_sort(x, locale = "zh-u-co-stroke")
#> [1] "王" "李" "張"
# reorder a data frame
df <- data.frame(name = x)
df[cjk_order(x, locale = "zh"), , drop = FALSE]
#> name
#> 3 李
#> 2 王
#> 1 張
