options nocenter nodate; /* SIMPLE-3way: For 3-way interactions between two continuous moderators and a continuous or dichotomous independent variable */ /***************** START OF TRIAL-RUN DATA****************** The following commands generate artificial data that can be used for a trial-run of the program. Just run this whole file. */ data testdata; do cases = 1 to 100; idv = 10 * normal (1) ; mod1 = 10 * normal (1) ; mod2 = 10 * normal (1) ; e = normal (1) * 140; mod1=idv * .40 + mod1 * sqrt(1 - .40**2); mod2=idv * -.40 + mod2 * sqrt(1 - -.40**2); dv = idv + mod1 + mod2 + idv*mod1 + idv*mod2 + idv*mod1*mod2 + e; x1 = idv * mod1; x2 = idv * mod2; x3 = mod1 * mod2; x4 = idv * mod1 * mod2; output; end; /* moderated regression on the artificial data*/ proc reg data=testdata; eq1: model dv = idv mod1 mod2 ; eq2: model dv = idv mod1 mod2 x1 x2 x3 x4;run; /***************** END OF TRIAL-RUN DATA ********************/ /* This version of the program reads and processes a raw data file containing the variable scores. (In contrast, the previous version of the program read and processed saved matrix data). For analyses of your own data, it may be easiest to create the following four variables in your data file: idv, mod1, mod2, & dv. e.g., simply set "idv" = name of the independent variable; e.g., simply set "mod1" = name of the first moderator variable; e.g., simply set "mod2" = name of the second moderator variable; e.g., simply set "dv" = name of the dependent variable. There must be no missing values. The program automatically computes dummy codes & product terms. The syntax lines below must be run as a group, i.e., run all of the commands between the PROC IML and QUIT commands at once, including the PROC IML and QUIT commands. Simple slopes and plot data will be provided in two sets, one for when Moderator 1 is high, and one for when Moderator 1 is low; Therefore choose the "mod1" and "mod2" variables in a manner that will make the most sense to you; running the analyses a second time, switching the way you define mod1 and mod2, will provide an alternative perspective on the interactions. */ proc iml; /* The next command line reads the data file & creates a data matrix ("datamat"). Enter the name of the dataset on the USE command (e.g., testdata). Then enter the names of the variables on the READ command inside the curly brackets, after "var". Only four variables names are permitted, and they must appear in the following order: idv, mod1, mod2, dv. If the name of the idv in your dataset is something other than "idv", then enter the correct name in the place of "idv" inside the curly brackets. If the name of the first moderator variable in your dataset is something other than "mod1", then enter the correct name in the place of "mod1" inside the curly brackets. If the name of the second moderator variable in your dataset is something other than "mod2", then enter the correct name in the place of "mod2" inside the curly brackets. If the name of the dv in your dataset is something other than "dv", then enter the correct name in the place of "dv" inside the curly brackets. */ use testdata; read all var{idv mod1 mod2 dv} into datamat; /* The program automatically computes dummy codes and product terms. */ /* The program was set to compute simple slope stats for two levels of the moderators: one SD below the mean, & one SD above the mean; Alternative low and high levels may be specified now -- simply change the value for multMOD from "1.0" to whatever multiple of the SD you prefer: e.g., enter "2" for 2 SDs below & above the Mean, e.g., enter "0.5" for .5 of a SD below & above the Mean; enter multiplication factors for both moderator variables */ multMOD1 = 1.0 ; multMOD2 = 1.0 ; /* Similarly, the program generates data for plots of simple regression lines; The IDV range was set at 2 SDs below and 2 SDs above the IDV mean; Alternative low and high levels may be specified now -- simply change the value for multiIDV from "2.0" to whatever multiple of the SD you prefer */ multiIDV = 2.0 ; /* If the IDV is dichotomous, insert "1" in the place of "0" for "dichotom," and insert the coded values (numbers) you used immediately afterwards for "dichotLo" and "dichotHi"; The values do not affect the computations, but the data plot could make more sense to you if you insert the values you used, although the set values of "1" & "2" will do fine */ dichotom = 0 ; dichotLo = 1 ; dichotHi = 2 ; /****************** End of User Specifications ***********************/ show datasets; show contents; /* creating the data matrix with product terms */ datam = ( datamat[,1:3] || (datamat[,1]#datamat[,2]) || (datamat[,1]#datamat[,3]) || (datamat[,2]#datamat[,3]) || (datamat[,1]#datamat[,2]#datamat[,3])|| datamat[,4] ); /* n, mean, sd, & correlation matrix (Bernstein, p. 77-79) */ n = nrow(datam); rawsp = t(datam) * datam; rsums = t(datam[+,]); mn = t(rsums) / n; corsp = rawsp - (1/n) * (rsums) * t(rsums); vcv = corsp * (1/(n-1)); sd = t(sqrt(vecdiag(vcv))); d = inv(diag(sd)); r = d * vcv * d; /* Overall regression coeffs */ beta = inv(r[1:7,1:7]) * r[1:7,8] ; b = (sd[1,8] / sd[1,1:7]) # t(beta) ; a = mn[1,8] - ( sum ( mn[1,1:7] # b ) ) ; r2all = t(beta) * r[1:7,8] ; /* for the X1 interaction, with main effects, X2, & X3 in the equation */ r2 = t( (inv(r[1:6,1:6])*r[1:6,8])) * r[1:6,8] ; r1 = ( ( r[1:3,1:3] || r[1:3,5:6] ) // ( r[5:6,1:3] || r[5:6,5:6] ) ); r21=t(inv(r1)*(r[1:3,8]//r[5:6,8]))*(r[1:3,8]//r[5:6,8]); r2chX1 = r2 - r21; fsquare1 = (r2 - r21) / (1 - r2) ; F1 = ((r2-r21)/1) / ((1-r2)/(n-6-1)); df6 = n - 6 - 1; pF1 = 1 - probf(F1, 1, df6); /* for the X2 interaction, with main effects, X1 & X3 in the equation */ cr2 = ( ( r[1:4,1:4] || r[1:4,6] ) // ( r[6,1:4] || r[6,6] ) ); r22 = t(inv(cr2)*(r[1:4,8]//r[6,8]))*(r[1:4,8]//r[6,8]); r2chX2 = r2 - r22; fsquare2 = (r2 - r22) / (1 - r2) ; F2 = ((r2-r22)/1) / ((1-r2)/(n-6-1)); pF2 = 1 - probf(F2, 1, df6); /* for the X3 interaction, with main effects, X1 & X2 in the equation */ r23 = t(inv(r[1:5,1:5])*r[1:5,8])*r[1:5,8]; r2chX3 = r2 - r23; fsquare3 = (r2 - r23) / (1 - r2) ; F3 = ((r2-r23)/1) / ((1-r2)/(n-6-1)); pF3 = 1 - probf(F3, 1, df6); /* for X4, the 3-way interaction */ r2main = t(inv(r[1:6,1:6])*r[1:6,8])*r[1:6,8]; r2chXn = r2all - r2main; fsquared = (r2all - r2main) / (1 - r2all) ; F = ((r2all-r2main)/1) / ((1-r2all)/(n-7-1)); dferror = n - 7 - 1; pF = 1 - probf(F, 1, dferror); twoways = ((r2chX1//r2chx2//r2chx3) || (F1//F2//F3) || {1,1,1} || (df6//df6//df6) || (fsquare1//fsquare2//fsquare3)) || (pF1//pF2//pF3) ; rr = { "idv-md1" "idv-md2" "md2-md3"} ; c = { "Rsq. ch." "F" "df num." "df denom." "fsquared" "Sig. F" } ; print "Coefficients for the 2-way Interactions:" , twoways[colname=c rowname=rr format=12.3]; threeway = (r2chXn || F || {1} || dferror || fsquared || pF); c = { "Rsq. ch." "F" "df num." "df denom." "fsquared" "Sig. F"}; print , "Coefficients for the 3-way Interaction:" , threeway[colname=c format=12.3]; betas = (t(b) || beta); rr = { "idv" "md1" "md2" "idv-md1" "idv-md2" "md2-md3" "3-way"}; c = { "raw b" "std.beta" } ; print , "Beta weights for the full equation:" , betas[colname=c rowname=rr format=12.3]; print "The intercept is:" a[format=12.3]; /* simple slope info */ mod1lo = mn[1,2] - (sd[1,2] * multMOD1) ; mod1hi = mn[1,2] + (sd[1,2] * multMOD1) ; mod2lo = mn[1,3] - (sd[1,3] * multMOD2) ; mod2hi = mn[1,3] + (sd[1,3] * multMOD2) ; slopes=( (b[1,1]+(b[1,4]*mod1lo)+(b[1,5]*mod2lo)+(b[1,7]*mod1lo*mod2lo)) // (b[1,1]+(b[1,4]*mod1lo)+(b[1,5]*mod2hi)+(b[1,7]*mod1lo*mod2hi)) // (b[1,1]+(b[1,4]*mod1hi)+(b[1,5]*mod2lo)+(b[1,7]*mod1hi*mod2lo)) // (b[1,1]+(b[1,4]*mod1hi)+(b[1,5]*mod2hi)+(b[1,7]*mod1hi*mod2hi)) ); ints = ( (b[1,2]*mod1lo + b[1,3]*mod2lo + b[1,6]*mod1lo*mod2lo + a ) // (b[1,2]*mod1lo + b[1,3]*mod2hi + b[1,6]*mod1lo*mod2hi + a ) // (b[1,2]*mod1hi + b[1,3]*mod2lo + b[1,6]*mod1hi*mod2lo + a ) // (b[1,2]*mod1hi + b[1,3]*mod2hi + b[1,6]*mod1hi*mod2hi + a ) ); mse = (n/(n-7))*(sd[1,8]**2)*(1-r2all); d=diag( sd[1,1:7] ); Sb = mse * inv((d*r[1:7,1:7]*d)*(n-1)) ; SEslopes=( (sqrt ((1||0||0||mod1lo||mod2lo||0||(mod1lo*mod2lo)) * Sb * t((1||0||0||mod1lo||mod2lo||0||(mod1lo*mod2lo))) )) // (sqrt ((1||0||0||mod1lo||mod2hi||0||(mod1lo*mod2hi)) * Sb * t((1||0||0||mod1lo||mod2hi||0||(mod1lo*mod2hi))) )) // (sqrt ((1||0||0||mod1hi||mod2lo||0||(mod1hi*mod2lo)) * Sb * t((1||0||0||mod1hi||mod2lo||0||(mod1hi*mod2lo))) )) // (sqrt ((1||0||0||mod1hi||mod2hi||0||(mod1hi*mod2hi)) * Sb * t((1||0||0||mod1hi||mod2hi||0||(mod1hi*mod2hi))) )) ); tslopes = slopes / SEslopes ; df = ( (n-7-1) // (n-7-1) // (n-7-1) // (n-7-1) ); zslopes = slopes # (sd[1,1]/sd[1,8]); zSE = SEslopes # (sd[1,1]/sd[1,8]) ; dfs = n-7-1 ; pslopes = (1 - probt(abs(tslopes),dfs)) * 2; /* df & t values--from Darlington p 516 & Howell 87 p 586--p=05 two-tailed */ dft={1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 22 24 26 28 30 32 34 36 38 40 43 46 49 52 56 60 65 70 75 80 85 90 95 100 110 120 130 150 175 200 250 300 400 500 600 700 800 900 1000 1000000000, 12.706 4.303 3.182 2.776 2.571 2.447 2.365 2.306 2.262 2.228 2.201 2.179 2.160 2.145 2.131 2.120 2.110 2.101 2.093 2.086 2.074 2.064 2.056 2.048 2.042 2.037 2.032 2.028 2.024 2.021 2.017 2.013 2.010 2.007 2.003 2.000 1.997 1.994 1.992 1.990 1.988 1.987 1.985 1.984 1.982 1.980 1.978 1.976 1.974 1.972 1.969 1.968 1.966 1.965 1.964 1.963 1.963 1.963 1.962 1.962 }; tabledT = 0; do q = 1 to 59 ; if dfs >= dft[1,q] & dfs < dft[1,q+1] then tabledT = dft[2,q] ; end; confidLo = (zslopes - (tabledT # zSE)) ; confidHi = (zslopes + (tabledT # zSE)) ; /* simple slopes for when Moderator 1 is low */ simple = (ints[1:2,]||slopes[1:2,]|| tslopes[1:2,]||df[1:2,] )||pslopes[1:2,]; rr = { "md2=low" "md2=high" }; c = { "a" "raw b" "t-test" "df" "Sig. T" }; print ,, "Simple Slope Coefficients at 2 levels of Moderator 2 when Moderator 1 is low:" , simple[colname=c rowname=rr format=12.3]; stand = ( zslopes[1:2,] || zSE[1:2,] || confidLO[1:2,] || confidHI[1:2,] ); rr = { "md2=low" "md2=high" }; c = { "std. beta" "SE" "95% Low" "95% Hi" }; print , "Standardized Simple Slopes & 95% Confidence Intervals for the above: " , stand[colname=c rowname=rr format=12.3]; intersct = ( (b[1,3]+(b[1,6]*mod1lo)) / (b[1,5]+(b[1,7]*mod1lo)) *-1); print "When Mod1=low, the simple slopes at Mod2=high and Mod2=low intersect at IDV =" , intersct; flat = ( (b[1,1]+(b[1,4]*mod1lo)) / (b[1,5]+(b[1,7]*mod1lo)) *-1); print "When Mod1=low, the simple slope for the DV on the IDV is zero (flat) at Mod 2 =" , flat; /* simple slopes for when Moderator 1 is high */ simple = (ints[3:4,] || slopes[3:4,] || tslopes[3:4,] || df[3:4,] || pslopes[3:4,]); rr = { "md2=low" "md2=high" }; c = { "a" "raw b" "t-test" "df" "Sig. T"}; print ,, "Simple Slope Coefficients at 2 levels of Moderator 2 when Moderator 1 is high:" , simple[colname=c rowname=rr format=12.3]; stand = ( zslopes[3:4,] || zSE[3:4,] || confidLO[3:4,] || confidHI[3:4,] ) ; rr = { "md2=low" "md2=high" }; c = { "std. beta" "SE" "95% Low" "95% Hi" }; print , "Standardized Simple Slopes & 95% Confidence Intervals for the above: " , stand[colname=c rowname=rr format=12.3]; intersct = ( (b[1,3]+(b[1,6]*mod1hi)) / (b[1,5]+(b[1,7]*mod1hi)) *-1); print "When Mod1=high, the simple slopes at Mod2=high and Mod2=low intersect at IDV =" , intersct; flat = ( (b[1,1]+(b[1,4]*mod1hi)) / (b[1,5]+(b[1,7]*mod1hi)) *-1); print "When Mod1=high, the simple slope for the DV on the IDV is zero (flat) at Mod 2 =" , flat; /* data for plots */ idvlo = mn[1,1] - (sd[1,1] * multiIDV); idvhi = mn[1,1] + (sd[1,1] * multiIDV) ; idv = ( idvlo// idvhi// idvlo// idvhi// idvlo// idvhi// idvlo// idvhi ) ; if dichotom = 1 then idv=( dichotLO//dichotHi// dichotLO//dichotHi// dichotLO//dichotHi// dichotLO//dichotHi ); mod1 = ( mod1lo//mod1lo//mod1lo//mod1lo//mod1hi//mod1hi//mod1hi//mod1hi ); mod2 = ( mod2lo//mod2lo//mod2hi//mod2hi//mod2lo//mod2lo//mod2hi//mod2hi ); dv=(b[1,1]#idv)+(b[1,2]#mod1)+(b[1,3]#mod2)+(b[1,4]#idv#mod1) +(b[1,5]#idv#mod2)+(b[1,6]#mod1#mod2)+(b[1,7]#idv#mod1#mod2)+a; data = ( idv || mod1 || mod2 || dv ); dataL = (data[1:4,1] || data[1:4,3:4]); c = { "IDV" "Mod2" "DV" }; print , "Data for simple slope plots for when Moderator 1 is Low:" , dataL[colname=c format=12.3]; dataH = (data[5:8,1] || data[5:8,3:4]); c = { "IDV" "Mod2" "DV" }; print , "Data for simple slope plots for when Moderator 1 is High:" , dataH[colname=c format=12.3]; create plotdatL from dataL[colname={"idv" "mod" "dv"}] ; append from dataL; create plotdatH from dataH[colname={"idv" "mod" "dv"}] ; append from dataH; quit; proc plot data=plotdatL vpct=100 hpct=100 nolegend; title 'Plots for When Mod1 is Low: H=Mod2 is high L=Mod2 is low'; plot dv*idv=mod / contour=3 s1='L' s3='H';run; proc plot data=plotdatH vpct=100 hpct=100 nolegend; title 'Plots for When Mod1 is High: H=high L=low'; plot dv*idv=mod / contour=3 s1='L' s3='H';run;