Coefficient plots are often more useful than tables but plotting raw coefficients can be misleading when the predictors are on different scales.

The packages arm and modelsummary are used to illustrate these plots, discovering some other problems with naive use of coefficient plots. I compare plotting:

Load packages

library(arm)           # coefplot
library(modelsummary)  # model plot
library(dplyr)
library(ggplot2)

Occupational prestige data

How does occupational prestige depend on education (years), income ($), %women, …?

I use a classic example from the carData package. The observations are averages for occupational categories rather than individuals. The data come from the 1971 Canadian census.

data(Prestige, package="carData")
head(Prestige, 5)
##                     education income women prestige census type
## gov.administrators      13.11  12351 11.16     68.8   1113 prof
## general.managers        12.26  25879  4.02     69.1   1130 prof
## accountants             12.77   9271 15.70     63.4   1171 prof
## purchasing.officers     11.42   8865  9.11     56.8   1175 prof
## chemists                14.62   8403 11.68     73.5   2111 prof

Fit a simple model

mod0 <- lm(prestige ~ education + income + women,
           data=Prestige)
summary(mod0)
## 
## Call:
## lm(formula = prestige ~ education + income + women, data = Prestige)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -19.825  -5.333  -0.136   5.159  17.504 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -6.794334   3.239089   -2.10    0.039 *  
## education    4.186637   0.388701   10.77  < 2e-16 ***
## income       0.001314   0.000278    4.73  7.6e-06 ***
## women       -0.008905   0.030407   -0.29    0.770    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 7.85 on 98 degrees of freedom
## Multiple R-squared:  0.798,  Adjusted R-squared:  0.792 
## F-statistic:  129 on 3 and 98 DF,  p-value: <2e-16

Visualize coefficients

The plots below show default coefficient plots for this model.

They are disappointing and misleading because these show the raw coefficients. It looks like only education has a non-zero effect. It is misleading because the predictors are on different scales, so it makes little sense to compare the change in prestige for a 1 year increase in education with the change for a $1 increase in income.

modelplot(mod0, coef_omit="Intercept", color="blue", size=1) +
  labs(title="Raw coefficients for mod0")

arm::coefplot(mod0, col.pts="red", cex.pts=1.5)

Fit a more complex model

Model log(income) and add an interaction with type of occupation.

mod1 <- lm(prestige ~ education + women +
             log(income)*type, data=Prestige)
summary(mod1)
## 
## Call:
## lm(formula = prestige ~ education + women + log(income) * type, 
##     data = Prestige)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -13.814  -4.685   0.656   3.966  16.569 
## 
## Coefficients:
##                       Estimate Std. Error t value Pr(>|t|)    
## (Intercept)          -152.2059    23.2499   -6.55  3.5e-09 ***
## education               2.9282     0.5883    4.98  3.1e-06 ***
## women                   0.0883     0.0323    2.73   0.0076 ** 
## log(income)            18.9819     2.8285    6.71  1.7e-09 ***
## typeprof               85.2642    30.4582    2.80   0.0063 ** 
## typewc                 29.4133    36.5075    0.81   0.4226    
## log(income):typeprof   -9.0124     3.4102   -2.64   0.0097 ** 
## log(income):typewc     -3.8334     4.2603   -0.90   0.3706    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 6.27 on 90 degrees of freedom
##   (4 observations deleted due to missingness)
## Multiple R-squared:  0.875,  Adjusted R-squared:  0.865 
## F-statistic: 90.1 on 7 and 90 DF,  p-value: <2e-16

Compare models

Several packages allow to compare several models in the same plot.

arm::coefplot(mod0, col.pts="red", cex.pts=1.5)
arm::coefplot(mod1, add=TRUE, col.pts="blue", cex.pts=1.5)

But WAIT: income was entered as log(income) in mod1. It was stupid to try to compare the coefficients, but this should have raised an error.

Standardize the variables, giving \(\beta\) coefficients

An alternative is to present the standardized coefficients. These are interpreted as the standardized change in prestige for a one standard deviation change in the predictors.

One way to do this is to transform all variables to standardized (\(z\)) scores. The syntax ultimately uses scale to transform all the numeric variables.

Prestige_std <- Prestige %>%
  as_tibble() %>%
  mutate(across(where(is.numeric), scale))

Fit the same models to the standardized variables

This accords better with the fitted (standardized) coefficients. income and education are now both large and significant compared with the effect of women.

But a nagging doubt remains: How can I interpret the numerical size of coefficients?

mod0_std <- lm(prestige ~ education + income + women,
               data=Prestige_std)

mod1_std <- lm(prestige ~ education + women +
             log(income)*type, 
           data=Prestige_std)
#summary(mod1_std)

Plot the standardized coefficients

These plots look more like what I was expecting to show the relative magnitudes of the coefficients and CIs so one could see which differ from zero.

modelplot(mod0_std, coef_omit="Intercept", color="blue", size=1) +
  labs(title="Standardized coefficients for mod0")

arm::coefplot(mod0_std, col.pts="red", cex.pts=1.5)
arm::coefplot(mod1_std, add=TRUE, col.pts="blue", cex.pts=1.5)

More meaningful units

A better substantative comparison of the coefficients could use understandable scales for the predictors, e.g., months of education, $50,000 income or 10% of women’s participation.

Note that the effect of this is just to multiply the coefficients and their standard errors by a factor. The statistical conclusions of significance are unchanged.

Prestige_scaled <- Prestige %>%
  mutate(education = 12 * education,
         income = income / 50,
         women = women / 10)

mod0_scaled <- lm(prestige ~ education + income + women,
               data=Prestige_scaled)

summary(mod0_scaled)
## 
## Call:
## lm(formula = prestige ~ education + income + women, data = Prestige_scaled)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -19.825  -5.333  -0.136   5.159  17.504 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -6.7943     3.2391   -2.10    0.039 *  
## education     0.3489     0.0324   10.77  < 2e-16 ***
## income        0.0657     0.0139    4.73  7.6e-06 ***
## women        -0.0891     0.3041   -0.29    0.770    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 7.85 on 98 degrees of freedom
## Multiple R-squared:  0.798,  Adjusted R-squared:  0.792 
## F-statistic:  129 on 3 and 98 DF,  p-value: <2e-16
arm::coefplot(mod0_scaled, col.pts="red", cex.pts=1.5,
              main = "Coefficients for prestige with scaled predictors",
              varnames=c("intercept", 
                         "education\n(/month)",
                         "income\n(/$50K)",
                         "women\n(/10%)")
               )

IycgLS0tDQojJyB0aXRsZTogIkNvZWZmaWNpZW50IHBsb3RzIGZvciBsaW5lYXIgbW9kZWxzIg0KIycgYXV0aG9yOiAiTWljaGFlbCBGcmllbmRseSINCiMnIGRhdGU6ICJgciBmb3JtYXQoU3lzLkRhdGUoKSlgIg0KIycgb3V0cHV0Og0KIycgICBodG1sX2RvY3VtZW50Og0KIycgICAgIHRoZW1lOiByZWFkYWJsZQ0KIycgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiMnIC0tLQ0KDQojKyBlY2hvPUZBTFNFDQprbml0cjo6b3B0c19jaHVuayRzZXQod2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgUi5vcHRpb25zPWxpc3QoZGlnaXRzPTQpKQ0KDQojJyBDb2VmZmljaWVudCBwbG90cyBhcmUgb2Z0ZW4gbW9yZSB1c2VmdWwgdGhhbiB0YWJsZXMNCiMnIGJ1dCBwbG90dGluZyBfcmF3IGNvZWZmaWNpZW50c18gY2FuIGJlIG1pc2xlYWRpbmcgd2hlbiB0aGUgcHJlZGljdG9ycyBhcmUNCiMnIG9uIGRpZmZlcmVudCBzY2FsZXMuDQojJyANCiMnIFRoZSBwYWNrYWdlcyBgYXJtYCBhbmQgYG1vZGVsc3VtbWFyeWAgYXJlIHVzZWQgdG8gaWxsdXN0cmF0ZSB0aGVzZSBwbG90cywNCiMnIGRpc2NvdmVyaW5nIHNvbWUgb3RoZXIgcHJvYmxlbXMgd2l0aCBuYWl2ZSB1c2Ugb2YgY29lZmZpY2llbnQgcGxvdHMuDQojJyBJIGNvbXBhcmUgcGxvdHRpbmc6DQojJw0KIycgKiByYXcgY29lZmZpY2llbnRzDQojJyAqIHN0YW5kYXJkaXplZCBjb2VmZmljaWVudHMNCiMnICogbWVhbmluZ2Z1bGx5IHNjYWxlZCBjb2VmZmljaWVudHMNCiMnIA0KDQojJyAjIyMgTG9hZCBwYWNrYWdlcw0KbGlicmFyeShhcm0pICAgICAgICAgICAjIGNvZWZwbG90DQpsaWJyYXJ5KG1vZGVsc3VtbWFyeSkgICMgbW9kZWwgcGxvdA0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dwbG90MikNCg0KDQojJyAjIyBPY2N1cGF0aW9uYWwgcHJlc3RpZ2UgZGF0YQ0KIycgSG93IGRvZXMgb2NjdXBhdGlvbmFsIHByZXN0aWdlIGRlcGVuZCBvbiBlZHVjYXRpb24gKHllYXJzKSwgaW5jb21lICgkKSwgJXdvbWVuLCAuLi4/DQojJyANCiMnIEkgdXNlIGEgY2xhc3NpYyBleGFtcGxlIGZyb20gdGhlIGBjYXJEYXRhYCBwYWNrYWdlLiBUaGUgb2JzZXJ2YXRpb25zIGFyZSBhdmVyYWdlcyBmb3Igb2NjdXBhdGlvbmFsDQojJyBjYXRlZ29yaWVzIHJhdGhlciB0aGFuIGluZGl2aWR1YWxzLiBUaGUgZGF0YSBjb21lIGZyb20gdGhlIDE5NzEgQ2FuYWRpYW4gY2Vuc3VzLg0KZGF0YShQcmVzdGlnZSwgcGFja2FnZT0iY2FyRGF0YSIpDQpoZWFkKFByZXN0aWdlLCA1KQ0KDQojJyAjIyBGaXQgYSBzaW1wbGUgbW9kZWwNCm1vZDAgPC0gbG0ocHJlc3RpZ2UgfiBlZHVjYXRpb24gKyBpbmNvbWUgKyB3b21lbiwNCiAgICAgICAgICAgZGF0YT1QcmVzdGlnZSkNCnN1bW1hcnkobW9kMCkNCg0KIycgIyMgVmlzdWFsaXplIGNvZWZmaWNpZW50cw0KIycgVGhlIHBsb3RzIGJlbG93IHNob3cgZGVmYXVsdCBjb2VmZmljaWVudCBwbG90cyBmb3IgdGhpcyBtb2RlbC4gDQojJyANCiMnIFRoZXkgYXJlIGRpc2FwcG9pbnRpbmcgYW5kIG1pc2xlYWRpbmcgYmVjYXVzZSB0aGVzZSBzaG93IHRoZSByYXcgY29lZmZpY2llbnRzLiANCiMnIEl0IGxvb2tzIGxpa2Ugb25seSBgZWR1Y2F0aW9uYCBoYXMgYSBub24temVybyBlZmZlY3QuIEl0IGlzIG1pc2xlYWRpbmcgYmVjYXVzZSB0aGUgcHJlZGljdG9ycw0KIycgYXJlIG9uIGRpZmZlcmVudCBzY2FsZXMsIHNvIGl0IG1ha2VzIGxpdHRsZSBzZW5zZSB0byBjb21wYXJlIHRoZSBjaGFuZ2UgaW4gYHByZXN0aWdlYCBmb3INCiMnIGEgMSB5ZWFyIGluY3JlYXNlIGluIGBlZHVjYXRpb25gIHdpdGggdGhlIGNoYW5nZSBmb3IgYSAkMSBpbmNyZWFzZSBpbiBgaW5jb21lYC4NCm1vZGVscGxvdChtb2QwLCBjb2VmX29taXQ9IkludGVyY2VwdCIsIGNvbG9yPSJibHVlIiwgc2l6ZT0xKSArDQogIGxhYnModGl0bGU9IlJhdyBjb2VmZmljaWVudHMgZm9yIG1vZDAiKQ0KDQphcm06OmNvZWZwbG90KG1vZDAsIGNvbC5wdHM9InJlZCIsIGNleC5wdHM9MS41KQ0KDQojJyAjIyBGaXQgYSBtb3JlIGNvbXBsZXggbW9kZWwNCiMnIE1vZGVsIGBsb2coaW5jb21lKWAgYW5kIGFkZCBhbiBpbnRlcmFjdGlvbiB3aXRoIGB0eXBlYCBvZiBvY2N1cGF0aW9uLg0KbW9kMSA8LSBsbShwcmVzdGlnZSB+IGVkdWNhdGlvbiArIHdvbWVuICsNCiAgICAgICAgICAgICBsb2coaW5jb21lKSp0eXBlLCBkYXRhPVByZXN0aWdlKQ0Kc3VtbWFyeShtb2QxKQ0KDQojJyAjIyBDb21wYXJlIG1vZGVscw0KIycgU2V2ZXJhbCBwYWNrYWdlcyBhbGxvdyB0byBjb21wYXJlIHNldmVyYWwgbW9kZWxzIGluIHRoZSBzYW1lIHBsb3QuDQphcm06OmNvZWZwbG90KG1vZDAsIGNvbC5wdHM9InJlZCIsIGNleC5wdHM9MS41KQ0KYXJtOjpjb2VmcGxvdChtb2QxLCBhZGQ9VFJVRSwgY29sLnB0cz0iYmx1ZSIsIGNleC5wdHM9MS41KQ0KDQojJyBCdXQgKipXQUlUKio6IGBpbmNvbWVgIHdhcyBlbnRlcmVkIGFzIGBsb2coaW5jb21lKWAgaW4gYG1vZDFgLiBJdCB3YXMgc3R1cGlkIHRvDQojJyB0cnkgdG8gY29tcGFyZSB0aGUgY29lZmZpY2llbnRzLCBidXQgdGhpcyBzaG91bGQgaGF2ZSByYWlzZWQgYW4gZXJyb3IuDQojJyANCg0KIycgIyMgU3RhbmRhcmRpemUgdGhlIHZhcmlhYmxlcywgZ2l2aW5nICRcYmV0YSQgY29lZmZpY2llbnRzDQojJyBBbiBhbHRlcm5hdGl2ZSBpcyB0byBwcmVzZW50IHRoZSBzdGFuZGFyZGl6ZWQgY29lZmZpY2llbnRzLiBUaGVzZSBhcmUgaW50ZXJwcmV0ZWQNCiMnIGFzIHRoZSBzdGFuZGFyZGl6ZWQgY2hhbmdlIGluIHByZXN0aWdlIGZvciBhIG9uZSBzdGFuZGFyZCBkZXZpYXRpb24gY2hhbmdlIGluIHRoZQ0KIycgcHJlZGljdG9ycy4NCiMnIA0KIycgT25lIHdheSB0byBkbyB0aGlzIGlzIHRvIHRyYW5zZm9ybSBhbGwgdmFyaWFibGVzIHRvIHN0YW5kYXJkaXplZCAoJHokKSBzY29yZXMuDQojJyBUaGUgc3ludGF4IHVsdGltYXRlbHkgdXNlcyBgc2NhbGVgIHRvIHRyYW5zZm9ybSBhbGwgdGhlIG51bWVyaWMgdmFyaWFibGVzLg0KIycgDQoNClByZXN0aWdlX3N0ZCA8LSBQcmVzdGlnZSAlPiUNCiAgYXNfdGliYmxlKCkgJT4lDQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIHNjYWxlKSkNCg0KIycgIyMjIEZpdCB0aGUgc2FtZSBtb2RlbHMgdG8gdGhlIHN0YW5kYXJkaXplZCB2YXJpYWJsZXMNCiMnIFRoaXMgYWNjb3JkcyBiZXR0ZXIgd2l0aCB0aGUgZml0dGVkIChzdGFuZGFyZGl6ZWQpIGNvZWZmaWNpZW50cy4gYGluY29tZWAgYW5kIGBlZHVjYXRpb25gDQojJyBhcmUgbm93IGJvdGggbGFyZ2UgYW5kIHNpZ25pZmljYW50IGNvbXBhcmVkIHdpdGggdGhlIGVmZmVjdCBvZiBgd29tZW5gLg0KIycgDQojJyBCdXQgYSBuYWdnaW5nIGRvdWJ0IHJlbWFpbnM6ICBIb3cgY2FuIEkgaW50ZXJwcmV0IHRoZSBudW1lcmljYWwgKipzaXplKiogb2YgY29lZmZpY2llbnRzPw0KDQptb2QwX3N0ZCA8LSBsbShwcmVzdGlnZSB+IGVkdWNhdGlvbiArIGluY29tZSArIHdvbWVuLA0KICAgICAgICAgICAgICAgZGF0YT1QcmVzdGlnZV9zdGQpDQoNCm1vZDFfc3RkIDwtIGxtKHByZXN0aWdlIH4gZWR1Y2F0aW9uICsgd29tZW4gKw0KICAgICAgICAgICAgIGxvZyhpbmNvbWUpKnR5cGUsIA0KICAgICAgICAgICBkYXRhPVByZXN0aWdlX3N0ZCkNCiNzdW1tYXJ5KG1vZDFfc3RkKQ0KDQojJyAjIyBQbG90IHRoZSBzdGFuZGFyZGl6ZWQgY29lZmZpY2llbnRzDQojJyBUaGVzZSBwbG90cyBsb29rIG1vcmUgbGlrZSB3aGF0IEkgd2FzIGV4cGVjdGluZyB0byBzaG93IHRoZSByZWxhdGl2ZSBtYWduaXR1ZGVzIG9mIHRoZSBjb2VmZmljaWVudHMNCiMnIGFuZCBDSXMgc28gb25lIGNvdWxkIHNlZSB3aGljaCBkaWZmZXIgZnJvbSB6ZXJvLg0KIycgDQptb2RlbHBsb3QobW9kMF9zdGQsIGNvZWZfb21pdD0iSW50ZXJjZXB0IiwgY29sb3I9ImJsdWUiLCBzaXplPTEpICsNCiAgbGFicyh0aXRsZT0iU3RhbmRhcmRpemVkIGNvZWZmaWNpZW50cyBmb3IgbW9kMCIpDQoNCmFybTo6Y29lZnBsb3QobW9kMF9zdGQsIGNvbC5wdHM9InJlZCIsIGNleC5wdHM9MS41KQ0KYXJtOjpjb2VmcGxvdChtb2QxX3N0ZCwgYWRkPVRSVUUsIGNvbC5wdHM9ImJsdWUiLCBjZXgucHRzPTEuNSkNCg0KDQoNCg0KIycgIyMgTW9yZSBtZWFuaW5nZnVsIHVuaXRzDQojJyBBIGJldHRlciBzdWJzdGFudGF0aXZlIGNvbXBhcmlzb24gb2YgdGhlIGNvZWZmaWNpZW50cyBjb3VsZCB1c2UgdW5kZXJzdGFuZGFibGUgc2NhbGVzIGZvciB0aGUNCiMnIHByZWRpY3RvcnMsIGUuZy4sIG1vbnRocyBvZiBlZHVjYXRpb24sICQ1MCwwMDAgaW5jb21lIG9yIDEwJSBvZiB3b21lbidzIHBhcnRpY2lwYXRpb24uDQojJyANCiMnIE5vdGUgdGhhdCB0aGUgZWZmZWN0IG9mIHRoaXMgaXMganVzdCB0byBtdWx0aXBseSB0aGUgY29lZmZpY2llbnRzIGFuZCB0aGVpciBzdGFuZGFyZCBlcnJvcnMgYnkgYSBmYWN0b3IuIA0KIycgVGhlIHN0YXRpc3RpY2FsIGNvbmNsdXNpb25zIG9mIHNpZ25pZmljYW5jZSBhcmUgdW5jaGFuZ2VkLg0KDQpQcmVzdGlnZV9zY2FsZWQgPC0gUHJlc3RpZ2UgJT4lDQogIG11dGF0ZShlZHVjYXRpb24gPSAxMiAqIGVkdWNhdGlvbiwNCiAgICAgICAgIGluY29tZSA9IGluY29tZSAvIDUwLA0KICAgICAgICAgd29tZW4gPSB3b21lbiAvIDEwKQ0KDQptb2QwX3NjYWxlZCA8LSBsbShwcmVzdGlnZSB+IGVkdWNhdGlvbiArIGluY29tZSArIHdvbWVuLA0KICAgICAgICAgICAgICAgZGF0YT1QcmVzdGlnZV9zY2FsZWQpDQoNCnN1bW1hcnkobW9kMF9zY2FsZWQpDQoNCmFybTo6Y29lZnBsb3QobW9kMF9zY2FsZWQsIGNvbC5wdHM9InJlZCIsIGNleC5wdHM9MS41LA0KICAgICAgICAgICAgICBtYWluID0gIkNvZWZmaWNpZW50cyBmb3IgcHJlc3RpZ2Ugd2l0aCBzY2FsZWQgcHJlZGljdG9ycyIsDQogICAgICAgICAgICAgIHZhcm5hbWVzPWMoImludGVyY2VwdCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICJlZHVjYXRpb25cbigvbW9udGgpIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiaW5jb21lXG4oLyQ1MEspIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAid29tZW5cbigvMTAlKSIpDQogICAgICAgICAgICAgICApDQoNCg==