R言語 (3)
from source
前回はsourceからRを作って、恒例のgdbにかけてみた。それで終わってはもったいないので、少し寄り道する。
startup
libの中にご本尊のRが鎮座してるので、それを直接起動
debian:~$ MINE/lib/R/bin/exec/R MINE/lib/R/bin/exec/R: error while loading shared libraries: libRblas.so: cannot open shared object file: No such file or directory
ライブラリーの一部が見つからないと文句を言われた。ならば伝統のライブラリィーパスの指定だな。
debian:~$ ls MINE/lib/R/lib libRblas.so* libRlapack.so* debian:~$ export LD_LIBRARY_PATH=/home/sakae/MINE/lib/R/lib debian:~$ MINE/lib/R/bin/exec/R Fatal error: R home directory is not defined
少しは先に進んだけど、今度は別のエラーが発生
debian:~$ export R_HOME_DIR=/home/sakae/MINE/lib/R debian:~$ export R_HOME=$R_HOME_DIR debian:~$ MINE/lib/R/bin/exec/R R version 3.5.2 (2018-12-20) -- "Eggshell Igloo" Copyright (C) 2018 The R Foundation for Statistical Computing Platform: i686-pc-linux-gnu (32-bit) : Error in library(ggplot2) : there is no package called ‘ggplot2’ [Previously saved workspace restored] >
bin/Rって起動スクリプトを参考に、必要そうな環境変数を設定。これでやっと起動してきた。 ggplot2が見つからないエラーは、.Rprofileで指定されたのが参照されたんだな。でもアドオンの場所に、そんなの無かったと言ってきてるんだ。 こういう遊びをやっておくと、後で問題が発生してもオロオロする事が無いと思うぞ。
main
Rを構成している本体は、src/mainの中に鎮座してる。ほとんどがC語なんだけど、一つだけxxxpr.fってフォートラン語が混じっていた。 ここの場所で、
debian:main$ etags *.[ch]
しとけば、軽やかにソース内を飛び割れるぞ。
datasets
Rの本体以外は、libraryってdirの中に格納されてる。datasetsって言う楽しい名前のやつも有った。データもRの一部ですってのが嬉しい。興味深く覗いてみるか。
dataとmanの部に分かれていた。その中のdataでは、何時もお世話になる iris.Rが居た。統計の父フィッシャー先生の研究データ。
"iris" <- data.frame( Sepal.Length = c(5.1, 4.9, 4.7, 4.6, 5, 5.4, 4.6, 5, 4.4, ...), Sepal.Width = c(3.5, 3, 3.2, 3.1, 3.6, 3.9, 3.4, 3.4, 2.9, ...), Petal.Length = c(1.4, 1.4, 1.3, 1.5, 1.4, 1.7, 1.4, 1.5, 1.4, 1.5, 1.5, ...), Petal.Width = c(0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.3, 0.2, ...), Species = gl(3,50, label = c("setosa", "versicolor", "virginica")))
こんな風にデータを持ってる訳ね。次は、代表的な時系列データを見ておく。
Nile.R
Nile <- stats::ts(c(1120, 1160, 963, 1210, 1160, 1160, 813, ...), start=1871)
UKgas.R
UKgas <- stats::ts(c(160.1, 129.7, 84.8, 120.1, 160.1, ...), start = 1960, frequency = 4)
最後はおまけで、R発案者の母国ニュージーランドの国土地理院発行?の、volcano火山のデジタル地図(標高)を見る。
"volcano" <- structure(c(100, 101, 102, 103, 104, 105, 105, 106, 107, ...), .Dim = c(87, 61))
Rって地理系もサポートしてるんですね。面白そう。
tcltk
同じくライブラリーの一員としてtcltkが有った。グラフィックが今のように発達していなかった頃には、重宝されました。昔のよしみで正会員の特権を保持してます。
demo(tkfaq)
とかして、眺めるもよし、下記の簡易GUIをやってみるのも、昭和の時代にタイムスリップしたようで楽しいかも。
library(tcltk) tt <- tktoplevel() tkpack(txt.w <- tktext(tt)) tkinsert(txt.w, "0.0", "plot(1:10)") eval.txt <- function() eval(parse(text = tclvalue(tkget(txt.w, "0.0", "end")))) tkpack(but.w <- tkbutton(tt, text = "Submit", command = eval.txt))
テキストエリアにplotコマンド(のテキスト)を表示しておいて、送信ボタンで、それを実行する例。使い終わった、 tkdestroy(tt) して、痕跡を消しておく事。
pb <- tkProgressBar("test progress bar", "Some information in %", 0, 100, 50) Sys.sleep(0.5) u <- c(0, sort(runif(20, 0, 100)), 100) for(i in u) { Sys.sleep(0.1) info <- sprintf("%d%% done", round(i)) setTkProgressBar(pb, i, sprintf("test (%s)", info), info) } Sys.sleep(5) close(pb)
プログレスバーの表示方法。CUIの人も、これぐらいなら使ってもいいかも。
po
ソースファイルの要所にpoって名前の翻訳済み文字列ファイルが用意されてる。我が日本もある。どなたかの貢献であろう。普段は英語版で使っているんだけど、たまには、日本語してみるかな。
debian:tmp$ LANG=ja_JP.utf8 R : 'demo()' と入力すればデモをみることができます。 'help()' とすればオンラインヘルプが出ます。 'help.start()' で HTML ブラウザによるヘルプがみられます。 'q()' と入力すれば R を終了します。
欠損値の処理
前回の富士山温度データに欠損が有った。
352 2019 4 13.6 -14.9 353 2019 5 20.0 NA 354 2019 6 21.8 5.3 355 2019 7 24.1 5.2 356 2019 8 28.4 6.9 357 2019 9 25.1 5.1
欠損値は無視するって言う na.rm=T が有ったけど、こんなの手軽に使っていいのか? 今回の場合は、問題の5月のデータは正規で30個。それが一つ欠けても大勢に影響は無いだろう。統計的な裏付けとして、30個の母数から29個をサンプリングして平均を求めましたって考え。
気味が悪いので、欠けたデータをねつ造する方法を考える。すぐに思い付くのは、4,6月の平均を使っちゃう方法。一番簡単そう。
でも、欠けたデータが8月だとしたらどうよ? 8月は一年で一番気温が上昇する月。7,9月の平均を取っちゃうと、2度も冷夏な年になっちゃう。さて、どうする?
思い付くのは、29年間のデータの平均を求めて、それを埋め込んじゃ方法。
!> mean(subset(tmp$fuji, tmp$mm == 8)) [1] 6.351613
欠損値は余り気にする必要は無いってスタンスが多いけど、場合によっては大変になる事があるそうな。そんな時の解決方法が、 欠損データの処理 で、解説されてた。
暑さ寒さは彼岸まで?
この所、涼しくなったかと思うと、また暑さがぶり返したりしていて、秋と夏が喧嘩してるみたい。昔の人は、そんな状況を、「暑さ寒さは彼岸まで」と言っていた。本当か?
二番煎じの感はあるけど、オイラーもやってみる。
気象庁へ行って、ローカルエリアのデータを取ってきた。(URLは前回参照)
2010-2019年にかけての9月と10月の日毎の平均、最高、最低温度。これだけ有れば何とかなるだろう。
library(dplyr) con <- pipe("sed '1,6d' data.csv | sed 's#/#,#g' | cut -d',' -f1-4,7,10") so <- read.csv(con, header=F) colnames(so) <- c("yy", "mm", "dd", "av", "hi", "lo")
取ってくるデータによってCSVの項目位置が微妙に違うので、前回からちょびっと修正した。 一つ注意が有る。EXCEL仕様の 2010/9/20 みたいなのの "/" を "," に置き換える時、正規表現の最後に g を付けておく事だ。このgが無いと、最初の / を変換しただけで満足してしまう。
取ってきたデータに欠損値が無いか一応確認。普通はsummary(so)とか使うんだけど、それだと、yy,mm,ddも出てきちゃう。そんな訳で、必要なものだけを選んでサマリーしてみた。 Rが提供するpipe関数といい、libraryがサポートしてるパイプといい、なかなか便利。
> so %>% select(av,hi,lo) %>% summary() av hi lo Min. : 5.50 Min. : 7.70 Min. : 1.80 1st Qu.:14.30 1st Qu.:19.50 1st Qu.:10.20 Median :18.20 Median :23.20 Median :14.15 Mean :17.99 Mean :23.28 Mean :13.95 3rd Qu.:21.40 3rd Qu.:26.98 3rd Qu.:17.80 Max. :28.50 Max. :34.80 Max. :24.90
わずか2月の間に、気温が大幅に低下してる。まあ、こちらには負けるけどね。 米コロラド州で48時間の間に気温38.3℃から1.1℃に急変。
冒頭で利用するライブラリーを宣言するのは、良い習慣だ。で、今回のやつを宣言すると
Attaching package: ‘dplyr’ The following objects are masked from ‘package:stats’: filter, lag The following objects are masked from ‘package:base’: intersect, setdiff, setequal, union
こんな警告が出て来る。名前の衝突が発見されたんで、stats::{filter,log}とかは、マスクしとな。statsとかbaseにあるやつを使いたい場合、パッケージ名を前置すればいいんだな。
こういう事が有るから、最初にロードしとくって重要。
データは時系列(9,10月が年毎に繰り返されるるやつ)なんで、それ風にしとく。
av.ts <- ts(so$av, start=c(2010,9),frequency=61) hi.ts <- ts(so$hi, start=c(2010,9),frequency=61) lo.ts <- ts(so$lo, start=c(2010,9),frequency=61)
61日分のデータが続いたら、年がインクリメントされるって指定で大丈夫だったよ。
ts.plot(window(av.ts,c(2012,1),c(2012,61)))
普通にグラフを書いちゃうと10年分が表示されちゃう。そういう時はwindowを使って期間を抜き出せばよい。但し、X軸の表示がチト怪しいけどね。
上で時系列データを作ったけど、こういう場合は使い道無いな。素直にウェザーニューズに倣ってやってみるか。それも能の無い話だ。
数字で見せられるよりグラフでしょう。統計っぽく、Rによる箱ひげ図の描画 を参考にね。
show <- function() { for (i in unique(so$yy) ){ q <- so %>% filter(yy== i) %>% select(av) boxplot(q[10:19,], q[20:29,], q[30:39,], main = as.character(i)) dmy <- readline("Hit <RET> key to next ") } }
元記事では、彼岸の前後の週も含めて、平均値が列挙されて表で結果が示されている。でも平均値と言うと丸められてしまって、寒暖差が見えない。ならば、箱ひげ図だろう。
一つの箱ひげデータが1週間では、ちと心もとないと思ったので、10日区切りにした。一年毎に1グラフ。年を抽出する為、ユニーク関数を使ってみた。年でフィルターして、平均気温の61個のデータをqに保存。
行番号は連番になってるんで、それをインデックスにして、抜き出した。最初、中間変数qを使うのを止めて、パイプで繋ぎ、boxplot( .[10:19,], .[20:29,], ..) としたけど、最初の箱しかグラフにならなかった。パイプで来たデータは展開されないのね。なんかそこを逃げる方法が有りそうだけど、深くは追及しない。
boxplotのタイトルは、main って引数に文字列で指定するようだ。helpでの説明には出ていなかったぞ。例を実行したら、使用してる奴が居て、それを真似させてもらった。それから、数字を文字列にする方法で、ちょっと悩んだのは秘密だ。
mainは何者ってんで、boxplot.Rを参照してみると、こんなコードが有った。
do.call("title", pars[names(pars) %in% c("main", "cex.main", "col.main", "sub", "cex.sub", "col.sub", "xlab", "ylab", "cex.lab", "col.lab")])
グラフィック関係の山ほど有るオプションの一員ぽい。全くGUI系は難儀だなあ。
まあ、これで、年毎にグラフを生成させて、フムフム出来るようになったけど、2019年のデータを見てて、あれ? 前年はどうだったって希望は叶えてくれない(老人性痴ほう症)。
ならば一遍に見られるようにしちゃえ。
# arg k for what graph: "lo", "av" (default), "hi" mkgr <- function(k="av") { par(mfrow = c(2,5)) for (Y in unique(so$yy) ){ q <- so %>% filter(yy== Y) %>% select(all_of(k)) boxplot(q[10:19,], q[20:29,], q[30:39,], main = as.character(Y), ylim = c(5,35)) } }
mfeowってパラメータを宣言して、縦に2段、横に5つのグラフ領域を指定。この描画デバイスにグラフを書くと、勝手にエリアを埋めてくれる。またboxplot中にylimを設定して、表示温度範囲を固定した。こうしておかないと、それぞれのグラフ内で最適化されてしまって、比較にならない。
この温度範囲の決定にあたって、最初は c(10,30) にしてたんだけど、これだと、最高気温、もしくは最低気温でレンジ外になる年が有ったんで、広めた。こんな所でも、この時期は寒暖の差が激しいって分かって、面白かったぞ。
> mkgr(k='hi') Note: Using an external vector in selections is ambiguous. Use `all_of(k)` instead of `k` to silence this message. See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>. This message is displayed once per session.
それから、最初debian機のR3.5.2で、パイプラインの最後のselectのを、select(k)ってしてたんだ。たまにはウブでもと思ってR3.6.4でも試したら、上記のような教育的指導がなされた。URL先にはFAQって書いてあるんで、みんなこの道を辿っているのね。安心した。
今度は別の要求。最高気温、平均気温、最低気温を同時に眺めてみたい。これはもうPDFにでも落として、印字してから眺める鹿。
pdf() mkgr(k='hi') dev.off()
こうすると、Rplots.pdfって名前のファイルが出来上がる。 何時も同じ名前じゃいやだって人はpdfデバイスを開く時に、ファイル名を指定してもよい。更に、紙の大きさとかfontsの指定等も、pdf関数の中で指定出来る。
これをUSBに焼いて、散歩の途中でコンビニに寄ってくれば良い。
改めて説明するまでも無いと思うけど、それぞれの年度内では、左から、9月中旬、9月下旬(アバウトに彼岸期間中)、10月上旬って並びになってる。
箱ひげの見方。データを昇順に並べる。上下の横棒は、最高・最低値。箱の上辺はデータの個数の3/4ポイントの値、下辺は1/4ポイントの値。箱の中の太い横棒は、丁度真ん中の値(ほぼ平均値)となる。横棒の外側に有る丸は、外れ値(平均から大幅にずれたやつ)を表している。
拡がりが大きいほど、寒暖差がある、体に堪えるって事だ。
今のコードは、落としてきたデータの半分しか使っていない。全部のデータを使ってやらないともったいない。各月を前半・後半に分けて、4本の箱ひげセットにしてみるかな。ひと箱、15日分のデータになるんで、箱の威力を増すだろう。改造は直ぐに出来るからやってみるか。そして、春の彼岸も確認してみるかな。
とか思っていたら、 女房が言うには、この時期(春の彼岸も)、頭が痛くなるそうな。気温より気圧が原因と言うけど、どうだろう。気圧も含めて検証しろって事かな?
夏の異常気象をオープン・データで確認 (1/2) あっ、こんなのも有った。
input
上でグラフを一枚ずつ送って行く方法。多分pauseとかだろうってんで、血眼になって探したけど、見つからず。そんな検索の途中で見つけたやつ。
> q <- menu(c('R', 'scheme', 'python')) 1: R 2: scheme 3: python Selection: 2 > q [1] 2
なんかCUIのI/Fで良く使ってそうなのが有った。脳の肥やしなるな。
で、件のやつは、素直にreadline()で良いらしい。
でも、グラフを書く exampleを実行すると、 "Hit <Return> to see next plot:" こんなのが出てくる。何処で出しているのだろう? ちょっと気になるんで探してみる。
./library/base/po/ja.po:msgid "Hit <Return> to see next plot: " ./library/base/po/nn.po:msgid "Hit <Return> to see next plot: " : ./main/devices.c: R_ReadConsole(_("Hit <Return> to see next plot: "), buf, 1024, 0);
poファイルは現地語用の翻訳データが入ったもの。原本は英語で書かれている。 main/devices.c
void NewFrameConfirm(pDevDesc dd) { if(!R_Interactive) return; /* dd->newFrameConfirm(dd) will either handle this, or return FALSE to ask the engine to do so. */ if(dd->newFrameConfirm && dd->newFrameConfirm(dd)) ; else { unsigned char buf[1024]; R_ReadConsole(_("Hit <Return> to see next plot: "), buf, 1024, 0); } }
この関数、何処から呼ばれてる? 追ってみたら、library/graphics/src/graphics.c からだった。更に潜ったら面白いかも。