问题
I came across a puzzling difference in standardized (beta) coefficients with linear regression model computed with R and SPSS using dummy coded variables. I have used the hsb2
data set and created a contrast (dummy coding), so that the third category is the reference. Here is the R code:
# Read the data
hsb2 <- read.table('https://stats.idre.ucla.edu/stat/data/hsb2.csv', header = TRUE, sep = ",")
# Create a factor variable with respondents' race
hsb2$race.f <- factor(hsb2$race, labels = c("Hispanic", "Asian", "African-Am", "Caucasian"))
# Add a contrast (dummy coding) to the new race variable, so that the third category is the reference.
contrasts(hsb2$race.f) <- contr.treatment(n = 4, base = 3)
# Scale the writing achievement score (mean of 0 and SD of 1), it will be the dependent variable
hsb2$write <- scale(hsb2$write)
# Fit the model and print the summary
summary(lm(write ~ race.f, hsb2))
The output I get:
Call:
lm(formula = write ~ race.f, data = hsb2)
Residuals:
Min 1Q Median 3Q Max
-2.43234300577889240 -0.57585945002954031 0.10259059641484436 0.73850677561040290 1.98341819735365221
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -0.48266692834536767 0.21290900103341129 -2.26700999999999997 0.0244812 *
race.f1 -0.18374751916973245 0.28828015018135283 -0.63739000000000001 0.5246133
race.f2 1.03390948585456388 0.35741973343705952 2.89270000000000005 0.0042513 **
race.f4 0.61772635713618673 0.22711822910747051 2.71984000000000004 0.0071181 **
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.050000000000000003 ‘.’ 0.10000000000000001 ‘ ’ 1
Residual standard error: 0.95215799866456285 on 196 degrees of freedom
Multiple R-squared: 0.1070625554447362515, Adjusted R-squared: 0.09339514557909434078
F-statistic: 7.833419535758452845 on 3 and 196 DF, p-value: 0.000057845156841983661
However, when I run the same analysis with SPSS I get quite different beta regression coefficients, here is the code:
* Create the dummy variables.
RECODE race (1 = 1) (ELSE = 0) INTO race.f1.
RECODE race (2 = 1) (ELSE = 0) INTO race.f2.
RECODE race (3 = 1) (ELSE = 0) INTO race.f3.
RECODE race (4 = 1) (ELSE = 0) INTO race.f4.
EXECUTE.
* Execute the analysis, so that the third category is the reference.
REGRESSION
/MISSING LISTWISE
/STATISTICS COEFF OUTS R ANOVA
/CRITERIA=PIN(.05) POUT(.10)
/NOORIGIN
/DEPENDENT write
/METHOD=ENTER race.f1 race.f2 race.f4.
Here is the SPSS output I get:
What really baffles me is that the everything else is the same (model statistics - R2, adjusted R2, degrees of freedom, F-statistic; and the t-values and p-values of the beta regression coefficients), but the standardized beta regression coefficients are not even close. If I run without standardization, the unstandardized regression coefficients and all other statistics match between R and SPSS.
Can anyone help with this? Am I missing something?
EDIT Following the source provided by aosmith (thanks once again), I did the dummy coding by hand, scaling the separate dummies:
hsb2 <- read.table('https://stats.idre.ucla.edu/stat/data/hsb2.csv', header = TRUE, sep = ",")
hsb2$write <- scale(hsb2$write)
hsb2$race.f1 <- scale(hsb2$race == 1)
hsb2$race.f2 <- scale(hsb2$race == 2)
hsb2$race.f3 <- scale(hsb2$race == 3)
hsb2$race.f4 <- scale(hsb2$race == 4)
summary(lm(write ~ race.f1 + race.f2 + race.f4, hsb2))
I got exactly the same results as in SPSS:
Call:
lm(formula = write ~ race.f1 + race.f2 + race.f4, data = hsb2)
Residuals:
Min 1Q Median 3Q Max
-2.4323430057788924 -0.5758594500295402 0.1025905964148444 0.7385067756104029 1.9834181973536520
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.000000000000000030665367318040625 0.067327737761672404315227424831392 0.00000000000000000 1.0000000
race.f1 -0.059860715422078700220787084163021 0.093915042280922900186368451613816 -0.63739000000000001 0.5246133
race.f2 0.236302452210854940783946176452446 0.081689123308428354675037041943142 2.89270000000000005 0.0042513 **
race.f4 0.276515793804944842726456499804044 0.101666015515960786452787090183847 2.71984000000000004 0.0071181 **
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.050000000000000003 ‘.’ 0.10000000000000001 ‘ ’ 1
Residual standard error: 0.95215799866456285 on 196 degrees of freedom
Multiple R-squared: 0.1070625554447362238, Adjusted R-squared: 0.09339514557909434078
F-statistic: 7.833419535758451957 on 3 and 196 DF, p-value: 0.000057845156841983668
However, using this approach in a custom function would not be quite handy. I wonder if there is a way to do it still using the contrasts
function to assign the dummies.
回答1:
As @aosmith pointed out the SPSS default is "interesting". But it seems fair that if we have a contr.SAS
we could have a contr.spss
. So with some timely help from others here is an option for you.
I put a reproducible slice of hsb2
below. Your original setup and @aosmith's insight.
# hsb2 <- read.table("hsb2.csv", header = TRUE, sep = ",")
hsb2$write <- scale(hsb2$write)
hsb2$race.f <- factor(hsb2$race, labels = c("Hispanic",
"Asian",
"African-Am",
"Caucasian"))
# Courtesy @aosmith
hsb2$race.f1 <- scale(hsb2$race == 1)
hsb2$race.f2 <- scale(hsb2$race == 2)
hsb2$race.f3 <- scale(hsb2$race == 3)
hsb2$race.f4 <- scale(hsb2$race == 4)
The function is longer than strictly necessary since I added some error checking. It only accepts factors
and you give it the factor name and what the base
is.
# Many thanks to @akrun
contr.spss <- function (variable, base = 1)
{
if (is.factor(variable)) {
column_names <- as.character(sort(unique(as.integer(variable))))
} else {
stop("the variable must be a factor to define contrasts")
}
if (nlevels(variable) > 2L) {
n <- nlevels(variable)
lvls <- levels(variable)
} else {
stop("not enough factor levels to define contrasts")
}
if (base < 1L | base > n) {
stop("baseline group number out of range")
}
m1 <- matrix(ncol = n, nrow = n, dimnames = list(lvls, column_names))
for(i in seq_along(lvls)) {
which_lvl <- unique(variable == lvls[i])
tmp <- unique(scale(variable == lvls[i]))[,1]
m1[i,i] <- ifelse(isTRUE(which_lvl[[1]]), tmp[1], tmp[2])
m1[-i,i] <- ifelse(isFALSE(which_lvl[[1]]), tmp[1], tmp[2])
}
m1 <-m1[, -base]
return(m1)
}
Default r
contrasts
contrasts(hsb2$race.f) # default
#> Asian African-Am Caucasian
#> Hispanic 0 0 0
#> Asian 1 0 0
#> African-Am 0 1 0
#> Caucasian 0 0 1
Use the function and apply new contrasts.
spss.contrasts <- contr.spss(hsb2$race.f, base = 3)
spss.contrasts
# Next two are equivalent
contrasts(hsb2$race.f) <- spss.contrasts
contrasts(hsb2$race.f) <- contr.spss(hsb2$race.f, base = 3)
# All set
contrasts(hsb2$race.f)
#> 1 2 4
#> Hispanic 2.7012343 -0.2406451 -1.6196240
#> Asian -0.3683501 4.1347200 -1.6196240
#> African-Am -0.3683501 -0.2406451 -1.6196240
#> Caucasian -0.3683501 -0.2406451 0.6143401
Voila same results
summary(lm(write ~ race.f1 + race.f2 + race.f4, hsb2))
#>
#> Call:
#> lm(formula = write ~ race.f1 + race.f2 + race.f4, data = hsb2)
#>
#> Residuals:
#> Min 1Q Median 3Q Max
#> -2.4323 -0.5759 0.1026 0.7385 1.9834
#>
#> Coefficients:
#> Estimate Std. Error t value Pr(>|t|)
#> (Intercept) 3.067e-17 6.733e-02 0.000 1.00000
#> race.f1 -5.986e-02 9.392e-02 -0.637 0.52461
#> race.f2 2.363e-01 8.169e-02 2.893 0.00425 **
#> race.f4 2.765e-01 1.017e-01 2.720 0.00712 **
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> Residual standard error: 0.9522 on 196 degrees of freedom
#> Multiple R-squared: 0.1071, Adjusted R-squared: 0.0934
#> F-statistic: 7.833 on 3 and 196 DF, p-value: 5.785e-05
summary(lm(write ~ race.f, hsb2))
#>
#> Call:
#> lm(formula = write ~ race.f, data = hsb2)
#>
#> Residuals:
#> Min 1Q Median 3Q Max
#> -2.4323 -0.5759 0.1026 0.7385 1.9834
#>
#> Coefficients:
#> Estimate Std. Error t value Pr(>|t|)
#> (Intercept) 3.067e-17 6.733e-02 0.000 1.00000
#> race.f1 -5.986e-02 9.392e-02 -0.637 0.52461
#> race.f2 2.363e-01 8.169e-02 2.893 0.00425 **
#> race.f4 2.765e-01 1.017e-01 2.720 0.00712 **
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> Residual standard error: 0.9522 on 196 degrees of freedom
#> Multiple R-squared: 0.1071, Adjusted R-squared: 0.0934
#> F-statistic: 7.833 on 3 and 196 DF, p-value: 5.785e-05
Your data reproduced...
hsb2 <- structure(list(id = c(70L, 121L, 86L, 141L, 172L, 113L, 50L,
11L, 84L, 48L, 75L, 60L, 95L, 104L, 38L, 115L, 76L, 195L, 114L,
85L, 167L, 143L, 41L, 20L, 12L, 53L, 154L, 178L, 196L, 29L, 126L,
103L, 192L, 150L, 199L, 144L, 200L, 80L, 16L, 153L, 176L, 177L,
168L, 40L, 62L, 169L, 49L, 136L, 189L, 7L, 27L, 128L, 21L, 183L,
132L, 15L, 67L, 22L, 185L, 9L, 181L, 170L, 134L, 108L, 197L,
140L, 171L, 107L, 81L, 18L, 155L, 97L, 68L, 157L, 56L, 5L, 159L,
123L, 164L, 14L, 127L, 165L, 174L, 3L, 58L, 146L, 102L, 117L,
133L, 94L, 24L, 149L, 82L, 8L, 129L, 173L, 57L, 100L, 1L, 194L,
88L, 99L, 47L, 120L, 166L, 65L, 101L, 89L, 54L, 180L, 162L, 4L,
131L, 125L, 34L, 106L, 130L, 93L, 163L, 37L, 35L, 87L, 73L, 151L,
44L, 152L, 105L, 28L, 91L, 45L, 116L, 33L, 66L, 72L, 77L, 61L,
190L, 42L, 2L, 55L, 19L, 90L, 142L, 17L, 122L, 191L, 83L, 182L,
6L, 46L, 43L, 96L, 138L, 10L, 71L, 139L, 110L, 148L, 109L, 39L,
147L, 74L, 198L, 161L, 112L, 69L, 156L, 111L, 186L, 98L, 119L,
13L, 51L, 26L, 36L, 135L, 59L, 78L, 64L, 63L, 79L, 193L, 92L,
160L, 32L, 23L, 158L, 25L, 188L, 52L, 124L, 175L, 184L, 30L,
179L, 31L, 145L, 187L, 118L, 137L), female = c(0L, 1L, 0L, 0L,
0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L,
0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L,
0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L,
0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L,
0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L,
0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L,
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L,
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L,
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L,
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L,
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L,
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L,
1L, 1L, 1L, 1L), race = c(4L, 4L, 4L, 4L, 4L, 4L, 3L, 1L, 4L,
3L, 4L, 4L, 4L, 4L, 3L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 3L, 1L, 1L,
3L, 4L, 4L, 4L, 2L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 1L, 4L, 4L,
4L, 4L, 3L, 4L, 4L, 3L, 4L, 4L, 1L, 2L, 4L, 1L, 4L, 4L, 1L, 4L,
1L, 4L, 1L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 1L, 4L, 4L, 4L,
4L, 4L, 1L, 4L, 4L, 4L, 1L, 4L, 4L, 4L, 1L, 4L, 4L, 4L, 4L, 4L,
4L, 2L, 4L, 4L, 1L, 4L, 4L, 4L, 4L, 1L, 4L, 4L, 4L, 3L, 4L, 4L,
4L, 4L, 4L, 3L, 4L, 4L, 1L, 4L, 4L, 1L, 4L, 4L, 4L, 4L, 3L, 1L,
4L, 4L, 4L, 3L, 4L, 4L, 2L, 4L, 3L, 4L, 2L, 4L, 4L, 4L, 4L, 4L,
3L, 1L, 3L, 1L, 4L, 4L, 1L, 4L, 4L, 4L, 4L, 1L, 3L, 3L, 4L, 4L,
1L, 4L, 4L, 4L, 4L, 4L, 3L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L,
4L, 4L, 1L, 3L, 2L, 3L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 2L,
2L, 4L, 2L, 4L, 3L, 4L, 4L, 4L, 2L, 4L, 2L, 4L, 4L, 4L, 4L),
write = c(52L, 59L, 33L, 44L, 52L, 52L, 59L, 46L, 57L, 55L,
46L, 65L, 60L, 63L, 57L, 49L, 52L, 57L, 65L, 39L, 49L, 63L,
40L, 52L, 44L, 37L, 65L, 57L, 38L, 44L, 31L, 52L, 67L, 41L,
59L, 65L, 54L, 62L, 31L, 31L, 47L, 59L, 54L, 41L, 65L, 59L,
40L, 59L, 59L, 54L, 61L, 33L, 44L, 59L, 62L, 39L, 37L, 39L,
57L, 49L, 46L, 62L, 44L, 33L, 42L, 41L, 54L, 39L, 43L, 33L,
44L, 54L, 67L, 59L, 45L, 40L, 61L, 59L, 36L, 41L, 59L, 49L,
59L, 65L, 41L, 62L, 41L, 49L, 31L, 49L, 62L, 49L, 62L, 44L,
44L, 62L, 65L, 65L, 44L, 63L, 60L, 59L, 46L, 52L, 59L, 54L,
62L, 35L, 54L, 65L, 52L, 50L, 59L, 65L, 61L, 44L, 54L, 67L,
57L, 47L, 54L, 52L, 52L, 46L, 62L, 57L, 41L, 53L, 49L, 35L,
59L, 65L, 62L, 54L, 59L, 63L, 59L, 52L, 41L, 49L, 46L, 54L,
42L, 57L, 59L, 52L, 62L, 52L, 41L, 55L, 37L, 54L, 57L, 54L,
62L, 59L, 55L, 57L, 39L, 67L, 62L, 50L, 61L, 62L, 59L, 44L,
59L, 54L, 62L, 60L, 57L, 46L, 36L, 59L, 49L, 60L, 67L, 54L,
52L, 65L, 62L, 49L, 67L, 65L, 67L, 65L, 54L, 44L, 62L, 46L,
54L, 57L, 52L, 59L, 65L, 59L, 46L, 41L, 62L, 65L)), class = "data.frame", row.names = c(NA,
-200L))
来源:https://stackoverflow.com/questions/62286253/standardized-regression-coefficients-with-dummy-variables-in-r-vs-spss