Rの関数:カスタム関数と関数型プログラミング
これまでの6回のレッスンでは、Rの組み込み関数 (
sum()mean()c()) を使ってきました。しかし、Rの真の強みは、独自の関数を作成できる点にあります。このレッスンでは、Rの「カスタム関数」について学びます。これは、繰り返し使用するコードを再利用可能なツールとしてまとめるものです。
Rは関数型プログラミング言語であり、関数は「第一級オブジェクト」として扱われます。つまり、関数は変数に代入したり、引数として渡したり、値として返したりすることができます。このレッスンでは、Rの関数のあらゆる機能について詳しく見ていきます。
1. 学習内容
- function(){} 基本的な構文
- 4種類のパラメータ(位置指定パラメータ、名前付きパラメータ、デフォルトパラメータ、変数パラメータなど…)
- 3種類の戻り値(return / 最終行 / 非表示)
- 匿名関数(ラムダ)
- オブジェクトとしての関数(代入、引数としての渡し、戻り値)
- do.call() による一括呼び出し
- 実践編:完全なユーティリティ関数の記述
2. コードの再利用に関する話
(1) 課題:コードの重複
チェンはデータアナリストで、1日に10回「売上データをレポートにまとめる」作業をこなさなければならない。そのたびに、彼は同じことを何度も繰り返し書いている:
# Repeat 10 The code for this
sales <- c(120, 150, 80, 200, 175)
total <- sum(sales)
avg <- mean(sales)
top <- max(sales)
cat("Total Sales:", total, "Average:", avg, "Highest:", top, "\n")
10回のコピー&ペースト操作=200行の重複コード。ある箇所のロジックを変更する必要がある場合、10箇所すべてを変更しなければなりません。遅かれ早かれ、何かを見落とすことになるでしょう。
(2) 関数の解法
# Write a function (just write 1 time)
report <- function(x) {
cat("Total Sales:", sum(x), " Average:", mean(x), " Highest:", max(x), "\n")
}
# Call 10 times (Every time 1 row)
report(c(120, 150, 80, 200, 175))
report(c(80, 90, 100))
report(c(1000, 2000, 3000))
関数コード5行+呼び出し10行=計15行。ロジックを変更するには、たった1か所を修正するだけで済みます。これこそが関数の力です。
3. function(){}: 基本的な構文
(1) 関数の定義
# The Simplest Function
greet <- function() {
cat("Hello, World!\n")
}
# Call
greet()
# Output:Hello, World!
(2) 引数を持つ関数
# Single parameter
greet <- function(name) {
cat("Hello,", name, "!\n")
}
greet("Alice")
# Output:Hello, Alice !
# Multiple parameters
greet_full <- function(name, age) {
cat("I am", name, ",This year", age, "yrs\n")
}
greet_full("Bob", 25)
# Output:I am Bob ,This year 25 yrs
(3) RとPythonの関数の違い
| 特集 | R | Python |
|---|---|---|
| 定義 | function(x) { ... } |
def func(x): |
| コードブロック | {} |
インデント |
| 戻り値 | 最後の行が 自動的に返される | return が必要 |
| ドキュメント | #' で始まる (roxygen2) |
docstring |
| 型宣言 | なし(動的型付け) | オプション(Python 3.5 以降) |
# R the special:The last line wraps automatically(Not necessary return)
add <- function(a, b) {
a + b # The last line wraps automatically
}
result <- add(3, 4)
result
# [1] 7
4. パラメータの4つの種類
(1) 位置パラメータと名前付きパラメータ
# Function Definition:3 parameter
power <- function(base, exp, mod) {
result <- (base ^ exp) %% mod
return(result)
}
# Location Parameters(In order)
power(2, 10, 1000)
# [1] 24 ← 2^10=1024, 1024 %% 1000 = 24
# Named parameters(By Name,Out of order)
power(mod = 1000, base = 2, exp = 10)
# [1] 24 ← The same result
(2) デフォルトパラメータ
# The second parameter has a default value.
power <- function(base, exp = 2, mod = 1000) {
result <- (base ^ exp) %% mod
return(result)
}
# Do not pass a second parameter,Use the default exp = 2
power(10)
# [1] 100 ← 10^2=100, 100 %% 1000 = 100
# Partial Parameter Passing
power(10, mod = 100)
# [1] 0 ← 10^2=100, 100 %% 100 = 0
(3) パラメータの順序に関する規則
Rのパラメータ照合ルール:
graph TB
A[Function Call power 10 exp=3] --> B[1. Exact Match for Named Parameters]
B --> C[2. Prefix Matching]
C --> D[3. Location Matching]
style A fill:#fff3cd
style B fill:#d4edda
style C fill:#d4edda
style D fill:#d4edda
- 完全一致:
exp = 3(最も具体的) - 接頭辞一致:
e = 3→exp(入力キー数が少ない) - 位置一致:最初の無名パラメータ →
base(セキュリティレベルが最も低い)
exp = 3)。少し入力の手間は増えますが、曖昧さを避けるのに役立ちます。
(4) 可変パラメータ ...
... は「引数の数がいくつでもよい」ことを表し、ラッパー関数でよく使われます:
# Use ... to accept any parameters
my_sum <- function(...) {
args <- list(...)
result <- sum(unlist(args))
cat("Summed up", length(args), "parameter:", result, "\n")
return(result)
}
my_sum(1, 2, 3)
# Summed up 3 parameter: 6
my_sum(10, 20, 30, 40, 50)
# Summed up 5 parameter: 150
... は、他の関数をラップする際(たとえば、カスタム my_mean() が mean() のいくつかのパラメータをラップする場合など)に非常に頻繁に使用されます。
5. 戻り値の3つの種類
(1) 最後の行は自動的に改行されます
R関数内の最後の式は、自動的に以下を返します:
add <- function(a, b) {
a + b # Last line,Auto-Return
}
add(3, 4)
# [1] 7
(2) 明示的 return()
# Use return() to return early
check_age <- function(age) {
if (age < 0) {
return("Age of Invalidity") # Return Early
}
if (age >= 18) {
return("Adulthood")
}
"Minor" # Implicit Return
}
check_age(-5) # [1] "Age of Invalidity" check_age(20) # [1] "Adulthood" check_age(15) # [1] "Minor"
(3) invisible(): 戻り値を非表示にする
# Ordinary Functions
add_print <- function(a, b) {
a + b # It will print automatically
}
add_print(3, 4)
# [1] 7 ← Automatic Printing
# invisible Function
add_silent <- function(a, b) {
invisible(a + b) # Do not print automatically
}
add_silent(3, 4)
# No output ← Do not print
# But it can still be assigned a value
result <- add_silent(3, 4)
result
# [1] 7 ← You can see it after the assignment.
<- invisible() を使って値を代入すると — x <- 5 結果が表示されません。
6. 匿名関数:ラムダ式
Rは匿名関数(名前のない関数)をサポートしています:
# Ordinary Functions
square <- function(x) x ^ 2
square(5)
# [1] 25
# Anonymous Functions(Use directly)
(function(x) x ^ 2)(5)
# [1] 25
# Common Use Cases for Anonymous Functions:lapply/sapply
numbers <- list(1, 2, 3, 4, 5)
result <- lapply(numbers, function(x) x ^ 2)
result
# [[1]] 1
# [[2]] 4
# [[3]] 9
# [[4]] 16
# [[5]] 25
apply 関数群では、匿名関数が非常に頻繁に使われます。これについては、第8課で詳しく解説します。
7. オブジェクトとしての関数(Rにおける関数型プログラミングの核心)
R の関数は「第一級」であり、これは関数が以下のことができることを意味します:
(1) 変数への値の代入
f <- function(x) x * 2
g <- f # A function has two names
f(5) # [1] 10 g(5) # [1] 10 ← Equivalent
(2) パラメータとして渡される
# Functions that take other functions as arguments
apply_twice <- function(f, x) {
f(f(x))
}
# Passing Different Functions
apply_twice(function(x) x + 1, 5) # 5+1+1=7
apply_twice(function(x) x * 2, 5) # 5*2*2=20
(3) 戻り値として
# A function that returns a function(Closure)
make_multiplier <- function(k) {
function(x) x * k
}
# Creating Functions with Different Scales
double <- make_multiplier(2)
triple <- make_multiplier(3)
double(5) # [1] 10 triple(5) # [1] 15
map() reduce()) の多くの関数でこれが活用されています。
8. do.call(): 動的呼び出し
do.call() リスト/ベクトルを関数の引数に展開する:
# Regular Call
sum(1, 2, 3, 4, 5)
# [1] 15
# do.call Call(The parameter is a list)
args <- list(1, 2, 3, 4, 5)
do.call(sum, args)
# [1] 15
# Practical Application:Dynamic Creation data.frame
col_names <- c("name", "age", "score")
col1 <- c("Alice", "Bob", "Charlie")
col2 <- c(25, 30, 35)
col3 <- c(95, 88, 92)
df <- do.call(data.frame, list(
name = col1,
age = col2,
score = col3
))
print(df)
# name age score
# 1 Alice 25 95
# 2 Bob 30 88
# 3 Charlie 35 92
9. スコープ:変数はどこで有効か?
Rではレキシカルスコープが採用されています:
# Global Variables
x <- 10
# Global variables can be accessed within a function.
read_x <- function() {
cat("x =", x, "\n") # Read the whole picture x
}
read_x()
# x = 10
# However, local variables within a function do not affect the global scope.
set_local <- function() {
x <- 999 # Local variables
cat("Inside a function x =", x, "\n")
}
set_local()
# Inside a function x = 999
cat("Global x =", x, "\n")
# Global x = 10 <- Global x unchanged
global)。グローバル変数を変更するには、<<- を使用してください(ただし、これは 推奨されません)。
10. 完全な例:売上レポートツールの機能
以下は、このレッスンで取り上げたすべての機能をつなぎ合わせた完全なワークフローの例です。
▶ サンプル:売上レポートツールの機能
# ============================================
# Sales Report Tool Functions
# Features:Encapsulate Common Analytical Logic,Reuse
# ============================================
# 1. Basic Statistical Functions(Back to List)
basic_stats <- function(x) {
list(
n = length(x),
sum = sum(x),
mean = round(mean(x), 2),
median = median(x),
sd = round(sd(x), 2),
min = min(x),
max = max(x)
)
}
# 2. Formatted Output Functions(invisible Back)
print_report <- function(name, x) {
stats <- basic_stats(x)
cat("=================================\n")
cat("Sales Report:", name, "\n")
cat("=================================\n")
cat("Sample size:", stats$n, "\n")
cat("Total Sales:", stats$sum, "\n")
cat("Average Sales:", stats$mean, "\n")
cat("Median:", stats$median, "\n")
cat("Standard Deviation:", stats$sd, "\n")
cat("Minimum:", stats$min, " / Maximum:", stats$max, "\n")
invisible(stats) # Silent Return
}
# 3. Classification Function(Vectorization)
classify_sales <- function(x) {
ifelse(x < 100, "Low",
ifelse(x < 200, "Mid", "High"))
}
# 4. Main Program
sales_data <- list(
"Beijing" = c(120, 150, 80, 200, 175),
"Shanghai" = c(95, 110, 130, 145, 160),
"Shenzhen" = c(200, 220, 180, 250, 300)
)
# Generate reports for each branch
for (city in names(sales_data)) {
sales <- sales_data[[city]]
print_report(city, sales)
# Batch Classification Using Anonymous Functions
levels <- sapply(sales, function(x) classify_sales(x))
cat("\nCategory Summary:\n")
print(table(levels))
cat("\n")
}
# 5. Use do.call to merge all branches
all_sales <- do.call(c, sales_data)
cat("=== Company-wide Summary ===\n")
print_report("Company-wide", all_sales)
期待される出力(抜粋):
================================= Sales Report: Beijing
Sample size: 5 Total Sales: 725 Average Sales: 145 Median: 150 Standard Deviation: 47.7 Minimum: 80 / Maximum: 200
Category Summary: levels Mid High 3 2
================================= Sales Report: Shanghai
...
❓ よくある質問
lapply()、sapply()、map() などの関数で用いられます。📖 まとめ
- R関数の定義
function(params) { code }、最後の行は自動的に返されます - 4種類のパラメータ:位置指定型、名前付き型(
name = value)、デフォルト型(name = default)、変数型(...) - 3つの戻り値:最終行(自動)、
return()(明示)、invisible()(サイレント、出力なし) - 匿名関数
function(x) x^2は、lapplyやsapplyで非常に一般的です。 - R は関数型言語であり、関数は「第一級オブジェクト」です。つまり、関数には代入ができ、引数として渡すことができ、また返値としても使用できます(クロージャ)。
do.call(func, list)リストを関数の引数に展開する- R はレキシカルスコープを採用しています。デフォルトでは、関数内からグローバル変数を変更することはできません(
<<-を使用すれば可能ですが、これは推奨されません)。
📝 練習問題
-
基本問題:摂氏を華氏に変換する関数
celsius_to_fahrenheit(c)を記述してください(計算式:F = C × 9/5 + 32)。入力値として 0、25、100 を試行し、出力値がそれぞれ 32、77、212 になることを確認してください。 -
基本問題:円の面積(
pi * r^2)を計算し、小数点以下2桁に丸める関数circle_area(r)を作成してください。r = 1、2、5の場合でテストしてください。 -
基本的な問題:デフォルト引数を持つ関数
greet(name = "World", greeting = "Hello")を記述し、「Hello, World!」または「Hi, Alice!」を出力するようにし、その呼び出し方法を3通り試してみてください。 -
応用問題:数値のベクトルを受け取り、
meanmedianmaxminpass_rateの合格率を含むリストを返す関数analyze_scores(scores)を作成してください。その際、invisible()を使用して自動出力されないようにしてください。関数を呼び出した後、result$pass_rateを使用して合格率にアクセスしてください。 -
課題:
function(x) x^nを返す 関数を返す関数make_power(n)を作成してください。make_power(2)を使ってsquareを作成し、make_power(3)を使ってcubeを作成し、square(5) = 25とcube(3) = 27をテストしてください。スクリーンショットを保存してください。



