15章 関数

15.0 ライブラリの読み込み

library("tidyverse")
library("lubridate")
library("MASS")

15.1 はじめに

練習問題はありません。

15.2 いつ関数を書くべきか?

練習問題1 TRUEはなぜrescale01()のパラメタではないのか。xに欠損値があり、na.rm = FALSEだとどうなるか。

rescale01 <- function(x) {
  rng <- range(x, na.rm = TRUE, finite = TRUE)
  (x - rng[1]) / (rng[2] - rng[1])
}

TRUEはなぜrescale01()のパラメタではないのか、の部分は意図がよくわからないので、それはさておき…作業しやすいように3連ドットで一部変更します。まず、先程の関数と同じ状態であれば、NAを除いて計算されるので、NANAとして表示されます。FALSEにしても結果が変わりません。

rescale01 <- function(x, ...) {
  rng <- range(x, ...)
  (x - rng[1]) / (rng[2] - rng[1])
}

x <- c(1:10, NA)  
rescale01(x, na.rm = TRUE, finite = TRUE)  
 [1] 0.0000000 0.1111111 0.2222222 0.3333333 0.4444444 0.5555556
 [7] 0.6666667 0.7777778 0.8888889 1.0000000        NA
 
rescale01(x, na.rm = FALSE, finite = TRUE) 
 [1] 0.0000000 0.1111111 0.2222222 0.3333333 0.4444444 0.5555556
 [7] 0.6666667 0.7777778 0.8888889 1.0000000        NA

これはfinite = TRUENAをドロップしているからです。なので、na.rm = TRUEかつfinite = FALSEにすることで、計算の中にNAが含まれるNAのベクトルを返します。

練習問題2 rescale01()の2つ目の引数によって、無限の値は変更されません。0-Infにマッピングされ、1Infにマップされるように書き直しなさい。

rescale01()の中で、条件に一致すれば変換を施すように修正します。

練習問題3 次のコードを関数に変えてください。

mean(is.na(x))は、NAの割合を計算する関数です。

この関数はベクトルの各値が全体のどの程度を占めるかを計算する関数です。

これは変動係数の計算式です。NAが含まれていると、デフォルトでNAを返すようにしておきます。

練習問題4 数値ベクトルの分散と歪度を計算するための独自の関数を作成しなさい。

分散を計算する関数を作ります。

歪度を計算する関数を作ります。

練習問題5 同じ長さの2つのベクトルを取り、両ベクトルのNAが一致する組み合わせの合計を返す関数both_na()を書きなさい。

練習問題6 次の関数は何をするのですか?

is_directory()はディレクトリかどうかを返す関数で、is_readable()はアクセス可能かどうかの関数です。

練習問題7 Little Bunny Foo Foo、この曲にはたくさんの重複があるので、関数を使用して重複を減らしなさい。

質問の意図がよくわからず、https://jrnold.github.io/r4ds-exercise-solutions/functions.htmの回答をそのまま記載しておきます。

15.3 関数は人間とコンピュータのためのもの

練習問題1 次の3つの関数を読んで、何をするのか調べなさい。また、良い名前をブレインストーミングしなさい。

これはprefixで文字列が始まるかをチェックする関数です。check_prefix()なんかでもいいかもしれません。

この関数は、ベクトルの最後の要素を取り除く関数です。シンプルにdel_last()なんかでもいいかもしれません。

この関数は、xの要素分、yを繰り返す関数です。シンプルにrecycle()なんかでもいいかもしれません。

練習問題2 あなたが最近書いた関数を取り、5分かけてより良い名前と内容をブレインストーミングします。

読者に譲ります。

練習問題3 rnorm()MASS::mvrnorm()を比較してください。それらをより一貫性のあるものにすることができるか?

MASS::mvrnorm は、多変量正規分布からのサンプルに対して、rnorm()は単変量正規分布からのサンプル。rnorm()の主な引数はnmeansd です。MASS::mvrnormの主な引数はnmuSigmaです。一貫性を保つために、それらの引数は同じ名前であるべきです。

練習問題4 rnorm(), dnorm()よりもnorm_r(), norm_d()が良いのはなぜか。単体のケースも作りなさい。

norm_r()norm_d()の場合、分布によって機能をサブセット化できますが、rnorm()dnorm()の場合、機能によって分布をサブセット化することになります。乱数を発生させる、確率密度を書きたい場合、まず思い浮かべるのは「どの分布にするのか」と考える。そうなると、思考順にあわせて、分布によって機能をサブセット化できるnorm_r()norm_d()のほうがよいのかもしれません。

  • ***_rnorm_r()binom_r()unif_r()exp_r()

  • ***_dnorm_d()binom_d()unif_d()exp_d()

15.4 条件付きの実行

練習問題1 ifとはifelse()の違いは何か。

ifは単一の条件を論理判定し、ifelse()は、各ベクトルの要素を論理判定します。ifはベクトル化されていない関数なので、ベクトルをインプットするとエラーが表示され、最初の要素だけ使われ、誤った出力が表示されます。

なので、それをベクトル化したければ、関数内部でベクトル化の処理を施すか、purrr::map_**()などでfをベクトル化します。

練習問題2 時間に応じて、「おはよう」、「こんにちは」、または「こんばんは」と言う挨拶関数を作成しなさい。

練習問題3 fizzbuzz()関数を実装しなさい。入力は単一のスカラを想定。

ベクトルの入力を許す場合はこんな感じ。

練習問題4 このネストされたif-elseステートメントのセットを単純化するために、cut()はどのように使用できるか?

cut()を使用することで、ネストさせなくても区間で区切ることが可能なので、より直感的でわかりやすいコードになります。また、if(cond)の場合、ベクトル化されていないので、複数の値を区切る場合には、ベクトル化が必要になります。

練習問題5 switch()で数値を使用するとどうなるのか?

要素に対応する値が返されますが、整数以外の数値は、切り捨てることに注意してください。

練習問題6 このswitch()の呼び出しは何をするのか?

このswitch()関数は、一致した名前の欠損していない引数値を返します。a = ,のような欠損値のある引数に遭遇すると、この場合は欠損値ではない次の引数の値を返します。したがって、"e"のようにそもそも、どこともマッチしないものは何も返しません。

一致しないならNULLを返し、欠損値の部分にも返り値を記載して明確にしておきます。

15.5 関数の引数

練習問題1 commas(letters, collapse = "-")はどのように機能しますか。

このコードは、文字列ベクトルをコンマで結合して1つの値にします。

commas(letters, collapse = "-")を入力すると、エラーが返されます。これは関数内部で定義しているcollapse = ", "と、commasで定義しているcollapse = "-"が衝突しているからです。

このような引数の衝突をさけるために、関数で柔軟に指定できるように書き換えます。

練習問題2 rule("Title", pad = "-+")は、なぜ現在動作しないのか?pad引数で調整しなさい。

このrule()関数のpadは、目的の幅からタイトルの長さと5文字を引いた長さを引いた数に等しい回数だけ複製します。これは暗黙のうちにpadが1文字だけであると仮定します。2つの文字があった場合、出力は2倍長くなります。

練習問題3 mean()trimは何をするか?いつ使用するか。

trimは、平均を計算する前に、ベクトルの両端からの一部をトリミングします。これは、外れ値に対してロバストな中心傾向を計算するのに役立ちます。

練習問題4 cor()methodc("pearson", "kendall", "spearman")です。どういう意味なのか?デフォルトでどの値が使われるのか?

これは、methodがこれら3つの値のいずれかを取ることができることを意味します。最初の値である"pearson"がデフォルトで使用されます。

15.6 戻り値

練習問題はありません。

15.7 環境

練習問題はありません。

最終更新

役に立ちましたか?