Closed uribo closed 3 years ago
ggplot2でのラベルで単位の漢数字表記というのは や というような形式を意図しているのでしょうか。
ラベルに利用ということについて、どういう形をイメージしているのかわからなかったので、確認してみたいなと思って書いてます。
ちなみに、前者については
ja.popu <- structure(list(年 = c("1955", "1960", "1965", "1970", "1975", "1980"),
総人口 = c(89275529, 93418501, 98274961, 103720060, 111939643, 117060396)),
row.names = c(NA, -6L),
class = c("tbl_df", "tbl", "data.frame"))
のデータを使ったとき、
ja.popu %>%
ggplot() +
geom_bar(aes(x = 年, y = 総人口), stat = "identity") +
scale_y_continuous(labels = function(x) {
ifelse(x < 1e5, x,
ifelse(x < 1e8, paste0(round(x/1e4, digits = 1), "万"),
ifelse(x < 1e13, paste0(round(x/1e8, digits = 1), "億"),
)))
})
で描画しています。
@indenkun はい、例でいただいた形式を意図していました。すべて漢字表記、単位のみ漢字の両方のパターンですね。
@uribo 回答ありがとうございます。
拙いなりになにか提案できることがあればと思って伺いました。
先に上げたグラフの前者の"数字 + (万, 億, 兆, 京)"について、{ggplot2}
のラベルとできるようなものとして、仮にlabel_number_kansuji()
という関数を考えたときに、先に書いたように
label_number_kansuji <- function(digits = 1, unit = NULL, sep = "", prefix = "", ...){
function(x){
ifelse(abs(x) < 1e5, x,
ifelse(abs(x) < 1e8, paste(prefix, prettyNum(round(x/1e4, digits = digits), ...), "万", unit, sep = sep),
ifelse(abs(x) < 1e13, paste(prefix, prettyNum(round(x/1e8, digits = digits), ...), "億", unit, sep = sep),
ifelse(abs(x) < 1e18, paste(prefix, prettyNum(round(x/1e12, digits = digits), ...), "億", unit, sep = sep),
ifelse(abs(x) < 1e23, paste0(prefix, prettyNum(round(x/1e16, digits = digits), ...), "京", unit, sep = sep))
))))
}
}
というものがぱっと思いつきます。
しかし、{scales}
パッケージのlabel_number_*
関数群と似たような挙動をしたほうが扱いやすい({scales}
のlabel_number_*
関数群と似たような記法で描ける)という方が良ければ{scales}
パッケージで、KやM、Gなどの接頭辞をggplot2のラベルで扱うためのlabel_number_si()
を少しいじって、
label_number_kansuji <- function(accuracy = 1, unit = NULL, sep = NULL, prefix = "", big.mark = "", significant.digits = FALSE, ...) {
function(x) {
breaks <- c(0, 10^c(万 = 4, 億 = 8, 兆 = 12, 京 = 16))
n_suffix <- cut(abs(x),
breaks = c(unname(breaks), Inf),
labels = c(names(breaks)),
right = FALSE)
n_suffix[is.na(n_suffix)] <- ""
suffix <- paste0(sep, n_suffix, unit)
scale <- 1 / breaks[n_suffix]
scale[which(scale %in% c(Inf, NA))] <- 1
if(significant.digits){
if(requireNamespace("scales", quietly = TRUE))
scales::number(x,
accuracy = accuracy,
scale = unname(scale),
suffix = suffix,
big.mark = big.mark,
...)
else{
warning("`scales` package needs to be installed.", call. = FALSE)
paste0(prefix, prettyNum(round(x * scale, digits = nchar(1 / (accuracy * 10))), big.mark = big.mark, ...), suffix, sep = sep)
}
}else{
paste0(prefix, prettyNum(round(x * scale, digits = nchar(1 / (accuracy * 10))), big.mark = big.mark, ...), suffix, sep = sep)
}
}
}
としたほうが、似たような記法で描画できるかと思います。
いじった箇所としては
label_number_si()
と同じようにscales::number
で処理すると指定した有効数字で表記されるようになるのを0であれば日本語的には表記しないものが多いかと思い、そちらをデフォルトとし、有効数字で表示(丸め)をおこないたい場合にはsignificant.digits
でTRUE
とすると有効数字で表記するようになる。というところです。
いずれの場合も、
ja.popu %>%
ggplot() +
geom_bar(aes(x = 年, y = 総人口), stat = "identity") +
scale_y_continuous(labels = label_number_kansuji())
と描画できるようになってます。
個人的にはどちらも基本的なアウトプットが一緒なのでどちらでもいいのですが、どちらのほうが妥当なのか……というところで止まっています。
後者のグラフの漢数字表記のみの場合は、処理の中でアラビア数字から漢数字への変換が必須になると思います。 自分で書いたパッケージで申し訳ないのですが、indenkun/arabic2kansujiを使えば、
label_number_kansuji <- function(unit = NULL, sep = "", prefix = "", big.mark = "", number = c("arabic", "kansuji"), ...){
number <- match.arg(number)
if(number == "arabic"){
function(x){
purrr::map(x, function(x){
x <- prettyNum(x, scientific = FALSE) %>% arabic2kansuji::arabic2kansuji_all()
x.kansuji <- stringr::str_split(x, pattern = "[^〇一二三四五六七八九十百千]")[[1]]
x.kansuji[x.kansuji == ""] <- NA
x.kansuji <- na.omit(x.kansuji)
for(i in 1:length(x.kansuji)){
if(is.na(x.kansuji[i])) break
x <- stringr::str_replace(x, pattern = x.kansuji[i], replacement = prettyNum(zipangu::kansuji2arabic_num(x.kansuji[i]), big.mark = big.mark, ...))
}
paste0(prefix, x, unit, sep = sep)
}
)
}
}else if(number == "kansuji"){
function(x){
x <- prettyNum(x, scientific = FALSE) %>% arabic2kansuji::arabic2kansuji_all()
paste0(prefix, x, unit, sep = sep)
}
}
}
で実現でき、
ja.popu %>%
ggplot() +
geom_bar(aes(x = 年, y = 総人口), stat = "identity") +
scale_y_continuous(labels = label_number_kansuji(number = "kansuji"))
で
ja.popu %>%
ggplot() +
geom_bar(aes(x = 年, y = 総人口), stat = "identity") +
scale_y_continuous(labels = label_number_kansuji(number = "arabic"))
で となるようにできます。
アラビア数字から漢数字に変換するための{arabic2kansuji}
の関数群を{zipangu}
に取り込んで貰えれば使えるかもしれないとは思います。ただ、アラビア数字から漢数字への変換を行う関数の取り込みがポリシー上難しいのであれば、漢数字のみ系のラベル関数は難しいと思います。
とりあえず暫定的にここまで考えていますが、上記2つの機能(「数字+億系」と「漢数字のみ系」)の関数をもし入れるとしたら別々の関数のほうがよいでしょうか。それとも引数で選択するような形のほうがいいでしょか。
素晴らしい提案をありがとうございます。 ggplot2だけがRの作図の全てではないですが、私含めユーザは多いと思うので ggplot2で使われているscalesパッケージの仕様に沿ったものが良いですね。
arabic2kansujiパッケージはCRANに登録する予定はないでしょうか。 zipanguがCRANにあるので、 リリースすることができません。もしarabic2kansujiがCRAN登録された場合には、該当の関数をインポートすることも考えられます (もちろんそちらのパッケージで目的が達成していても問題ありません)
上記2つの機能(「数字+億系」と「漢数字のみ系」)の関数をもし入れるとしたら別々の関数のほうがよいでしょうか
関数の構造をわかりやすくするために個別の関数にしたほうが良いと思います。
ありがとうございます。
関数群を取り込んでもらうというのは言葉足らずでしたが、アラビア数字を漢数字へ変換するzipanguにとっての新関数としてコードを取り込んでもらえれば(アラビア数字を漢数字へ変換する関数をzipanguにPull Requestしてマージしてもらえれば)と言うことを意図していたのですが、確かにCRANに登録できれば単純に::
で呼び出せばよいですよね。
年明けまでCARNのsubumittion teamがお休みのようなので、subumittion teamの休み明け以降に体裁を整えてCARNへの登録をトライしてみます。
関数の構造をわかりやすくするために個別の関数にしたほうが良いと思います。
別々の関数で検討してみます。
Close #27
億千万
など。ggplot2でのラベルにも利用できると良いかもしれない