先日、by関数について質問を受けたので、それを一例に少しまとめようと思います。
- by関数はヘルプを見れば分かるように、tapply関数と同じ作用を示す関数です。
- tapply関数は、名前から予想がつくようにapply関数の仲間です。(因みに、apply系の関数として、apply関数、lapply関数、sapply関数は既出です)
by関数は引数に、data、INDICES、FUN、simplyfyをとります。
それぞれを簡単に説明すると
- data --- by関数で処理したいデータセットになります。data.frameを想定していますが、matrixも使えます。
- INDICES --- dataに対して処理したいsubsetを与えます。引数dataに指定したdata.frameにfactorのデータ系列があったりすると、それをsubsetに指定できるので便利です。tapply巻子ではINDEXになります。
- FUN --- 引数dataに対して、適用したい関数を指定します。これはapply系関数で共通する概念ですね。察しのとおり、FUNで指定した関数に引数がある場合は、by関数の中に引数で追加できます。引数FUNの後につけることも忘れずに。
- simplify --- 処理結果の返値のデータ形式を指定します。TRUEならscalar、FALSEならlistで返します。
で、irisのデータで簡単な例を作ってみます。
irisはSpeciesがfactorで、 setosa、versicolor、virginicaがそれぞれ50サンプルあります。
> str(iris) 'data.frame': 150 obs. of 5 variables: $ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ... $ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ... $ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ... $ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ... $ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ... > summary(iris) Sepal.Length Sepal.Width Petal.Length Petal.Width Min. :4.300 Min. :2.000 Min. :1.000 Min. :0.100 1st Qu.:5.100 1st Qu.:2.800 1st Qu.:1.600 1st Qu.:0.300 Median :5.800 Median :3.000 Median :4.350 Median :1.300 Mean :5.843 Mean :3.057 Mean :3.758 Mean :1.199 3rd Qu.:6.400 3rd Qu.:3.300 3rd Qu.:5.100 3rd Qu.:1.800 Max. :7.900 Max. :4.400 Max. :6.900 Max. :2.500 Species setosa :50 versicolor:50 virginica :50
で、by関数はデータセットのsubsetに対して、FUNで指定する処理を実行できます。
例えば、Speciesの3系統のそれぞれの統計量を算出したいときは、
> by(iris, INDICES=iris$Species, FUN=summary) iris$Species: setosa Sepal.Length Sepal.Width Petal.Length Petal.Width Min. :4.300 Min. :2.300 Min. :1.000 Min. :0.100 1st Qu.:4.800 1st Qu.:3.200 1st Qu.:1.400 1st Qu.:0.200 Median :5.000 Median :3.400 Median :1.500 Median :0.200 Mean :5.006 Mean :3.428 Mean :1.462 Mean :0.246 3rd Qu.:5.200 3rd Qu.:3.675 3rd Qu.:1.575 3rd Qu.:0.300 Max. :5.800 Max. :4.400 Max. :1.900 Max. :0.600 Species setosa :50 versicolor: 0 virginica : 0 ------------------------------------------------------------ iris$Species: versicolor Sepal.Length Sepal.Width Petal.Length Petal.Width Species Min. :4.900 Min. :2.000 Min. :3.00 Min. :1.000 setosa : 0 1st Qu.:5.600 1st Qu.:2.525 1st Qu.:4.00 1st Qu.:1.200 versicolor:50 Median :5.900 Median :2.800 Median :4.35 Median :1.300 virginica : 0 Mean :5.936 Mean :2.770 Mean :4.26 Mean :1.326 3rd Qu.:6.300 3rd Qu.:3.000 3rd Qu.:4.60 3rd Qu.:1.500 Max. :7.000 Max. :3.400 Max. :5.10 Max. :1.800 ------------------------------------------------------------ iris$Species: virginica Sepal.Length Sepal.Width Petal.Length Petal.Width Min. :4.900 Min. :2.200 Min. :4.500 Min. :1.400 1st Qu.:6.225 1st Qu.:2.800 1st Qu.:5.100 1st Qu.:1.800 Median :6.500 Median :3.000 Median :5.550 Median :2.000 Mean :6.588 Mean :2.974 Mean :5.552 Mean :2.026 3rd Qu.:6.900 3rd Qu.:3.175 3rd Qu.:5.875 3rd Qu.:2.300 Max. :7.900 Max. :3.800 Max. :6.900 Max. :2.500 Species setosa : 0 versicolor: 0 virginica :50
となります。
次にFUNとFUNに対する引数で指定する例を示します。
例えば、colMeans関数を使ってSepal.LengthとSepal.Widthの系列毎の平均値を求めるときに、データに欠損値がある例を示します。
> set.seed(123) > data <- iris > data$Sepal.Length[sample(nrow(iris),10)] <- data$Sepal.Width[sample(nrow(iris),10)] <- NA > > by(data[,c("Sepal.Length", "Sepal.Width")], INDICES=data$Species, FUN=colMeans) data$Species: setosa Sepal.Length Sepal.Width NA NA ------------------------------------------------------------ data$Species: versicolor Sepal.Length Sepal.Width NA NA ------------------------------------------------------------ data$Species: virginica Sepal.Length Sepal.Width NA NA > > by(data[,c("Sepal.Length", "Sepal.Width")], INDICES=data$Species, FUN=colMeans, na.rm=TRUE) data$Species: setosa Sepal.Length Sepal.Width 4.997826 3.427083 ------------------------------------------------------------ data$Species: versicolor Sepal.Length Sepal.Width 5.950000 2.780435 ------------------------------------------------------------ data$Species: virginica Sepal.Length Sepal.Width 6.582609 2.952174
colMeansの引数na.rmを引数FUNの後につけることができ、NAをomitした結果を得ることができました。
ここまでくると、お気づきと思いますが、by関数、tapply関数はラッパー関数です。
colMeansの例は次のように記述できます。
> lapply(unique(data$Species), function(i) colMeans(subset(data, subset=Species==i, select=c("Sepal.Length", "Sepal.Width")), na.rm=TRUE)) [[1]] Sepal.Length Sepal.Width 4.997826 3.427083 [[2]] Sepal.Length Sepal.Width 5.950000 2.780435 [[3]] Sepal.Length Sepal.Width 6.582609 2.952174
コードをシンプルにできる点でapply系関数は非常に便利です。
FUNの部分に自作関数を持ってくると、複雑な処理もシンプルなコードで記述できるのがよいところだと思います。