R Basic Grammar

Rの基本文法 #

プログラミング言語 #

コンピュータとは文字通り,計算をしてくれる便利な機械です.極端な話,代入と四則演算,制御構文さえあれば,理論上はどんな計算でもできるらしいです.そして,便利な計算は他の誰かがすでに作ってくれてます.(著作権とか権利の問題はあるけどな)

てなわけで,こいつらをうまく組み合わせてやればいいんです.俺らがやる程度のプログラミングつったってやることはそんなとこ.

変数と代入,そして型 #

代入なんて,これまでの数学の勉強で耳にタコができるほど聞いてきたと思います.適当な変数に適当な値を入れるあれです.Rではx <- 10みたいに書きます.これで変数xに10が代入されたことを意味します.xに10を代入した状態だとx + 20は30ですね.

代入する時,変数名は数学の解答欄みたいにxといった無味乾燥なものである必要はなく,自由に名付けることができます.原則として,代入するものがなんなのか明示的に命名するのが大事です.例えば,BMIを計算するプログラムを書いているとすると,体重60kgを変数body_weightに代入するとしたら,body_weight <- 60と書くようにします.適切な名前を考えるのはプログラミングをする上で肝中の肝です.ただし,幾つかの単語は予約語といい,プログラミングにおいて特別な意味を持つので,変数として使うことができません.

別にxに代入するのは整数に限らず小数でも,文字列でも,なんでもありです.とはいえなんでもって言われても困るので,そのなんでもについてもう少し詳しく教えます.

#

変数になんでも入れていいにおけるなんでもとは型といいます.型とは数学における集合のことです.例えば,実数の集合だったり,小数の集合だったりします.なぜ型なんて言い方をするかというと,コンピュータ独特の都合があります.コンピュータの計算資源には限りがあります.いくら実数の集合と言ったって無限に続く数字を扱うのは無理です.コンピュータのストレージに限界が来てしまいます.コンピュータは2進数で計算をしているというのは聞いたことがあると思います.3.14でも0.45でも,ある小数を2進数で表そうとすると,結構な確率で無限に続く小数になってしまいます.そんなわけで,その時々の計算に適した実数の集合(例えば,0から255までという限界を定めた実数の集合)や,小数の集合を型として扱い,計算していきます.また,違う型どうしの計算も面倒臭いです.文字列という型と実数の型の足し算を求めてくださいと言われても困ってしまいます.ある型同士の計算には,それに適した計算,演算が定義されています.

要するに似た性質を持つもの,振る舞いが決まっているものの集合を型とよび,これらを意識してコードを書いていくことになります.自分で型を定義することもできますが,基本となる型を組み合わせることが多く,わざわざ自作せず,出来合いのものを使うのがいいと思います.てなわけで基本的な型を紹介していきます.

Rにおける具体的な型と演算子 #

Rは便利なプログラミング言語なので,型といっても厳密なものではなく,ざっくりとした直感的にわかりやすいものになっています.

また,ある型には,それぞれ演算が定義されていると説明しましたが,そのような演算を示す演算子というものがあります.そんな難しいものではないです.足し算という演算における演算子は+です.

Numeric(数値)型 #

数値を表す型です.こいつら同士は四則演算ができたりと,皆さんが想像する実数の集合そのものです.実は,さらにinteger(整数)型とdouble(倍精度浮動小数点, 要するに小数)型に分けることができますが別に無視でもいいです.

この型に定義されている演算は以下のとおりです.ちなみに,この演算子らを算術演算子といいます.(名前はいちいち覚えなくていいです)

演算子意味コード例結果
+3 + 69
-4 - 13
*19 * 8152
/9 / 24.5
%/%整数商 (あまりを無視した商)9 %/% 24
%%剰余 (余り)9 %% 21
^累乗2^416

また,これら値同士を比較する比較演算子というものもあり,これらの演算の結果,後述するLogical型の値を返します

演算子意味説明コード例結果
==同等左側と右側の値が等しいかを判定1 == 1TRUE
!=不等左側と右側の値が等しくないかを判定1 != 1FALSE
>大なり左側が右側より大きいかを判定100 > 101FALSE
>=以上左側が右側以上かを判定100 >= 100TRUE
<小なり左側が右側より小さいかを判定-100 < 100TRUE
<=以下左側が右側以下かを判定100000 < 1FALSE

Logical(bool, 論理)型 #

TRUE,FALSEの二つだけからなる型です.こいつらには論理演算が定義されてます.論理演算ってかつとかまたはとかいうやつです.論理の正誤を考える時に,非常に役立ちます.

演算子意味説明コード例結果
|論理和どちらか一方がTRUEならTRUETRUE | FALSETRUE
||論理和どちらか一方がTRUEならTRUEFALSE || FALSEFALSE
&論理積両方ともTRUEならTRUEFALSE & TRUEFALSE
&&論理積両方ともTRUEならTRUETRUE && TRUETRUE
!論理否定逆を返す!TRUEFALSE

Character(String, 文字列)型 #

文字列です.Rでは文字列は"ダブルクォーテーション"または'シングルクォーテーション'で囲んで作成できます.日本語も扱えますが,極力,英数字にすることを勧めます.

greeting <- "Hello, world!"

Factor(因子)型 #

因子(Factor)は,Rでカテゴリカルデータ(カテゴリー・分類データ)を扱うためのデータ構造です. 例えば「性別(男性/女性)」や「評価(高/中/低)」などの限定されたカテゴリ(レベル)を持つデータを管理するのに適しています.データ解析の際に各ファクターごとに解析するなど直感的な操作が可能になります.

# データ
data <- c("Male", "Female", "Female", "Male", "Male")
# 因子の作成
gender <- factor(data)

print(gender)
# [1] Male   Female Female Male   Male  
# Levels: Female Male

Function(関数)型 #

詳しくは後述しますが,関数を表した型です.

他にも様々な型がありますが,一旦これくらいにしておきます.

データ構造 #

これまで型という,値一つ一つが,どの集合に属するかといった話をしてきました.次にデータ構造についてお話しします.値をどのような形式で保管するか,コンピュータ上でデータを扱いやすくするため一定の形式で収納されたデータの構造体のことをデータ構造といいます.何のこっちゃといった感じだと思いますが,実はプログラミングをする上でトップクラスに大事なところです.じっくり勉強しましょう.

Vector(ベクター) #

最も基本的かつ,最重要なデータ構造です.同じ型の値が一つ以上格納されているデータ構造です.要するに,同じ型の値を一列に並べたものです.

以下の例は変数vecに10, 20, 30, 40, 50という値たちを収納したvectorを代入している例です.ベクターを作成するにはc関数(cはcombineの頭文字っぽいです)というものの中に作りたいベクターの中身を書きます.個々の値を一列に結合させるイメージです.関数については後述の章を参照してください. vectorに収められた,値一つ一つに個別にアクセスするにはvec[1]のように,前から何個目の値が欲しいかを書く必要があります.

vec <- c(10, 20, 30, 40, 50)
#> 10 20 30 40 50

vec[3] == 30
#> TRUE

# 連続した値をまとめたいときは,[開始値]:[終了値]のようにかける
vec <- c(1:10)
#> 1 2 3 4 5 6 7 8 9

データ解析をする以上,複数の値を保存しておく必要があります.ベクターは,最も単純であるが故に,最も使い道の多く汎用性のあるデータ構造であるといえます. この後,さまざまなデータ構造を学びますが,本質的にはこのベクターの概念を拡張したものに過ぎません.

Dataframe(データフレーム) #

データ解析でよく見るデータ構造です.早い話が,Excelのように表形式になったデータフレームです. 縦の列がベクターとなっている(同じ型)こと,全ての列の値の個数が同じであることが特徴です. とはいえ,データ解析をする上では後述するtibbleというデータフレームを拡張したデータ構造を使うことが近年一般的になってきています.

df <- data.frame(
    Name = c("Alice", "Bob", "Charlie"),
    Age = c(24, 30, 22),
    Score = c(90, 85, 88)
)

print(df)
#      Name Age Score
# 1   Alice  24    90
# 2     Bob  30    85
# 3 Charlie  22    88

List(リスト) #

listは一見vectorによく似た,値を一列に並べたデータ構造です.vectorと違う点は,値の型が全て同じでなくていいという点です.いろんな型をごちゃ混ぜに一緒にしておけるデータ構造です. いろんな型を一気に扱えるならリストの方がいいじゃん,とかListとvector分ける意味あるの?とかお思いになった方もいると思いますが,基本的にデータが厳密であればあるほど計算や演算が楽になります.つまりは,管理の大変さと計算の大変さはトレードオフの関係にあると考えていただければOKです.そのため,それぞれ使い分けが重要です.

lst <- list(
    num_vec = c(1, 2, 3),
    df = data.frame(
        Name = c("Alice", "Bob", "Charlie"),
        Age = c(24, 30, 22),
        Score = c(90, 85, 88)
    )
)

# リストの中身を参照
print(lst$num_vec)  # ベクトル
# [1] 1 2 3

print(lst$df)       # データフレーム
#      Name Age Score
# 1   Alice  24    90
# 2     Bob  30    85
# 3 Charlie  22    88

tibble型 #

tibbleはtidyverseというパッケージ群で提供されている.Dataframeを拡張したデータ構造です.詳しい内容は tidyverse および tibble のページをご覧ください.

制御構文 #

プログラムは基本的に上から下へ順繰りに書いたものが実行されていきます.しかし,条件によって実行したいプログラムが違ったり,同じ処理を何度もして欲しい時があります.このような処理を記述するために制御構文があります.具体的には条件分岐のためのif,反復処理のためのforなどがあります.

つまりプログラミングで何ができるかというと,ある型の値を代入し,条件分岐や反復処理などによって計算を繰り返し,得られた結果を出力する作業になっています.

条件分岐 #

プログラムは上から順番に書いてある順に計算が進んでいきます.条件によって計算の順序や内容を変えたい時にif文を使い条件分岐をすることができます. 例えば,あるテストの得点scoreに応じて,評価を下すプログラムを書くとします.80点以上なら天才,60点以上なら合格,それ未満なら不合格と判定するプログラムです. 基本的にif (論理式) { 処理内容 }の形で書いていくことになります.論理式の部分について,上で説明した論理型を返す関数や,値同士の比較演算を書く必要があります.

score <- 78

if (score >= 80) {
    print("天才")
} else if (score >= 60) {
    print("合格")
} else {
    print("不合格")
}

if (score >= 80)の()の中の部分では,比較演算子を用いてscoreが80点以上であるとき,TRUEになる演算が書かれています.()の中身がTRUEになった時,{}の内容が実行されます.else ifについては初めのif ()がFALSEでelse if ()の中身がTRUEの時,実行されます.最後のelseでは上のifelse ifが全てFALSEだった時に実行される様になっています.

条件分岐を書く際には,毎回else ifelseを書く必要はなく,例えば例外的な処理をしたい時,if (例外の条件) {例外処理}のようにif文だけを書くこともできます,また括弧内の論理式の部分には,論理型の説明にあったように&|を用いて論理演算を記述することで複雑な条件を表現することもできます.

反復処理(ループ処理) #

人がプログラムを書くのは反復処理をしたいがためでしょう.反復処理では,同じような計算を繰り返す際に,少ない記述量で書くことができます. 例えば,テストの得点のvectorscoresが与えられたとします.合格点以上の点数のみを取り出したい時,for文を使って書いてみましょう.

scores <- c(100, 24, 60, 59, 81, 40, 45)

for (score in scores) {
    if (score >= 60) {
        print(score)
    }
}

for文を使わない場合,if (scores[1] >= 60) { ... } if (scores[2] >= 60) ...の様に,同じ内容を繰り返して書くことになります.for文を用いることで繰り返し処理をまとめて書くことができます.for (変数名 in ベクター) { 変数を用いた処理 }の様に記述することができますが,for (変数名 in [開始値]:[終了値])など様々なバリエーションがあります.

関数 #

お待たせしました.よく聞いたことあるけど,皆さんの知ってる関数とは少し違うのがプログラミングのおける関数という概念です.

数学における関数というのは例えば \( f(x) = 2 x \) のようにある値から,ある値への対応を示したものです.より厳密な言い方をすれば,ある集合Aの要素に対し,その要素を入力すると,その要素に対応した集合Bの特定の要素をただ一つ出力するものを関数といいます.

一方,プログラミングにおける関数とは,一連の処理をまとめたものです.サブルーチンともいいます.処理をまとめるメリットとして,同じ処理を何度も書かなくてよく,再利用しやすいこと,コードがすっきりと読みやすくなり可読性が向上すること,修正する時,修正箇所が少なく済むなど様々なメリットがあります. 例えば以下の例を見てみましょう.

# 3人の従業員のデータ
employee1_base_salary <- 300000
employee1_overtime_hours <- 10
employee1_bonus <- 50000
employee1_tax_rate <- 0.2
employee1_ovetime_rate <- 2000
employee1_overtime_pay <- employee1_overtime_hours * employee1_ovetime_rate
employee1_salary_after_tax <- (employee1_base_salary + employee1_overtime_pay + employee1_bonus) * (1 - employee1_tax_rate)

employee2_base_salary <- 400000
employee2_overtime_hours <- 5
employee2_bonus <- 60000
employee2_tax_rate <- 0.22
employee2_ovetime_rate <- 2500
employee2_overtime_pay <- employee2_overtime_hours * employee2_ovetime_rate
employee2_salary_after_tax <- (employee2_base_salary + employee2_overtime_pay + employee2_bonus) * (1 - employee2_tax_rate)

employee3_base_salary <- 350000
employee3_overtime_hours <- 8
employee3_bonus <- 55000
employee3_tax_rate <- 0.21
employee3_ovetime_rate <- 2200
employee3_overtime_pay <- employee3_overtime_hours * employee3_ovetime_rate
employee3_salary_after_tax <- (employee3_base_salary + employee3_overtime_pay + employee3_bonus) * (1 - employee3_tax_rate)

# 結果を表示
print(employee1_salary_after_tax)
print(employee2_salary_after_tax)
print(employee3_salary_after_tax)

目がチカチカしますね.これを関数を使うことで以下のように書き直すことができます.

# 給与を計算する関数
calculate_salary <- function(base_salary, overtime_hours, overtime_rate, bonus, tax_rate) {
  overtime_pay <- overtime_hours * overtime_rate
  gross_salary <- base_salary + overtime_pay + bonus
  net_salary <- gross_salary * (1 - tax_rate)
  net_salary
  # return(net_salary)でも可
}

result_employee1 <- calculate_salary(employee1_base_salary, employee1_overtime_hours, employee1_ovetime_rate, employee1_bonus, employee1_tax_rate) 
result_employee2 <- calculate_salary(employee2_base_salary, employee2_overtime_hours, employee2_ovetime_rate, employee2_bonus, employee2_tax_rate)
result_employee3 <- calculate_salary(employee3_base_salary, employee3_overtime_hours, employee3_ovetime_rate, employee3_bonus, employee3_tax_rate)

print(result_employee1)
print(result_employee2)
print(result_employee3)

上記の例ではcalculate_salary関数を作成することで,同じような処理をまとめることができました.

関数を作成する際は関数名 <- function (引数名) { 処理内容 }のように記述します.functionキーワードを用いて関数型のオブジェクトを作成し,関数名に代入します.()の中には,関数の入力が入ります.これを引数といいます.{}の中には引数を用いた処理内容を記述します.最終的に関数が返す値は一番最後の行に書いたものが返されます.(return関数を用いて明示的に返すのも可).これを戻り値(返り値)といいます.

関数は引数を受け取ることで,決まった一連の処理を少ない記述量で実行できます.そして,便利な関数は他の誰かが作ってくれていることがほとんどです. 具体的には,複数の引数を受け取ってvectorを生成するc関数や,受け取ったものを画面に表示するprint関数などがあります.

予約語 #

これまでの制御構文や関数に使われるようなキーワードたち(if, else, function, forなど)は変数として使えません.詳細は?ReservedをR内で実行すると確認できます.

コメントアウト #

コメントアウトは,プログラミングでコードの一部を一時的に無効化したり,注釈を書き込むための方法です. Rでは文頭に#を書くことで,その行が無視されます.

print("Hello world")
# print("この行は実行されない")

# 体重(kg)
body_weight <- 60

# 関数の説明: 受け取った値を2倍にする
# 引数:
#   x - 数値型
# 戻り値:
#   2*x - 数値型
f <- function(x) { 2 * x }

プログラムを書いていると処理が煩雑になりがちです.後日見返してみると,この変数は何を示した変数なのか,この関数は何の処理を行なっているのか,分からなくなることがよくあります.そのような事態を避けるためにも,説明をコード上に残しておくことが重要です.昨日や明日の自分は他人だと思いましょう.

また,コードを書き直す際にも一旦コードを削除して一から書き直すのではなく,一部コードをコメントアウトして,そこを参照しながら推敲すると良いでしょう.書き直しがうまくいかない時はコメントアウトを外すだけで元に戻るので.

パッケージ #

他の誰かが作ってくれた関数やコードの活用方法を学びましょう.他の誰かが作ってくれたコードの塊をパッケージといいます. パッケージには,Rそのものに備え付けされている標準パッケージと,他の誰かが作ってくれていて自分でインストールする必要のあるパッケージがあります.

標準パッケージにはベクターやリストを作るc関数や,画面に出力するprint関数,二つのベクターを与えればt検定してくれるt.test関数など特に特別な記述を必要とせず使える便利な関数がたくさんあります.

もう一方のパッケージたちは,世界各国の現場の研究者達やRStudioチームをはじめとするオープンソース開発者達が独自に開発して公開しています.なのでそのコード達を自分たちで手元にインストールする必要があります.公開のされ方には様々ありますが,大体がCRANやGitHubといったサイトからインストールすることになります.CRANは,The Comprehensive R Archive Network(包括的Rアーカイブネットワーク)の略称でRに関するコードとドキュメントを配布しているサーバー群です.GitHubはRに限らず様々なソースコードを保管,共有,公開しているサービスです.

ここではCRANからパッケージをインストールすることを扱います.install.packages("パッケージ名")を実行し,欲しいパッケージをインストールします.これはインストール時に1回実行するだけで大丈夫です.インストール後は library(パッケージ名)を実行することで,パッケージに含まれる関数を呼び出すことができるようになります.

パッケージのインストール,管理方法については別のページにて今後立項予定です.

これらのパッケージは用途ごとに膨大な数があり,何か行いたい処理があるたびに検索など調べ物を繰り返し,使い方を学習していく必要があります.AIを活用して勉強していきましょう.