J's blog

趣味で統計•データ解析をしています

R起動時にpackageを自動ロードする

よく使うパッケージを起動のたびにlibrary()やrequire()をするのは面倒かと思います。その時は.Rprofile内に

suppressMessages(library(hoge))

の一文を入れておきましょう。(違いました。追記を見て下さい。)

suppressMessages()はなくても構いませんが、出力として表示されるはずのメッセージを表示しないようにしてくれます。

この類似としてsuppressWarnings()という警告メッセージのみを抑制する関数も存在しています。

(追記)

上のコードで読み込んだ際のサーチパスが思うように行きませんでした。ここではdplyrパッケージをロードしたのですが、下の実行結果を見て下さい。

> search()
 [1] ".GlobalEnv"        "tools:rstudio"   "package:stats"    "package:graphics"
 [5] "package:grDevices" "package:utils"   "package:datasets" "package:dplyr"   
 [9] "JapanEnv"          "package:methods" "Autoloads"        "package:base" 

本来(Rコンソールからロードした時)ならば、2つ目の要素に "package:dplyr" が来ていなければなりません。dplyrパッケージの場合はfilter()とlag()がpackage:statsにマスクされているので、このサーチパスのおかしい状態で関数を呼ぶとpackage:statsのfilter()とlag()が呼び出されてしまいます。

ならどうしたらいいの?
ということで試行錯誤しましたが、Startupのhelpマニュアルを見たらちゃんと書いてありました。不要なところをはしょると、.Rprofileの中身を読み込んでから.First.sys()を実行するという順序になっています。
.First.sys()はoptions("defaultPackages")にあるパッケージをロードする関数で、つまりは

「.Rprofileでdplyrをロード → デフォルトパッケージをロード」

の順になっているわけです。
問題を解決するためには、デフォルトパッケージと同時にdplyrをロードするようなコードにする必要があります。
ですから、初めのコードの修正としてはデフォルトパッケージ自体を修正する

options(defaultPackages = c(getOption("defaultPackages"), "dplyr"))

というコードになります。
これでもう一度確認すると、正しいサーチパスになっていることがわかります。

> search()
 [1] ".GlobalEnv"       "tools:rstudio"     "package:dplyr" "package:stats"   
 [5] "package:graphics" "package:grDevices" "package:utils" "package:datasets"
 [9] "JapanEnv"         "package:methods"   "Autoloads"     "package:base" 

永続代入(スーパーアサインメント)演算子

Rのお話です。


永続代入演算子(<<-)というのがあります。
これを「グローバル変数への代入演算子」と勘違いして覚えてしまうような扱いをよく見る気がします。それでも基本困ることはないと思いますが、ここで正しい意味を覚えてしまいましょう。

説明

下の実行結果を見てください。

> x <- 5  # グローバル変数
> func <- function() {
+     super <- function() {
+         x <<- x + 10  # ローカル変数(super)
+     }
+     x <- 3  # ローカル変数(func)
+     super()
+     return(x)  # 出力1へ
+ }
> func()  # 関数の実行
[1] 13  # 出力1
> x
[1] 5  # 出力2

 基本的にローカル変数に限らず、Rはその現在の環境にないオブジェクトを扱おうとすると、まずその1つ上の環境に同じ名前のオブジェクトはないか探します。あればそれを、なければさらに上の環境を探すといったところです。今回のテーマもそれに近いところがあります。
 出力1, 2を見てください。永続代入演算子によってfunc関数内のローカル変数xには13が代入されていますが、グローバル変数xは実行前と変わりません。これは、代入する変数xがあるかどうか、最も近い上の環境から1つずつ順に調べていった結果、初めに見つけたfunc関数内のローカル変数xに代入を実行しているからです。

 よくわからないですね。では、もしもfunc関数内のローカル変数xがなければどうなるでしょう。

> x <- 5  # グローバル変数
> func <- function() {
+     super <- function() {
+         x <<- x + 10  # ローカル変数(super)
+     }
+     # x <- 3  # ローカル変数(func)
+     super()
+     return(x)  # 出力1へ
+ }
> func()  # 関数の実行
[1] 15  # 出力1
> x
[1] 15  # 出力2

 func関数の実行結果とグローバル変数xの出力が同じになりました。これは、super関数より上の環境におけるxの代入先が、グローバル変数xのみになってしまったためです。

 ならばグローバル変数xもなくしてしまえばどうなるでしょう。グローバル環境は変数サーチの終点になります。よってこれ以上、上の環境を探すことはありませんが、変数がないので新たに変数xを生成して代入します。

まとめ

 永続代入(スーパーアサインメント)演算子とは、グローバル環境までに最も近い上の環境の変数を探し、存在すればそれに代入を、なければグローバル変数を生成してでも代入する役割を果たす演算子のこと。

Rの関数の中身を知りたい

Rの関数の中身を知りたい時のお話です。


基本的には、
> 関数名
としてしまえば下のmatrix関数のように参照することができます。

> matrix
function (data = NA, nrow = 1, ncol = 1, byrow = FALSE, dimnames = NULL) 
{
    if (is.object(data) || !is.atomic(data)) 
        data <- as.vector(data)
    .Internal(matrix(data, nrow, ncol, byrow, dimnames, missing(nrow), 
        missing(ncol)))
}
<bytecode: 0x7f8ffc2bad78>
<environment: namespace:base>


しかし、aggregate関数のように

> aggregate
function (x, ...) 
UseMethod("aggregate")
<bytecode: 0x7fa16dab4f18>
<environment: namespace:stats>

と参照できない場合があります。このような関数は総称的関数といい、様々なクラスに対応させるため、引数に入れる変数のクラスごとの関数(メソッドという)をまとめた関数になります。これは総称的関数の中に複数のメソッドを含む形となります。総称的関数を実行すると、その時に入れた引数のクラスによって適切なメソッドが選択され、実行されます。
UseMethodにあるのが総称的関数ですので、その中のメソッドを見るためにはmethods関数で呼び出しましょう。

> methods(aggregate)
[1] aggregate.data.frame aggregate.default    aggregate.formula*   aggregate.ts        

   Non-visible functions are asterisked

これで、aggregate関数はaggregate.data.frame、aggregate.default、aggregate.formula*、aggregate.tsの4つのメソッドで構成されていることがわかりました。
このうち、「*」のついているメソッドは中身が隠蔽されており、matrix関数のように参照することができません。この場合は、getS3method関数で(またはgetAnywhere関数)
> getS3method("メソッド名", "クラス名")
とすると参照できます。

> getS3method("aggregate", "formula")
function (formula, data, FUN, ..., subset, na.action = na.omit) 
{
    if (missing(formula) || !inherits(formula, "formula")) 
        stop("'formula' missing or incorrect")
    if (length(formula) != 3L) 
 # 以下略

NAを含む時のdist関数

Rのdist関数において、対象とするベクトル内にNAが含まれている場合を調べました。


初めはNAで返ってくるのか考えていましたが、どうやらNAがあっても数値として計算して返してくれるようです。
計算過程がわかりやすいようにmanhattan距離で計算してみました。

> (dist.na <- matrix(c(1,2,4,NA,7,5,4,1,8,NA,9,10),3,4))
     [,1] [,2] [,3] [,4]
[1,]    1   NA    4   NA
[2,]    2    7    1    9
[3,]    4    5    8   10
> dist(dist.na, method = "manhattan")
   1  2
2  8   
3 14 12

確かにNAが含まれている1行目との距離もしっかり数値で返ってきていますね。


helpマニュアルを参照すると、
「 If some columns are excluded in calculating a Euclidean, Manhattan, Canberra or Minkowski distance, the sum is scaled up proportionally to the number of columns used.」
とあります。列数に比例して和を取ってくれるとのこと。
これは、NAの含まれていない列の計算結果の平均を、NAの含まれている列の計算結果としているみたいです。言い換えると、平均値代入しているということです。

ここで先ほどのmanhattan距離の例を見てみますと、

# 1,2行間のmanhattan距離
> abs(1-2) + abs(4-1) + ((abs(1-2) + abs(4-1)) / 2) * 2
[1] 8
# 1,3行間のmanhattan距離
> abs(1-4) + abs(4-8) + ((abs(1-4) + abs(4-8)) / 2) * 2
[1] 14

と、一致しました。


ちなみに、euclidean距離もこのとおり正しい結果であることがわかります。

> dist(dist.na, method = "euclidean")
         1        2
2 4.472136         
3 7.071068 7.615773

# 1,2行間のeuclidean距離
> sqrt((1-2)^2 + (4-1)^2 + (((1-2)^2 + (4-1)^2) / 2) * 2)
[1] 4.472136
# 1,3行間のeuclidean距離
> sqrt((1-4)^2 + (4-8)^2 + (((1-4)^2 + (4-8)^2) / 2) * 2)
[1] 7.071068