perl遣いのためのR言語入門


[R Advent Calendar 2020](https://qiita.com/advent-calendar/2020/rlang)－ 20日目の記事です。

本記事のトピックが「ささる」読者層はかなり限られるかもしれません。すでにRを触った経験のある方にはあまり有用な情報はないと思います……。

# はじめに

筆者は、元々perlを得意としていて、仕事の都合上Rもマスターせなとなって、「何なのこれ?」と大いに戸惑いながら、何とか数百行程度のスクリプトは組めるようになった者です。

そんな人がこの日本にどれぐらい居るのか知りませんが、ゼロというわけではありますまい。今後あとに続くかもしれない人たちのために覚書的なものを書いておきたいと思います。

# perlとRはこの辺が違う

## 守備範囲が違う

**教訓：適材適所で使い分けよう。文字列をRで処理するな。グラフをperlで書くな。**

### perlの方がうまくやれること

文字列の処理は、もう圧倒的にperlが楽です。そして、普通にperlでスクリプトを書けばRよりは相当に高速処理をこなしてくれます。ここでいう文字列の処理には、文字列データの分割・結合・正規表現による検索などを含みます。

### Rの方がうまくやれること

たくさんの数値データが集合したデータセットに対して「何かする」のがRは得意です。特に、統計計算とグラフの計算においてはperlは比較になりません。手順が小難しい統計処理を一発でやり遂げてくれる関数、そうした関数をパッケージングしたライブラリモジュールがたくさん用意されています。論文などでよく見る計算、よく見るグラフについては、たいていそれを実現するためのライブラリがどこかで公開されています。

## 用語が違う

配列・行列・リスト--この紛らわしすぎる代物たち……。

* perlの「リスト」・「配列」はRの「ベクトル」である
* Rの「リスト」はperlでは「リファレンスを値として持っているハッシュ」である（内部的には知らない、少なくとも実用上は）
* Rの「配列」は3次元以上のデータを取り扱えるようにした行列である（私は実際に使われているところを見たことがない）
* Rの「行列」は見かけ上二次元のデータを表現できるようにした「ベクトル」である
* Rの「データフレーム」も二次元データを扱い、一見行列と同じに見えるが、「列のデータ」をもつ等長のベクトルを束ねてリストにしたものであり、中身は全然違う

Rを勉強し始めたとき、特に行列とデータフレームの違いには戸惑いました。関数によってはどちらも同じように引数として受け付けてくれたりするので、余計に混乱に拍車がかかります。

**教訓：特に最初のころは「自分はどちらの言語の『リスト』を扱おうとしているのか」常に頭のモードを明確にしておこう。ほんと紛らわしいので。**


## プログラミングスタイルが違う

### Rは「型の違い」にうるさい

perlの変数は、「その時の都合」に応じて数値として扱ったり文字列として扱ったり柔軟で、こんなことが普通に出来ますが:

```
$a="1";
$b="2";
print $a + $b + 3;
```

Rの変数は、最初に厳密に「型」を決めて使い始めなければなりません。文字列として使い始めた変数をそのまま数値として扱ったりはできません。次のスクリプトは最後の行でエラーを吐いて止まります。

```
a <- "1"
b <- "2"
print(a + b + 3)
```
こういう時はいちいち関数を呼び出して型の変更を行わなければなりません。

```
a <- "1"
b <- "2"
print(as.numeric(a) + as.numeric(b) + 3)
```

perlに慣れ親しんでいるとこれが面倒でね…。

**教訓：扱っている変数にどんな種類のデータが入るべきなのか常に意識してスクリプトを書こう！**

### エラーを教えてくれない！

スクリプトを作り始めて、最初に困惑を覚えるのがここではないでしょうか。

perlのスクリプトを実行中にエラーが発生した場合、perlインタプリタはエラーメッセージと共にエラー箇所の行番号を出力してくれます。ところがRはエラーの種類は分かりますが、行番号を出してくれないので、長いスクリプトになるとどこを見直せばいいのか皆目分からなかったりします。

いちおうtraceback()関数というのがあり、エラーが発生した**直後**であれば、問題が起きた行の番号を知ることができます。でも、一度でも他のことをやってしまうと、`traceback()`を呼び出そうとしても「もう分からない」といってそっぽを向かれてしまいます。非常に不便。とにかく、エラーが起きたらすかさず`traceback`する習慣を身につけておかねばならないのです。

実は、オプションを変えて、エラー発生時にただちにブラウジングモードに入る手もないわけではありません。

参考:
[Debug in R #4: Post-Mortem Debugging](https://kohske.wordpress.com/2011/05/13/debug-in-r-4-post-mortem-debugging/)

でも、これくらいのことは標準で普通に出来るようになっているべきだよねえ。perlを含む他の大半のプログラム言語がそうなっているように…。

```
options(error=recover)
```

perlに慣れ親しんでいるとこれが面倒でね…。

**教訓：エラーで止まったらその場で空かさずtraceback()を忘れるな!**

## 普段融通が利かないくせに妙なところで余計なおせっかいをかますR

### csvファイルからデータを読み込むと、文字列を勝手に因子型とかいう訳の分からないデータ様式に代えてしまう。

`read.table()`の仕様で、オプションに`stringsAsFactors=F`というオプションをつけておかないとこういうことをされます。統計学で言うところの「カテゴリー変数」に対応しようとした結果としてこう云う仕様が採用されているのだと理解はできますが、バイオインフォマティクスなどやっているとふつう文字列そのものが解析対象となっているので、いろいろ面倒な問題が発生します[^1]。
[^1]: R-4.0からこの仕様は変更されたらしい（詳細未確認）。

**教訓：read.table()には必ずstringAsFactorsをつけよう。**

### データフレームからデータを切り出すとデータフレームじゃなくなる(ことがある）件

```
> df1 <- data.frame(x=1:2, y=1:2, z=1:2)
> df2 <- data.frame(x=1:2, y=1:2)
> df1
  x y z
1 1 1 1
2 2 2 2
> df2
  x y
1 1 1
2 2 2
> class(df1[,-1])
[1] "data.frame"
```

ここまではいいのですが、

```
> class(df2[,-1])
[1] "integer" 
```

ああもう！ スライスした結果が「1列だけ残る」場合、R先生はこれを勝手にデータフレームではなくしてベクトルに変換してしまうのです。さらに奇妙なのは、列方向にはこういうことをやってくれるのに、行方向のスライスには何もしないことですね。訳が分かりません。

対話処理だけで仕事を進めているのであればこれでも困ることは少ないのかもしれませんが、スクリプトを組んで長いバッチ処理をさせようとするとき、こういうパターン（同じ処理を試みたはずなのに似て非なる形態の結果が得られる)が途中にあるととても困るのです。

一応の対策は用意されていて、こういうときは、次のようにしなければなりません。

```
df2[,-1, drop=F]
```
最初からそうやってよーと思うのは私だけではないのではないでしょうか。

**教訓：Rは気まぐれ。perl、君は良くできた子だった。**

# まとめ

要するにこの筆者はRが嫌いなんだな、と思った貴方。**正解です。**何となくpythonが人気なのも理解できる気がするんだ……。そして個人的にはjuliaの発展に期待しています。

とはいえ、2020年末の時点においてグラフ作成の多機能ぶりでRに太刀打ちできる処理系はあまり見当たらないし、あるものは使わないわけにはいかないので、**いろいろ癖があることを理解したうえで勉強に取り組みましょう、**というのを当記事の結論としておきます。