## Using the margins command with different functional forms: Proportional versus natural logarithm changes

**margins** is a powerful tool to obtain predictive margins, marginal predictions, and marginal effects. It is so powerful that it can work with any functional form of our estimated parameters by using the **expression()** option. I am going to show you how to obtain proportional changes of an outcome with respect to changes in the covariates using two different approaches for linear and nonlinear models.

**Linear models with binary variables**

After fitting a linear regression,

$$E(y|x) = a + b*x$$

we can estimate the proportional change of the fitted values with respect to a change in \(x\) (also known as, semielasticity) using **margins, eydx(x)**. The formulas to estimate the semielasticity differ depending on whether \(x\) is continuous or discrete. If \(x\) is continuous,

\begin{equation}

{\bf eydx} = \frac{d({\rm ln}\widehat{y})}{dx}= \frac{dy}{dx}* \frac{1}{\widehat{y}} = \frac{\widehat{b}}{\widehat{y}}

\tag{1}

\end{equation}

When \(x\) is discrete, intuitively, we think the formula would be

\begin{equation}

{\bf eydx} = \frac{E(\widehat{y}|x = 1) – E(\widehat{y} |x = 0)}{E(\widehat{y}|x = 0)}\tag{2}

\end{equation}

However, this is not exactly what **margins, eydx(x)** calculates. Instead, **margins** obtains the difference of \(\widehat{y}\) relative to the base in the natural logarithm form,

\begin{equation}

{\bf eydx} = E\{{\rm ln}(\widehat{y})|x = 1\} – E\{{\rm ln}(\widehat{y})|x = 0\}\tag{3}

\end{equation}

To see an example, let’s fit our model with a continuous and a binary variable.

. webuse lbw, clear (Hosmer & Lemeshow data) . quietly regress bwt age i.ui . margins, eydx(age ui) Average marginal effects Number of obs = 189 Model VCE: OLS Expression: Linear prediction, predict() ey/dx wrt: age 1.ui ------------------------------------------------------------------------------ | Delta-method | ey/dx std. err. t P>|t| [95% conf. interval] -------------+---------------------------------------------------------------- age | .003225 .003309 0.97 0.331 -.003303 .0097531 1.ui | -.2082485 .0570519 -3.65 0.000 -.3208005 -.0956964 ------------------------------------------------------------------------------ Note: ey/dx for factor levels is the discrete change from the base level.

We regressed babies’ birthweights (**bwt**) against mothers’ age (**age**) and the presence of uterine irritability (**ui**), where **age** is continuous and **ui** is a binary variable; then we computed semielasticities. We can manually calculate the proportional changes. The proportional change of the fitted value of **bwt** with respect to a change in **age** should be calculated as **_b[age]/\(\widehat{{\bf bwt}}\)**, where **_b[age]** is the coefficient estimated for **age** and \(\widehat{{\bf bwt}}\) is the prediction for **bwt**.

. generate propage = _b[age]/(_b[_cons] + _b[age]*age + _b[1.ui]*ui) . summarize propage Variable | Obs Mean Std. dev. Min Max -------------+--------------------------------------------------------- propage | 189 .003225 .0002682 .0029186 .0039631

The reported mean, 0.003225, matches the semielasticity that **margins** yielded. We calculated the prediction of **bwt** using the **generate** command, but the prediction could more easily have been obtained by using the **predict** command. In the output of **margins**, we saw **Expression: Linear prediction, predict()** because in this case **margins** operates on the linear prediction. The expression can be changed and specified using the **expression()** option.

Let’s move on to **ui**, the proportional change of **bwt** with respect to a change in **ui**, based on (2),

$$\frac{\widehat{{\bf bwt}}|({\bf ui} = 1)-\widehat{{\bf bwt}}|({\bf ui} = 0)}{\widehat{{\bf bwt}}|({\bf ui} = 0)}$$

. preserve . replace ui = 0 (28 real changes made) . predict bwthat0 (option xb assumed; fitted values) . replace ui = 1 (189 real changes made) . predict bwthat1 (option xb assumed; fitted values) . generate propui = (bwthat1 - bwthat0)/bwthat0 . summarize propui Variable | Obs Mean Std. dev. Min Max -------------+--------------------------------------------------------- propui | 189 -.187989 .0030706 -.1935099 -.1760018

This time, \(-0.187989\) does not match the semielasticity reported by **margins**, \(-0.2082485\). As we already know, **margins, eydx()** does not calculate the proportional change with respect to a change in the categorical variable using (2). Instead, **margins, eydx()** replaces the derivative on the right-hand side of (1) with a change from the base level of the log of \(y\) [see (3)]. Let’s verify this in our example:

. generate lnbwt0 = ln(bwthat0) . generate lnbwt1 = ln(bwthat1) . generate propui2 = lnbwt1 - lnbwt0 . summarize propui2 Variable | Obs Mean Std. dev. Min Max -------------+--------------------------------------------------------- propui2 | 189 -.2082484 .0037771 -.2150636 -.1935868 . restore

**margins, eydx()** uses a method based on logs because this method has better numerical properties. In addition, if we have a model with the natural log of \(y\) on the left-hand side, then expressing the derivative with respect to the categorical variable as the difference from a base category is a common way of computing the proportional change. Suppose we have a model:

$$E\{{\rm ln}(y)\} = a + b*x$$

If variable x is categorical, then \(E({\rm ln}(y)|x = 1) – E({\rm ln}(y)|x = 0) = b\). There are cases when computing \(dy/dx*(1/y)\) differs from the change in the logs. This is true as we move away from models that have a logarithmic representation but even in these cases the approximation may be adequate if the proportional change in the outcome given a change in the covariate is small.

If we want to obtain the proportional change in (2) and its associated standard error, we can do this with **margins, expression()**.

. margins, expression(_b[1.ui]/(_b[_cons] + _b[age]*age)) warning: option expression() does not contain option predict() or xb(). Predictive margins Number of obs = 189 Model VCE: OLS Expression: _b[1.ui]/(_b[_cons] + _b[age]*age) ------------------------------------------------------------------------------ | Delta-method | Margin std. err. z P>|z| [95% conf. interval] -------------+---------------------------------------------------------------- _cons | -.187989 .0463242 -4.06 0.000 -.2787827 -.0971952 ------------------------------------------------------------------------------

In the **expression()** option, the denominator is the predicted **bwt** when **ui** = 0, and the numerator is the difference between the prediction of **bwt** when **ui** is 1 and when **ui** is 0. In the output of **margins**, the argument of **expression()** is specified after **Expression:**. The warning message cautions about the use of **expression()** without the **predict()** or **xb()** option. It is safe to ignore this message here. We will see examples demonstrating the use of these options in the *Shortcuts in expression() option* section.

**Linear models with categorical variables**

**ui** is a binary variable with values of 0 or 1. What if we have categorical variables with more than two groups? How can we use **margins, expression()** to compute a proportional change in this case? We would like to obtain the proportional change in the predicted outcome when the categorical variable changes from the base level to one of the other levels. Now, I am going to introduce the racial category variable (**race** =1 [White] 2 [Black] 3 [Other]) in the model, instead of uterine irritability.

. quietly regress bwt age i.race . generate double propblack = _b[2.race]/(_b[_cons] + _b[age]*age) . generate double propother = _b[3.race]/(_b[_cons] + _b[age]*age) . summarize propblack propother Variable | Obs Mean Std. dev. Min Max -------------+--------------------------------------------------------- propblack | 189 -.1183368 .0012359 -.1205344 -.1134239 propother | 189 -.0927893 .0009691 -.0945124 -.0889371 . margins, expression((_b[2.race]*2.race + _b[3.race]*3.race)/ > (_b[_cons]+_b[age]*age)) dydx(race) warning: option expression() does not contain option predict() or xb(). Average marginal effects Number of obs = 189 Model VCE: OLS Expression: (_b[2.race]*2.race + _b[3.race]*3.race)/ (_b[_cons]+_b[age]*age) dy/dx wrt: 2.race 3.race ------------------------------------------------------------------------------ | Delta-method | dy/dx std. err. z P>|z| [95% conf. interval] -------------+---------------------------------------------------------------- race | Black | -.1183368 .050575 -2.34 0.019 -.2174619 -.0192117 Other | -.0927893 .0358962 -2.58 0.010 -.1631447 -.022434 ------------------------------------------------------------------------------ Note: dy/dx for factor levels is the discrete change from the base level.

The manual calculations **propblack** and **propother** match what **margins** reported. Here I specified both the **expression()** and **dydx()** options. In the **expression()** option, the numerator includes the coefficient for each level of race (such as **_b[2.race]**) times the indicator for the observation belonging to the corresponding race (such as **2.race**). The coefficient represents the difference between this race and the base category for race. After taking the derivative using **dydx()** with respect to each level,

\[

\frac{d({\bf expression})}{d({\bf 2.race})} = \frac{d\{({\bf \_b[2.race]*2.race} + {\bf \_b[3.race]*3.race})/({\bf \_b[\_cons]} + {\bf \_b[age]*age})\}}{d({\bf 2.race})}\\

\frac{d({\bf expression})}{d({\bf 3.race})} = \frac{d\{({\bf \_b[2.race]*2.race} + {\bf \_b[3.race]*3.race})/({\bf \_b[\_cons]} + {\bf \_b[age]*age})\}}{d({\bf 3.race})}

\]

we end up with the expressions of proportional changes with respect to Black and Other mothers,

$$

{\bf \_b[2.race]}/({\bf \_b[\_cons]} + {\bf \_b[age]*age}) \\

{\bf \_b[3.race]}/({\bf \_b[\_cons]} + {\bf \_b[age]*age})

$$

We could have instead used two separate **margins, expression()** commands to compute the proportional change for each group compared with the base, similar to what we did previously for the binary variable. Putting them in one expression makes it possible to further manipulate those margins. For instance, by estimating both proportional changes with one **margins** command, we now obtain both estimates along with the covariance of the estimates, and this allows us to test for a difference in the proportional changes.

**Nonlinear models**

The computations can be generalized to nonlinear models; the major difference would be the link function to compute the predicted outcome. In the next example, we use probit and Poisson regressions.

. quietly probit low age i.race . margins, expression((normal(_b[_cons] + _b[age]*age > + _b[2.race]*2.race + _b[3.race]*3.race) > - normal(_b[_cons] + _b[age]*age))/ > normal(_b[_cons] + _b[age]*age)) dydx(race) warning: option expression() does not contain option predict() or xb(). Average marginal effects Number of obs = 189 Model VCE: OIM Expression: (normal(_b[_cons] + _b[age]*age + _b[2.race]*2.race + _b[3.race]*3.race) - normal(_b[_cons] + _b[age]*age))/ normal(_b[_cons] + _b[age]*age) dy/dx wrt: 2.race 3.race ------------------------------------------------------------------------------ | Delta-method | dy/dx std. err. z P>|z| [95% conf. interval] -------------+---------------------------------------------------------------- race | Black | .662603 .5016623 1.32 0.187 -.3206371 1.645843 Other | .4874649 .3652971 1.33 0.182 -.2285042 1.203434 ------------------------------------------------------------------------------ Note: dy/dx for factor levels is the discrete change from the base level.

In the probit regression, we modeled the probability of having a low-birthweight baby by using mothers’ age and racial categories. We obtained the proportional changes of predicted probability comparing Black with White mothers and Other with White mothers.

. webuse dollhill3, clear (Doll and Hill (1966)) . quietly poisson deaths smokes i.agecat, exposure(pyears) . margins, expression((exp(_b[smokes]*smokes + _b[2.agecat]*2.agecat > + _b[3.agecat]*3.agecat + _b[4.agecat]*4.agecat > + _b[5.agecat]*5.agecat + _ b[_cons])*pyears > - exp(_b[smokes]*smokes + _ b[_cons])*pyears)/ > (exp(_b[smokes]*smokes + _ b[_cons])*pyears)) > dydx(agecat) warning: option expression() does not contain option predict() or xb(). Average marginal effects Number of obs = 10 Model VCE: OIM Expression: (exp(_b[smokes]*smokes + _b[2.agecat]*2.agecat + _b[3.agecat]*3.agecat + _b[4.agecat]*4.agecat + _b[5.agecat]*5.agecat + _ b[_cons])*pyears - exp(_b[smokes]*smokes + _ b[_cons])*pyears)/ (exp(_b[smokes]*smokes + _ b[_cons])*pyears) dy/dx wrt: 2.agecat 3.agecat 4.agecat 5.agecat ------------------------------------------------------------------------------ | Delta-method | dy/dx std. err. z P>|z| [95% conf. interval] -------------+---------------------------------------------------------------- agecat | 45–54 | 3.410584 .8605197 3.96 0.000 1.723996 5.097171 55–64 | 12.8392 2.542638 5.05 0.000 7.85572 17.82268 65–74 | 27.51678 5.269878 5.22 0.000 17.18801 37.84555 75–84 | 39.45121 7.775511 5.07 0.000 24.21148 54.69093 ------------------------------------------------------------------------------ Note: dy/dx for factor levels is the discrete change from the base level.

In the Poisson regression, the number of deaths was explained by smoking status and age category. The proportional changes of predicted number of deaths were computed by comparing each age group with the base.

**margins** operates on marginal prediction of the outcome, where the prediction equals xb in linear regression, equals \({\rm normal}(xb)\) in probit regression, and equals \({\rm exp}(xb) \times {\rm exposure}\) in Poisson regression. Furthermore, **margins** computes the prediction for each observation and reports the mean as the predictive margins.

**Shortcuts in the expression() option**

The functional forms we feed into **margins, expression()** can be as flexible and complex as needed. There are a few shortcuts that I would like to highlight here. Let’s go back to the probit regression.

. quietly probit low c.age##i.race . margins, eydx(age) Average marginal effects Number of obs = 189 Model VCE: OIM Expression: Pr(low), predict() ey/dx wrt: age ------------------------------------------------------------------------------ | Delta-method | ey/dx std. err. z P>|z| [95% conf. interval] -------------+---------------------------------------------------------------- age | -.0318742 .0234627 -1.36 0.174 -.0778603 .0141118 ------------------------------------------------------------------------------

I introduced a two-way, full-factorial term between **age** and **race**. Here is the functional form spelled out to obtain **margins, eydx(age)**:

\[

{\bf xb} = {\bf \_b[age]*age} + {\bf \_b[2.race\#c.age]*2.race*age} +{\bf \_b[3.race\#c.age]*3.race*age}

+ {\bf \_b[2.race]*2.race} + {\bf \_b[3.race]*3.race} + {\bf \_b[\_cons]} \\[.1in]

\frac{d {\rm ln}\{{\rm normal}(xb)\}}{d {\bf age}} =\frac{{\bf \_b[age]} + {\bf \_b[2.race\#c.age]*2.race} + {\bf \_b[3.race\#c.age]*3.race}}{{\rm normal}(xb)} \times {\rm normalden}(xb)

\]

In the **expression()** option, it is not necessary to write out the expression for things like linear prediction; thus, the above proportional change can be computed by

. margins, expression((_b[age] + _b[2.race#c.age]*2.race > + _b[3.race#c.age]*3.race)*normalden(predict(xb))/normal(predict(xb) > )) Predictive margins Number of obs = 189 Model VCE: OIM Expression: (_b[age] + _b[2.race#c.age]*2.race + _b[3.race#c.age]*3.race)*normalden(predict(xb))/ > normal(predict(xb)) ------------------------------------------------------------------------------ | Delta-method | Margin std. err. z P>|z| [95% conf. interval] -------------+---------------------------------------------------------------- _cons | -.0318742 .0234627 -1.36 0.174 -.0778603 .0141118 ------------------------------------------------------------------------------

where **predict(xb)** is a shortcut to refer to the linear prediction. Further, **normal(predict(xb))** can be replaced with **predict(pr)**, where **pr** stands for probability of the positive outcome. The **expression()** option in **margins** lets us construct any functions of the estimated parameters and feed them back to **margins**. We then can use all the computations and graphical tools with **marginsplot** to visualize the result.

. quietly margins r.race, expression((_b[age] + _b[2.race#c.age]*2.race > + _b[3.race#c.age]*3.race)*normalden(predict(xb))/normal(predict(xb))) > at(age=(14(5)50)) . quietly marginsplot, noci ytitle("expression in -margins-")

I obtained the proportional changes of predicted probability of having a low-birthweight baby with respect to the changes in mothers’ ages by comparing those from Black mothers and Other mothers with those from White mothers. The contrast is also visualized across a range of mothers’ ages from 14 through 50. The slightly diverging pattern might indicate the interaction effect that racial category moderates the relationship between the probability of having a low-birthweight baby and mothers’ age.

**Conclusion**

In this post, we learned how to use the **expression()** option with the **margins** command to obtain proportional changes of the outcome with respect to the changes in categorical covariates in both linear and nonlinear models. The **expression()** option is a versatile and powerful option that allows any functional form of estimated parameters and lets **margins** operate on the prediction of the outcome.