Exercises for Section 4.1 Simple linear regression#
This notebook contains the exercises from Section 4.1 Simple linear regression in the No Bullshit Guide to Statistics.
Notebook setup#
# Ensure required Python modules are installed
%pip install --quiet numpy scipy seaborn pandas ministats
[notice] A new release of pip is available: 26.2.1 -> 26.2.2
[notice] To update, run: pip install --upgrade pip
Note: you may need to restart the kernel to use updated packages.
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# Figures setup
plt.clf() # needed otherwise `sns.set_theme` doesn't work
sns.set_theme(
context="paper",
style="whitegrid",
palette="colorblind",
rc={"figure.figsize": (6, 1.6)},
)
%config InlineBackend.figure_format = "retina"
<Figure size 640x480 with 0 Axes>
# Simplified int and float __repr__
np.set_printoptions(legacy='1.25')
# Check the `ministats` version to make sure all the datasets will be in place.
import ministats
from packaging.version import Version
if Version(ministats.__version__) <= Version("0.5.19"):
print("You have version", ministats.__version__, "of the `ministats` library.")
print("You must use ministats version 0.5.20 or later.")
print("Run the following command in a new notebook cell:")
print(" %pip install --upgrade ministats")
You have version 0.5.7 of the `ministats` library.
You must use ministats version 0.5.20 or later.
Run the following command in a new notebook cell:
%pip install --upgrade ministats
# %pip install --upgrade ministats
# Download datasets/ directory if necessary
from ministats import ensure_datasets
ensure_datasets()
datasets/ directory already exists.
\(\def\stderr#1{\mathbf{se}_{#1}}\) \(\def\stderrhat#1{\hat{\mathbf{se}}_{#1}}\) \(\newcommand{\Mean}{\textbf{Mean}}\) \(\newcommand{\Var}{\textbf{Var}}\) \(\newcommand{\Std}{\textbf{Std}}\) \(\newcommand{\Freq}{\textbf{Freq}}\) \(\newcommand{\RelFreq}{\textbf{RelFreq}}\) \(\newcommand{\DMeans}{\textbf{DMeans}}\) \(\newcommand{\Prop}{\textbf{Prop}}\) \(\newcommand{\DProps}{\textbf{DProps}}\) $\( \newcommand{\CI}[1]{\textbf{CI}_{#1}} \newcommand{\CIL}[1]{\textbf{L}_{#1}} \newcommand{\CIU}[1]{\textbf{U}_{#1}} \newcommand{\ci}[1]{\textbf{ci}_{#1}} \newcommand{\cil}[1]{\textbf{l}_{#1}} \newcommand{\ciu}[1]{\textbf{u}_{#1}} \)\( (this cell contains the macro definitions like \)\stderr{\overline{\mathbf{x}}}\(, \)\stderrhat{}\(, \)\Mean$, …)
Exercises#
E4.1#
Use statsmodels to fit a linear model to the doctors dataset
(datasets/doctors.csv)
based on the formula "score 1 + alc".
Interpret the estimated slope parameter in your own words.
import pandas as pd
doctors = pd.read_csv("datasets/doctors.csv")
doctors.head(3)
| permit | loc | work | hours | caf | alc | weed | exrc | score | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 93273 | rur | hos | 21 | 2 | 0 | 5.0 | 0.0 | 63 |
| 1 | 90852 | urb | cli | 74 | 26 | 20 | 0.0 | 4.5 | 16 |
| 2 | 92744 | urb | hos | 63 | 25 | 1 | 0.0 | 7.0 | 58 |
import statsmodels.formula.api as smf
...
Ellipsis
E4.2#
Consider the following dataset with six observations \([\mathbf{x},\mathbf{y}]=\) \((105, 45)\), \((113,63)\), \((125,86)\), \((137,118)\), \((141,112)\), \((153,169)\). We’re interested in the linear model \(Y|x \sim \mathcal{N}(\beta_0 + \beta_1x, \; \sigma)\) for this data.
a) Use the formulas for \(\widehat{\beta}_{0}\) and \(\widehat{\beta}_{1}\) from page to estimate the intercept \(\beta_0\) and slope \(\beta_1\) model parameters.
b) Use the formula from page to estimate the model’s standard deviation parameter \(\sigma\).
c) Use statsmodels to fit a linear model to this dataset.
Verify your answers to a) and b)
by accessing the appropriate attributes of the fitted model.
Hint:
For part c),
you can “package” the data \(\mathbf{x}=\texttt{xs}\) and \(\mathbf{y}=\texttt{ys}\)
as a data frame object using df_xys=pd.DataFrame({"x":xs,"y":ys}).
# Data
xs = [105, 113, 125, 137, 141, 153]
ys = [45, 63, 86, 118, 112, 169]
E4.3#
You’re looking to rent an apartment. To get an idea of how rent depends on apartment size, you check the classifieds and find the following offers: a 200 sq ft studio for \(\$500\), a 300 sq ft studio for \(\$620\), a 400 sq ft small apartment for \(\$750\), a 500 sq ft one-bedroom condo for \(\$890\), a 900 sq ft two-bedroom apartment for \(\$1250\), and a 1000 sq ft apartment for \(\$1300\). Find the best-fitting linear model \(p(a) = \beta_0 + \beta_1 a\) for the above data, where \(p(a)\) represents the rent price for an apartment with \(a\) square feet of livable area. What is the predicted price for a 700 sq ft apartment?
areas = [200, 300, 400, 500, 900, 1000] # in square feet
prices = [500, 620, 750, 890, 1250, 1300] # in dollars
# Package `areas` and `prices` as a data frame
df_apts = pd.DataFrame({ ... })
# Fit the linear model
import statsmodels.formula.api as smf
...
Ellipsis
E4.4#
Repeat the calculations to obtain the regression result object lm1 for the linear model \(S|e \sim \mathcal{N}(\beta_0 + \beta_1 e, \sigma)\),
which describes the students’ scores \(S\) as a function of their effort \(e\).
a) Use the method lm1.get_prediction to predict the mean score
for a student that invests \(10\) hours of \(\texttt{effort}\),
\(\mu_{\widehat{S}}(10)\).
b) Construct a 90% confidence interval \(\ci{\mu_{S},0.9}(10)\)
for the mean score \(\mu_{S}(10)\) by calling the .conf_int method.
c) Use lm1.get_prediction to predict the score
of a student that invests \(10\) hours of \(\texttt{effort}\),
\(\hat{s}(10)\).
d) Report a 90% prediction interval \(\ci{S,0.9}(10)\) for the score.
Hint: Use the code examples from Section 4.1 as a reference.
# Load the students dataset
import pandas as pd
students = pd.read_csv("datasets/students.csv")
# Fit the linear model
import statsmodels.formula.api as smf
lm1 = smf.ols("score ~ 1 + effort", data=students).fit()
lm1.params
Intercept 32.465809
effort 4.504850
dtype: float64
E4.5#
The OECD Better Life Index (BLI) dataset
(datasets/oecd_bli.csv)
contains information about life satisfaction
collected from different countries around the world.
We want to know if being rich,
as measured by the gross domestic product per capita,
as measured by the variable gdp,
is linked to higher life satisfaction
as measured by the variable lifesat.
Use simple linear regression to model this relationship.
Generate a scatter plot of the data and the regression line.
Hint:
You can use the ministats function plot_reg for the plot.
oecd_bli = pd.read_csv("datasets/oecd_bli.csv")
oecd_bli.head()
| Country | educ | income | lifesat | salary | gdp | |
|---|---|---|---|---|---|---|
| 0 | Mexico | 42.0 | 16269.0 | 6.0 | 16230.0 | 23659.0 |
| 1 | Greece | 76.0 | 20791.0 | 5.8 | 27207.0 | 38397.0 |
| 2 | Slovak Republic | 92.0 | 21149.0 | 6.5 | 23619.0 | 40586.0 |
| 3 | Hungary | 86.0 | 21026.0 | 6.0 | 25409.0 | 43476.0 |
| 4 | Portugal | 55.0 | 24877.0 | 5.8 | 28410.0 | 44963.0 |
import statsmodels.formula.api as smf
...
Ellipsis
E4.6#
The marketing dataset
(datasets/marketing.csv)
contains information about advertising spending on different platforms
and the resulting sales figures of a business.
a) Fit a model for the relationship between youtube ad spend and sales.
b) Use the ministats function plot_resid
to plot the residuals.
c) Comment on the modelling assumptions based on your observations from the residuals plot you generated in part b).
marketing = pd.read_csv("datasets/marketing.csv")
marketing.head(3)
| youtube | newspaper | sales | ||
|---|---|---|---|---|
| 0 | 276.12 | 45.36 | 83.04 | 26.52 |
| 1 | 53.40 | 47.16 | 54.12 | 12.48 |
| 2 | 20.64 | 55.08 | 83.16 | 11.16 |
# a)
import statsmodels.formula.api as smf
...
Ellipsis
# b)
from ministats import plot_resid
...
Ellipsis
E4.7#
The function get_rdataset defined in statsmodels.datasets
allows us to downlaod datasets provided with R packages as pandas data frames.
For example,
if we want to download the Duncan occupational prestige dataset
from the carData package,
we can use the code get_rdataset("Duncan","carData").data.
Fit the linear model for an occupation’s prestige
as a function of income variable based on the data in the Duncan dataset.
Hint:
Use smf.ols based on the formula "prestige 1 + income".
from statsmodels.datasets import get_rdataset
Duncan = get_rdataset("Duncan", "carData").data
Duncan.head()
| type | income | education | prestige | |
|---|---|---|---|---|
| rownames | ||||
| accountant | prof | 62 | 86 | 82 |
| pilot | prof | 72 | 76 | 83 |
| architect | prof | 75 | 92 | 90 |
| author | prof | 55 | 90 | 76 |
| chemist | prof | 64 | 86 | 90 |
import statsmodels.formula.api as smf
...
Ellipsis