2022年10月18日火曜日

データサイエンティストになるのにRとPythonのどちらを勉強したら良いですか?

このエントリーをはてなブックマークに追加
Pocket

「データサイエンティストになるのにRとPythonのどちらを勉強したら良いですか?」と言う御題が出されて、伝統的な統計解析だとRのライブラリが充実しているが、機械学習だとPythonが充実しており、システム開発ではPythonの一択だが、同僚や共同研究者にあわせないといけないから両方覚えることになると言うような指針が示された上で、それぐらい自分で考えられない人は何者にもなれないと言うような話になっていた*1

適切な説明だが、RとPythonの違いが気になる人は少なくない。あえてデータ分析の道具として大きな優劣にならない文法面からのRとPythonの違いをもう少し細かく考えてみよう。Pythonはクラス型オブジェクト指向の性質が、Rは関数型の性質が強く出ていることが分かる。つまり、

  1. Pythonはインデントを使ったブロック定義などの可読性をあげるための工夫があるが、Rは関数の最後の評価式が戻り値になるなど、可読性よりも簡潔さを重視している
  2. Pythonには(他の人気プログラミング言語と同様に)変更可(mutable)な型があるがRにはないので、Rの方がPythonよりも変数の取り扱いが明快
  3. Pythonのスコープ外変数の更新はnonlocal宣言とgloal宣言の使い分けで非直観的な面がある*2が、RのEnvironment指定は一貫性がある*3
  4. Rの関数はそもそも無名関数が変数に入っているだけなので、Pythonの無名関数(ラムダ式)のような制限*4は無い
  5. Pythonはクラスごとに演算子オーバーライドができるが、Rはそれに加えて新たな演算子を作ることができ、ビルトイン関数のクロネッカー積%x%やtidyverse/dplyrのパイプ演算子%>%がこれで実現されている
  6. Pythonのモジュールよりも、Rのパッケージの方が短い記述で扱える。Pythonはモジュールで定義された関数を呼ぶときに接頭語をつける必要があるが、Rのパッケージの関数はビルトイン関数と同様に扱える*5
  7. Pythonはモジュールで定義されたオブジェクトは、明示的にモジュールで定義された関数に渡す必要がある*6が、Rは総称関数になっていて簡潔に記述できることが多い*7
  8. Pythonは(mapなどの例外はあるにしろ)ループでスカラーごとにデータ処理を書かないといけないことが多いが*8、Rは(スカラー変数は長さ1のベクトル変数に過ぎないだけに)多くの関数がベクトル処理対応となっていて、ユーザー定義関数もベクトル処理ができないともはや不便なので、文化的にループが少ない
  9. RとPythonの基本構成で比較すれば、統計解析に特化しているRにはデータ構造や関数がビルトインされている一方で、Pythonの方には便利な道具は何も無い。random、datetime、Pandas、Numpy→SciPy、Statsmodels、Matplotlibと言ったモジュールをインポートする必要がある
  10. Rは行列やデータフレームがビルトインされたデータ構造なので、CやFotranで書いたR拡張にそれらを引き渡ししやすいし、Cでそれらを作成してRに引き渡すことも容易である*9
  11. 1と2と3で色々と変化したPythonと比較して、Rは後方互換性が保たれてきたので*10、今後も同様であることが期待できる

と言うような違いがあって、Rの方がコードが短くて済むし、慣れれば直観的であるため、だらだらとコードを書いて試行錯誤的に分析を行なうのに向く。ただし、これらの特徴のために可読性や拡張性や省メモリ性を犠牲にしている面もあり、Pythonの方が初学者に易しく習熟しやすと言うのが(私は信じていない)定説だ。

以上の違いは今後解消されることもありえる。昔はRとPythonの違いはもっとあったが、Pandas、Statsmodels、Matplotlibの登場で差はかなり縮まったし*11、文法的にもRの数式には代入ができるがPythonの数式はできなかったと言うのはPython3.8で無くなったりもしている*12。重ねて言明しておくが、PythonよりもRを学ぶべきと言う話にはならないので注意されたい。

ところでこれから学びはじめる人にこんな具体的で細かい話をしても理解してもらえないとは言わないように。

*1RとPythonのどちらを勉強したら良いですか? - Togetter

*2関数の親スコープにある変数を更新する場合はnonlocal宣言をする仕様だが、親スコープがグローバル領域の場合は例外的にglobalを使わないといけない(Pythonの静的スコープはめちゃめちゃなのか? - Qiita

)。

*3assign("変数名", 値, envir = parent.env(environment()))と書いておけば、親スコープがグローバルでもそうでなくても値を代入できる。

*4宣言文無しの一行で書けるもので、関数の呼び出しを含まないものに限る。

*5パッケージ::関数()と言うように明示的に呼び出すこともできる。

*6import numpy as npをした後に、np.array([1,2,3])と言うように接頭語をつける。

*7書いているときは良いのだが、ぱっと見、何のオブジェクトをどう処理しているか判別が付かないので、可読性は低下する。例えばsummary(result)と書けるが、これではresultが何の結果か分からない。ひどいときはresultがデータフレームだって事もある。

*8Pandasを入れなければapply関数もない。

*9Rcppを使えばかなり透過的に扱えるが、唯のCでもSEXP構造体として気軽に扱える(e.g. Rの拡張でライフゲームを作ってみる - 餡子付゛録゛)。Pythonの場合は、オブジェクト指向の影響が強いためか、実際に数値解析を行なうコード以外に、PyMethodDef,PyModuleDef構造体とPyMODINIT_FUNCを戻す関数も書かないといけないし、データ解析でよく使うデータ構造はモジュールが定めたデータ構造なので、Pythonだけではなくモジュール依存のコードになる。お決まりの呪文で、大した分量でもなく、NumPyの行列も多次元配列なわけでC拡張で扱うのが困難と言うわけではないが。

*10そうは言ってもプロットの日本語処理回りでの変化に戸惑ったことがある。

*11これは逆も真で、機械学習、特に深層学習ではPythonの一択のような雰囲気があるが、RにもCaretと言う有名パッケージもあるし、Pythonから何年も遅れてで、機械学習は最先端をいじってなんぼ感があるから致命的な気もするが、KerasやTorch(PyTorchのポーティング)やLightGBMといった人気製品が利用可能になっている。

*12Assignment Expressionsが実装された。

0 コメント:

コメントを投稿