本文原文地址http://dbtemp.blogspot.com/2011/08/structural-equation-modeling-in-openmx.html
模型的自由度
結構模型的一個重要特征就是可以用簡單的方式對數據進行解釋。我們可以很容易的用優化步驟估計數據的協方差矩陣和均值,而不用對他們之間的關系進行建模。下面我們將會看到,我們可以通過對模型進行估計,提供一個baseline(一個飽和模型,'saturated model'),將結構模型與這個模型相比較,可以得到結構模型的似然度。在模型構建時,一個重要的法則便是:簡單就是正義!因此,我們認為好的結構模型應該可以通過對數目較少的參數進行建模來解釋觀測到的數據。需要估計的參數和觀測的統計量之間的數量差,與模型的自由度有關。
在Part1中提到的模型中,
假設我們將潛變量(V和N)的方差設為1,即不用去估計這兩個值,我們還有8個需要估計的變量(筆者:8個也是好多啊)。這8個變量是:a,b,c,d,e,f,g,h。我們放入模型里的觀測到的協方差矩陣一共包含10個不同的值(因為矩陣是對稱的,即我跟你的協方差=你跟我的協方差,所以我們只用考慮矩陣一半的值)即,這里我們只考慮下三角的值。
共有10個值,其中兩個是不用估計的1。因此10-2=8即為模型自由度。
實際上,在OpenMx中,均值和協方差都要估計,在如上圖所示的模型里,這意味著一共有14個觀測值以及12個要估計的值,自由度仍然是2。
一般來說,自由度大于零是很重要的,這樣的模型被稱作overidentified模型(過度識別?)。相反的,如果我們設計的模型中要估計的參數比觀測的參數還要多的時候,這個模型就是underidentified(識別不足的)并且不能得到很好地結果。下面我們會討論到,針對一些模型,給其中的一些參數固定值是有必要的(這樣他們就不算在要估計的參數中了),或是,設定好兩條路徑之間的關系(這樣相當于兩條路徑變成了一條,另外一條隨之變化),這樣就可以保證模型是過度識別的。
現在,我們就可以開始應用我們的模擬數據,對模型進行擬合了。
首先,設定所有變量的均值都是0,方差為1,W和S的協方差為0.6,B和P的協方差是0.5,其他變量之間協方差是0。
這里我們需要用到mvrnorm函數來生成數據,在R中查閱函數用法有兩種方式:
help(mvrnorm)
example(mvrnorm)
但是有時候,你得到的信息是:
這是因為mvrnorm函數所在的package沒有被R自動加載,我們需要手動加載一下。
require(OpenMx)
加載完畢后就可以使用help和example了。
下面生成4個變量
使用下面程序可以生成相關變量,可以設定相關系數的大小
#-------------------------------------------------------------------------------------------------------
# Simcorr
# (based on p 11 of OpenMx manual )
# Modified version by: DVM Bishop, 3rd March 2010
# Simulates data on X and Y from 50 cases, with correlation of .5 between them
# (NB using smaller N than in original example, so user can see mismatch between
# obtained correlation and that specified by user).
#-------------------------------------------------------------------------------------------------------
require(MASS) # MASS is R package used for generating multivariate normal data
set.seed(2) # A seed is a value used in creating random numbers;
# You don't need to understand how it works and you can use any number.
# Keep the seed the same if you want the same random numbers every time you run
# Change the seed to any other number to run the script and get different random numbers
rs=.5 # User-specified correlation between variables
# The next command has been broken up with comments to explain each part
# But normally would just be written in one line as:
# mydata=mvrnorm(50,c(0,0),matrix(c(1,rs,rs,1),2,2))
mydata=mvrnorm(50, # Create matrix of multivariate random normal deviates, mydata,
# and specify number of XY pairs to generate
c(0,0), # User-specified mean values for X and Y; NB c denotes concatenation in R
matrix(c(1,rs,
rs,1),
2,2)) # Covariance matrix to be simulated, 2 rows, 2 columns
# this will give a matrix as follows: [1 .5
# .5 1]
mylabels=c('X','Y') # Put labels (X and Y) for the two variables in a vector
dimnames(mydata)=list(NULL,mylabels) # Allocate labels to mydata (our created dataset)
# A list contains a vector of row labels and a vector of column labels;
# We just want the second vector, and so NULL is the first entry
summary(mydata) # Print means for mydata
print('Covariances')
cov(mydata) # Print covariance for mydata
print('Correlations')
cor(mydata) # Print correlation for mydata
print('Note that actual values may differ from specified value of .5, especially with small sample size')
print('SDs')
sd(mydata) # Print SD for mydata
print('Note that the correlation = covariance/(SD_X * SD_Y)')
plot(mydata) # Plot a scatterplot of X vs Y
write.table(mydata,"myfile") # Save a copy of mydata in your R directory under name "myfile"
# If you want to re-read your data another time, you can use a command such as
# newdata=read.table("myfile");
# This will create a matrix called newdata containing the data
#------------------------------------------------------------------------------------------------------------------------
或者讀者可以用以下命令生成,以W和S為例,協方差0.6,均值0,方差1,80個觀測值
WandS=mvrnorm(80, # 指定變量中樣本個數
c(0,0), #指定每個變量的均值
matrix(c(1,0.6,0.6,1),2,2)) #指定變量間的協方差矩陣,并指定為2*2大小,
#該矩陣這里為(變量自己和自己的協方差為變量方差)
# |1 0.6|
# |0.6 1|
以上生成的變量都是隨機的,即每次運行的結果都不同。
CovMat = matrix(c(0),nrow=4,ncol=4)
CovMat[1,2] = 0.6
CovMat[3,4] = 0.5
CovMat = t(CovMat)+diag(1,4)+CovMat
myscore=mvrnorm(80,c(0,0,0,0),
matrix(CovMat,4,4))
myW = FourVar[,1]
myS = FourVar[,2]
myB = FourVar[,3]
myP = FourVar[,4]
write.table(x = myscore,file = 'my4var')
基于模擬的數據可以用cor(myscore)
來檢驗,得到的結果會和一開始設定好的有所不同,這是由于樣本量小的緣故。最后的結果保存在my4Var內
下面就開始真正的OpenMx的使用了
OpenMx為我們提供了一個選項可以用來設定我們的模型的類型為RAM,這對初學者來說是非常有用的,因為我們可以很清楚的用這個模型構架出圖上的模型。對于一個RAM模型來說,我們在模型中會指定哪些是顯變量,哪些是潛變量,以及這些變量之間哪些路徑是單方向箭頭、哪些是雙方向箭頭。代碼如下:
# -----------------------------------------------------------------------
# Program: PathCov_2factor, based on TwoFactorModel_PathCov.R
# Author: DVM Bishop
# Date: 4th March 2010
#
# Two Factor model to estimate factor loadings and residual variances
# Path style model input - Covariance matrix data input
# -----------------------------------------------------------------------
require(OpenMx)
#準備數據
myscore=read.table("my4var") #把我們之前保存的數據讀入
mylabels=c('W', 'S', 'B', 'P')
colnames(myscore)=mylabels # 指定矩陣中的每一列都叫什么名字
myFADataCov=cov(myscore) # 把這些變量之間真實的協方差列出
# -----------------------------------------------------------------------
#生成一個 MxModel 對象; 然后通過 mxRun 進行模型擬合
# 因為我們選取的是RAM類型的模型,這里需要對每一條路徑進行建模
# -----------------------------------------------------------------------
db_twoFactorModel <- mxModel("DB_Two Factor Model_Path", type="RAM",
mxData( #指定輸入數據集
observed=myFADataCov, # 觀測變量為原始
type="cov",
numObs=nrow(myscore),
),
manifestVars=c("W", "S", "B", "P"), #指定顯變量名字
latentVars=c("V","N"), #指定潛變量名字
# 剩余方差
mxPath(
from=c("W", "S", "B", "P"), #設置路徑起點
arrows=2, #箭頭是雙向的
free=TRUE, #設置所有的值是不固定的,需要估計
values=1, #初始值為1
labels=c("e1","e2","e3","e4") #設定路徑名
),
# latent variances ; NB no covariance between factors in this model
mxPath(
from=c("V","N"), #設置路徑
arrows=2, # 箭頭是雙向的
free=FALSE, # 設置所有的值是固定的,不需要估計
values=1, #初始值為 1
labels=c("varV","varN") #設定路徑名
),
# factor loadings for verbal variables
mxPath(
from="V",#設置路徑從V出發
to=c("W","S"),#設置路徑指向W和S
arrows=1, # 箭頭是單向的
free=TRUE, #設置所有的值是不固定的,需要估計
values=c(1,1), # 兩個變量的初始值都為1
labels=c("a","b")
),
# 非語言變量的因素載荷 (這里已經忘記了的就去看part1吧)
mxPath(
from="N",
to=c("B", "P"),
arrows=1,
free=c(TRUE,TRUE), # 只是為了展示另外一種賦值的方法
values=c(1,1),
labels=c("c","d")
)
)
#模型設置完畢
db_twoFactorFit <- mxRun(db_twoFactorModel) #運行模型
summary(db_twoFactorFit)
db_twoFactorFit@output$estimate
omxGraphviz(db_twoFactorModel, dotFilename = "twofact.dot")
# 生成一個可以通過Graphviz讀入的腳本
#----------------------------------------------------------------
如上的一個RAM模型中
- 必須設定那些是顯變量,用
manifestVars=
指定,并且指定那些是潛變量,用latentVars=
指定。在本例中,顯變量是W,S,B,P,潛變量是V和N。 - 使用
mxPath
函數串講路徑,一次調用可以同時創建多條路徑。參數里的'from'
指的是路徑的起點,'to'
則指明路徑的終點(也被稱作水池‘sink’)。如果終點沒有給定,則認為終點和起點是同一個。默認的,起點中第i個元素和終點中第i個元素相匹配,按順序創建路徑。 -
arrows
參數用來指定路徑的方向(單方向箭頭、雙方向箭頭) -
free
變量是一個布爾向量,其值只能為True或是False,用來指定路徑的值是固定的還是非固定的 -
values
是一個數值變量,指定每條路徑的初始值 -
labels
是一個字符變量,對每個參數給予一個字符標記 - 通過
summary
函數,可以很方便的顯示模型擬合之后的結果。一般需要檢查這個表來保證結果是合適的。就我們的模型來說,summary的結果首先給我數據集的分布統計信息。(我們通過observed
選項給予模型的數據集)。如果我們使用raw
來給定輸入數據集,這輸出的統計信息將包含所有變量的均值等。這里,我們給定的是觀測變量之間的協方差矩陣,summary的信息會給我們協方差的均值,這個其實沒啥用。不過,我們后面有可以看到原始變量均值的方法。
- 后面的表格里,輸出的是矩陣A(單向箭頭矩陣)、S(雙向箭頭矩陣)以及M(均值矩陣)中的參數。標準誤可以通過
db_twoFactorFit@output$standardErrors
給出,傳統方法中可以用均值和標準誤來給出置信區間。即95% CI 為估計均值-1.96標準誤~估計均值+1.96標準誤。若得到的區間不包括0,則路徑是顯著的,因而不能從模型中去掉。現在有了不是這么估計CI的方法,但是類似軟件,如Mplus中還是這么估計,so,筆者認為都可以吧。畢竟還是那句話,不顯著的,不管你怎么折騰,也不是那么顯著的。ps. 結果差別還挺大的。 - 自由度也在summary中給出了,與我們預期的一樣是2。
- 負2倍的log似然度(-2LL),其代表了在擬合模型的優化過程中,最小的似然度。然而,這個模型的-2LL本身并沒有什么意義,需要與其他模型的似然度作比較,才能給出模型擬合數據的好壞,并用差值去評價模型之間的差異(去掉的變量)。
一下介紹兩種很有用的比較。
1. OpenMx中自動提供的,與“飽和模型”的比較。
雖然“飽和模型”被稱為模型,但是其與結構方程模型通常意義下的模型并不一樣。區別在于:飽和模型中不包含潛變量。這個模型僅僅代表了對觀測變量最優的擬合過程。和我們之前估計似然度的腳本類似,一開始對一對變量進行估計,然后對三個變量進行估計。(這個部分還沒翻譯)飽和模型的-2LL的值總是要小于包含潛變量的模型,這是因為包含潛變量的模型在尋優過程中是不受到限制的。飽和模型并不是我們感興趣的,它具有與觀測變量數量相同的待估計變量,因此沒有自由度(df=0)。
那我們引入飽和模型的目的是什么呢?飽和模型可以為我們設置的上文中提到的過度識別模型提供-2LL值的基線標準。我們設置的模型的-2LL減去飽和模型的-2LL后得到的值是一個服從卡方分布的統計量,該統計量對應的自由度就是這個模型的自由度減去飽和模型的自由度(0)。
因此,上述例子中,chi2 = 204.3614 – 201.7717 = 2.5897; df = 2 – 0 = 2
如腳本的輸出所示,相對應的 p=0.273,通常意義上的不顯著。這里不要擔心或者是灰心喪氣,當擬合結構模型的時候,不顯著的卡方值意味著模型擬合的很好,這是因為這個結果表示:雖然我們設置的模型的擬合程度更差一些,但是它的的-2LL值與飽和模型的-2LL值是沒有顯著差異的。
2. AIC、BIC以及RMSEA也是可以衡量擬合程度好壞的指標。
就我們的模型來說,Akaike's Information Criterion (AIC) 赤池信息量準則的值小于0,這也說明模型擬合的很好。Bayesian information criterion (BIC) 貝葉斯信息量準則的值也是越小越好。對于RMSEA來說,一般的,其值小于0.05時說明擬合質量非常好,介于0.05和0.10之間時,說明擬合的不錯。
代碼的最后一行可以輸出路徑圖。可以使用免費軟件打開保存的結果。Graphviz from:
http://www.graphviz.org/
現在我們嘗試把模型中某一條路徑去掉。一般的,我們會將設置的模型和飽和模型相比較得到擬合優度牡丹石不同的設置模型之間也可以直接進行比較(如果他們是嵌套模型nested)。
嵌套模型
嵌套模型指的是一系列模型的集合,這些模型具有相同路徑結構,只是不同的模型中某些特定的路徑的值是固定的。如果我們將路徑的值設為:0,然后將其類型設置為:free=FALSE,這就意味著OpenMx將不會對該路徑進行估計,而是會保證其值固定位初始值不變。這樣做的結果就是,這條路徑被拿掉了。
下面我們將演示把模型中的路徑d去掉。
mxPath(
from="N",
to=c("B", "P"),
arrows=1,
free=c(TRUE,FALSE),#固定值
values=c(1,0), #d的初始值為0
labels=c("c","d")
現在,模型的自由度增加了1(待估計變量少了一個),去掉路徑d之后的效果如下:
chi2為22.28,自由度為1,相對應的p<0.001。結果告訴我們去掉路徑d之后會顯著的降低模型擬合的質量。
note: 使用R計算chi2的p值的腳本
1-pchisq(chi,df)
從以上結果不難發現,由于不顯著對應于模型擬合更好,因此,-2LL越大則意味著擬合的更差。這與傳統的統計完全相反,即顯著的chi2表示擬合效果很差。傳統統計中,常常將chi2與隨機情況相對比,從而得到結果中有多少不能被隨機因素解釋。而在SEM中,擬合程度是與飽和模型的擬合程度相比較,或是將過度識別模型與自由度較少的模型相互比較(如上所述)。對比模型之間相差的需要估計的參數越多,則能得到的chi2就越大,因此也就越“顯著”。這里,顯著性指的是估計值遠離觀測值的程度。讀者可以嘗試改變樣本數量,會發現樣本數量越大則模型需要擬合的點也就越多,因此統計值也就越準確(標準誤更小),但是相應的模型也會相對于飽和模型有更顯著的差異。