#まえがき
##これは何?
統計データの処理、解析に特化した言語であるRの文法を、汎用言語であるJavaの書式と比較して対置してみたものです。
Javaがわかる人がRを書きたい時に使えるように、参考にしてください。
##必要な前提知識(Javaサイド)
・変数の型(int, double, boolean, String, Object辺り)について理解している
・多次元配列がわかる
・List, Mapが使える(一部サンプルコードで使用しています)
・拡張for文[Java 8]が使える(同上)
Rでやりたいことって言うと、だいたいこの辺が押さえられていればわかると思います。
##必要な前提知識(Rサイド)
・Rをインストールした
・文末にセミコロンが無くていいことぐらいは知っている
・やるき
#R-Java対置辞典
##汎用の文法
###JavaとRで共通のもの
・除算絡み以外の算術演算子(+, -, *)
・比較演算子(<, >, <=, >=, !=)
・条件論理演算子(&&, ||)、論理演算子(&, |)、否定(!)
###異なるもの
Java | R | |
---|---|---|
代入 | n = 0 |
n <- 0 |
真偽値 | true, false |
TRUE, FALSE (大文字) |
除算(整数商) | a / b |
a %/% b *1
|
剰余 | a % b |
a %% b |
累乗 | Math.pow(a, b) |
a ^ b |
インクリメント デクリメント |
++a or a++ --a or a--
|
(a <- a+1 )存在しない |
標準出力 | System.out.println("Hello, world!"); |
print("Hello, world!") |
標準入力 | Scannerクラスなど |
readline("input :") *2
|
定数の定義 | final int N = 0; |
存在しない |
コメントアウト |
//comment /* comment */
|
#comment *3
|
注釈
*1 : Rでは整数型同士の除算をしても浮動小数点型(double)で返してくれてしまうので、例えば単に10/4を入力すると2.5を返されてしまう。
*2 : readLines()
という似ているようで全く意味の違う関数が存在するためオートコンプリートに頼る場合は注意。また、コンソールinteractiveでない場合この記述は無視され、空文字列が入力されたものとして以降の処理を進めてしまう。
なお、interactiveであるかどうかはprint(interactive())
で確認できる。
*3 : 複数行にわたってコメントアウトする場合は全ての行に#を入れる必要がある。
##if, for, switch文
if(statement){
System.out.println(statement is true);
}
for(int i=0;i<n;i++){
System.out.println("for loop " + i);
}
switch(num){
case 0:
System.out.println("switch num=0");
break;
case 1:
System.out.println("switch num=1");
break;
default:
System.out.println("switch default");
}
if(statement){
print("statement is TRUE")
}
for(i in 1:n){
print(paste("for loop ", i-1))
}
switch(num,
"0" = {print("switch num=0")},
"1" = {print("switch num=1")},
{print("switch default")}
)
注釈
R for文内の標準出力にあるように、文字列と数値を+でつなぐような出力はできない(厳密にはJavaでも非推奨)。paste()
メソッドを使用する。
R switch文の各ステートメント内の{}は中身が1文のみの場合は省略可。
##二次元配列(R:行列)の各要素の呼び出し方
int[][] a = new int[4][4];
a <- matrix(ncol=4, nrow=4)
どちらも見た目はほぼ同じものを作っている(初期値がjavaだと0
、RだとNA
など違いはある。またそもそもRの場合はmatrix型の中身が取る値は数値とは限らない)
この行列の各要素の参照の仕方は以下の通り。
Java | R | |
---|---|---|
1要素の参照 | a[2][3] |
a[3,4] |
1要素の参照 | a[3][0] |
a[4] またはa[4,1] 行の先頭に限り1は省略できる |
行の参照 | a[1] |
a[2,] |
列の参照 | - | a[,2] |
##for文を使わないで済ませる(applyファミリー)
for文を使わなくなったらR一人前、みたいな風潮がある割にはあまり解説してもらえないので、Javaと対置してapplyファミリーが何をしているか見てみることにする。
###多次元配列の各要素に対する関数の適用(apply)
//ここから配列の生成と初期化
int[][] mtrx = new int[4][4];
for(int i=0;i<4;i++){
for(int j=0;j<4;i++){
mtrx[i][j] = i * 4 + j;
}
}
//ここまで
//ここから繰り返し適用する関数の用意
int add(int i){
return i+1;
}
//ここまで
//ここから配列の走査と関数の適用
for(int i=0;i<4;i++){
for(int j=0;j<4;i++){
mtrx[i][j] = add(mtrx[i][j]);
}
}
//ここまで
0~15の数値が入った二次元配列を生成し、各要素に1を加算している(もちろん対比して説明するためにこんな身の毛もよだつような冗長な書き方をしている)。
#ここから行列の生成と初期化
a <- matrix(c(1:16), ncol=4, nrow=4, byrow=TRUE)
#ここまで
#ここから繰り返し適用する関数の用意
add <- function(i){
return(i+1)
}
#ここまで
#ここから行列の走査と関数の適用
apply(a, c(1,2), add)
#ここまで
このように極めて単純に書くことができる。ここでは繰り返し実行する関数を作成したが、用意されている関数なら何でも使うことができる(例えば、sqrtを使うと全ての要素の平方根を取った行列が得られる)。
注釈
・matrix()
関数のbyrow
オプションは行列にする元ベクトル(この例ではc(1:16)
)を行単位で並べるオプション。デフォルトだとFALSEなので、このコードを実行して得られる行列を転置したものが生成される。
・apply()
関数の2番目のオプション(c(1:2)
)は適用範囲の指定。1で「全ての行に対して」、2で「全ての列に対して」同じ操作を行う。単に2次元配列に対してforループを回したくて書くのであればここは全く気にする必要はないので、不用意にいじらないこと。
具体的に書くと、以下の2つのコードがだいたい同じことをやっている。Rは書き落としとかではなく本当にこれだけ。
int sum(int[] arr){ //配列の総和を出す関数。Rには標準で用意されている
int sum = 0;
for(int i : arr){
sum += i;
}
return sum;
}
int[] sum_arr = new int[4];
for(int i=0;i<4;i++){
sum_arr[i] = sum(mtrx[i]); //全ての行に対してsum()関数を適用する
}
apply(a, 1, sum)
###HashMapの各要素に対する関数の適用(lapply)
まず、Rにおけるlist型の変数はその内部に保持する値の型を揃える必要はない、ということを理解しておく必要がある。
つまり特に何も宣言しなければJavaのMapでオブジェクト型を扱うと宣言しているようなもの。
Map<String, Object> a = new HashMap<>();
a.put("i", 123);
a.put("d", 4.56d);
a.put("b", false);
a <- list(i = 123, d = 4.56, b = FALSE)
このように整数型と倍精度浮動小数点型と論理型を同時に持つことができる。
lapplyはリストや行列に対して処理を実行し、結果をlistで返す。
Map<String, Object> a = new HashMap<>();
a.put("i", 123);
a.put("d", 4.56d);
a.put("b", false);
for(Object o : a.values()){
add(o);
} //コンパイルエラー!
a <- list(i = 123, d = 4.56, b = FALSE)
lapply(a, add) #add()関数はapply.Rから流用
apply.javaで定義したadd()関数の引数はint型なので、Javaでは上記のコードは実行できない。しかしRでは型宣言の必要がなく、変数は自動的に最も大きい型に合わせられるため、このコードを実行することができ(FALSE=0, TRUE=1として扱われる)、以下のように出力される。
> lapply(a, add)
$i
[1] 124
$d
[1] 5.56
$b
[1] 1
2次元配列に対してもlapplyは使用できるが、list型で出力しなければいけない状況でもない限り扱いづらいだろう。こんな関数だと想定してもらえればよい。
apply.java(apply.R)で生成した4×4の行列に対して、以下の2つのlapply()はだいたい同じものを出力する。
Map<String, Integer> lapply(int[][] arr){
Map<String, Integer> a = new HashMap<>();
for(int i=0;i<arr[0].length;i++){
for(int j=0;j<arr.length;j++){
a.put(String.valueOf(i * 4 + j + 1), add(arr[j][i]));
//arr[i][j]ではなくarr[j][i]であることに注意。
//Rでは2次元配列に対する走査は列単位で行われる
}
}
return a;
}
lapply(a, add)
#apply.Rと同様にaの全ての要素に対してadd()を適用するが、
#出力結果の見た目がapply.Rとはまるで違う。実行して確認してみてください
注釈
・lapply3.javaで、Map<String, Integer>
を戻り値の型としているが、実際にはKeyに入るのは1~16の数値(をString型にキャストしたもの)なので冗長に見える。
Rの行列は行番号及び列番号に名前を持たせることができるので、厳密に対応しようとするとKeyに入る値が数値ではなくなる可能性があるためこのような記述を用いている。
##文字列の処理
全体的に文字列処理はRの方が苦手。データの前処理に文字列の処理が大量に必要になるのであれば他の言語を使ったり表計算ソフトで先に済ませておくなりすることも考慮したい。
###連結
//例1
String str = "Hello" + " " + "world!"; //メモリ消費が激しくなるので非推奨
//例2
StringBuilder builder = new StringBuilder();
builder.append("Hello");
builder.append(" ");
builder.append("world!");
String str = builder.toString();
str <- paste("Hello", " ", "world!")
#附則
##本ページ内コードの実行環境
Java
-Java version: 1.8.0_231
-IDE: Intellij IDEA Community Edition 2019.2
R
-R version: R x64 3.6.0
-IDE: R Studio 1.2.1335
どうしてもうまく実行できない場合は参考にしてください。