Session 4 — Statistical Tests and Regressions
\[ \definecolor{wongBlack}{RGB}{0,0,0} \definecolor{wongGold}{RGB}{230, 159, 0} \definecolor{wongLightBlue}{RGB}{86, 180, 233} \definecolor{wongGreen}{RGB}{0, 158, 115} \definecolor{wongYellow}{RGB}{240, 228, 66} \definecolor{wongBlue}{RGB}{0, 114, 178} \definecolor{wongOrange}{RGB}{213, 94, 0} \definecolor{wongPurple}{RGB}{204, 121, 167} \definecolor{colA}{RGB}{255, 221, 85} \definecolor{colB}{RGB}{148, 78, 223} \definecolor{colC}{RGB}{63, 179, 178} \definecolor{colGpeZero}{RGB}{127, 23, 14} \definecolor{colGpeUn}{RGB}{27, 149, 224} \]
The slides were adapted from those of Pierre Michel and Morgan Raux, both researchers at AMSE, who kindly shared their work.
This slide deck was made with Quarto and reveal.js, it is translated by Claude Sonnet 5 from a LaTex presentation previously made with beamer.
A common hypothesis test is the Student’s t-Test, used to compare the means of two independent samples.
Welch Two Sample t-test
data: happy$gdp and unhappy$gdp
t = 3.7208, df = 3.4399, p-value = 0.02669
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
3249.704 28710.630
sample estimates:
mean of x mean of y
43502.17 27522.00
ggplot(
data = tb |> filter(year == 2018),
mapping = aes(x = gdp, y = life_satisf)
) +
geom_point() +
geom_smooth(method = "lm", se = FALSE) +
labs(
x = "Real per capita GDP in 2013 (2020 Euro)",
y = "Life satisfaction"
) +
scale_x_continuous(
labels = scales::label_number(
suffix = "", scale = 1, big.mark = ","
)
)
In the previous slide, we assumed the following model: \[ {\color{blue}Y_i} = {\color{green}\beta_0} + {\color{orange}\beta_1} {\color{purple}X_i} +\varepsilon_i, \] where
Interactive Application A cool interactive online graphical application to understand the mechanisms of OLS can be accessed at the following URL: https://www.econometrics-with-r.org/SimpleRegression.html
R, the OLS estimators of a linear model can be easily obtained, using the lm() function:
lm() function with a formula of the relationship we want to model,sim1 included in the modelr package, there are two variables, y and x. If we want to explain the variations of y by the variations of x, we write:lm() in the console, we have:Hence, the estimated model writes: \[ \hat{y}_i = 4.221 + 2.05 \times x_i \]
coef() function.Instead of computing the predicted values using the extracted values of the coefficients and the matrix of explanatory variables, R offers the function predict():
1 2 3 4 5 6 7 8
6.272355 6.272355 6.272355 8.323888 8.323888 8.323888 10.375421 10.375421
9 10 11 12 13 14 15 16
10.375421 12.426954 12.426954 12.426954 14.478487 14.478487 14.478487 16.530020
17 18 19 20 21 22 23 24
16.530020 16.530020 18.581553 18.581553 18.581553 20.633087 20.633087 20.633087
25 26 27 28 29 30
22.684620 22.684620 22.684620 24.736153 24.736153 24.736153
Similarly, for the residuals, R provides the residuals() function:
1 2 3 4 5 6
-2.072442018 1.238279125 -4.146882207 0.664969362 1.919217378 2.972935148
7 8 9 10 11 12
-3.019056466 0.129928252 0.136179642 0.007634878 -0.534352991 1.831009860
13 14 15 16 17 18
4.651562487 -2.740466108 1.546366596 -3.256043368 -0.574045413 0.364775796
19 20 21 22 23 24
1.504439222 -1.409703118 1.354755377 1.092815968 -2.242173633 1.842466099
25 26 27 28 29 30
4.092390235 0.120490168 -1.556314330 0.231946675 -1.389730610 -2.760952005
The summary() function applied to a linear regression model provides a lot of useful information on model fit:
Call:
lm(formula = y ~ x, data = modelr::sim1)
Residuals:
Min 1Q Median 3Q Max
-4.1469 -1.5197 0.1331 1.4670 4.6516
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 4.2208 0.8688 4.858 4.09e-05 ***
x 2.0515 0.1400 14.651 1.17e-14 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 2.203 on 28 degrees of freedom
Multiple R-squared: 0.8846, Adjusted R-squared: 0.8805
F-statistic: 214.7 on 1 and 28 DF, p-value: 1.173e-14
R, there is a convenient package called {stargazer} which provides a function with the same name that allows you to export your summary table as a nice table in HTML or LaTeX format.
type argument).Using type = "text" (as shown on the right-hand side) is convenient for looking at your results in the console. However, in a report, prefer another format.
===============================================
Dependent variable:
---------------------------
y
-----------------------------------------------
x 2.052***
(0.140)
Constant 4.221***
(0.869)
-----------------------------------------------
Observations 30
R2 0.885
Adjusted R2 0.880
Residual Std. Error 2.203 (df = 28)
F Statistic 214.660*** (df = 1; 28)
===============================================
Note: *p<0.1; **p<0.05; ***p<0.01
factor in R.levels argument of the factor() function,fct_relevel() from the {forcats} package to directly set the reference level.x is a character variable which contains the following unique values: "a", "b", "c", "d"."a".Using the factor() function or the fct_relevel() function from {forcats}:
We may believe the effect of a variable (\(x_1\)) depends on the value of another (\(x_2\)).
We will consider two cases:
Call:
lm(formula = y ~ x1 + x2, data = sim3)
Coefficients:
(Intercept) x1 x2b x2c x2d
1.8717 -0.1967 2.8878 4.8057 2.3596
Call:
lm(formula = y ~ x1 * x2, data = sim3)
Coefficients:
(Intercept) x1 x2b x2c x2d x1:x2b
1.30124 -0.09302 7.06938 4.43090 0.83455 -0.76029
x1:x2c x1:x2d
0.06815 0.27728
ggplot(
data = sim3 |>
modelr::gather_predictions(mod_bench, mod_interact) |>
mutate(
model = factor(
model,
levels = c("mod_bench", "mod_interact"),
labels = c("Benchmark", "Interaction~between~x[1]~and~x[2]"))
),
mapping = aes(x = x1, y = y, colour = x2)
) +
geom_point() +
geom_line(aes(y = pred)) +
facet_wrap(
~ model,
labeller = labeller(
model = label_parsed
)
)
ggplot(
data = sim3 |>
modelr::gather_residuals(mod_bench, mod_interact) |>
mutate(
model = factor(
model,
levels = c("mod_bench", "mod_interact"),
labels = c("Benchmark", "Interaction~between~x[1]~and~x[2]"))
),
mapping = aes(x = x1, y = resid, colour = x2)
) +
geom_point() +
facet_grid(
model ~ x2,
labeller = labeller(
model = label_parsed
)
) +
labs(y = "residuals")
# A tibble: 600 × 6
model x1 x2 rep y pred
<chr> <dbl> <dbl> <int> <dbl> <dbl>
1 mod1 -1 -1 1 4.25 0.996
2 mod1 -1 -1 2 1.21 0.996
3 mod1 -1 -1 3 0.353 0.996
4 mod1 -1 -0.778 1 -0.0467 0.378
5 mod1 -1 -0.778 2 4.64 0.378
6 mod1 -1 -0.778 3 1.38 0.378
7 mod1 -1 -0.556 1 0.975 -0.240
8 mod1 -1 -0.556 2 2.50 -0.240
9 mod1 -1 -0.556 3 2.70 -0.240
10 mod1 -1 -0.333 1 0.558 -0.859
# ℹ 590 more rows

R formula, if the transformation involves one of the following signs, the transformation must appear inside the I() function when writing the formula:
+, *, ^, -.poly() function (polynomials):The dataset containing real per capita GDP merged with the dataset giving life satisfaction, both downloaded from Eurostat (see tutorial 1) contains annual values (from 2010 to 2023) for multiple countries (6).
# A tibble: 84 × 4
country year gdp life_satisf
<chr> <dbl> <dbl> <dbl>
1 FR 2010 32548. 6.72
2 IT 2010 26774. 6.59
3 DE 2010 38145. 7.17
4 ES 2010 24253. 6.67
5 NL 2010 42162. 7.94
6 PT 2010 19958. 6.42
7 FR 2011 33245. 7.13
8 IT 2011 27502. 6.45
9 DE 2011 39567. 7.32
10 ES 2011 24455. 6.93
# ℹ 74 more rows
The FE model estimated with the lm() function:
Using the {plm} package:
With the lm() function:
With the plm() function:
The package {fixest} seems nice:
Practice with the third tutorial!

Introduction to programming for data analysis — Session 2