uribo / zipangu

Japanese utility functions and data
https://uribo.github.io/zipangu
Other
56 stars 7 forks source link

単位の漢字表記 #9

Closed uribo closed 3 years ago

uribo commented 4 years ago

億千万 など。ggplot2でのラベルにも利用できると良いかもしれない

indenkun commented 3 years ago

ggplot2でのラベルで単位の漢数字表記というのは Rplot01Rplot02 というような形式を意図しているのでしょうか。

ラベルに利用ということについて、どういう形をイメージしているのかわからなかったので、確認してみたいなと思って書いてます。

ちなみに、前者については

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), "億"),
                  )))
  })

で描画しています。

uribo commented 3 years ago

@indenkun はい、例でいただいた形式を意図していました。すべて漢字表記、単位のみ漢字の両方のパターンですね。

indenkun commented 3 years ago

@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)
    }
  }
}

としたほうが、似たような記法で描画できるかと思います。

いじった箇所としては

というところです。

いずれの場合も、

ja.popu %>% 
  ggplot() +
  geom_bar(aes(x = 年, y = 総人口), stat = "identity") +
  scale_y_continuous(labels = label_number_kansuji())

696032fd-f8ca-4481-bca2-fd095d4acccb と描画できるようになってます。

個人的にはどちらも基本的なアウトプットが一緒なのでどちらでもいいのですが、どちらのほうが妥当なのか……というところで止まっています。

後者のグラフの漢数字表記のみの場合は、処理の中でアラビア数字から漢数字への変換が必須になると思います。 自分で書いたパッケージで申し訳ないのですが、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"))

fbe201a4-3046-4243-bd64-83de8a43a29f

ja.popu %>% 
  ggplot() +
  geom_bar(aes(x = 年, y = 総人口), stat = "identity") +
  scale_y_continuous(labels = label_number_kansuji(number = "arabic"))

960363ab-e296-434e-a0cd-70d287db7057 となるようにできます。

アラビア数字から漢数字に変換するための{arabic2kansuji}の関数群を{zipangu}に取り込んで貰えれば使えるかもしれないとは思います。ただ、アラビア数字から漢数字への変換を行う関数の取り込みがポリシー上難しいのであれば、漢数字のみ系のラベル関数は難しいと思います。

とりあえず暫定的にここまで考えていますが、上記2つの機能(「数字+億系」と「漢数字のみ系」)の関数をもし入れるとしたら別々の関数のほうがよいでしょうか。それとも引数で選択するような形のほうがいいでしょか。

uribo commented 3 years ago

素晴らしい提案をありがとうございます。 ggplot2だけがRの作図の全てではないですが、私含めユーザは多いと思うので ggplot2で使われているscalesパッケージの仕様に沿ったものが良いですね。

arabic2kansujiパッケージはCRANに登録する予定はないでしょうか。 zipanguがCRANにあるので、 リリースすることができません。もしarabic2kansujiがCRAN登録された場合には、該当の関数をインポートすることも考えられます (もちろんそちらのパッケージで目的が達成していても問題ありません)

上記2つの機能(「数字+億系」と「漢数字のみ系」)の関数をもし入れるとしたら別々の関数のほうがよいでしょうか

関数の構造をわかりやすくするために個別の関数にしたほうが良いと思います。

indenkun commented 3 years ago

ありがとうございます。

関数群を取り込んでもらうというのは言葉足らずでしたが、アラビア数字を漢数字へ変換するzipanguにとっての新関数としてコードを取り込んでもらえれば(アラビア数字を漢数字へ変換する関数をzipanguにPull Requestしてマージしてもらえれば)と言うことを意図していたのですが、確かにCRANに登録できれば単純に::で呼び出せばよいですよね。 年明けまでCARNのsubumittion teamがお休みのようなので、subumittion teamの休み明け以降に体裁を整えてCARNへの登録をトライしてみます。

関数の構造をわかりやすくするために個別の関数にしたほうが良いと思います。

別々の関数で検討してみます。

uribo commented 3 years ago

Close #27