niszetの日記

アナログCMOS系雑用エンジニアが頑張る備忘録系日記

(R) skimrの表示をカスタマイズしたい 3回目(追記修正)

まさかの3回目。

これらの記事の続きです。

niszet.hatenablog.com

niszet.hatenablog.com

先日の2回目の記事によって、無事にhistの表示のさせ方を変えることが出来たのですが、ここでteramonagiさんから

と追加の情報をいただきました。それぞれのページ、

blog.recyclebin.jp

qiita.com

を見て、ちょっとやってみた感じの内容です。

なお、この方法で関数の挙動を変えるのは上級者向けという気がします。ちゃんと内部の挙動を把握しているので後からでも分かるよ!という場合以外は1おススメしないですが、こういうことも出来るという紹介の記事ということで。

やってみる。

先の記事2つのうち、kos59125さんのブログの方で書かれている方でやってみます。

まず、inline_histの中身を確認します。

skimr::inline_hist
#> function (x) 
#> {
#>     if (length(x) < 1 || all(is.na(x))) 
#>         return(structure(" ", class = c("spark", "character")))
#>     if (all(x == 0)) 
#>         x <- x + 1
#>     hist_dt <- table(cut(x, options$formats$character$width))
#>     hist_dt <- hist_dt/max(hist_dt)
#>     structure(spark_bar(hist_dt), class = c("spark", "character"))
#> }
#> <environment: namespace:skimr>

問題となっているのはoptions$formats$character$widthが外部から変更できない(skimr:::optionsを直接編集することが出来ない)ことでした。2回目の記事では関数を変更する方法で対応していました(ただし関数はグローバル環境に置かれている)が、今回はこの環境オブジェクトskimr:::optionsをコピーした上で修正し、再びskimr名前空間に入れるという方法です(記事にあったやり方の通りです)

さて、やってみます。

# 一旦適当な変数に代入
rr<-skimr:::options

# 変更がわかりやすいように、思い切って幅を20にしてみます。グローバル環境にある自分で作った変数なのでアクセスし放題ですね。
rr$formats$character$width <- 20

# そして、skimrの名前空間に変数rrをoptionsという名前でassignする
assignInNamespace("options", rr, getNamespace("skimr"))

なお、私の今の環境ではreprexで表示させると文字化けするので以下はコンソールコピペです…2。 デフォルトの動作だと、

> skimr::inline_hist(iris$Petal.Length)
▇▁▁▂▅▅▃▁

ですが、上記の操作をした後は

> skimr::inline_hist(iris$Petal.Length)
▁▇▂▁▁▁▁▁▁▁▃▃▃▅▂▃▂▁▁▁

となります。元の関数のまま、見事に挙動が変わりましたね…。

もちろん、skimr::skim()でも同じようになります。

> skimr::skim(iris)
Skim summary statistics
 n obs: 150 
 n variables: 5 

Variable type: factor 
 variable missing complete   n n_unique                       top_counts ordered
  Species       0      150 150        3 set: 50, ver: 50, vir: 50, NA: 0   FALSE

Variable type: numeric 
     variable missing complete   n mean   sd  p0 p25 median p75 p100                 hist
 Petal.Length       0      150 150 3.76 1.77 1   1.6   4.35 5.1  6.9 ▁▇▂▁▁▁▁▁▁▁▃▃▃▅▂▃▂▁▁▁
  Petal.Width       0      150 150 1.2  0.76 0.1 0.3   1.3  1.8  2.5 ▇▂▂▁▁▁▁▂▁▅▂▃▁▁▃▂▂▁▂▂
 Sepal.Length       0      150 150 5.84 0.83 4.3 5.1   5.8  6.4  7.9 ▂▂▃▇▆▁▆▇▅▆▂▇▃▆▂▁▂▁▂▁
  Sepal.Width       0      150 150 3.06 0.44 2   2.8   3    3.3  4.4 ▁▁▁▁▃▃▅▃▇▇▂▃▂▁▃▁▁▁▁▁

ということで、skim()の方でもhistの表示幅が変わっていることが確認できましたね。

パラメータを変えるのは良くないかも。関数を定義しなおして置きなおすべきかも。

ここまで書いておいてなんですが…

今回は関数を置き換える形ではなく、パラメータを管理しているskimr:::optionsという環境オブジェクトの値を更新しました。
しかし注意すべきなのは、options$formats$character$widthはその構造と名前から察するに(本来?)文字列の表示する幅を制御する用途と考えられます。そのため、このようなパッケージ内部の変数を変更するのは副作用が考えられるので、パッケージ全体を確認する必要があるのかなと思います3。 なので、ちゃんと(?)関数を定義して、固有のパラメータを与えてあげる方が良いでしょう。この記事は長くなってしまったのでもう一回やるかな…4

元に戻すには…?

ちなみにこの方法で挙動を変えた場合、RStudioを終了して再度起動したら元に戻っていました。起動ごとにlibrary()でパッケージをロードしないといけないのと似ていますね。
パッケージ内の深いところにある関数のバグの暫定対応としてこの方法で挙動を変えた場合は、逆に起動のたびに忘れずに実行しないといけませんね。

補足

ちょっと文章的によろしくないところを全体的に直しました(2/26 1830ごろ)

最近名前空間とかスコープに関係する内容が多くなってきた気がしますね。自分なりにまとめる時期なのかもしれません。

Enjoy!!


  1. つまりniszetさんにはまだ早い技術。

  2. 一貫性がなくてすみません。これについてはちょっと調べないと理解しきれていないので…

  3. が、iris$Species <- as.character(iris$Species)して[^2]表示させてもfactorとcharacterで表示が変わらないような…?なので今回は副作用はないかもしれないです。しかしコード全てを見たわけではないので確かなことは言えないです…。あと、こうやってirisを直接編集しちゃダメですよ…。data(iris)しましょうね…。ここでは簡単のためにね…。

  4. どうかな…。inline_hist()skim()それぞれの関係とかちょっと調べることがあるので時間かかりそうです。