2016-07-03 9 views
-3

Rで数式がどのように機能するのか理解できません。 毎月の時系列を含むベクトルがある場合、データを季節的なロジックに分割するボックスプランをどうすれば作成できますか?私は月に1箱12箱持っていたいと思います。数式がrのボックスプロット

+3

あなたはboxplot' '?の例を試してみて、'?formula' – rawr

+0

を読みました、はい、残念ながら、私は事 – carlo

+0

を理解していませんでした。 'data.frame'として配置されたデータを使った例:boxplot(Ozone〜Month、data = airquality) –

答えて

6

ストレージレベルでは、式はparse treeです。解析ツリーは、1つまたは2つの引数を取る`~`()関数への呼び出しをエンコードします。片面式の場合は、RHSを表す単一の引数をとり、両面式の場合は、式のLHSとRHSを表す2つの引数をとります。

式の構文解析ツリー記憶表現に埋め込まれた`~`()の呼び出しは、実際には何も意味しないことに注意してください。一般に、`~`()関数は、明示的に(例えば、`~`(a+b,c/d))、またはR言語によって提供される構文的砂糖機能(例えば、a+b~c/d)を使用して、数式オブジェクトの作成を許可する以外は、実際には何もしません。数式の解析木記憶表現の最上位レベルの符号化に`~`()関数を使用することは、かなり恣意的で重要な実装の詳細である。これについては後で詳しく説明します。


R言語は、これらの解析木の構造を検査し、理解するために私たちを助けることができる再帰的なリスト構造へ構文木を打破することが可能となります。

私はこれを行うことができ、短い再帰関数書いている:それでは、例を見てみましょう

ptunwrap <- function(x) if (typeof(x)=='language') lapply(as.list(x),ptunwrap) else x; 

を:

f1 <- a+b~c/d; 
f1; 
## a + b ~ c/d 
ptunwrap(f1); 
## [[1]] 
## `~` 
## 
## [[2]] 
## [[2]][[1]] 
## `+` 
## 
## [[2]][[2]] 
## a 
## 
## [[2]][[3]] 
## b 
## 
## 
## [[3]] 
## [[3]][[1]] 
## `/` 
## 
## [[3]][[2]] 
## c 
## 
## [[3]][[3]] 
## d 
## 

解析ツリーは、関数呼び出し、関数呼び出しが含まれている場合再帰的リスト構造内の単一のリストノードとして表される。関数のシンボルは、そのリストの最初のリスト要素として埋め込まれ、その引数はリストの後続のリスト要素として埋め込まれます。

上記の解析ツリーには3つの関数呼び出しがあります。最上位レベルのリストは、前述のように`~`()関数への呼び出しを表します。 2番目のトップレベルのリストコンポーネントは、別のリストにさらに分岐します。これは、`+`()関数への呼び出しで構成され、それ自体は2つの引数、記号aおよびbを取ります。第3の構成要素は同様であり、`/`()関数への呼び出しを表し、再び2つの引数、すなわちcおよびdを受け取る。


パースツリーは、常に構文的に有効なRコードを表し、単一の結果値を生成するために評価される能力を有しているが、それはパースを評価する必要ではないことを理解することが重要です木。解析木を作成して評価することは全く不可能です。

それでは、評価しないパーズツリーを作成する目的は何ですか? Rでは、これは、便利な構文を使用して特定の情報を関数に容易に伝達するためによく行われます。

ランダムな例として、data.tableパッケージでは、次の構文糖が既存のデータに列を追加することができます。テーブル、:=演算子使用:

library(data.table); 
dt <- data.table(a=1:3); 
dt[,b:=a*2L]; 
dt; 
## a b 
## 1: 1 2 
## 2: 2 4 
## 3: 3 6 

内部、data.tableは二番目の引数の解析ツリー取得するために、非標準の引数の評価を使用する(関数定義に技術的に第三に、構文糖形態の第2の)にパラメータ名がjであるため、しばしば "j引数"と呼ばれる`[.data.table`()関数です。

data.table:::`[.data.table`; 
## function (x, i, j, by, keyby, with = TRUE, nomatch = getOption("datatable.nomatch"), 
##  mult = "all", roll = FALSE, rollends = if (roll == "nearest") c(TRUE, 
##   TRUE) else if (roll >= 0) c(FALSE, TRUE) else c(TRUE, 
##   FALSE), which = FALSE, .SDcols, verbose = getOption("datatable.verbose"), 
##  allow.cartesian = getOption("datatable.allow.cartesian"), 
##  drop = NULL, on = NULL) 
## { 
## 
## ... snip ... 
## 
##  if (!missing(j)) { 
##   jsub = substitute(j) 
##   if (is.call(jsub)) 
##    jsub = construct(deconstruct_and_eval(replace_dot(jsub), 
##     parent.frame(), parent.frame())) 
##  } 
## 
## ... snip ... 
## 

我々は、彼らがj引数のパースツリーを取得するためにsubstitute(j)を使用している見ることができます:あなたがしたい場合は、あなたが実際に直接R.ソース自体を調べることができますここでは、コードの最も関連性の高い作品の抜粋です。上記デモのために、これは、彼らが得るであろうものである:トップレベルの関数記号は、オペレータが追加(または変更、または削除のためにサポートされ:=、ある場合

ptunwrap(substitute(b:=a*2L)); 
## [[1]] 
## `:=` 
## 
## [[2]] 
## b 
## 
## [[3]] 
## [[3]][[1]] 
## `*` 
## 
## [[3]][[2]] 
## a 
## 
## [[3]][[3]] 
## [1] 2 
## 

後でコードで、それらはテストRHSがNULLの列)をdata.tableに追加します。そうであれば、LHSが単一の裸の単語で構成されているかどうかをテストします。これは、追加する(または変更または削除する)列の名前とみなされます。実際にはは不可能であることを確認してください。この場合、data.tableにはまだ存在しないシンボルで構成されているため、解析木のLHSを実際に評価するにはを実際に評価してください。しかし、RHSは新しい名前の下でdata.tableに追加される列ベクトルを生成するために評価されることになります。

したがって、式はRのさまざまなコンテキストで使用でき、常に評価されるわけではありません。場合によっては、構文解析ツリーを調べて、呼び出し元から呼び出し先に渡された情報を取得することもあります。 の文脈であっても、LHSまたはRHS(またはその両方)のみが独立して評価され、作成時に解析ツリーに埋め込まれた最上位の関数シンボルは無視されることがあります。

boxplot()機能に目を向けると

、のはformula引数のドキュメントを見てみましょう:

、このようなyが分割するデータ値の数値ベクトルであるY〜GRP、などの式、グループ化変数grp(通常は因子)に従ってグループに分類します。

この場合、数式の両辺は独立して評価され、LHSはデータベクトルを提供し、RHSはグループ定義を提供します。

次のようにこれを実証するための良い方法がある:式の両側はリテラル式で構成する方法

boxplot(1:9~1:9%%3L); 

simple-boxplot

お知らせ:内部

1:9; 
## [1] 1 2 3 4 5 6 7 8 9 
1:9%%3L; 
## [1] 1 2 0 1 2 0 1 2 0 

boxplot()をしなければなりませんでしたあなたが2つの表現を通過したかのように、数式の各辺を独立して評価してデータとグループ化ベクトルを生成します別々の引数としてns。あなたがしたい場合

N <- 36L; df <- data.frame(date=seq(as.Date('2016-01-01'),by='month',len=N),y=rnorm(N)); 
df; 
##   date   y 
## 1 2016-01-01 -1.56004488 
## 2 2016-02-01 0.65699747 
## 3 2016-03-01 0.05729631 
## 4 2016-04-01 -0.02092276 
## 5 2016-05-01 0.46673530 
## 6 2016-06-01 -0.18652580 
## 7 2016-07-01 0.06228650 
## 8 2016-08-01 1.54452267 
## 9 2016-09-01 1.06643594 
## 10 2016-10-01 -1.51178160 
## 11 2016-11-01 0.82904673 
## 12 2016-12-01 0.37667201 
## 13 2017-01-01 -0.10135801 
## 14 2017-02-01 0.94692462 
## 15 2017-03-01 -1.60781946 
## 16 2017-04-01 0.47189753 
## 17 2017-05-01 -1.32869317 
## 18 2017-06-01 -0.49821455 
## 19 2017-07-01 0.54474606 
## 20 2017-08-01 0.47565264 
## 21 2017-09-01 -0.97494730 
## 22 2017-10-01 -1.22781588 
## 23 2017-11-01 -0.34919086 
## 24 2017-12-01 -0.78153843 
## 25 2018-01-01 -0.59355220 
## 26 2018-02-01 -2.58287605 
## 27 2018-03-01 1.42148186 
## 28 2018-04-01 -1.01278176 
## 29 2018-05-01 -0.80961662 
## 30 2018-06-01 0.19793126 
## 31 2018-07-01 -1.03072915 
## 32 2018-08-01 -0.87896416 
## 33 2018-09-01 -2.36216655 
## 34 2018-10-01 1.82708221 
## 35 2018-11-01 0.05579195 
## 36 2018-12-01 1.28612246 
boxplot(y~months(date),df); 

monthly-time-series-boxplot


は、トレースを必要とするソースコードを、学ぶことができます。


それでは、月次、時系列箱ひげ図の簡単なデモを作成してみましょうS3ルックアッププロセス:

boxplot; 
## function (x, ...) 
## UseMethod("boxplot") 
## <bytecode: 0x600b50760> 
## <environment: namespace:graphics> 
graphics:::boxplot.formula; 
## function (formula, data = NULL, ..., subset, na.action = NULL) 
## { 
##  if (missing(formula) || (length(formula) != 3L)) 
##   stop("'formula' missing or incorrect") 
##  m <- match.call(expand.dots = FALSE) 
##  if (is.matrix(eval(m$data, parent.frame()))) 
##   m$data <- as.data.frame(data) 
##  m$... <- NULL 
##  m$na.action <- na.action 
##  m[[1L]] <- quote(stats::model.frame) 
##  mf <- eval(m, parent.frame()) 
##  response <- attr(attr(mf, "terms"), "response") 
##  boxplot(split(mf[[response]], mf[-response]), ...) 
## } 
## <bytecode: 0x6035c67f8> 
## <environment: namespace:graphics> 

match.call()経由)、自身の呼び出しを引き起こした解析ツリー、それは少し、最も注目すべきは、その後stats::model.frameで、独自機能のシンボルboxplot.formulaを交換し、その新しい解析木を評価するマッサージそれはほとんどexasperatinglyラウンドアバウトと複雑ですが、graphics:::boxplot.formula()が効果的に取得しますこれにより、stats::model.frame()が呼び出されます。この関数は、非常に複雑そのものであり、さらにS3のルックアップを必要とするが、ここでは最も関連性の高いコードです:

model.frame; 
## function (formula, ...) 
## UseMethod("model.frame") 
## <bytecode: 0x601464b18> 
## <environment: namespace:stats> 
model.frame.default; 
## function (formula, data = NULL, subset = NULL, na.action = na.fail, 
##  drop.unused.levels = FALSE, xlev = NULL, ...) 
## { 
## 
## ... snip ... 
## 
##  if (!inherits(formula, "terms")) 
##   formula <- terms(formula, data = data) 
##  env <- environment(formula) 
##  rownames <- .row_names_info(data, 0L) 
##  vars <- attr(formula, "variables") 
##  predvars <- attr(formula, "predvars") 
##  if (is.null(predvars)) 
##   predvars <- vars 
##  varnames <- sapply(vars, function(x) paste(deparse(x, width.cutoff = 500), 
##   collapse = " "))[-1L] 
##  variables <- eval(predvars, data, env) 
## 
## ... snip ... 
## 

だから、最終的には、それは式オブジェクトから個々の表現を取得し、与えられたデータとeval()を使用してそれらを評価します結果ベクトルを与える文脈としての式、の.frame及び閉鎖環境:以前に作られた点を繰り返しに

attr(terms(y~months(date),data=df),'variables'); 
## list(y, months(date)) 
eval(attr(terms(y~months(date),data=df),'variables'),df); 
## [[1]] 
## [1] -1.56004488 0.65699747 0.05729631 -0.02092276 0.46673530 -0.18652580 0.06228650 1.54452267 1.06643594 -1.51178160 0.82904673 0.37667201 -0.10135801 0.94692462 -1.60781946 0.47189753 -1.32869317 -0.49821455 0.54474606 
## [20] 0.47565264 -0.97494730 -1.22781588 -0.34919086 -0.78153843 -0.59355220 -2.58287605 1.42148186 -1.01278176 -0.80961662 0.19793126 -1.03072915 -0.87896416 -2.36216655 1.82708221 0.05579195 1.28612246 
## 
## [[2]] 
## [1] "January" "February" "March"  "April"  "May"  "June"  "July"  "August" "September" "October" "November" "December" "January" "February" "March"  "April"  "May"  "June"  "July" 
## [20] "August" "September" "October" "November" "December" "January" "February" "March"  "April"  "May"  "June"  "July"  "August" "September" "October" "November" "December" 
## 

`~`()関数は、評価プロセス中に発見さどこではないことに注目してください。これは、数式オブジェクトの構文木記憶表現における最上位関数記号として`~`()関数を符号化するR式の任意の実装詳細である。数式の辺の実際の評価は、それが発生した場合、その関数の評価を伴わない。

最後に、数式の記憶域表現を構成するパーズツリー全体を実際に評価するとどうなるかを考えてみましょう。 `~`()関数は、その引数から数式を作成するだけであることを思い出してください。私は興味があるかもしれ解析木や数式上の他の回答のカップルを書いた

f1; 
## a + b ~ c/d 
eval(f1); 
## a + b ~ c/d 
eval(eval(f1)); 
## a + b ~ c/d 

:したがって、式を評価するだけで評価された非常に同じ式を吐き出しの面白い効果を持っています

+1

良い悲しみは、この質問に対する信じられない答えです。 – gung

+1

これは私が答えとして期待していた以上のものです。どうもありがとうございました! – carlo

関連する問題