4 Rの関数とパッケージ

4.1 概要

このページでは、

について解説します。

4.2 Rの関数

4.2.1 関数を使う

プログラミングにおける関数とは、与えられたデータに対して何らかの処理(計算)を行い、その結果を返す機能のことです。

関数を使うには、入力データとなる「引数」 (ひきすう) を()の中に指定します。そして、関数が処理を終えて出てきた結果を「返り値」と呼びます。

次の例では、数値の平均を計算するmean() 関数を使っています。

mean(1:5) # 値を直接代入して計算
## [1] 3

x = c(1, 2, 3, 12)
mean(x) # 変数を代入して計算
## [1] 4.5

この例では、mean関数、() の中に入れた x が引数、そして計算結果の 4.5 が返り値です。これらの用語は覚えてください。また、プログラミングの世界ではよく「mean 関数に x を渡すと 4.5 が返ってくる」といった表現をします。

関数によっては複数の引数を受け取ることもあります。例えば、数値を四捨五入するround()関数は、どの桁で四捨五入するかを二番目の引数を使って指定できます。

round(3.14159, 0)
## [1] 3

round(3.14159, 1)
## [1] 3.1

round(3.14159, 2)
## [1] 3.14

名前付き引数

関数に引数を渡す際、引数名 = 値という形式で、引数の名前を指定できます。

round(x = 3.14159, digits = 2)
## [1] 3.14

ここで使用しているround() 関数は xdigits という名前の引数を持つ関数です。
ですので、引数を渡す際にこれらの名前と一緒に引数の値を入力できます。

関数が持つ引数の名前は help() 関数で確認できます。

help(round)

または

?round

を実行すると round() 関数のヘルプ(マニュアル)が表示されます。

ヘルプの中の Usage の項目に

round(x, digits = 0)

と表記されており、この関数には xdigits という2つの引数があることが確認できます。
また、Arguments の欄には引数の意味についての説明があります。

関数に名前無しで引数が渡された場合は、Usage に書かれた順序で引数の意味が解釈されます。
つまり、1つ目の引数は x、2つ目の引数は digits のことであると解釈して計算が実行されます。

関数に名前付きで引数を渡すことのメリットとして以下のようなものがあります。

  1. 引数の意味が分かりやすくなる
  2. 引数の入力する順序を自由に変えられる
  3. 関数が持つ引数のうち、一部の引数だけに値を渡すことができる

2点目について、例えば以下のようにすることができます。

round(digits = 2, x = 3.14159)
## [1] 3.14

3点目については、例えば plot() 関数では図の見た目を指定するための非常に多くの引数があります。名前付きで引数を渡すことで、指定したい引数のみに値を与えることができるようになります(plot 関数については詳しくはこちらで解説)。

引数のデフォルト値

関数の引数には、あらかじめデフォルト値が設定されていることがあります。引数を指定しなかった場合、このデフォルト値が自動的に使われます。

例えば round() 関数の場合、help の記載は以下のようでした。

round(x, digits = 0)

ここの digits = 0 の部分がデフォルト値を伝えています。
round 関数の digits という引数は、0 をデフォルト値に持っているのです。
x にはデフォルト値がないこともわかります)

引数がデフォルト値を持つ場合、関数を使う際に引数の入力を省略できます。
(他方でデフォルト値を持たない引数は必ず入力する必要があります)

# 第2引数を省略したので、digits にはデフォルト値である 0 が使用される
round(3.14159)
## [1] 3

# digits 引数に 0 を指定した場合。結果は同じ。
round(3.14159, 0)
## [1] 3

改行について

関数の括弧内では改行が可能です。以下のような書き方をしても問題ありません。

round(x = 3.14159,
    digits = 2)
## [1] 3.14

round(
    x = 3.14159,
    digits = 2)
## [1] 3.14

round(
    x = 3.14159,
    digits = 2
)
## [1] 3.14

引数ごとに改行する書き方は、引数の多い関数を使う場合にしばしば使われます。

例えば、plot() 関数は図の見た目を調整するための多くの引数があるため、以下のように引数ごとに改行すると引数の変更や追加などの編集がしやすくなります。

plot(x = c(1, 2, 4, 8, 16, 32),
    type = "o",      # 点と折れ線
    col = "#2792b3", # 色
    pch = 19,        # 点の種類
    cex = 3,         # 点の大きさ
    lwd = 5,         # 線の太さ
    ylab = "Score"   # Y軸のラベル
)

4.2.2 関数を調べる

関数の使い方を詳しく知りたい時や、やりたい処理を実現する関数がわからない時のための調べ方を紹介します。

ヘルプドキュメントの活用

関数の名前がわかっている場合は、?の後ろに関数名を入力して実行すると、その関数のヘルプ(公式マニュアル)を閲覧できます。

たとえば、round関数について知りたい場合はConsoleで以下の文を実行します。

?round
# または help(round) でも同じ

ヘルプには多くの情報が含まれていますが、特に以下の項目が重要です。

  • Usage (使い方): 関数の基本的な書き方と、引数の順番、デフォルト値がわかります。
  • Arguments (引数): 各引数がどのようなデータを受け取り、何を意味するのかが説明されています。
  • Value (返り値): 関数がどのような値を返すかが説明されています。
  • Examples (例): コピペして実行できるサンプルコードが載っており、具体的な使い方を理解するのに役立ちます。

キーワードで関数を探す

関数の名前がわからない場合は、??の後ろに調べたいキーワード(文字列なので " で囲む)を入力して実行すると、関連する関数の一覧を探すことができます。

# 例えば「標準偏差 (standard deviation)」に関連する関数を探す
?? "standard deviation"

これにより、標準偏差を計算するsd()関数などを見つけることができます(一覧の中ではstats::sdと表示されています)。

4.2.3 関数を作る

Rでは、既存の関数を使うだけでなく、自分でオリジナルの関数を作成することもできます。

なぜ自作関数が必要か?

同じような処理を何度も書いていると、コードが長くなり、間違いも起きやすくなります。一連の処理を一つの関数としてまとめておくことで、以下のようなメリットがあります。

  • 再利用性が高まる: 同じ処理を短い関数名で何度も呼び出せる。
  • 可読性が向上する: コードがスッキリして、何をしているかが分かりやすくなる。
  • 修正が容易になる: 処理内容を変更したい場合、関数の定義を1箇所修正するだけで済む。

自作関数の基本構文

関数はfunction()という命令を使って作成します。基本的な構文は以下の通りです。

関数名 = function(引数1, 引数2, ...) {
  # ここに実行したい処理を書く
  # return()で返り値を指定する(省略可)
}

例として、税抜き価格を受け取り、消費税を加えた税込価格を計算する関数を作ってみます。

calc_tax = function(price, tax_rate = 0.1) {
  result = price * (1 + tax_rate)
  return(result)
}

# デフォルトの税率(10%)で計算
calc_tax(1000)
## [1] 1100

# 軽減税率(8%)を指定して計算
calc_tax(1000, tax_rate = 0.08)
## [1] 1080

この関数はpricetax_rateという2つの引数を持ちます。tax_rateにはデフォルト値として0.1が設定されています。

4.3 パッケージ

4.3.1 はじめに

パッケージとは、特定の目的のために作られた関数やデータをセットにしたものです。Rには基本的な機能を提供するパッケージが最初から組み込まれていますが、世界中の開発者が作成した便利なパッケージを追加することで、Rの機能を拡張できます。

これらのパッケージの多くは、CRAN (The Comprehensive R Archive Network, クランまたはシーランと発音する) という公式サイトで集中管理・公開されています。

現在インストール済みのパッケージを確認するには、

library()

または

p = installed.packages()
rownames(p)

を実行すると良いでしょう。

4.3.2 パッケージのインストールと読み込み

パッケージを利用するには、1.「インストール」と2.「読み込み」という2つのステップが必要です。

  1. インストール
  • PCにパッケージをダウンロードする作業です。
  • 一度だけ行えば、次回以降は行う必要はありません(ただし、パッケージをバージョンアップしたい場合は別)。
  • install.packages()関数を使い、パッケージ名は""で囲みます。
# ggplot2パッケージをインストール
install.packages("ggplot2")
  1. 読み込み
  • インストール済みのパッケージを使える状態にする作業です。
  • Rを起動するたびに毎回必要です。
  • library()関数を使い、このときパッケージ名に""は不要です。
# ggplot2パッケージを読み込む
library(ggplot2)

インストールは済んでいるのに library() を忘れると、「そんな関数はありません」というエラーが出てしまうので注意しましょう。

R を起動した際に、インストール済みのパッケージのうちいくつかのパッケージは自動で読み込みも行われます(例えば base パッケージなど)。ですので、base パッケージに含まれる関数(library(help = "base") で確認できます)は library(base) という命令を実行しなくても最初から使えます。

他方で、ユーザーが自分でインストールしたパッケージなど、大部分のパッケージはインストールはされていても R の起動時にはまだ読み込まれていません。なので、例えば ggplot2 パッケージを使用する場合はまず library(ggplot2) という命令を実行してパッケージの読み込みを行う必要があるのです。

読み込み済みのパッケージは search() 命令を実行すると確認できます。
(「package:パッケージ名」という形式で表示されます)

4.3.3 関数の所属を明記する::演算子

Rでは、dplyrパッケージとstatsパッケージ(Rの基本機能)の両方にfilter()という関数があるように、異なるパッケージに同じ名前の関数が存在することがあります。これを「関数の競合(コンフリクト)」と呼びます。

Consoleやスクリプトファイルにlibrary(dplyr)と書いてdplyrパッケージをRに読み込ませると、そのパッケージの関数が優先され、元々あった同名の関数が隠されて(マスクされて)しまいます。そうすると、statsパッケージのfilter()関数を使ったつもりなのに、実際にはdplyrパッケージのfilter()関数が実行され、予期せぬエラーやバグの原因になります。

この問題を解決するのが::演算子です。これを使うと、関数を使用する際に「どのパッケージに所属する関数か」を明確に指定できます。これは、名前が同じ複数の人物を名字も含めてフルネームで呼ぶことで区別をすることに似ています。

書き方:パッケージ名::関数名()

  • dplyr::filter() # dplyrパッケージのfilter関数が使われます
  • stats::filter() # statsパッケージのfilter関数が使われます

具体的な例を以下に示します。

# --- dplyr::filter() の使用例 (データフレームの行を絞り込む) ---
df = data.frame(group = c("A", "A", "B"), value = c(10, 15, 20))
df2 = dplyr::filter(df, group == "A") # groupが "A" の行だけを抽出する
print(df2)
##   group value
## 1     A    10
## 2     A    15

# --- stats::filter() の使用例 (時系列データなどに使う) ---
sales = c(10, 10, 20, 20, 30, 30)
# statsのfilterは専門的な計算(線形フィルタリング)に使う
result = stats::filter(sales, filter = 0.5)
print(result)
## Time Series:
## Start = 1 
## End = 6 
## Frequency = 1 
## [1]  5  5 10 10 15 15

このように::を使って関数が所属するパッケージ名を明記することで、コードの可読性が高まり、誰が読んでもどのパッケージの関数を使っているかが一目瞭然になります。これにより、より安全で信頼性の高いコードを書くことができます。

特定のパッケージの関数をlibrary()を使わないままで使う

この::演算子を使うことの別のメリットとして、library()関数を使わなくても特定のパッケージの関数が使えるようになる、というものがあります。

通常、何かのパッケージ(の関数)を使いたい場合、スクリプトの冒頭などにlibrary()関数を記述します。dplyrパッケージを使いたい場合なら、library(dplyr)を実行しておけば、dplyrパッケージの関数、たとえばfilter()関数を、単にfilter()と書くだけで使うことができます。

しかし、::演算子を使って、dplyr::filter()という形でdplyrのfilter関数を使う場合、事前にlibrary(dplyr)を実行する必要はありません。

この性質は、あるパッケージの特定の関数だけしか使わないという場合に便利です。library()関数を使った場合、そのパッケージに所属するすべての関数が読み込まれます。それまでに読み込んでいる他のパッケージやRのデフォルトの関数と同じ名前の関数が新たに読み込んだパッケージに含まれている場合、関数の競合(上述)が発生してしまいます。library()関数を使わないままで::演算子を使って関数を使用すれば、こうした面倒が回避できて効率的です。

以下のような基準でlibrary()関数を使うかどうかを決めるとよいでしょう。

  • そのパッケージの関数をたくさん、繰り返し使う場合は、関数を使用する際に毎回::演算子を使った記法を使うのは面倒なので、library関数でそのパッケージを読み込む。dplyrggplot2などはここに該当するでしょう。
  • そのパッケージの関数を数回しか使わない場合や、他の関数との名前の衝突を絶対に避けたい場合は、library関数は使わずに::演算子を使って関数を呼び出すとよいでしょう。

4.4 まとめ

このページでは、Rの基本となる関数とパッケージについて学びました。

  • 関数は引数を受け取り、処理を行い、返り値を返す便利な命令のまとまりです。
  • ???を使って関数について調べることができます。
  • function()を使って関数を自作することができます。
  • パッケージをインストールし読み込むことで、Rの機能を拡張できます。

4.5 確認問題

4.5.1 問題

2つの数値 a と b を受け取り、それらを足し合わせる関数add_nums()を考えます。2つ目の引数 b には、デフォルト値として 10 が設定されています。

add_nums = function(a, b = 10) {
  return(a + b)
}

この関数をadd_nums(5)と呼び出した場合と、add_nums(5, 5)と呼び出した場合の実行結果はそれぞれどうなりますか?

解答
# 関数の定義
add_nums = function(a, b = 10) {
  return(a + b)
}

# ケース1: 引数を1つだけ指定
result1 = add_nums(5)
print(paste("add_nums(5) の結果:", result1))
## [1] "add_nums(5) の結果: 15"

# ケース2: 引数を2つ指定
result2 = add_nums(5, 5)
print(paste("add_nums(5, 5) の結果:", result2))
## [1] "add_nums(5, 5) の結果: 10"
  • add_nums(5)の場合: 引数 a に 5 が渡されます。引数 b は呼び出し時に指定されていないため、関数の定義で設定されたデフォルト値の 10 が使われます。 したがって、計算は a + b すなわち 5 + 10 となり、結果は 15 になります。
  • add_nums(5, 5)の場合: 引数 a に 5 が、引数 b に 5 が渡されます。引数 b は呼び出し時に指定されているため、デフォルト値の 10 は使われず、指定された値 5 で上書きされます。 したがって、計算は a + b すなわち 5 + 5 となり、結果は 10 になります。

4.5.2 問題

3つの引数 x, y, z を受け取り、(x - y) * zを計算する関数calc_val()があります。この関数は以下のように定義されています。

calc_val = function(x, y, z) {
  return((x - y) * z)
}

この関数をcalc_val(10, 5, 2)と呼び出す場合とcalc_val(z = 2, x = 10, y = 5)と呼び出す場合の実行結果は同じになるでしょうか、異なるでしょうか? また、z = 2のように引数名を指定して呼び出すことの利点は何だと考えられますか?

解答
# 関数の定義
calc_val = function(x, y, z) {
  return((x - y) * z)
}

# ケース1: 引数名なし(位置で指定)
result1 = calc_val(10, 5, 2)
print(paste("引数名なしの場合:", result1))
## [1] "引数名なしの場合: 10"

# ケース2: 引数名あり(順序不同)
result2 = calc_val(z = 2, x = 10, y = 5)
print(paste("引数名ありの場合:", result2))
## [1] "引数名ありの場合: 10"

実行結果は同じになります。

  • calc_val(10, 5, 2)の場合:
    引数名が指定されていないため、引数は関数定義の順番(位置)に基づいて渡されます。 1番目の 10 が x に、2番目の 5 が y に、3番目の 2 が z に入ります。
  • calc_val(z = 2, x = 10, y = 5)の場合:
    引数名が明示的に指定されています。この場合、Rは関数定義の順番に関わらず、指定された名前の引数に値を渡します。 z に 2 が、 x に 10 が、 y に 5 が入ります。

引数名を指定する利点:

  • 可読性の向上:
    calc_val(10, 5, 2)という呼び出し方だけでは、10 や 5 がどの引数に対応するのか、関数の定義を見に行かないと分かりません。calc_val(x = 10, y = 5, z = 2)と書けば、コードを読む人がどの値がどの引数に渡されているか一目で理解できます。
  • 順序間違いの防止:
    引数が多くなってくると、正しい順番で値を指定するのが難しくなります。引数名を指定すれば、順番を気にする必要がなくなり、バグを防ぐことができます。
  • デフォルト引数の活用:
    たとえばmy_func(a = 0, b = 10)のような関数で、a だけを指定したい場合はadd_nums(5)と書けますが、b だけを指定したい場にもadd_nums(b = 20)のように書くことができます。引数名を指定せずに引数の位置で値を渡す場合だと、add_nums(0, 20)のように、a の値はデフォルトでよい場合でも必ず a の値を記入する必要があります。

4.5.3 問題

以下の仕様を満たす自作関数calc_bmi()を作成してください。

  • 仕様1: 2つの引数 height (身長, m単位) と weight (体重, kg単位) を受け取ります。
  • 仕様2: BMI(体格指数)を計算します。計算式は 体重[kg] / (身長[m] * 身長[m]) です。
  • 仕様3: 計算したBMIの値を返します (return()を使ってください)。

作成した関数を使って、身長1.75m、体重68kgの人のBMIを計算するスクリプトを作成してください。

解答
# 関数の定義
calc_bmi = function(height, weight) {
  bmi = weight / (height * height)
  return(bmi)
}

# 関数の利用
h = 1.75
w = 68
result_bmi = calc_bmi(height = h, weight = w)

# 結果の表示
print(paste("身長", h, "m, 体重", w, "kg のBMI:", round(result_bmi, 1)))
## [1] "身長 1.75 m, 体重 68 kg のBMI: 22.2"

# 参考:別の値でもテスト
bmi = calc_bmi(1.60, 55)
print(paste("身長1.60m, 体重55kg のBMI:", round(bmi, 1)))
## [1] "身長1.60m, 体重55kg のBMI: 21.5"
  • {}の中で、引数の変数を使ってBMIの計算を行い、その結果をreturn()関数を使って関数の返り値とします。
  • 計算結果をプリントする際にround()関数を使って値を四捨五入しています。