一元配置分散分析をRで実装する
手計算でもaov
でも一元配置分散分析は出来るので、Rで実装してみようと思った次第です。F分布の累積分布を除いてほぼほぼベクトル演算?を使っています。データは水準の繰り返し数のトータルから適当に生成しています。統計学的観点はほぼ0です。
実装
# データ設定 #### ## 水準 catv <- c( rep("A",5),rep("B",5),rep("C",10),rep("D",3)) ## データの範囲 data.range <- 10:30 ## データ datav <- sample(data.range,length(catv),T) # 計算 #### ## 総平均 mean.all <- mean(datav) ## 水準ごとの平均 mean.cat <- tapply(datav, catv, mean) ## 水準平方和 catv.sumsq <- sum( table(catv) * (mean.cat - mean.all)^2 ) ## 自由度: 水準の種類数 - 1 catv.df <- length(table(catv))-1 ## 全平方和 total.sumsq <- sum((datav - mean.all)^2) ## 残差平方和 ### 残差平方和 := 全平方和 - 水準平方和 zansa.sumsq <- total.sumsq - catv.sumsq ## 自由度: 水準ごとの繰り返し数-1 をそれぞれ足す zansa.df <- sum(table(catv)-1) ### 別解: 残差平方和 := 要素 - 水準平均 の平方和 #abs( zansa.sumsq - sum( (datav - rep(mean.cat,table(catv)))^2 ) ) < 0.01 # 答え合わせ #### # aov summary( aov(datav ~ catv) ) # 自力で計算 cat("\t ","Df","Sum Sq"," Mean Sq","F value","Pr(>F)","\n" ," catv"," ",catv.df,"",round(catv.sumsq,2), round(catv.sumsq/catv.df,2) ," ",round((catv.sumsq/catv.df)/(zansa.sumsq/zansa.df),3)," ",round(1-pf((catv.sumsq/catv.df)/(zansa.sumsq/zansa.df),catv.df,zansa.df ),3),"\n" ," Residuals",zansa.df,"",round(zansa.sumsq,2), "",round(zansa.sumsq/zansa.df,2) )
実行例
> # 答え合わせ #### > # aov > summary( aov(datav ~ catv) ) Df Sum Sq Mean Sq F value Pr(>F) catv 3 170.9 56.96 2.349 0.105 Residuals 19 460.8 24.25 > > # 自力で計算 > cat("\t ","Df","Sum Sq"," Mean Sq","F value","Pr(>F)","\n" + ," catv"," ",catv.df,"",round(catv.sumsq,2), round(catv.sumsq/catv.df,2) + ," ",round((catv.sumsq/catv.df)/(zansa.sumsq/zansa.df),3)," ",round(1-pf((catv.sumsq/catv.df)/(zansa.sumsq/zansa.df),catv.df,zansa.df ),3),"\n" + ," Residuals",zansa.df,"",round(zansa.sumsq,2), "",round(zansa.sumsq/zansa.df,2) + ) Df Sum Sq Mean Sq F value Pr(>F) catv 3 170.89 56.96 2.349 0.105 Residuals 19 460.77 24.25
学び
table関数の出力結果はベクトルのようである。
確信はないのだけれどtable関数の出力はベクトルっぽく扱えます。
例
> table( c(rep("A",2),rep("B",4)) ) A B 2 4 > table( c(rep("A",2),rep("B",4)) ) +3 A B 5 7
rep関数の繰り返し回数はベクトルを渡すことができる
水準ごとの平均を水準内の繰り返し数分だけ繰り返す、といった処理をしたいときにrepとtableを組み合わせて対処することができます。
例
10を1回、20を2回、30を3回、40を4回繰り返す。
> rep(c(10,20,30,40),times = c(1,2,3,4)) [1] 10 20 20 30 30 30 40 40 40 40
参考
Rで線形計画法
lpSolve
*1を使って線形計画法を解いてみた。理論は難しいから敬遠していたがソルバー使うと一瞬で解けて気持ち良い。
関数lp
に渡すパラメータは以下の通り。第一パラメータは"max"か"min"。詳しいことはrdocumentationなどを参照。
- f_obj : ベクトル。目的関数の係数を格納する。
- f_con : 行列。制約条件の係数を格納する。
- f_dir : ベクトル。不等号または等号を格納する。
- f_rhs : ベクトル。right hand side(右辺)、制約条件の右辺の係数(値)を格納する。
以下実例。
install and load library
install.packages('lpSolve') library(lpSolve)
その1*2
線型計画法
f_obj <- c(1,1) #目的関数の係数 f_con <- matrix(c(1,2,2,1),ncol=2) #制約条件を行列で f_dir <- c("<=","<=") #不等号? f_rhs <- c(4,4) # 不等号の右側
最適解を表示
lp("max",f_obj,f_con,f_dir,f_rhs)
最適解を取る時の引数
lp("max",f_obj,f_con,f_dir,f_rhs)$solution
整数計画法
lp("max",f_obj,f_con,f_dir,f_rhs,int.vec=1:2) #int.vecはintegerなvectorのindicesを与えていると思われる。 lp("max",f_obj,f_con,f_dir,f_rhs, int.vec=1:2)$solution
0-1 整数計画法
f_obj01 <- c(16,19,23,11) f_con01 <- matrix( c(2,3,4,5), ncol=4, nrow=1) f_dir01 <- c("<=") f_rhs01 <- c("6") lp("max",f_obj01,f_con01,f_dir01,f_rhs01,all.bin=T) lp("max",f_obj01,f_con01,f_dir01,f_rhs01,all.bin=T)$sol
その2*3
f_obj3 <- c(4,6) f_con3 <- matrix(c(1,3,0,2,0,4),nrow=3,ncol=2) f_dir3 <- rep("<=",3) f_rhs3 <- c(8,12,12) f_sol3 <- lp("max", f_obj3,f_con3,f_dir3,f_rhs3) f_sol3 f_sol3$sol
f_obj4 <- c(20,30) f_con4 <- matrix(c(1,3,3,2,4,1),nrow=3,ncol=2) f_dir4 <- rep("<=",3) f_rhs4 <- c(800,1800,1500) f_sol4 <- lp("max",f_obj4,f_con4,"<=",f_rhs4) f_sol4 f_sol4$sol
その3*4
f_obj5 <- c(1,1) f_con5 <- matrix(c(3,1,1,3),nrow=2,ncol=2) f_dir5 <- rep("<=",2) f_rhs5 <- c(9,6) f_sol5 <- lp("max",f_obj5,f_con5,f_dir5,f_rhs5) f_sol5 f_sol5$solution
母比率の信頼区間に含まれる2次不等式を解く
母比率の信頼区間
母比率 の母集団からサイズの標本を抽出する。このとき標本割合 について、 は近似的に平均、分散 の正規分布 に従う。ただし、 である。したがって、 と標準化した は 標準正規分布 に従う。
さて、確率 から不等式 を抜き出したものに の右辺を代入すると、の%信頼区間は
と表される。例えば とすれば、である。通常ここでは未知であり不等式の左右のにあるはnが大なるときの一致性からと置き換えて、
という公式が用いられる。
2次不等式を解く
それでは、を用いず
をについて解いてみる。以下、式変形。
式変形
となる。
具体例で検証
統計学基礎のp.118の例9の数字を使って検証してみる。例9の主要な数字は、
標本サイズ n = 1200
標本比率 にて
母比率 の95%信頼区間を求める。
である。ここで、 とする。
公式を用いる
なので、 ]
となる。
2次不等式を解いた結果を用いる
なので、Rを用いて計算すると
n <- 1200
ph <- 0.054
z0 <- 1.96
A <- (-2*(n*ph) - (z0)^2)/(n+(z0)^2)
B <- (n*ph^2)/(n+z0^2)c(-(1/2)*A - sqrt( (1/4)*A ^2 - B),-(1/2)*A + sqrt( (1/4)*A ^2 - B))
> c(-(1/2)*A - sqrt( (1/4)*A ^2 - B),-(1/2)*A + sqrt( (1/4)*A ^2 - B))
[1] 0.04257642 0.06827005
より、 ]
となる。
参考
2次元正規分布の確率密度関数
東京大学出版会『統計学入門』 の p.145 図7.5 2次元正規分布 について、与えられたパラメータから楕円群(等高線の式)を導出する。
パラメータ
,
代入
を計算すると、
となるので、確率密度関数に代入するとexpの中身は として
となる。ここでexpの中身を適当な定数c と置き、-200倍すると、
となり、p.145にある楕円群の式とほぼ等しくなる。
AB = E ならば BA = Eをランクを用いて示す
チャート式シリーズ 大学教養 線形代数を買ってから、行列の構造、特に「ランク」の理解が深まりました。ということでタイトルの命題の証明です。
AB = E ならば BA = E
AもBもn次正方行列、Eはn次単位行列とします。 ここで、rankA = r ≦ n , rankB = s ≦ n とし、rankE = nです。 まず、基本行列Pを左からAに掛けた結果をPA = X とすると、 rankPA = rankA = r = rankX です。また、rankP = nです。 次に、PA = Xを用い、P(AB) = PE = P および (PA)B = XB を得、総じて XB = P を得ます。 さて、XB = P のランクについて考えると、右辺:rankP = nなので rankXB = nを得ます。 rankXB = n ということは、rankX = nかつrankB = n ということが分かります。 ここで、XはAをPを用いて階段化した行列であり、 なおかつrankX = nよりXは正則であることから、 X = Eを得ます。したがって、XB = P ⇔ EB = P ⇔ B = P です。 以上より、仮定はAB = Eだったので AB = E ⇔ AP = E ⇔ P = A⁻¹ですので、 BA = PA = A⁻¹A = E となります。■
参考
行簡約行列をRで
pracmaのrrefを使って行簡約行列を出してみます。これで適当な行列を手計算で簡約化して答え合わせができますね。
ソース
# install.packages('pracma') # library('pracma') ## 行ベクトルを4本 a1 <- c(1,2,0) a2 <- c(2,4,0) a3 <- c(0,1,3) a4 <- c(1,3,3) ## 行列にする M <- matrix(c(a1,a2,a3,a4),nrow = 3, ncol=4) ## 行列を表示 print(M) ## 行簡約行列を表示 rref(M) ## 行列の階数を表示 Rank(M)
実行結果
> a1 <- c(1,2,0) > a2 <- c(2,4,0) > a3 <- c(0,1,3) > a4 <- c(1,3,3) > M <- matrix(c(a1,a2,a3,a4),nrow = 3, ncol=4) > print(M) [,1] [,2] [,3] [,4] [1,] 1 2 0 1 [2,] 2 4 1 3 [3,] 0 0 3 3 > Rank(M) [1] 2 > rref(M) [,1] [,2] [,3] [,4] [1,] 1 2 0 1 [2,] 0 0 1 1 [3,] 0 0 0 0 >
update文
とある1レコードの値で本番のデータを全件更新してしまうというヤラカシをやってしまったので、次回は気をつける意を込めた記録です。 SQLはcompile sql server onlineを使用します。
仕様
- tbl1.val1をtbl2.val2へと更新する
- 更新の条件
- tbl1.idとtbl2.idを結合し、tbl2.val2 = 'foo'の時だけ更新する
- tbl2.val2 <> 'foo'の時は更新しない
tbl1
tbl2
ということでこの場合、id = {10,30,80}が更新対象となります。
問題のコード(ヤラカシあり)
-- データ作成 ここから create table tbl1 ( id int primary key , val1 nvarchar(10) null ) ; insert into tbl1 values (10,'a') ,(20,'b') ,(30,'c') ,(40,'d') ,(50,'e') ,(60,'f') ,(70,'g') ,(80,'h') ,(90,'i') ; create table tbl2 ( id int primary key , val2 nvarchar(10) null , constraint fk_id foreign key(id) references tbl1 ) ; insert into tbl2 values (10,'foo') ,(30,'foo') ,(50,'baz') ,(60,'baz') ,(80,'foo') ; -- データ作成 ここまで -- 一時テーブルに tbl1 のレコードを格納 select * into #t1 from tbl1 ; -- tbl1 更新前 select * from tbl1 ; -- 更新処理 update tbl1 set val1 = tbl2.val2 from #t1 inner join tbl2 on #t1.id = tbl2.id and tbl2.val2 = 'foo' ; -- tbl1 更新後 select * from tbl1 ;
実行結果
上:更新前のtbl1, 下:更新後のtbl1なのですが、見ての通り残念な感じに更新されてしまいました。。
better なコード
★の箇所のように、updateで更新対象となるテーブルをinner join してあげるとinner join で抽出される結果通りに更新されます。
update tbl1 set val1 = tbl2.val2 from #t1 inner join tbl2 on #t1.id = tbl2.id and tbl2.val2 = 'foo' inner join tbl1 on tbl1.id = #t1.id -- ★ ;
better なコードの実行結果
仕様を満たした更新結果となりました。
まなび
更新対象のテーブルをちゃんとjoin しましょう。