404 Not Found

404 Not Found


nginx

Rの関数:カスタム関数と関数型プログラミング

これまでの6回のレッスンでは、Rの組み込み関数 (sum() mean() c()) を使ってきました。しかし、Rの真の強みは、独自の関数を作成できる点にあります。このレッスンでは、Rの「カスタム関数」について学びます。これは、繰り返し使用するコードを再利用可能なツールとしてまとめるものです。

Rは関数型プログラミング言語であり、関数は「第一級オブジェクト」として扱われます。つまり、関数は変数に代入したり、引数として渡したり、値として返したりすることができます。このレッスンでは、Rの関数のあらゆる機能について詳しく見ていきます。

1. 学習内容



2. コードの再利用に関する話

(1) 課題:コードの重複

チェンはデータアナリストで、1日に10回「売上データをレポートにまとめる」作業をこなさなければならない。そのたびに、彼は同じことを何度も繰り返し書いている:

R
# 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) 関数の解法

R
# 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) 関数の定義

R
# The Simplest Function
greet <- function() {
  cat("Hello, World!\n")
}

# Call
greet()
# Output:Hello, World!

(2) 引数を持つ関数

R
# 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
# 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) 位置パラメータと名前付きパラメータ

R
# 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) デフォルトパラメータ

R
# 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

R
    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
  1. 完全一致exp = 3(最も具体的)
  2. 接頭辞一致e = 3exp(入力キー数が少ない)
  3. 位置一致:最初の無名パラメータ → base(セキュリティレベルが最も低い)
💡 ヒント: 本番環境のコードでは完全一致を使用してください (exp = 3)。少し入力の手間は増えますが、曖昧さを避けるのに役立ちます。

(4) 可変パラメータ ...

... は「引数の数がいくつでもよい」ことを表し、ラッパー関数でよく使われます:

R
# 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関数内の最後の式は、自動的に以下を返します:

R
add <- function(a, b) {
  a + b   # Last line,Auto-Return
}

add(3, 4)
# [1] 7

(2) 明示的 return()

R
# 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(): 戻り値を非表示にする

R
# 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は匿名関数(名前のない関数)をサポートしています:

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) 変数への値の代入

R
f <- function(x) x * 2
g <- f  # A function has two names

f(5) # [1] 10 g(5) # [1] 10 ← Equivalent

(2) パラメータとして渡される

R
# 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) 戻り値として

R
# 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

💡 ヒント: 戻り値としての関数は、Rの「関数型プログラミング」の中核となる概念であり、tidyverse (map() reduce()) の多くの関数でこれが活用されています。



8. do.call(): 動的呼び出し

do.call() リスト/ベクトルを関数の引数に展開する:

R
# 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ではレキシカルスコープが採用されています:

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
⚠️ 注意:デフォルトでは、Rの関数は グローバル変数を変更することはできません(Pythonとは異なります global)。グローバル変数を変更するには、<<- を使用してください(ただし、これは 推奨されません)。



10. 完全な例:売上レポートツールの機能

以下は、このレッスンで取り上げたすべての機能をつなぎ合わせた完全なワークフローの例です。

▶ サンプル:売上レポートツールの機能

R
# ============================================
# 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

...


❓ よくある質問

Q 匿名関数はいつ使うべきですか?
A 主に lapply()sapply()map() などの関数で用いられます。

📖 まとめ


📝 練習問題

  1. 基本問題:摂氏を華氏に変換する関数 celsius_to_fahrenheit(c) を記述してください(計算式:F = C × 9/5 + 32)。入力値として 0、25、100 を試行し、出力値がそれぞれ 32、77、212 になることを確認してください。

  2. 基本問題:円の面積(pi * r^2)を計算し、小数点以下2桁に丸める関数circle_area(r)を作成してください。r = 1、2、5の場合でテストしてください。

  3. 基本的な問題:デフォルト引数を持つ関数 greet(name = "World", greeting = "Hello") を記述し、「Hello, World!」または「Hi, Alice!」を出力するようにし、その呼び出し方法を3通り試してみてください。

  4. 応用問題:数値のベクトルを受け取り、mean median max min pass_rate の合格率を含むリストを返す関数 analyze_scores(scores) を作成してください。その際、invisible() を使用して自動出力されないようにしてください。関数を呼び出した後、result$pass_rate を使用して合格率にアクセスしてください。

  5. 課題function(x) x^n を返す 関数を返す関数 make_power(n) を作成してください。make_power(2) を使って square を作成し、make_power(3) を使って cube を作成し、square(5) = 25cube(3) = 27 をテストしてください。スクリーンショットを保存してください。

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%