R 言語

作成日 : 2020-10-06
最終更新日:

R 言語との出会い

R 言語との出会いは最近で、2020 年のことだ。統計には興味を持っていたが、年寄りの常で、 新しい言語を覚えるのが苦痛になっていたのだ。

インストール

Linux へのインストール

図書館で、馬場 真哉:R 言語で始めるプログラミングとデータ分析 という本を見つけ、重い腰を上げることにした。 まずインストールだ。 p.23 では、Windows へのインストール方法が示されている。 私は例によって Windows Subsystem for Linux (WSL) の Ubuntu 20.04 にインストールしてみることにした。

まず、統計数理研究所の https://cran.ism.ac.jp/ にアクセスする。 Download R for Linux をクリックすると、5種類のディストリビューションがあるので、 ubuntu をクリックすると、いくつかフォルダがある。よくわからないので、 README.html を見てみる。R 4.0 が最新のようである。Ubuntu の 20.04 の開発コードは Focal Fossa であるから、
deb https://cloud.r-project.org/bin/linux/ubuntu focal-cran40/
この行を /etc/apt/sources.list に加えて
$ sudo apt-get update
$ sudo apt-get install r-base
でインストールできる、と読んだ。果たしてその通りできるだろうか。 なお、現在は apt-get ではなく、単に apt を使うべきである。

$ sudo apt update
取得:1 https://cloud.r-project.org/bin/linux/ubuntu focal-cran40/ InRelease [3,622 B]
エラー:1 https://cloud.r-project.org/bin/linux/ubuntu focal-cran40/ InRelease
 公開鍵を利用できないため、以下の署名は検証できませんでした: NO_PUBKEY xxxxxxxxxxxxxxxx
W: GPG エラー: https://cloud.r-project.org/bin/linux/ubuntu focal-cran40/ InRelease: 公開鍵を利用できないため、以下の署
名は検証できませんでした: NO_PUBKEY xxxxxxxxxxxxxxxx
E: リポジトリ https://cloud.r-project.org/bin/linux/ubuntu focal-cran40/ InRelease は署名されていません。
N: このようなリポジトリから更新を安全に行うことができないので、デフォルトでは更新が無効になっています。
N: リポジトリの作成とユーザ設定の詳細は、apt-secure(8) man ページを参照してください。	

なんじゃこりゃあ。
わからないので「R 言語 ubuntu インストール」で検索した。 なお、最近私は検索エンジンには、Google ではなく、 duckduckgo.com を使っている。ヒット率は Google にはかなわないが、 主要なページは網羅されているのでこれで十分だ。 いくつかのページを見てわかったことは、 ダウンロード元の公開鍵を取得して、aptに登録する必要があるということだった。 そこで、その作業を行ってみる。

$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys xxxxxxxxxxxxxxxx
Executing: /tmp/apt-key-gpghome.m2enwflrRQ/gpg.1.sh --keyserver keyserver.ubuntu.com --recv-keys 51716619E084DAB9
gpg: 鍵xxxxxxxxxxxxxxxx: 公開鍵"Xxxxxxx Xxxxxx<xxxxxxx@xxxxx.com>"をインポートしました
gpg: 処理数の合計: 1
gpg:               インポート: 1
$

それらしい応答が返ってきた。よし、やってみよう。

$ sudo apt update

問題ない。

$ sudo apt install r-base

問題ない。

では起動できるのだろうか。馬場氏の本では端末から R を起動する方法を示していないので、これまた検索で調べる。 プロンプトに対して R と打つだけだ。

$ R

R version 4.0.2 (2020-06-22) -- "Taking Off Again"
Copyright (C) 2020 The R Foundation for Statistical Computing
Platform: x86_64-pc-linux-gnu (64-bit)

R は、自由なソフトウェアであり、「完全に無保証」です。
一定の条件に従えば、自由にこれを再配布することができます。
配布条件の詳細に関しては、'license()' あるいは 'licence()' と入力してください。

R は多くの貢献者による共同プロジェクトです。
詳しくは 'contributors()' と入力してください。
また、R や R のパッケージを出版物で引用する際の形式については
'citation()' と入力してください。

'demo()' と入力すればデモをみることができます。
'help()' とすればオンラインヘルプが出ます。
'help.start()' で HTML ブラウザによるヘルプがみられます。
'q()' と入力すれば R を終了します。

> q()
Save workspace image? [y/n/c]: n
$

日本語ではないか。すごい。

次に RStudio をインストールする。馬場氏の本では Windows のデスクトップ版のインストールを紹介している。

(注:RStuio は、2024 年 4 現在 Posit という名前に変更されている。2024-05-03

私はデスクトップ版よりはサーバ版がいいと思っていたら、やはりあった。 https://www.rstudio.org から情報をたどる。

$ sudo apt install gdebi-core
$ wget https://download2.rstudio.org/server/bionic/amd64/rstudio-server-1.3.1093-amd64.deb
$ sudo gdebi rstudio-server-1.3.1093-amd64.deb
Reading package lists... Done
Building dependency tree
Reading state information... Done
Reading state information... Done
以下のパッケージのインストールが必要です: libclang-10-dev libclang-dev

RStudio Server
 RStudio is a set of integrated tools designed to help you be more productive with R. It includes a console, syntax-high
lighting editor that supports direct code execution, as well as tools for plotting, history, and workspace management.
ソフトウェアパッケージをインストールしますか? [y/N]:y
/usr/bin/gdebi:113: FutureWarning: Possible nested set at position 1
  c = findall("[[(](\S+)/\S+[])]", msg)[0].lower()
0% [www.ftp.ne.jp へ接続しています]
0% [www.ftp.ne.jp (2001:200:601:10:206:5bff:fef0:466c) へ接続しています]
0% [www.ftp.ne.jp (192.26.91.193) へ接続しています]
0% [ヘッダの待機中です]
0% [ftp-srv2.kddilabs.jp へ接続しています]
0% [ftp-srv2.kddilabs.jp (202.255.47.226) へ接続しています]
0% [ヘッダの待機中です]
Get:1 http://ftp-srv2.kddilabs.jp/Linux/packages/ubuntu/archive focal/universe amd64 libclang-10-dev amd64 1:10.0.0-4ubu
ntu1 [19.4 MB]
99% [ヘッダの待機中です]
Get:2 http://ftp-srv2.kddilabs.jp/Linux/packages/ubuntu/archive focal/universe amd64 libclang-dev amd64 1:10.0-50~exp1 [
2880 B]
Fetched 19.4 MB in 0s (0 B/s)
以前に未選択のパッケージ libclang-10-dev を選択しています。
(データベースを読み込んでいます ... 現在 137638 個のファイルとディレクトリがインストールされています。)
.../libclang-10-dev_1%3a10.0.0-4ubuntu1_amd64.deb を展開する準備をしています ...
libclang-10-dev (1:10.0.0-4ubuntu1) を展開しています...
以前に未選択のパッケージ libclang-dev を選択しています。
.../libclang-dev_1%3a10.0-50~exp1_amd64.deb を展開する準備をしています ...
libclang-dev (1:10.0-50~exp1) を展開しています...
libclang-10-dev (1:10.0.0-4ubuntu1) を設定しています ...
libclang-dev (1:10.0-50~exp1) を設定しています ...
以前に未選択のパッケージ rstudio-server を選択しています。
(データベースを読み込んでいます ... 現在 138378 個のファイルとディレクトリがインストールされています。)
rstudio-server-1.3.1093-amd64.deb を展開する準備をしています ...
rstudio-server (1.3.1093) を展開しています...
rstudio-server (1.3.1093) を設定しています ...
Couldn't find an alternative telinit implementation to spawn.
$

Couldn't find ... と出てきて大丈夫なのだろうか。そう思いながら、 Windows 10 側のブラウザから localhost:8787 にアクセスしてみた。 画面が出てくるではないか。すばらしい。RStudio の機能がブラウザで使えることを確かめた。

なお、今後サービスを起動するときは

$ sudo systemctl start rstudio-server.service

を使う。サービスを停止するときは

$ sudo systemctl stop rstudio-server.service

とする。

WSL を Windos 11 配下で動く WSL2 の Ubuntu 22.04.3 に変更したが、新たな Ubuntu へのインストールへは行っていない(2024-05-03

Windows へのインストール

R 本体は、
https://cran.ism.ac.jp
の上部にある「Download R for Windows」をクリックすればよい。R for Windows のページが出てくる。このページには、base, contrib, old contrib, Rtools の 4 種類がある。base を選べばいい。

クリックすると、2024-05-03 現在、R-4.4.0 for Windows というページになる。ここの Download R-4.4.0 for Windows をクリックする。exe ファイルがダウンロードされる。

RStudio は、
https://www.rstudio.com/products/rstudio/download/
から RStudio Desktop (Free) をダウンロードした。最近はこれで作業をしている。

注:上記は Windows 10 時代のインストールである。現在は Rstudio (現 Posit)は使っていない。(2024-05-03)


平方数の和問題の解法

R 言語でのプログラミングは不慣れである。そこで、 平方数の和の問題を R 言語(以下 R) で解くにはどうすべきかを考えることで慣れることにした。 その経過をメモする。 一から考えるのは苦労するので、関数型言語である Scala でのアプローチを参考にした。 さて、プログラミング言語 R は関数型言語なのだろうか。 関数型言語には近いが、完全な関数型言語とは言い難い、 というのが衆目の一致するところである。とはいえ、私にとっては、 R は関数型言語として理解したい。したがって、Scala に対するアプローチを手がかりにしたのはそのためである。

まず、基本となる数の列をどのように作るかが問題である。参考にした Scala ではリストが基本となる。 R の場合は、ベクトルや行列がある。実際のデータを視野に入れたデータセットもあるが、 まずはベクトルや行列でいいだろう。

さて、平方数の和の問題だから、まずは平方数を一覧で表示したい。 平方数の和を 50 以下と限定していろいろ試行錯誤をすることにした。 まず、1, 2, 3, ... 7 という初項 1 、階差 1 、末項 7 の等差数列を出したい。 わざわざリテラルで c(1, 2, 3, 4, 5, 6, 7) などと手で打ち込むのはばからしい。 R には、初項と末項を指定するだけで階差 1 の等差数列を作る式がある。

    > seq <- 1:7
    > seq
    [1] 1 2 3 4 5 6 7

第一段はよい。次に各項を2乗したい。

    > sq <- seq * seq
    > sq
    [1] 1 4 9 16 25 36 49

ちょっと安直すぎるのではないかと思ったが、これができるのが R の長所だ。 これらを足すのも容易だ。

    > sqsum0 <- sq + sq
    > sqsum0
    [1]  2  8 18 32 50 72 98

さて、scala の場合は、平方数を表す列をずらしながら和を取るときに、次の方法を考えたのだった。 まず、ずらす幅が 1 のときの和をとる方法を考え、それを一般化するというものだった。 R でもそれはできる。

    > sq1 <- sq[-1]
    > sq1
    [1]  4  9 16 25 36 49

ただ、sq と sq1 を加えたとき、短いほうに切り詰められるという仕様にはなっていない。

    > sq + sq1
    [1]  5 13 25 41 61 85 53
     警告メッセージ:
     sq + sq1 で:
       長いオブジェクトの長さが短いオブジェクトの長さの倍数になっていません
    >    

この「長さの倍数になっていません」というのが曲者である。 もし長いオブジェクトの長さが短いオブジェクトの長さの倍数になっていれば、 短いオブジェクトが繰り返されて長いオブジェクトに足しこまれる。

    > c(7,8,3,7,9,4) + c(1,2,3)
    [1]  8 10  6  8 11  7

これは c(1,2,3,1,2,3) を加えたのと同じ結果である。 この挙動は、今回の平方数の和を求める目的には合わない。

その後の調査により、head(sq1 tail(n)) + tail(sq head(n)) という式で、 sq の先頭から n 項と sq1 の末尾から n 項を加えるということができることがわかったが、 次の方法を思いついた後だったので、この head と tail を使う方法は採用しなかった。

考え方を変えてみる。数列の末項 7 を与えて、次の数列を得る関数 series を作る方法を考えてみる。

    > series(7)
    [1] 1 2 3 4 5 6 7 1 2 3 4 5 6 1 2 3 4 5 1 2 3 4 1 2 3 1 2 1

いかにも再帰関数で作ってくださいと言わんばかりの並び方だ。まず、2つのベクトルを結合する関数を調べる。

    > append(1:7, 1:6)
    [1] 1 2 3 4 5 6 7 1 2 3 4 5 6

あとは、再帰関数を作ればいい。

    > series <- function(n) {
    + if (n <= 1) {
    + M <- 1
    + } else {
    + M <- append(1:n, Recall(n-1))
    + }
    + return(M)
    + }
    > series(7)
    [1] 1 2 3 4 5 6 7 1 2 3 4 5 6 1 2 3 4 5 1 2 3 4 1 2 3 1 2 1

その後、上記の series(7) と同じ数列を作る方法として、 組み込み関数d sequence(x) を使う方法があることがわかった。 ベクトル x に対し sequence(x) は seq(x[1]), seq(x[2]), ... をこの順に並べたベクトルを生成する。
http://www.okadajp.org/RWiki/?ベクトルTips大全#h138ee6f
したがって、series(7) は sequence(7:1)とと同じである。 これがあるとわかっていれば、下記の inits 関数を作る必要はなかった。 しかし、下記の tails に相当する既存の関数はなさそうである。結局は自作するしかなかったようだ。

さて、以上は初項 1 から末項 n までの階差 1 の等差数列を前提としていたが、 ベクトルを与えてこのようなことができるかを確かめたい。つまり、 次の挙動をする関数 inits と tails を定義したい。

    > inits(1:7*1:7)
    [1] 1 4 9 16 25 36 49 1 4 9 16 25 36 1 4 9 16 25 1 4 9 16 1 4 9 1 4 1
    > tails(1:7*1:7)
    [1] 1 4 9 16 25 36 49 4 9 16 25 36 49 9 16 25 36 49 16 25 36 49 25 36 49 36 49 49

ベクトル v で最初の要素を除くベクトルを返す操作は v[-1] で、 最後の要素を除くベクトルを返す操作は v[-length(v)] で、それぞれ与えられる。 これを手がかりに作ることができるだろう。

    > inits <- function(v) {
    + if (length(v) <= 1) {
    + M <- v
    + } else {
    + M <- append(v, Recall(v[-length(v)]))
    + }
    + return (M)
    + }
    > inits(1:7*1:7)
     [1]  1  4  9 16 25 36 49  1  4  9 16 25 36  1  4  9 16 25  1  4  9 16  1  4  9
    [26]  1  4  1
    > tails <- function(v) {
    + if (length(v) <= 1) {
    + M <- v
    + } else {
    + M <- append(v, Recall(v[-1]))
    + }
    + return (M)
    + }
    > tails(1:7*1:7)
     [1]  1  4  9 16 25 36 49  4  9 16 25 36 49  9 16 25 36 49 16 25 36 49 25 36 49
    [26] 36 49 49

おそらくこの inits と tails は、一つの高階関数を作ってそこから導けると思うのだが、ここでは省略する。

さて、i, i*i, j, j*j に対して、i と i*i には inits を、j と j*j には tails をそれぞれ作用させる。 そして作用させた結果のベクトル4本、すなわち inits(i), inits(i*i), tails(j), tails(j*j) を、 この順に上から下に並べて行列にする:

    > rbind(inits(1:7), inits(1:7*1:7), tails(1:7), tails(1:7*1:7))
          [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14]
     [1,]    1    2    3    4    5    6    7    1    2     3     4     5     6     1
     [2,]    1    4    9   16   25   36   49    1    4     9    16    25    36     1
     [3,]    1    2    3    4    5    6    7    2    3     4     5     6     7     3
     [4,]    1    4    9   16   25   36   49    4    9    16    25    36    49     9
          [,15] [,16] [,17] [,18] [,19] [,20] [,21] [,22] [,23] [,24] [,25] [,26]
     [1,]     2     3     4     5     1     2     3     4     1     2     3     1
     [2,]     4     9    16    25     1     4     9    16     1     4     9     1
     [3,]     4     5     6     7     4     5     6     7     5     6     7     6
     [4,]    16    25    36    49    16    25    36    49    25    36    49    36
          [,27] [,28]
     [1,]     2     1
     [2,]     4     1
     [3,]     7     7
     [4,]    49    49

ここまでくれば、上記の行列を、2 行目と 4 行目の和、3 行目、 1 行目をそれぞれこの順に選んだ行列を作り、 その行列に対して和の昇順に列を整列させれば答が出るだろう。

    > x <- rbind(inits(1:7*1:7)+tails(1:7*1:7), tails(1:7), inits(1:7))
    > x[,order(x[1,])]
         [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14]
    [1,]    2    5    8   10   13   17   18   20   25    26    29    32    34    37
    [2,]    1    2    2    3    3    4    3    4    4     5     5     4     5     6
    [3,]    1    1    2    1    2    1    3    2    3     1     2     4     3     1
         [,15] [,16] [,17] [,18] [,19] [,20] [,21] [,22] [,23] [,24] [,25] [,26]
    [1,]    40    41    45    50    50    52    53    58    61    65    72    74
    [2,]     6     5     6     5     7     6     7     7     6     7     6     7
    [3,]     2     4     3     5     1     4     2     3     5     4     6     5
         [,27] [,28]
    [1,]    85    98
    [2,]     7     7
    [3,]     6     7

整列を行う関数の名前に sort が使われていないことに注意する。 まず order で整列されるべき列の指標、すなわち添字のベクトルを order によって求める。 値を整列させるためには、order によって求めた添字のベクトルによって並べ替える。 このあたりが、統計計算から生まれた言語であることを感じさせる。 たとえ整列させるためであっても、データのもとの順序は動かさずにおくということがわかるからだ。

さて、求める結果が得られるまでもう少しである。現在は 1 から 7 までの数列に対して操作しているが、 これを 1 から 32 までとする(平方数の和は 1000 以下なので、これでよい)。 さらに、現在は横方向に長い行列になっているが、 これを適当なタイミングで縦方向に長い行列にしよう。そして、和が 1000 以下となるように、 適当なところでフィルタをかければよい。

さて、データを多くすることは問題ない。 また、横方向に長い行列を縦方向に長い方向にするには t() 関数が使える。 t だけで済ませるのはいかにも数学的だ。というのも、数学では行列 A の転置を表すのに、 AT とか、tA のように、 T または t の1文字の記号で済ませるからだ。

問題はフィルタをかけるところだ。Scala でも、また Haskell でも、filter という関数があって、 リストに対して条件を満たす要素を抽出できた。ところが、 R は違うのである。まず、ベクトルの filter に相当する処理はこうだ。

    > x <- 1:10
    > x
    [1]  1  2  3  4  5  6  7  8  9 10
    > x[x < 5]
    [1] 1 2 3 4

これが行列になると一筋縄ではいかない。

    > mm <- array(1:12, c(3,4))
    > mm
         [,1] [,2] [,3] [,4]
    [1,]    1    4    7   10
    [2,]    2    5    8   11
    [3,]    3    6    9   12
    > mm[mm<5]
    [1] 1 2 3 4

行列の形が崩れてしまう。列ベクトル[,1],[,2],[,3],[,4]の第1要素が 5 未満であるような列ベクトルを抽出するにはどうしたらよいか。 また、行ベクトル[1,],[2,],[3,],[4,]の第1要素が 5 未満であるような行ベクトルを抽出するにはどうしたらよいか。 行列演算ではこれに該当するものが見つからない。

このような場合は、行列をデータフレームに変換する。この場合、 横長行列を縦長行列にしておく必要がある。ついでに、列に名前、すなわちラベルをつける。

    > nn <- data.frame(t(mm))
    > colnames(nn) <- c("s","i","j")
    > nn
        s  i  j
     1  1  2  3
     2  4  5  6
     3  7  8  9
     4 10 11 12

こうしておいて、subset 関数と列のラベルによる条件を指定して行を抽出する。

    > subset(nn, nn["s"] <= 6)
      s i j
    1 1 2 3
    2 4 5 6

この subset 関数は、行列に対してはうまくいかない。 行列にもラベルをつけて同じようなことを行なってみようとした。

    > colnames(mm) <- c("col1", "col2", "col3", "col4")
    > mm
         col1 col2 col3 col4
    [1,]    1    4    7   10
    [2,]    2    5    8   11
    [3,]    3    6    9   12
    > subset(mm, mm["col1"] <= 2)
         col1 col2 col3 col4

以上の通り、期待した結果にはならない。

元へもどって、今までの処理をまとめてなんとかスクリプトにした。 プログラミング言語遍歴の R 言語の項を参照されたい。

なお、関数型言語における map, fold, filter の各概念における R 言語との対応は、 R のベクトルやデータフレームにリスト処理 (map, fold , filter) を適用する (miyakawataku.hatenablog.com) が詳しい。

また、【R】ふつうのスクリプト言語プログラマーのためのR言語入門 (kiito.hatenablog.com) という記事では、R オリジナルの Map, Filter, Reduce などを紹介した上で、 purrr ライブラリの有用性について述べている。

R 上の Lambda, Map, Reduce, Filter について、英語になるがここでも紹介がある。
https://helloacm.com/r-programming-tutorial-map-reduce-filter-and-lambda-examples/
こんな例がある。

    > x <- 1:100
    > Map({function (a) a*2 }, x)

パッケージについて

時系列解析パッケージ forcast

時系列解析には forcast パッケージが使える。ただし、2024-05-03 現在はインストールしていない。

    > install.packates("forcast")
    (中略)
    package ‘xts’ successfully unpacked and MD5 sums checked
    package ‘TTR’ successfully unpacked and MD5 sums checked
    package ‘quadprog’ successfully unpacked and MD5 sums checked
    package ‘quantmod’ successfully unpacked and MD5 sums checked
    package ‘fracdiff’ successfully unpacked and MD5 sums checked
    package ‘lmtest’ successfully unpacked and MD5 sums checked
    package ‘timeDate’ successfully unpacked and MD5 sums checked
    package ‘tseries’ successfully unpacked and MD5 sums checked
    package ‘urca’ successfully unpacked and MD5 sums checked
    package ‘zoo’ successfully unpacked and MD5 sums checked
    package ‘RcppArmadillo’ successfully unpacked and MD5 sums checked
    package ‘forecast’ successfully unpacked and MD5 sums checked
    

時系列解析パッケージ TSSS

時系列解析パッケージには日本初の TSSS も使える。統計数理研究所のR package TSSS(jasp.ism.ac.jp) を参照のこと。 左記のページに対しての追記は次のとおりである。

参考:Rによる時系列モデリング入門 第1章 データのグラフ化まで(xx-prime.hatenablog.com)

まりんきょ学問所コンピュータの部屋統計活用術 > R 言語


MARUYAMA Satosi