diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 83edb953..2bf6cc98 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -17,8 +17,10 @@ jobs:
strategy:
fail-fast: false
matrix:
- os: [ubuntu-latest, macos-latest, windows-latest]
- python-version: ['3.11']
+ # Currently only developing on windows.
+ os: [windows-latest]
+ # os: [ubuntu-latest, macos-latest, windows-latest]
+ python-version: ['3.12']
steps:
- uses: actions/checkout@v3
- name: create build environment
@@ -35,7 +37,7 @@ jobs:
micromamba activate gha-testing
pytest --cov-report=xml --cov=./
- name: Upload coverage report.
- if: runner.os == 'Linux' && matrix.python-version == '3.11'
+ if: runner.os == 'Linux' && matrix.python-version == '3.12'
uses: codecov/codecov-action@v3
# with:
# token: ${{ secrets.CODECOV_TOKEN }}
diff --git a/.gitignore b/.gitignore
index 564210b0..295d151a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,10 @@ bld/
*.pdf
*.run.xml
*.synctex.gz
+*.synctex.gz.sum.synctex
+*.toc
+*.snm
+*.nav
+
+# Jupyter notebooks
+*.ipynb
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 6baf6859..e5f63d0d 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -5,10 +5,12 @@ repos:
- id: check-useless-excludes
# - id: check-hooks-apply # Leave nbqa in here.
# - id: identity # Prints all files passed to pre-commits. Debugging.
- - repo: https://github.com/lyz-code/yamlfix
- rev: 1.13.0
- hooks:
- - id: yamlfix
+ # For some reason the yamlfix hook returns a ModuleNotFoundError:
+ # No module named 'maison.schema'. Deactivate it for now.
+ # - repo: https://github.com/lyz-code/yamlfix
+ # rev: 1.13.0
+ # hooks:
+ # - id: yamlfix
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
@@ -36,7 +38,7 @@ repos:
rev: 23.9.1
hooks:
- id: black
- language_version: python3.11
+ language_version: python3.12
- repo: https://github.com/asottile/blacken-docs
rev: 1.16.0
hooks:
@@ -91,7 +93,7 @@ repos:
rev: v2.2.6
hooks:
- id: codespell
- args: [--skip="**.ipynb"]
+ args: [-I, inst/WORDLIST.txt]
- repo: https://github.com/asottile/setup-cfg-fmt
rev: v2.2.0
hooks:
diff --git a/environment.yml b/environment.yml
index 5c66ddb4..32aa9d6a 100644
--- a/environment.yml
+++ b/environment.yml
@@ -1,24 +1,44 @@
---
name: thesis
-channels: [conda-forge, nodefaults]
+channels: [conda-forge, r, nodefaults]
dependencies:
+ - python ==3.12
+ - pip >=21.1
+
+ # Development dependencies
- conda-lock
- ipykernel
- jupyterlab
- - pandas
- - pip >=21.1
- - plotly>=5.13.0
- pre-commit
- - pytask-latex>=0.4.0
- - pytask-parallel>=0.4.0
- - pytask>=0.4.0
+
+ # Language agnostic template project dependencies
+ - pytask >=0.5.0
+ - pytask-latex >=0.4.2
+ - pytask-parallel >=0.5.0
- pytest
- pytest-cov
- pytest-xdist
- - python-graphviz
- - python=3.11
- - pyyaml
- - setuptools_scm
+
+ # Python template project dependencies
- statsmodels
- - toml
- - pip: [-e ., kaleido, pdbp]
+ - numpy
+ - pandas >=2.2
+ - plotly >=5.2.0,<6
+
+ # R template project dependencies
+ # Currently we are not using R hence we do not need to install these dependencies.
+ # - pytask-r >=0.4.1
+ # - pyreadr
+ # - r-plyr
+ # - r-precommit
+ # - r-yaml
+ # - r-forcats
+
+ # Install project
+ - pip:
+ - -e .
+ - pdbp
+ - kaleido==0.1.0.post1
+ - joblib
+ - git+https://github.com/buddejul/pyvmte@bernstein
+ - coptpy
diff --git a/inst/WORDLIST b/inst/WORDLIST.txt
similarity index 97%
rename from inst/WORDLIST
rename to inst/WORDLIST.txt
index 6bb4261b..50c0cad2 100644
--- a/inst/WORDLIST
+++ b/inst/WORDLIST.txt
@@ -25,6 +25,8 @@ cirmmt
cls
conda
config
+complier
+compliers
cookiecutter
cov
CSE
@@ -97,6 +99,10 @@ Koepke
Kopka
KopkaDaly
Langtangen
+late
+lates
+LATE
+LATEs
LeClere
linux
Loong
diff --git a/paper/figures/sm_sol_lower.tex b/paper/figures/sm_sol_lower.tex
new file mode 100644
index 00000000..47f25d19
--- /dev/null
+++ b/paper/figures/sm_sol_lower.tex
@@ -0,0 +1,26 @@
+\begin{tikzpicture}
+ \begin{axis}[
+ xmin=-1.1, xmax=1.1,
+ ymin=-1.1, ymax=1.1,
+ xlabel=$\beta_s$,
+ ylabel={$\underline{\beta^*}$},
+ xtick={-1, 0, 1}, % Specify the x-axis ticks here
+ ytick={-1, 1},
+ extra y ticks={-0.7, 0.3}, % Specify the positions of the special x-axis ticks together
+ extra y tick style={yticklabel style={color=black}}, % Apply a style to all extra x ticks
+ extra y tick labels={$-(1-\omega)$, $\omega$}, % Customize the labels of the special x-axis ticks
+ ]
+
+ % Unconstrained solution
+ \addplot [domain=-1:1, color=black] {0.3*x - (1-0.3)};
+
+ % Solution with monotonicity constraint
+ \addplot [domain=-1:0, color=blue] {0.3*x - (1-0.3)};
+ \addplot [domain=0:1, color=blue] {x - (1-0.3)};
+
+ % Add text for each line
+ \node at (axis cs:-0.25, 0.75) [color=black, anchor=west] {\footnotesize No Constraint};
+ \node at (axis cs:-0.25, 0.5) [color=blue, anchor=west] {\footnotesize Increasing MTR Functions};
+
+ \end{axis}
+\end{tikzpicture}
diff --git a/paper/figures/sm_sol_upper.tex b/paper/figures/sm_sol_upper.tex
new file mode 100644
index 00000000..cb259542
--- /dev/null
+++ b/paper/figures/sm_sol_upper.tex
@@ -0,0 +1,26 @@
+\begin{tikzpicture}
+ \begin{axis}[
+ xmin=-1.1, xmax=1.1,
+ ymin=-1.1, ymax=1.1,
+ xlabel=$\beta_s$,
+ ylabel={$\overline{\beta^*}$},
+ xtick={-1, 0, 1}, % Specify the x-axis ticks here
+ ytick={-1, 1},
+ extra y ticks={-0.3, 0.7}, % Specify the positions of the special x-axis ticks together
+ extra y tick style={yticklabel style={color=black}}, % Apply a style to all extra x ticks
+ extra y tick labels={$-\omega$, $1-\omega$}, % Customize the labels of the special x-axis ticks
+ ]
+
+ % Unconstrained solution
+ \addplot [domain=-1:1, color=black] {1-0.3 + 0.3*x};
+
+ % Solution with monotonicity constraint
+ \addplot [domain=-1:0, color=blue] {x + 0.7};
+ \addplot [domain=0:1, color=blue] {0.3*x + 0.7};
+
+ % Add text for each line
+ \node at (axis cs:-0.25, -0.5) [color=black, anchor=west] {\footnotesize No Constraint};
+ \node at (axis cs:-0.25, -0.75) [color=blue, anchor=west] {\footnotesize Increasing MTR Functions};
+
+ \end{axis}
+\end{tikzpicture}
diff --git a/paper/figures/sm_upper_incr.tex b/paper/figures/sm_upper_incr.tex
new file mode 100644
index 00000000..c8c29dea
--- /dev/null
+++ b/paper/figures/sm_upper_incr.tex
@@ -0,0 +1,28 @@
+\begin{tikzpicture}
+ \begin{axis}[
+ xmin=-0.1, xmax=1.1,
+ ymin=-0.1, ymax=1.1,
+ xlabel=$u$,
+ ylabel={$\E[Y(d)|U=u]$},
+ xtick={0,1}, % Specify the x-axis ticks here
+ extra x ticks={0.2, 0.5, 0.8}, % Specify the positions of the special x-axis ticks together
+ extra x tick style={xticklabel style={color=black}}, % Apply a style to all extra x ticks
+ extra x tick labels={$p(0)$, $p(1)$, $p(1) + \overline{u}$}, % Customize the labels of the special x-axis ticks
+ ]
+
+ \addplot [domain=0.2:0.5, color=black] {0.2} node[pos=1, right] {\footnotesize $\theta_{20} = -\beta_s$};
+ \addplot [domain=0.2:0.5, color=black] {0} node[pos=1, right] {\footnotesize $\theta_{21} = 0$};
+
+ \draw [decorate,decoration={brace,amplitude=10pt,raise=0pt},yshift=0pt, xshift=-1pt]
+ (axis cs:0.2,0) -- (axis cs:0.2,0.2) node [black,midway,xshift=-1cm] {\footnotesize$-\beta_s$};
+
+ \addplot [domain=0.5:0.8, color=red] {0.2} node[pos=1, right, color=red] {\footnotesize $\theta_{30} = -\beta_s$};
+ \addplot [domain=0.5:0.8, color=red, dashed] {0} node[pos=1, right, color=red] {\footnotesize Infeasible};
+ \addplot [domain=0.5:0.8, color=red] {1} node[pos=1, right, color=red] {\footnotesize $\theta_{31} = 1$};
+
+ \draw [decorate,decoration={brace,amplitude=10pt,mirror,raise=0pt},yshift=0pt, xshift=1pt, color=red]
+ (axis cs:0.8,0.2) -- (axis cs:0.8,1) node [red,midway,xshift=1cm] {\footnotesize$\beta_u$};
+
+
+ \end{axis}
+\end{tikzpicture}
diff --git a/paper/figures/sm_upper_no_restr.tex b/paper/figures/sm_upper_no_restr.tex
new file mode 100644
index 00000000..3d5a2b57
--- /dev/null
+++ b/paper/figures/sm_upper_no_restr.tex
@@ -0,0 +1,27 @@
+\begin{tikzpicture}
+ \begin{axis}[
+ xmin=-0.1, xmax=1.1,
+ ymin=-0.1, ymax=1.1,
+ xlabel=$u$,
+ ylabel={$\E[Y(d)|U=u]$},
+ xtick={0,1}, % Specify the x-axis ticks here
+ extra x ticks={0.2, 0.5, 0.8}, % Specify the positions of the special x-axis ticks together
+ extra x tick style={xticklabel style={color=black}}, % Apply a style to all extra x ticks
+ extra x tick labels={$p(0)$, $p(1)$, $p(1) + \overline{u}$}, % Customize the labels of the special x-axis ticks
+ ]
+
+ \addplot [domain=0.2:0.5, color=black] {0} node[pos=1, right] {\footnotesize $\theta_{20} = 0$};
+ \addplot [domain=0.2:0.5, color=black] {0.2} node[pos=1, right] {\footnotesize $\theta_{21} = 0.2$};
+
+ \draw [decorate,decoration={brace,amplitude=10pt,raise=0pt},yshift=0pt, xshift=-1pt]
+ (axis cs:0.2,0) -- (axis cs:0.2,0.2) node [black,midway,xshift=-1cm] {\footnotesize$\beta_s$};
+
+ \addplot [domain=0.5:0.8, color=red] {0} node[pos=1, right, color=red] {\footnotesize $\theta_{30} = 0$};
+ \addplot [domain=0.5:0.8, color=red] {1} node[pos=1, right, color=red] {\footnotesize $\theta_{31} = 1$};
+
+ \draw [decorate,decoration={brace,amplitude=10pt,mirror,raise=0pt},yshift=0pt, xshift=1pt, color=red]
+ (axis cs:0.8,0) -- (axis cs:0.8,1) node [red,midway,xshift=1cm] {\footnotesize$\beta_u$};
+
+
+ \end{axis}
+\end{tikzpicture}
diff --git a/paper/mte_papers.md b/paper/mte_papers.md
new file mode 100644
index 00000000..96281171
--- /dev/null
+++ b/paper/mte_papers.md
@@ -0,0 +1,38 @@
+# MTE Papers
+
+A collection of papers related to the MTE framework.
+In particular, applied papers that employ the framework of Mogstad et al. ECMA.
+
+## Applied Papers using Mogstad et al. ECMA Framework
+
+### Rose and Shem-Tov 2021 JPE
+
+Title: *How Does Incarceration Affect Reoffending? Estimating the Dose-Response Function*
+
+They use and extend the MTE model for extrapolation. They don't provide inference.
+For example, see Figure 5 and Table 6.
+
+### Koichiro, Takanori Makoto AER
+
+Title: Selection on Welfare Gains: Experimental Evidence from Electricity Plan Choice†
+
+They don't seem to primarily use the MST method, but rather the parametric method of Brinch et al.
+
+
+### Shea WP
+
+https://jkcshea.github.io/
+
+# To Check
+
+- https://direct.mit.edu/rest/article-abstract/doi/10.1162/rest_a_01483/124131/The-Value-of-Piped-Water-and-Sewers-Evidence-from?redirectedFrom=fulltext
+- https://direct.mit.edu/rest/article-abstract/105/3/646/102834/Reconciling-Seemingly-Contradictory-Results-from
+- https://academic.oup.com/ej/article-abstract/132/646/2231/6548192
+- https://academic.oup.com/restud/article-abstract/90/1/432/6582594
+- https://papers.ssrn.com/sol3/papers.cfm?abstract_id=4405043
+- https://www.aeaweb.org/articles?id=10.1257/aer.20181756
+- Appendix of https://academic.oup.com/qje/article-abstract/133/4/1885/5025665
+
+# Misc
+
+- Mismeasurement tratment: https://www.sciencedirect.com/science/article/pii/S0304407623002725
diff --git a/paper/presentation.tex b/paper/presentation.tex
new file mode 100644
index 00000000..d7494c2c
--- /dev/null
+++ b/paper/presentation.tex
@@ -0,0 +1,287 @@
+\documentclass[11pt, aspectratio=169]{beamer}
+% \documentclass[11pt,handout]{beamer}
+\usepackage[T1]{fontenc}
+\usepackage[utf8]{inputenc}
+\usepackage{textcomp}
+\usepackage{float, afterpage, rotating, graphicx}
+\usepackage{epstopdf}
+\usepackage{longtable, booktabs, tabularx}
+\usepackage{fancyvrb, moreverb, relsize}
+\usepackage{eurosym, calc}
+\usepackage{amsmath, amssymb, amsfonts, amsthm, bm}
+\usepackage[
+ natbib=true,
+ bibencoding=inputenc,
+ bibstyle=authoryear-ibid,
+ citestyle=authoryear-comp,
+ maxcitenames=3,
+ maxbibnames=10,
+ useprefix=false,
+ sortcites=true,
+ backend=biber
+]{biblatex}
+\AtBeginDocument{\toggletrue{blx@useprefix}}
+\AtBeginBibliography{\togglefalse{blx@useprefix}}
+\setlength{\bibitemsep}{1.5ex}
+\addbibresource{refs.bib}
+
+\hypersetup{colorlinks=true, linkcolor=black, anchorcolor=black, citecolor=black,
+filecolor=black, menucolor=black, runcolor=black, urlcolor=black}
+
+\setbeamertemplate{footline}[frame number]
+\setbeamertemplate{navigation symbols}{}
+\setbeamertemplate{frametitle}{\centering\vspace{1ex}\insertframetitle\par}
+
+\newcommand{\indep}{\perp\!\!\!\!\perp}
+
+
+\begin{document}
+
+\title{Thesis}
+
+% \author[Julian Budde]
+% {
+% {\bf Julian Budde}\\
+% {\small University of Bonn}\\[1ex]
+% }
+
+
+\begin{frame}
+ \titlepage
+ \note{~}
+\end{frame}
+
+\begin{frame}
+ \frametitle{Marginal Treatment Effect Model: Notation}
+
+ Based on~\cite{mogstad2018using}.
+
+ \vspace{0.5cm}
+
+ Program evaluation setting:
+ \begin{itemize}
+ \item Outcome $Y$
+ \item Binary Treatment $D$
+ \item Potential Outcomes $Y = D Y_{i1} + (1-D) Y_{i0}$
+ \item Binary Instrument $Z$
+ \end{itemize}
+
+ \vspace{0.5cm}
+
+ \textbf{Treatment Choice Equation}:
+ \begin{equation}
+ D = I\{p(Z) - U \geq 0\}
+ \end{equation}
+ where $p(z)$ is the propensity score and $U\sim Unif[0,1]$.
+
+ \vspace{0.5cm}
+
+ $U$ is ``resistance'' to treatment: Small $u$ $\rightarrow$ always take treatment.
+
+\end{frame}
+
+\begin{frame}
+ \frametitle{MTE Model: Assumptions}
+ $(Y,D,Z)$ are observables, $(Y_1, Y_0, U)$ unobservables.
+
+
+ \begin{itemize}
+ \item $U \indep Z$
+ \item $E[Y_d|Z,U] = E[Y_d|U]$ and $E[Y_d^2] < \infty$ for $d \in \{0,1\}$
+ \item $U$ is uniform on $[0,1]$ conditional on $Z$.
+ \end{itemize}
+
+\end{frame}
+
+\begin{frame}
+ \frametitle{MTR Functions}
+
+ Key to the paper: Define everything in terms of (unobservable) MTR functions.
+
+ For $d\in\{0,1\}$:
+ \begin{equation}
+ m_d(u) \equiv E[Y_d | U=u].
+ \end{equation}
+
+ \vspace{0.5cm}
+
+ \textbf{Extrapolation}: Combine
+ \begin{itemize}
+ \item \textit{Information} on point-identified estimands, with
+ \item \textit{Assumptions} on MTR functions.
+ \end{itemize}
+
+ \vspace{0.5cm}
+
+ $\Rightarrow$ Linear program to get identified set.
+
+ \vspace{0.5cm}
+
+ Question: How to perform inference?
+\end{frame}
+
+\section{Simple Example}
+
+\begin{frame}
+ \frametitle{Setup}
+
+ \begin{itemize}
+ \item Outcome $Y \in [0,1]$.
+ \item Binary Treatment $D$
+ \item Binary Instrument $Z$
+ \item Propensity score: $p(0) = 0.4$ and $p(1) = 0.6$.
+ \end{itemize}
+
+ \vspace{0.5cm}
+
+ \textbf{Identify LATE}: $\beta_0^s = E[Y_1 - Y_0 | u \in (0.4, 0.6]]$.
+
+ \vspace{0.5cm}
+
+ \textbf{Target Parameter}: $\beta^* = E[Y_1 - Y_0 | u \in (0.4, 0.8]]$.
+
+\end{frame}
+
+\begin{frame}
+ \frametitle{Solution}
+ The linear programs have an explicit solution:
+ \begin{equation}
+ \beta^* \in [\omega \beta_0^s + (1 - \omega) * (-1), \omega \beta_0^s + (1-\omega) * 1],
+ \end{equation}
+ where $\omega = \frac{p(1) - p(0)}{\overline{u} + p(1) - p(0)}$ is the relative complier share.
+\end{frame}
+
+\begin{frame}
+ \frametitle{Solution with Constraints}
+
+ We can assume that MTR functions must be \textit{increasing} in $u$.
+ This introduces a \textit{kink} in the solution.
+
+ \begin{equation}
+ \overline{\beta^*}(\beta_s)=
+ \begin{cases}
+ \omega \beta_s + (1 - \omega),& \quad \text{if } \beta_s \geq 0\\
+ \beta_s + (1 - \omega), & \quad \text{if } \beta_s < 0,
+ \end{cases}
+ \end{equation}
+ and
+ \begin{equation}
+ \underline{\beta^*}(\beta_s)=
+ \begin{cases}
+ \beta_s - (1 - \omega),& \quad \text{if } \beta_s \geq 0\\
+ \omega \beta_s - (1 - \omega), & \quad \text{if } \beta_s < 0.
+ \end{cases}
+ \end{equation}
+
+\end{frame}
+
+\begin{frame}
+ \frametitle{Inference}
+
+ From~\cite{fang2019inference} we know the standard \textit{bootstrap fails} when $\phi(\theta)$ is not fully differentiable.
+
+ \vspace{0.5cm}
+
+ \textbf{Alternatives}: Based on \textit{directional} differentiability of $\phi$ we can use adjusted delta methods.
+ \begin{itemize}
+ \item \textit{Analytical} delta method, e.g.~\cite{fang2019inference}.
+ \item \textit{Numerical} delta method, e.g.~\cite{hong2018numerical}.
+ \end{itemize}
+
+
+\end{frame}
+
+\begin{frame}
+ \frametitle{Analytical delta bootstrap}:
+ \begin{itemize}
+ \item[1.] Bootstrap approximation to distribution of $\sqrt{n}(\hat{\beta^s} - \beta_0^s)$.
+ \item[2.] Suitable estimator $\hat{\phi'_n}$ for directional derivative.
+ \item[3.] Delta method:
+ \begin{equation*}
+ \hat{\phi'_n}(\sqrt{n}(\hat{\beta^s}^*_n - \hat{\beta^s}_n))
+ \end{equation*}
+ \end{itemize}
+
+ In our case: Simple pre-test
+ \begin{equation*}
+ \hat{\phi'_n}(h)=
+ \begin{cases}
+ h \omega,& \quad \text{if } \sqrt{n}\hat{\beta^s}/\hat{\sigma^s} > \kappa_n\\
+ I(h < 0) h + I(h > 0) h \omega,& \quad \text{if } |\sqrt{n}\hat{\beta^s}/\hat{\sigma^s}| \leq \kappa_n\\
+ h,& \quad \text{if } \sqrt{n}\hat{\beta^s}/\hat{\sigma^s} < -\kappa_n\\
+ \end{cases}
+ \end{equation*}
+ where we require $\kappa_n \to \infty$ but more slowly than $\sqrt{n}$, i.e. $\kappa_n / \sqrt{n} \to 0$.
+\end{frame}
+
+\begin{frame}
+ \frametitle{Numerical delta bootstrap}
+
+ If we don't know the analytical structure of $\phi'$, we can use a numerical approximation:
+ \begin{equation*}
+ \hat{\phi'}_{n, s_n} \equiv \frac{1}{s_n}\{\phi(\hat{\beta^s} + s_n h) - \phi(\hat{\beta^s})\}
+ \end{equation*}
+
+ Combining this with a bootstrap approximation to $\sqrt{n}(\hat{\beta^s} - \beta_0^s)$ we get
+ \begin{equation*}
+ \hat{\phi'}_{n, s_n}(\sqrt{n}(\hat{\beta^s} - \beta_0^s)) \equiv \frac{1}{s_n}\{\phi(\hat{\beta^s} + s_n \sqrt{n}(\hat{\beta^s} - \beta_0^s)) - \phi(\hat{\beta^s})\},
+ \end{equation*}
+ the distribution of which we can use to construct confidence intervals.
+ We require $s_n\to0$ but $s_n\sqrt{n} \to \infty$.
+\end{frame}
+
+\begin{frame}
+ \frametitle{Monte Carlo Simulations}
+
+ \begin{itemize}
+ \item True parameter $\beta^*$ at upper bound of identified set.
+ \item Compare: Standard bootstrap, analytical and numerical delta method.
+ \end{itemize}
+
+ \vspace{0.5cm}
+
+ To construct $1-\alpha = 0.95$ confidence interval for \textit{true parameter}:
+ \begin{itemize}
+ \item Use asymptotic approximations from above.
+ \item Construct one-sided $\alpha/2$ intervals for the upper and lower bound.
+ \item By~\cite{imbens2004confidence} logic these should be \textit{conservative}.
+ \end{itemize}
+
+ \vspace{0.5cm}
+
+ Setting: $N_{boot} = N_{sims} = 250$, $s_n = 1 / \sqrt{n}$, $\kappa_n = \sqrt{n}$.
+
+\end{frame}
+
+\begin{frame}
+ \frametitle{Results: Coverage}
+
+ \begin{figure}
+ \includegraphics[width=\textheight]{../bld/boot/figures/coverage.png}
+ \caption{Coverage by Method and True Parameter}
+ \end{figure}
+
+\end{frame}
+
+\begin{frame}
+ \frametitle{Outlook}
+ \begin{itemize}
+ \item[1.] Get inference to work: CI construction, tuning params, check analytical delta.
+ \item[2.] Understand solutions under different constraints: Shape restrictions, parametric restrictions (written package for this)
+ \item[3.] Simulate bootstrap and numerical delta method in
+ \begin{itemize}
+ \item[(a)] More complicated example $\Rightarrow$ original paper example
+ \item[(b)] Empirically relevant example $\Rightarrow$ literature
+ \end{itemize}
+ \item[4.] Justify numerical delta bootstrap theoretically by studying characteristics of LP solutions
+ \end{itemize}
+
+\end{frame}
+
+\begin{frame}[allowframebreaks]
+ \frametitle{References}
+ \renewcommand{\bibfont}{\normalfont\footnotesize}
+ \printbibliography
+\end{frame}
+
+\end{document}
diff --git a/paper/refs.bib b/paper/refs.bib
index d04e8e66..eab15c16 100644
--- a/paper/refs.bib
+++ b/paper/refs.bib
@@ -1,487 +1,139 @@
-
-@article{AcemogluJohnsonRobinson01,
- author = {Daron Acemoglu and Simon Johnson and James A. Robinson},
- journal = {The American Economic Review},
- month = dec,
- number = {5},
- pages = {1369-1401},
- title = {The Colonial Origins of Comparative Development: An Empirical Investigation},
- volume = {91},
- year = {2001}
-}
-
-@article{Albouy12,
- author = {David Y. Albouy},
- journal = {American Economic Review},
- number = {6},
- pages = {3059-3076},
- title = {The Colonial Origins of Comparative Development: An Investigation of the Settler Mortality Data},
- volume = {102},
- year = {2012}
-}
-
-@book{Arbuckle10,
- address = {Birmingham, UK},
- author = {Daniel Arbuckle},
- publisher = {Packt Publishing},
- title = {Python Testing: Beginner's Guide},
- year = {2010}
-}
-
-@article{Baiocchi07,
- author = {Baiocchi, Giovanni},
- journal = {Computational Economics},
- month = {August},
- number = {1},
- pages = {19-40},
- title = {Reproducible research in computational economics: guidelines, integrated approaches, and open source software},
- volume = {30},
- year = {2007}
-}
-
-@article{Barnes10,
- author = {Nick Barnes},
- journal = {Nature},
- month = {October},
- pages = {753},
- title = {Publish Your Computer Code: It is Good Enough},
- volume = {467},
- year = {2010}
-}
-
-@book{Barr04,
- author = {Adam Barr},
- publisher = {Addison Wesley},
- title = {Find the Bug. A Book of Incorrect Programs},
- year = {2004}
-}
-
-@unpublished{Brandl10,
- author = {Georg Brandl},
- month = {July},
- note = {Available at \url{http://sphinx.pocoo.org/}},
- title = {Sphinx Documentation},
- year = {2010}
-}
-
-@book{CampbellEtAl09,
- author = {Jennifer Campbell and Paul Gries and Jason Montojo and Gregory V. Wilson},
- editor = {Daniel H. Steinberg},
- publisher = {The Pragmatic Programmers},
- series = {Practical Programming},
- title = {An Introduction to Computer Science Using {Python}},
- year = {2009}
-}
-
-@article{DewaldThursbyAnderson86,
- author = {William G. Dewald and Jerry G. Thursby and Richard G. Anderson},
- journal = {The American Economic Review},
- month = sep,
- number = {4},
- pages = {587-603},
- title = {Replication in Empirical Economics: The Journal of Money, Credit and Banking Project},
- volume = {76},
- year = {1986}
-}
-
-@book{Doar05,
- address = {Sebastopol, {CA, USA}},
- author = {Matthew B. Doar},
- publisher = {O'Reilly Media},
- title = {Practical Development Environments},
- year = {2005}
-}
-
-@article{Dubois05,
- author = {Paul F. Dubois},
- journal = {Computing in Science \& Engineering},
- number = {3},
- pages = {80-85},
- title = {Maintaining correctness in scientific programs},
- volume = {7},
- year = {2005}
-}
-
-@article{FomelHennenfent07,
- author = {Sergey Fomel and Gilles Hennenfent},
- journal = {IEEE International Conference on Acoustics, Speech and Signal Processing, 2007. ICASSP 2007.},
- pages = {IV1257-IV1260},
- title = {Reproducible computational experiments using {SCons}},
- volume = {4},
- year = {2007}
-}
-
-@book{FreemanPryce10,
- address = {Boston, MA, USA},
- author = {Steve Freeman and Nat Pryce},
- publisher = {Addison Wesley},
- title = {Growing Object-Oriented Software, Guided by Tests},
- year = {2010}
-}
-
-@book{Friedl06,
- author = {Jeffrey E. F. Friedl},
- edition = {3},
- publisher = {O'Reilly Media},
- title = {Mastering Regular Expressions},
- year = {2006}
-}
-
-@unpublished{GaudeckerEconProjectTemplates,
- author = {Hans-Martin von Gaudecker},
- note = {\url{https://doi.org/10.5281/zenodo.7780520}},
- title = {Templates for Reproducible Research Projects in Economics},
- year = {2023}
-}
-
-@article{GaudeckerSoestWengstrom11prefhet,
- author = {Hans-Martin von Gaudecker and Arthur van Soest and Erik Wengström},
- journal = {American Economic Review},
- month = {April},
- number = {2},
- pages = {664-694},
- title = {Heterogeneity in Risky Choice Behaviour in a Broad Population},
- volume = {101},
- year = {2011}
-}
-
-@article{Hoxby00,
- author = {Caroline M. Hoxby},
- journal = {The American Economic Review},
- month = dec,
- number = {5},
- pages = {1209-1238},
- title = {Does Competition among Public Schools Benefit Students and Taxpayers?},
- volume = {90},
- year = {2000}
-}
-
-@article{Hoxby07,
- author = {Caroline M. Hoxby},
- journal = {The American Economic Review},
- month = dec,
- number = {5},
- pages = {2038-2055},
- title = {Does Competition Among Public Schools Benefit Students and Taxpayers? {Reply}},
- volume = {97},
- year = {2007}
-}
-
-@article{InceHattonGraham12,
- author = {Darrel C. Ince and Leslie Hatton and John Graham-Cumming},
- journal = {Nature},
- month = {February},
- number = {7386},
- pages = {485-488},
- title = {The Case for Open Computer Programs},
- volume = {482},
- year = {2012}
-}
-
-@article{KellyHookSanders09,
- author = {Diane Kelly and D. Hook and Rebecca Sanders},
- journal = {Computing in Science \& Engineering},
- number = {5},
- pages = {48-53},
- title = {Five Recommended Practices for Computational Scientists Who Write Software},
- volume = {11},
- year = {2009}
-}
-
-@unpublished{KellySanders08,
- author = {Diane Kelly and Rebecca Sanders},
- month = {May},
- note = {Paper presented at the First International Workshop on Software Engineering for Computational Science and Engineering, {Leipzig, Germany}},
- title = {Assessing the Quality of Scientific Software},
- year = {2008}
-}
-
-@article{King95,
- author = {Gary King},
- journal = {{PS:} Political Science and Politics},
- month = sep,
- number = {3},
- pages = {444-452},
- title = {Replication, Replication},
- volume = {28},
- year = {1995}
-}
-
-@article{KleiberZeileis10,
- author = {Christian Kleiber and Achim Zeileis},
- journal = {German Economic Review},
- number = {4},
- pages = {404-417},
- title = {The Grunfeld Data at 50},
- volume = {11},
- year = {2010}
-}
-
-@article{KoenkerZeileis09,
- author = {Roger Koenker and Achim Zeileis},
- journal = {Journal of Applied Econometrics},
- number = {5},
- pages = {833-847},
- title = {On Reproducible Econometric Research},
- volume = {24},
- year = {2009}
-}
-
-@article{Koepke11,
- author = {Hoyt Koepke},
- journal = {Hacker Monthly},
- title = {Why Python Rocks for Research},
- volume = {8},
- year = {2011}
-}
-
-@book{KopkaDaly04,
- author = {Helmut Kopka and Patrick W. Daly},
- edition = {4},
- publisher = {Addison Wesley / Pearson Education},
- title = {Guide to \LaTeX},
- year = {2004}
-}
-
-@book{Langtangen08,
- address = {Berlin and Heidelberg, Germany},
- author = {Hans Petter Langtangen},
- edition = {3},
- publisher = {Springer},
- title = {Python Scripting for Computational Science},
- year = {2008}
-}
-
-@book{Langtangen09,
- address = {Berlin and Heidelberg, {Germany}},
- author = {Hans Petter Langtangen},
- publisher = {Springer},
- title = {A Primer on Scientific Programming with Python},
- year = {2009}
-}
-
-@article{LeClere10,
- author = {Felicia {LeClere}},
- journal = {The Chronicle of Higher Education},
- month = {3 August},
- title = {Too Many Researchers Are Reluctant to Share Their Data},
- year = {2010}
-}
-
-@article{Levitt02,
- author = {Steven D. Levitt},
- journal = {The American Economic Review},
- month = sep,
- number = {4},
- pages = {1244-1250},
- title = {Using Electoral Cycles in Police Hiring to Estimate the Effects of Police on Crime: Reply},
- volume = {92},
- year = {2002}
-}
-
-@article{Levitt97,
- author = {Steven D. Levitt},
- journal = {The American Economic Review},
- month = jun,
- number = {3},
- pages = {270-290},
- title = {Using Electoral Cycles in Police Hiring to Estimate the Effect of Police on Crime},
- volume = {87},
- year = {1997}
-}
-
-@article{Loong07,
- author = {Theresa Song Loong},
- journal = {The {Prac\TeX} Journal},
- number = {3},
- title = {The Beginner's Forest of {\LaTeX}},
- year = {2007}
-}
-
-@book{Lutz07,
- author = {Mark Lutz},
- edition = {3},
- publisher = {O'Reilly Media},
- title = {Learning Python},
- year = {2007}
-}
-
-@article{McCrary02,
- author = {Justin {McCrary}},
- journal = {The American Economic Review},
- month = sep,
- number = {4},
- pages = {1236-1243},
- title = {Using Electoral Cycles in Police Hiring to Estimate the Effect of Police on Crime: Comment},
- volume = {92},
- year = {2002}
-}
-
-@article{McCullough07,
- author = {B. D. {McCullough}},
- journal = {Econ Journal Watch},
- month = {September},
- number = {3},
- pages = {326-337},
- title = {Got Replicability? {The} \emph{Journal of Money, Credit and Banking Archive}},
- volume = {4},
- year = {2007}
-}
-
-@article{McCullough08,
- author = {B. D. {McCullough}},
- journal = {Computational Statistics \& Data Analysis},
- month = jun,
- number = {10},
- pages = {4568-4569},
- title = {Special section on {Microsoft Excel 2007}},
- volume = {52},
- year = {2008}
-}
-
-@article{McCullough09,
- author = {B. D. {McCullough}},
- journal = {Economic Analysis \& Policy},
- month = {March},
- number = {1},
- pages = {117-126},
- title = {Open Access Economics Journals and the Market for Reproducible Economic Research},
- volume = {39},
- year = {2009}
-}
-
-@article{McCulloughVinod03,
- author = {B. D. {McCullough} and Hrishikesh D. Vinod},
- journal = {American Economic Review},
- month = {June},
- number = {3},
- pages = {873-892},
- title = {Verifying the Solution from a Nonlinear Solver: A Case Study},
- volume = {93},
- year = {2003}
-}
-
-@article{Merali10,
- author = {Zeeya Merali},
- journal = {Nature},
- pages = {775-777},
- title = {Computational science: ...Error},
- volume = {467},
- year = {2010}
-}
-
-@article{Panko98,
- author = {Raymond R. Panko},
- journal = {Journal of End User Computing},
- month = {Spring},
- number = {2},
- pages = {15-21},
- title = {What We Know About Spreadsheet Errors},
- volume = {10},
- year = {1998/2008}
-}
-
-@article{Rothstein07comment,
- author = {Jesse Rothstein},
- journal = {American Economic Review},
- month = {December},
- number = {5},
- pages = {2026-2037},
- title = {Does Competition Among Public Schools Benefit Students and Taxpayers? {Comment}},
- volume = {97},
- year = {2007}
-}
-
-@unpublished{Rothstein07rejoinder,
- author = {Jesse Rothstein},
- month = {November},
- note = {Available at \url{http://gsppi.berkeley.edu/faculty/jrothstein/hoxby/rejoinder.pdf}},
- title = {Rejoinder to {Hoxby}},
- year = {2007}
-}
-
-@article{Schelling69,
- author = {Thomas C. Schelling},
- journal = {The American Economic Review},
- number = {2},
- pages = {488-493},
- title = {Models of Segregation},
- volume = {59},
- year = {1969}
-}
-
-@unpublished{Segal08,
- author = {Judith Segal},
- month = {September},
- note = {Proceedings of the Psychology of Programming Interest Group, {University of Lancaster, UK}},
- title = {Scientists and Software Engineers: A Tale of Two Cultures.},
- year = {2008}
-}
-
-@book{Sink11,
- address = {Champaign, Illinois},
- author = {Eric Sink},
- publisher = {Pyrenean Gold Press},
- title = {Version Control by Example},
- year = {2011}
-}
-
-@unpublished{StachurskiSargent13,
- author = {John Stachurski and Thomas J. Sargent},
- note = {{Available at \url{http://quant-econ.net/index.html}}},
- title = {Quantitative Economics},
- year = {2013}
-}
-
-@unpublished{Verfaille08,
- author = {Vincent Verfaille},
- month = {January},
- note = {Available at \url{http://www.cirmmt.mcgill.ca/activities/workshops/training/latex/details/pdf-slides}},
- title = {{\LaTeX} workshop for advanced users... or at least not beginners!},
- year = {2008}
-}
-
-@book{Wilson05,
- author = {Gregory V. Wilson},
- publisher = {The Pragmatic Programmers},
- title = {Data Crunching},
- year = {2005}
-}
-
-@article{Wilson06,
- author = {Gregory V. Wilson},
- journal = {American Scientist},
- month = {January--February},
- number = {1},
- pages = {5-6},
- title = {Where's the Real Bottleneck in Scientific Computing?},
- volume = {94},
- year = {2006}
-}
-
-@unpublished{WilsonEtAl12,
- author = {Gregory V. Wilson and D. A. Aruliah and C. Titus Brown and Neil P. Chue Hong and Matt Davis and Richard T. Guy and Steven H. D. Haddock and Katy Huff and Ian Mitchell and Mark Plumbley and Ben Waugh and Ethan P. White and Paul Wilson},
- month = {November},
- note = {Available at \url{http://arxiv.org/abs/1210.0530}},
- title = {Best Practices for Scientific Computing},
- year = {2012}
-}
-
-@unpublished{WSJ05,
- author = {{Wall Street Journal}},
- month = {{24 October}},
- note = {{Available at http://gsppi.berkeley.edu/faculty/jrothstein/hoxby/wsj.pdf}},
- title = {Novel Way to Assess School Competition Stirs Academic Row},
- year = {2005}
-}
-
-@article{YaleRoundtable10,
- author = {{Yale Law School Roundtable on Data and Code Sharing}},
- journal = {Computing in Science and Engineering},
- month = {September/October},
- number = {5},
- pages = {8-13},
- title = {Reproducible Research: Addressing the Need for Data and Code Sharing in Computational Science},
- volume = {12},
- year = {2010}
+@article{mogstad2018using,
+ title={Using instrumental variables for inference about policy relevant treatment parameters},
+ author={Mogstad, Magne and Santos, Andres and Torgovitsky, Alexander},
+ journal={Econometrica},
+ volume={86},
+ number={5},
+ pages={1589--1619},
+ year={2018},
+ publisher={Wiley Online Library}
+}
+
+@article{shea2023ivmte,
+ title={ivmte: An R Package for Extrapolating Instrumental Variable Estimates Away From Compliers},
+ author={Shea, Joshua and Torgovitsky, Alexander},
+ journal={Observational Studies},
+ volume={9},
+ number={2},
+ pages={1--42},
+ year={2023},
+ publisher={University of Pennsylvania Press}
+}
+
+@techreport{mogstad2017using,
+ title={Using Instrumental Variables for Inference about Policy Relevant Treatment Effects},
+ author={Mogstad, Magne and Santos, Andres and Torgovitsky, Alexander},
+ year={2017},
+ institution={National Bureau of Economic Research}
+}
+
+@article{andrews2009invalidity,
+ title={Invalidity of the bootstrap and the m out of n bootstrap for confidence interval endpoints defined by moment inequalities},
+ author={Andrews, Donald WK and Han, Sukjin},
+ journal={The Econometrics Journal},
+ volume={12},
+ pages={S172--S199},
+ year={2009},
+ publisher={Oxford University Press Oxford, UK}
+}
+
+@article{imbens2004confidence,
+ title={Confidence intervals for partially identified parameters},
+ author={Imbens, Guido W and Manski, Charles F},
+ journal={Econometrica},
+ volume={72},
+ number={6},
+ pages={1845--1857},
+ year={2004},
+ publisher={Wiley Online Library}
+}
+
+@article{bei2023inference,
+ title={Inference on Union Bounds with Applications to DiD, RDD, Bunching, and Structural Counterfactuals},
+ author={Bei, Xinyue},
+ year={2023},
+ journal={Working Paper},
+}
+
+@article{marx2024sharp,
+ title={Sharp bounds in the latent index selection model},
+ author={Marx, Philip},
+ journal={Journal of Econometrics},
+ volume={238},
+ number={2},
+ pages={105561},
+ year={2024},
+ publisher={Elsevier}
+}
+
+
+@article{han2024computational,
+ title={A computational approach to identification of treatment effects for policy evaluation},
+ author={Han, Sukjin and Yang, Shenshen},
+ journal={Journal of Econometrics},
+ volume={240},
+ number={1},
+ pages={105680},
+ year={2024},
+ publisher={Elsevier}
+}
+
+@article{fang2023inference,
+ title={Inference for Large-Scale Linear Systems With Known Coefficients},
+ author={Fang, Zheng and Santos, Andres and Shaikh, Azeem M and Torgovitsky, Alexander},
+ journal={Econometrica},
+ volume={91},
+ number={1},
+ pages={299--327},
+ year={2023},
+ publisher={Wiley Online Library}
+}
+
+@article{ye2023negative,
+ title={A negative correlation strategy for bracketing in difference-in-differences},
+ author={Ye, Ting and Keele, Luke and Hasegawa, Raiden and Small, Dylan S},
+ journal={Journal of the American Statistical Association},
+ pages={1--13},
+ year={2023},
+ publisher={Taylor \& Francis}
+}
+
+@article{kaido2019confidence,
+ title={Confidence intervals for projections of partially identified parameters},
+ author={Kaido, Hiroaki and Molinari, Francesca and Stoye, J{\"o}rg},
+ journal={Econometrica},
+ volume={87},
+ number={4},
+ pages={1397--1432},
+ year={2019},
+ publisher={Wiley Online Library}
+}
+
+@article{poirier2024quantifying,
+ title={Quantifying the Internal Validity of Weighted Estimands},
+ author={Poirier, Alexandre and S{\l}oczy{\'n}ski, Tymon},
+ journal={arXiv preprint arXiv:2404.14603},
+ year={2024}
+}
+
+@article{hong2018numerical,
+ title={The numerical delta method},
+ author={Hong, Han and Li, Jessie},
+ journal={Journal of Econometrics},
+ volume={206},
+ number={2},
+ pages={379--394},
+ year={2018},
+ publisher={Elsevier}
+}
+
+@article{fang2019inference,
+ title={Inference on directionally differentiable functions},
+ author={Fang, Zheng and Santos, Andres},
+ journal={The Review of Economic Studies},
+ volume={86},
+ number={1},
+ pages={377--412},
+ year={2019},
+ publisher={Oxford University Press}
}
diff --git a/paper/task_paper.py b/paper/task_paper.py
index aef7001a..9c64f6e1 100644
--- a/paper/task_paper.py
+++ b/paper/task_paper.py
@@ -9,6 +9,7 @@
for document in documents:
+ @pytask.mark.skip()
@pytask.mark.latex(
script=PAPER_DIR / f"{document}.tex",
document=BLD / "latex" / f"{document}.pdf",
diff --git a/paper/thesis.tex b/paper/thesis.tex
index 07949fec..c0110c88 100644
--- a/paper/thesis.tex
+++ b/paper/thesis.tex
@@ -1,138 +1,412 @@
-\documentclass[11pt, a4paper, leqno]{article}
-\usepackage{a4wide}
-\usepackage[T1]{fontenc}
-\usepackage[utf8]{inputenc}
-\usepackage{float, afterpage, rotating, graphicx}
-\usepackage{epstopdf}
-\usepackage{longtable, booktabs, tabularx}
-\usepackage{fancyvrb, moreverb, relsize}
-\usepackage{eurosym, calc}
-% \usepackage{chngcntr}
-\usepackage{amsmath, amssymb, amsfonts, amsthm, bm}
-\usepackage{caption}
-\usepackage{mdwlist}
-\usepackage{xfrac}
-\usepackage{setspace}
-\usepackage[dvipsnames]{xcolor}
-\usepackage{subcaption}
-\usepackage{minibox}
-% \usepackage{pdf14} % Enable for Manuscriptcentral -- can't handle pdf 1.5
-% \usepackage{endfloat} % Enable to move tables / figures to the end. Useful for some
-% submissions.
-
-\usepackage[
- natbib=true,
- bibencoding=inputenc,
- bibstyle=authoryear-ibid,
- citestyle=authoryear-comp,
- maxcitenames=3,
- maxbibnames=10,
- useprefix=false,
- sortcites=true,
- backend=biber
-]{biblatex}
-\AtBeginDocument{\toggletrue{blx@useprefix}}
-\AtBeginBibliography{\togglefalse{blx@useprefix}}
-\setlength{\bibitemsep}{1.5ex}
-\addbibresource{refs.bib}
-
-\usepackage[unicode=true]{hyperref}
-\hypersetup{
- colorlinks=true,
- linkcolor=black,
- anchorcolor=black,
- citecolor=NavyBlue,
- filecolor=black,
- menucolor=black,
- runcolor=black,
- urlcolor=NavyBlue
+\documentclass[11pt,a4paper,english]{article} %document type and language
+\usepackage[utf8]{inputenc} % set character set to support some UTF-8
+\usepackage{babel} % multi-language support
+% \usepackage{sectsty} % allow redefinition of section command formatting
+\usepackage{tabularx} % more table options
+\usepackage{titling} % allow redefinition of title formatting
+\usepackage{imakeidx} % create and index of words
+\usepackage{xcolor} % more color options
+\usepackage{enumitem} % more list formatting options
+\usepackage{tocloft} % redefine table of contents, new list like objects
+
+\usepackage[centering,noheadfoot,margin=1in]{geometry}
+
+%set TOC margins
+\setlength{\cftbeforesecskip}{15pt} % skip in TOC
+
+% remove paragraph white space and modify space between list items
+\usepackage{parskip}
+
+% Set font globally
+\usepackage{lmodern} % load Latin modern fonts
+\usepackage[defaultsans]{cantarell} % cantarell fonts
+
+% HACK: https://tex.stackexchange.com/questions/58087/how-to-remove-the-warnings-font-shape-ot1-cmss-m-n-in-size-4-not-available
+\usepackage{anyfontsize}
+
+% set LaTeX global font
+\renewcommand{\familydefault}{\sfdefault}
+\renewcommand{\sfdefault}{lmss}
+
+% set styling headings
+%\allsectionsfont{\usefont{OT1}{phi}{b}{n}}
+
+\usepackage{float} % floats
+\usepackage{graphicx} % Graphics
+\usepackage{amsmath} % extensive math options
+\usepackage{amssymb} % special math symbols
+\usepackage[Gray,squaren,thinqspace,thinspace]{SIunits} % elegant units
+\usepackage{listings} % source code
+
+% Custom Operators
+%% Expectation symbol
+\DeclareMathOperator*{\E}{\mathbb{E}}
+\DeclareMathOperator*{\Cov}{\mathrm{Cov}}
+\DeclareMathOperator*{\Var}{\mathrm{Var}}
+
+% missing math commands
+\providecommand{\abs}[1]{\left\lvert#1\right\rvert} % |.|
+\providecommand{\br}[1]{\left(#1\right)} % (.)
+\providecommand{\sbr}[1]{\left[#1\right]} % [.]
+\providecommand{\ddfrac}[2]{\frac{\displaystyle #1}{\displaystyle #2}}
+% use \math rm{d} to include math differential
+
+% independence symbol
+% https://tex.stackexchange.com/questions/79434/double-perpendicular-symbol-for-independence
+\newcommand{\indep}{\perp\!\!\!\!\perp}
+
+
+% options for listings
+\lstset{
+ breaklines=true,
+ postbreak=\raisebox{0ex}[0ex][0ex]{\ensuremath{\color{red}\hookrightarrow\space}},
+ numbers=left,
+ numbersep=5pt,
+ numberstyle=\tiny\color{gray},
+ basicstyle=\footnotesize\ttfamily
}
-
-\widowpenalty=10000
-\clubpenalty=10000
-
-\setlength{\parskip}{1ex}
-\setlength{\parindent}{0ex}
-\setstretch{1.5}
-
-
-\begin{document}
-
-\title{Master Thesis\thanks{Julian Budde, University of Bonn. Email: \href{mailto:buddejul@gmail.com}{\nolinkurl{buddejul [at] gmail [dot] com}}.}}
-
+% NEEDS to be before hyperref, cleveref and autonum
+% number figures, tables and equations within the sections
+\numberwithin{equation}{section}
+\numberwithin{figure}{section}
+\numberwithin{table}{section}
+
+% references and annotation, citations
+\usepackage[small,bf,hang]{caption} % captions
+\usepackage{subcaption} % adds sub figure & sub caption
+\usepackage{sidecap} % adds side captions
+\usepackage{hyperref} % add hyperlinks to references
+\usepackage[noabbrev,nameinlink]{cleveref} % better references than default~\ref
+% Hack:https://tex.stackexchange.com/questions/285950/package-autonum-needs-the-obsolete-etex-package
+\expandafter\def\csname ver@etex.sty\endcsname{3000/12/31}
+\let\globcount\newcount
+\usepackage{autonum} % only number referenced equations
+\usepackage{url} % URLs
+\usepackage{cite} % well formed numeric citations
+
+% biblatex for references
+% \usepackage{biblatex}
+% \addbibresource{literature.bib}
+% csquotes recommended: https://tex.stackexchange.com/questions/229638/package-biblatex-warning-babel-polyglossia-detected-but-csquotes-missing
+% \usepackage{csquotes}
+
+% format hyperlinks
+\colorlet{linkcolour}{black}
+\colorlet{urlcolour}{blue}
+\hypersetup{colorlinks=true,
+ linkcolor=linkcolour,
+ citecolor=linkcolour,
+ urlcolor=urlcolour}
+
+%\usepackage{todonotes} % add to do notes
+\usepackage{epstopdf} % process eps-images
+\usepackage{float} % floats
+\usepackage{fancyhdr} % header and footer
+% HACK: https://tex.stackexchange.com/questions/664532/fancyhr-warning-footskip-is-too-small
+\setlength{\footskip}{14pt}
+
+% default path for figures
+\graphicspath{{figures/}}
+
+% If we have multiple directories, specify them like this: \graphicspath{{figures_ch1/}{figures_ch2/}}.
+
+% For rendering tikz
+\usepackage{pgfplots}
+\pgfplotsset{compat=1.18}
+\usetikzlibrary{decorations.pathreplacing} % Load the library for drawing braces
+
+
+% Define some math environments
+\usepackage{amsthm}
+
+\newtheorem{theorem}{Theorem}[section]
+\newtheorem{corollary}{Corollary}[theorem]
+\newtheorem{lemma}[theorem]{Lemma}
+
+\theoremstyle{definition}
+\newtheorem{definition}{Definition}[section]
+
+\theoremstyle{remark}
+\newtheorem*{remark}{Remark}
+
+% set header and footer
+\pagestyle{fancy}
+\fancyhf{} % clear all header and footer fields
+\cfoot{\thepage} % add page number
+\renewcommand{\headrulewidth}{0pt} % add horizontal line of 0.4pt thick
+
+\title{Inference in a Simple MTE Model}
\author{Julian Budde}
-
-\date{
- {\bf Preliminary -- please do not quote}
- \\[1ex]
- \today
-}
-
+\date{\today}
+\begin{document}
\maketitle
-
-
-\begin{abstract}
- Some abstract here.
-\end{abstract}
-
-\clearpage
-
-
-\section{Introduction} % (fold)
-\label{sec:introduction}
-
-If you are using this template, please cite this item from the references:
-\citet{GaudeckerEconProjectTemplates}.
-
-The data set for the example project is taken from
-\url{https://www.stem.org.uk/resources/elibrary/resource/28452/large-datasets-stats4schools}.
-It contains data on smoking habits in the UK, with 1691 observations and 12 variables.
-We consider only 4 of the 12 features for the prediction of the variable
-\texttt{smoking}: \texttt{marital\_status}, \texttt{highest\_qualification},
-\texttt{gender} and \texttt{age}. We model the dependence using a Logistic model. All
-numerical features are included linearly, while categorical features are expanded into
-dummy variables. Figures below illustrate the model predictions over the lifetime. You
-will find one figure and one estimation summary table for each installed programming
-language.
-
-
-
-\begin{figure}[H]
-
- \centering
- \includegraphics[width=0.85\textwidth]{../python/figures/smoking_by_marital_status}
-
- \caption{\emph{Python:} Model predictions of the smoking probability over the
- lifetime. Each colored line represents a case where marital status is fixed to one
- of the values present in the data set.}
- \label{fig:python-predictions}
-
+This document analyzes a simple version of the MTE model with a binary instrument and bounded outcome.
+The goal is to extrapolate from a point-identified LATE for instrument-compliers to a larger sub-population.
+
+I discuss solutions for constant splines, which in this case --- constant weights of target and identified parameters over a finite number of population intervals --- deliver sharp bounds (Theorem 4 in the paper).
+The linear program in this simple example has analytical solutions which helps to illustrate the nature of the inference problem.
+
+In particular, I discuss solutions in the cases of unrestricted MTR functions and under shape restrictions (e.g.\ increasing MTR functions).
+In the first case, if the outcome is bounded between $0$ and $1$, the identified set is of the form
+\begin{equation}
+ \omega \beta_s \pm (1 - \omega),
+\end{equation}
+where both $\beta_s$ and $\omega$ are unknown and depend on the propensity score and the CEF of the outcome given the instrument.
+In the second case, the solution to either the upper or the lower bound depends on whether the constraint is binding or not.
+For example, with \textit{increasing} MTR functions, the solution to the upper bound is given by
+\begin{equation}
+ \overline{\beta^*}=
+ \begin{cases}
+ \omega \beta_s + (1 - \omega),& \quad \text{if } \beta_s \geq 0\\
+ \beta_s + (1 - \omega), & \quad \text{if } \beta_s < 0.
+ \end{cases}
+\end{equation}
+This means that the upper bound is strictly smaller than the unrestricted bound stated above whenever $\beta_s$ is small enough.
+With increasing MTRs the bound is attained by maximizing the MTR under treatment while leaving the MTR under no treatment unchanged.
+If $\beta_s$ is negative, $1 \geq MTR^c_0 \geq MTR^c_1 \geq 0$ for the complier population, hence the unknown subpopulation cannot have a treament effect of $1$.
+
+The next section formalizes the example.
+
+\section{Simple Example: Extrapolating a LATE}
+This section introduces the notation and setup considered here.
+\subsection{Setup}
+We consider a simple model with a binary instrument.
+In this case we can identify a \textit{local} average treatment effect for the sub-population of instrument compliers.
+Our goal is to extend this to a larger subpopulation.
+
+The outcome is denoted $Y_i$ and has bounded support $[0,1]$ (without loss of generality).
+The instrument $Z_i$ is binary.
+Binary treatment $D_i$ is determined by the selection model with
+\begin{equation}
+ D_i = I\{p(Z_i) \geq U_i\},
+\end{equation}
+where $U_i\sim U(0,1)$, $U_i \indep Z_i$ and $I\{A\}$ denotes the indicator function for event $A$.
+Thus, $p(z) \equiv P(D_i = 1 | Z_i = z)$ is the propensity score.
+
+Assuming that it is known that $p(0) \leq p(1)$ we can identify $LATE(p(0), p(1))$, the local average treatment effect for the subpopulation with $U_i$ realizations in $\mathopen(p(0), p(1)\mathclose]$\footnote{In practice, it is unknown to the researcher whether $p(0) < p(1)$ and hence whether the target has upper bound $p(0) + \overline{u}$ or $p(1) + \overline{u}$. So probably the target should be treated as $\max\{p(1), p(0)\} + \overline{u}$ and the lower bound equivalently.}.
+We are interested in extending this to $LATE(p(0), p(1) + \overline{u})$ with $\overline{u} \geq 0$ and such that $p(1) + \overline{u} \leq 1$.
+
+In this simple case we can find an exact solution to the linear program presented in the previous section using a finite-dimensional approximation to the underlying MTR functions.
+The reason --- as shown in the paper --- is that all weights, both for the identified and the target estimand, are constant over intervals of $u$.
+In particular, partition $u\in[0,1]$ into the intervals $[0, p(0)], (p(0), p(1)], (p(1), \overline{u}], (\overline{u}, 1]$. Note that the relevant endpoints are the two propensity scores and the upper bound of the target parameter.
+
+To approximate the underlying MTR functions for the non-parametric identified set we can simply use constant splines on each of the partitions, namely
+\begin{align}
+ I\{0 \leq u \leq p(0)\}, \\
+ I\{p(0) < u \leq p(1)\}, \\
+ I\{p(1) < u \leq \overline{u}\}, \\
+ I\{\overline{u} < u \leq 1\}.
+\end{align}
+
+The weights on these splines are the choice variables of the linear program which can be stated as follows:
+\begin{equation}
+ [\text{Describe LP}].
+\end{equation}
+
+We have eight choice variables $\theta_{kd}$: One for each partition $k\in\{1,2,3,4\}$ and for each treatment state $d\in\{0,1\}$.
+The choice variables in our setting are nuisance parameters.
+We are solely interested in the upper and lower bound for the target parameter, that is the value of the linear program.
+
+
+This linear program can easily be solved by hand and results in the following identified set:
+\begin{equation}
+ \beta^* \in [\omega\beta_s - (1 - \omega), \omega\beta_s + (1 - \omega)],
+\end{equation}
+where $\omega = \frac{p(1) - p(0)}{\overline{u} + p(1) - p(0)} = \frac{p(1) - p(0)}{\overline{u} + p(1) - p(0)}$.
+
+This is intuitive: The target estimand is a weighted average of the identified $LATE(p(0), p(1))$ and the unknown $LATE(p(1), \overline{u})$.
+The weights correspond to the relative size of the subpopulations and the bounds for the unknown LATE are $-1$ and $1$, respectively.
+Importantly, this only holds because $LATE(p(0), p(1))$ does not put any restrictions on the MTR functions outside the interval $(p(0), p(1)]$, which is intuitive and can easily be seen from the LP constraints.
+This is generally different for other identified target estimands like the OLS slope, which put non-zero weight outside this interval\footnote{OLS does so asymmetrically for $d=0$ and $d=1$ which reflects why OLS does not correspond to a well defined causal quantity in this model. Note: This would be interesting to connect to the ideas in~\cite{poirier2024quantifying}: How could we think about the subpopulation in terms of $u$?}.
+
+
+Note that by the usual identification argument we have
+\begin{equation}
+ \beta_s = \frac{\E\left[Y|Z=1\right] - \E\left[Y|Z=0\right]}{p(1) - p(0)}.
+\end{equation}
+But this implies the interval simplifies to
+\begin{equation}
+ \beta^* \in \left[\frac{\E\left[Y|Z=1\right] - \E\left[Y|Z=0\right]}{\overline{u} + p(1) - p(0)} \pm (1 - \omega)\right].
+\end{equation}
+
+Now if $\overline{u}\geq0$ is fixed, this immediately tells we no longer have the weak identification problem when $p(1) - p(0)\geq0$ is close to zero. Further, in the case $p(1) - p(0) = 0$ we have that the numerator is also equal to zero by the exclusion restriction on $Z$. Hence, the identified set is --- obviously so --- given by $[-1, 1]$.
+
+However, for asymptotics with \textit{both} $p(1) - p(0)$ and $\overline{u}$ going to zero with the sample size at the right rate issues with a denominator close to zero should again arise.
+
+\subsection{Graphical Illustration}
+Figure~\ref{fig:sm_upper_no_restr} illustrates the solution to the extrapolation problem for the upper bound.
+First, note that the MTR functions outside $[p(0), p(1) + \overline{u}]$ are irrelevant as neither the identified nor the target parameter depends on them. Hence they are not depicted.
+Next, note that in this special non-parametric case without any additional restrictions, the choices of $\theta_{2d}$ and $\theta_{3d}$ are independent.
+Thus, the upper bound is given by any arguments that satisfy $\theta_{21} - \theta_{20} = \beta_s$ and the maximum upper bound is attained at $\beta_u = 1$ at $\theta_{31} = 1, \theta_{30} = 0$.
+One such set of choice variables is depicted in the graph.
+
+\begin{figure}
+ \input{figures/sm_upper_no_restr.tex}
+ \caption{Solution for Upper Bound without Restrictions}\label{fig:sm_upper_no_restr}
\end{figure}
+\subsection{Solutions with Restrictions}
+\subsubsection{Monotone MTR Functions}
+The first set of restrictions we consider are increasing MTR functions.
+In terms of the program this corresponds to the following set of constraints:
+\begin{equation}
+ \theta_{k+1,d} \geq \theta_{k,d} \quad \text{for} \quad k \in \{2, 3, 4\} \text{ and } d \in \{0,1\}
+\end{equation}
+
+Under these additional restrictions the logic to attaining the upper bound is as follows:
+We are only allowed to increase the MTR functions and $\beta_s$ is increasing in $\theta_{31}$ and decreasing in $\theta_{30}$.
+Since both are constrained from below by $\theta_{21}$ and $\theta_{20}$ we make both as small as possible subject to $\theta_{21} - \theta_{20} = \beta_s$.
+Then the has the feature $1 = \theta_{31} \geq \theta_{21}$ and $\theta_{30} = \theta_{20} \geq 0$.
+To determine the remaining choice variables, differentiate between $\beta_s > 0$ and $\beta_s < 0$:
+\begin{itemize}
+ \item When $\beta_s > 0$ the constraint is \textit{not binding/redundant} (at least for the upper bound)\footnote{Technically it is binding but only since we have the general constraint $\theta_{kd} \geq 0$. In this sense it is redundant.}. Note that $\beta_s = \theta_{21} > \theta_{20} = 0$ is a possible solution satisfying the $\beta_s$ constraint.
+ Hence, $1 = \theta_{31} > \theta_{30} = \theta_{20} = 0$ is a feasible solution implying $\beta_u = 1$. This is the upper bound without restrictions.
+ \item When $\beta_s < 0$ the constraint will be \textit{binding}. The smallest set $\theta_{21}, \theta_{20}$ consistent with $\beta_s$ is given by $-\beta_s = \theta_{20} > \theta_{21} = 0$.
+ Then the upper bound is attained by $1 = \theta_{31} > \theta_{30} = -\beta_s$ which results in $\beta_u = 1 + \beta_s < 1$. Thus, the constraint decreases the upper bound.
+\end{itemize}
+
+To summarize, the solutions to the upper and lower bound are given by the following function of $\beta_s$, which has a kink at $\beta_s = 0$:
+
+\begin{equation}
+ \overline{\beta^*}(\beta_s)=
+ \begin{cases}
+ \omega \beta_s + (1 - \omega),& \quad \text{if } \beta_s \geq 0\\
+ \beta_s + (1 - \omega), & \quad \text{if } \beta_s < 0,
+ \end{cases}
+\end{equation}
+and
+\begin{equation}
+ \underline{\beta^*}(\beta_s)=
+ \begin{cases}
+ \beta_s - (1 - \omega),& \quad \text{if } \beta_s \geq 0\\
+ \omega \beta_s - (1 - \omega), & \quad \text{if } \beta_s < 0.
+ \end{cases}
+\end{equation}
+
+Figure~\ref{fig:sm_upper_incr.tex} illustrates how the constraint is binding in the case with $\beta_s < 0$ for the upper bound.
+Note we would like to set $\theta_{30} = 0$ but this violates the constraint $\theta_{30} \geq \theta_{20} = -\beta_s > 0$.
+
+\begin{figure}
+ \input{figures/sm_upper_incr.tex}
+ \caption{Upper Bound with Monotone MTR Functions}\label{fig:sm_upper_incr.tex}
+\end{figure}
-\begin{table}[!h]
- \input{../python/tables/estimation_results.tex}
- \caption{\label{tab:python-summary}\emph{Python:} Estimation results of the
- linear Logistic regression.}
-\end{table}
-
-
-
-
-% section introduction (end)
-
-
-
-\setstretch{1}
-\printbibliography
-\setstretch{1.5}
-
+Figure~\ref{fig:sm_sol_upper} displays the solution to the upper bound $\overline{\beta^*}$ and lower bound $\underline{\beta^*}$ as a function of $\beta_s$ under different restrictions.
+\begin{figure}
+ \centering
+ \begin{subfigure}{0.45\textwidth}
+ \centering
+ \input{figures/sm_sol_upper.tex}
+ \caption{Solution to $\overline{\beta^*}$}
+ \end{subfigure}
+ \hfill
+ \begin{subfigure}{0.45\textwidth}
+ \centering
+ \input{figures/sm_sol_lower.tex}
+ \caption{Solution to $\underline{\beta^*}$}
+ \end{subfigure}
+ \caption{Solutions under Different Constraints}\label{fig:sm_sol_upper}
+\end{figure}
-% \appendix
+\subsection{Estimation and Inference}
+\paragraph{Unknown propensity score}
+One subtle conceptual difficulty is that the researcher does not observe $p(0), p(1)$ but only a noisy estimate when they perform their extrapolation exercise. In terms of sampling, this would mean we over- or under-sample compliers, i.e.\ those with realizations $u_i\in (p(0), p(1)]$, resulting in a too large or too small difference in propensity scores.
+
+We still consider the case where the researcher wants to perform inference on the parameter $p(0), p(1)$, which seems to be the only reasonable approach.
+
+\paragraph{Plug-in Estimator}
+The explicit solution above suggests a simple plug-in estimator for the identified set:
+\begin{equation}
+ \hat{\Theta}_0 = \left[\hat{\omega} \hat{\beta}^{LATE} - (1-\hat{\omega}), \hat{\omega}\hat{\beta}^{LATE} + (1-\hat{\omega}) \right].
+\end{equation}
+Here,
+\begin{equation}
+ \hat{\beta}^{LATE} = \frac{1/N_1 \sum_{i=1}^N Z_i Y_i - 1/N_0 \sum_{i=1}^N(1-Z_i)Y_i}{1/N_1\sum_{i=1}^N Z_i D_i - 1/N_0\sum_{i=1}^N (1-Z_i)D_i}.
+\end{equation}
+where $N_1 = \sum_{i=1}^N Z_i$ and $N = N_0 + N_1$. Further,
+\begin{equation}
+ \hat{\omega} = \frac{\hat{p}(1) - \hat{p}(0)}{\overline{u} + \hat{p}(1) - \hat{p}(0)},
+\end{equation}
+where $\hat{p}(z)$ is an estimator of the propensity score.
+
+Using the simplification noted above, the estimator can be equivalently written as
+\begin{equation}
+ .
+\end{equation}
+
+\section{Inference Approaches}
+
+The target is to make inference on the parameter $\beta^*$ which for the simulation purposes I set to $LATE(0.4, 0.6)$.
+\footnote{Inference for the interval-identified parameter $\beta^*$ and its identified set are not necessarily the same as pointed out by \cite{imbens2004confidence} in simple examples. I will return to this point below.}
+That means, we want to extrapolte the LATE for the instrument-complier subpopulation with $u\in[0.4, 0.6]$ to a broader sub-population.
+Inference is complicated by the fact that the optimal solution $\beta^* = \beta^*(\beta_s)$ exhibits a kink at $\beta_s=0$ and hence is not fully differentiable at that point.
+However, as pointed out by~\cite{fang2023inference} and~\cite{hong2018numerical}, a delta method can be used that only requires \textit{directional} differentiability of the functional.
+~\cite{fang2023inference} propose a method that combines a bootstrap approximation to the underlying parameter with an estimate of the directional derivative of the functional $\phi$ that exploits its analytical structure.
+~\cite{hong2018numerical} on the other hand propose to use a numerical approximation to the directional derivative.
+Henceforth, I will call these methods the \textit{analytical} and \textit{numerical} delta bootstrap, respectively.
+
+\subsection*{Notation}
+In the following I will use the notation of~\cite{fang2023inference}.
+Our goal is to perform inference on the real-valued parameter $\beta^*$, which is only set-identified.
+As shown above the set is given by $[\underline{\beta^*}(\beta_s), \overline{\beta^*}]$,
+with the following explicit solutions:
+\begin{equation*}
+ \overline{\beta^*}(\beta_s)=
+ \begin{cases}
+ \omega \beta_s + (1 - \omega),& \quad \text{if } \beta_s \geq 0\\
+ \beta_s + (1 - \omega), & \quad \text{if } \beta_s < 0,
+ \end{cases}
+\end{equation*}
+and
+\begin{equation*}
+ \underline{\beta^*}(\beta_s)=
+ \begin{cases}
+ \beta_s - (1 - \omega),& \quad \text{if } \beta_s \geq 0\\
+ \omega \beta_s - (1 - \omega), & \quad \text{if } \beta_s < 0.
+ \end{cases}
+\end{equation*}
+In the notation of~\cite{fang2023inference} we have an underlying parameter $\theta_0 \equiv \beta_0^s$ which in our case is real-valued.
+The mapping $\phi: \mathbb{R} \to \mathbb{R}$ is the solution to the upper or lower bound, respectively.
+That is, we have $\overline{\phi} \equiv \overline{\beta^*}$ with the mapping given above.
+As is evident, this function has a kink at $\beta_s = 0$ and hence is only directionally differentiable at that point.
+
+If we can find a method to construct confidence intervals for $\overline{\phi}(\beta_0^s)$ and $\underline{\phi}(\beta_0^s)$, we can also construct a confidence interval.
+
+
+Note that our setting is the same as Example 2.2 of~\cite{hong2018numerical}, which itself is a more general version of Example 2 in~\cite{fang2023inference}.
+
+\section{Simulation Results}
+
+\begin{figure}
+ \centering
+ \begin{subfigure}{0.45\textwidth}
+ \centering
+ \includegraphics[width=\textwidth]{../bld/boot/figures/coverage.png}
+ \caption{Coverage for True Parameter}
+ \end{subfigure}
+ \hfill
+ \begin{subfigure}{0.45\textwidth}
+ \centering
+ \includegraphics[width=\textwidth]{../bld/boot/figures/length.png}
+ \caption{Length of CIs}
+ \end{subfigure}
+
+ \begin{subfigure}{0.45\textwidth}
+ \centering
+ \includegraphics[width=\textwidth]{../bld/boot/figures/means.png}
+ \caption{Means of Upper and Lower Bound}
+ \end{subfigure}
+ \hfill
+ \begin{subfigure}{0.45\textwidth}
+ \centering
+ \includegraphics[width=\textwidth]{../bld/boot/figures/coverage_eps_fun.png}
+ \caption{Coverage by Step Size for Numerical Delta Method}
+ \end{subfigure}
+ \caption{Simulation: CI Properties for Standard and Numerical Delta Bootstrap}\label{fig:simulation_ci_bootstrap}
+\end{figure}
-% The chngctr package is needed for the following lines.
-% \counterwithin{table}{section}
-% \counterwithin{figure}{section}
+\bibliographystyle{plain}
+\bibliography{refs.bib}
\end{document}
diff --git a/pyproject.toml b/pyproject.toml
index b7f1da35..26699b1c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -12,10 +12,16 @@ pdbcls = "pdbp:Pdb"
target-version = "py311"
select = ["ALL"]
fix = true
-fix-only = true # No linting errors will be reported
+# Might want to reduce this at some point, currently just the set to whatever number
+# is required.
+pylint.max-args = 13
+fix-only = false # No linting errors will be reported
extend-ignore = [
"S101", # Use of `assert` detected.
"ANN", # Missing type annotations
+ "TD002", # to do hooks
+ "TD003", # to do hooks
+ "FIX002", # to do hooks
]
[tool.ruff.per-file-ignores]
@@ -47,3 +53,6 @@ norecursedirs = ["docs"]
[tool.yamlfix]
line_length = 88
none_representation = "null"
+
+[tool.codespell]
+skip = "*.ipynb,inst/WORDLIST"
diff --git a/setup.cfg b/setup.cfg
index 829d224b..4c741747 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -26,7 +26,7 @@ project_urls =
packages = find:
install_requires =
pytask
-python_requires = >=3.11
+python_requires = >=3.12
include_package_data = True
package_dir =
=src
diff --git a/src/thesis/analysis/__init__.py b/src/thesis/analysis/__init__.py
deleted file mode 100644
index 3b4b3ba3..00000000
--- a/src/thesis/analysis/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-"""Code for the core analyses."""
diff --git a/src/thesis/analysis/model.py b/src/thesis/analysis/model.py
deleted file mode 100644
index 20f776b3..00000000
--- a/src/thesis/analysis/model.py
+++ /dev/null
@@ -1,54 +0,0 @@
-"""Functions for fitting the regression model."""
-
-import statsmodels.formula.api as smf
-from statsmodels.iolib.smpickle import load_pickle
-
-
-def fit_logit_model(data, data_info, model_type):
- """Fit a logit model to data.
-
- Args:
- data (pandas.DataFrame): The data set.
- data_info (dict): Information on data set stored in data_info.yaml. The
- following keys can be accessed:
- - 'outcome': Name of dependent variable column in data
- - 'outcome_numerical': Name to be given to the numerical version of outcome
- - 'columns_to_drop': Names of columns that are dropped in data cleaning step
- - 'categorical_columns': Names of columns that are converted to categorical
- - 'column_rename_mapping': Old and new names of columns to be renamend,
- stored in a dictionary with design: {'old_name': 'new_name'}
- - 'url': URL to data set
- model_type (str): What model to build for the linear relationship of the logit
- model. Currently implemented:
- - 'linear': Numerical covariates enter the regression linearly, and
- categorical covariates are expanded to dummy variables.
-
- Returns:
- statsmodels.base.model.Results: The fitted model.
-
- """
- outcome_name = data_info["outcome"]
- outcome_name_numerical = data_info["outcome_numerical"]
- feature_names = list(set(data.columns) - {outcome_name, outcome_name_numerical})
-
- if model_type == "linear":
- # smf.logit expects the binary outcome to be numerical
- formula = f"{outcome_name_numerical} ~ " + " + ".join(feature_names)
- else:
- message = "Only 'linear' model_type is supported right now."
- raise ValueError(message)
-
- return smf.logit(formula, data=data).fit()
-
-
-def load_model(path):
- """Load statsmodels model.
-
- Args:
- path (str or pathlib.Path): Path to model file.
-
- Returns:
- statsmodels.base.model.Results: The stored model.
-
- """
- return load_pickle(path)
diff --git a/src/thesis/analysis/predict.py b/src/thesis/analysis/predict.py
deleted file mode 100644
index 2801951c..00000000
--- a/src/thesis/analysis/predict.py
+++ /dev/null
@@ -1,42 +0,0 @@
-"""Functions for predicting outcomes based on the estimated model."""
-
-import numpy as np
-import pandas as pd
-
-
-def predict_prob_by_age(data, model, group):
- """Predict smoking probability for varying age values.
-
- For each group value in column data[group] we create new data that runs through a
- grid of age values from data.age.min() to data.age.max() and fixes all column
- values to the ones returned by data.mode(), except for the group column.
-
- Args:
- data (pandas.DataFrame): The data set.
- model (statsmodels.base.model.Results): The fitted model.
- group (str): Categorical column in data set. We create predictions for each
- unique value in column data[group]. Cannot be 'age' or 'smoke'.
-
- Returns:
- pandas.DataFrame: Predictions. Has columns 'age' and one column for each
- category in column group.
-
- """
- age_min = data["age"].min()
- age_max = data["age"].max()
- age_grid = np.arange(age_min, age_max + 1)
-
- mode = data.mode()
-
- new_data = pd.DataFrame(age_grid, columns=["age"])
-
- cols_to_set = list(set(data.columns) - {group, "age", "smoke"})
- new_data = new_data.assign(**dict(mode.loc[0, cols_to_set]))
-
- predicted = {"age": age_grid}
- for group_value in data[group].unique():
- _new_data = new_data.copy()
- _new_data[group] = group_value
- predicted[group_value] = model.predict(_new_data)
-
- return pd.DataFrame(predicted)
diff --git a/src/thesis/analysis/task_analysis.py b/src/thesis/analysis/task_analysis.py
deleted file mode 100644
index 1859c9e5..00000000
--- a/src/thesis/analysis/task_analysis.py
+++ /dev/null
@@ -1,47 +0,0 @@
-"""Tasks running the core analyses."""
-
-from pathlib import Path
-
-import pandas as pd
-import pytask
-
-from thesis.analysis.model import fit_logit_model, load_model
-from thesis.analysis.predict import predict_prob_by_age
-from thesis.config import BLD, GROUPS, SRC
-from thesis.utilities import read_yaml
-
-fit_model_deps = {
- "scripts": [Path("model.py"), Path("predict.py")],
- "data": BLD / "python" / "data" / "data_clean.csv",
- "data_info": SRC / "data_management" / "data_info.yaml",
-}
-
-
-def task_fit_model_python(
- depends_on=fit_model_deps,
- produces=BLD / "python" / "models" / "model.pickle",
-):
- """Fit a logistic regression model (Python version)."""
- data_info = read_yaml(depends_on["data_info"])
- data = pd.read_csv(depends_on["data"])
- model = fit_logit_model(data, data_info, model_type="linear")
- model.save(produces)
-
-
-for group in GROUPS:
- predict_deps = {
- "data": BLD / "python" / "data" / "data_clean.csv",
- "model": BLD / "python" / "models" / "model.pickle",
- }
-
- @pytask.task(id=group)
- def task_predict_python(
- group=group,
- depends_on=predict_deps,
- produces=BLD / "python" / "predictions" / f"{group}.csv",
- ):
- """Predict based on the model estimates (Python version)."""
- model = load_model(depends_on["model"])
- data = pd.read_csv(depends_on["data"])
- predicted_prob = predict_prob_by_age(data, model, group)
- predicted_prob.to_csv(produces, index=False)
diff --git a/src/thesis/bootstrap_kink/__init__.py b/src/thesis/bootstrap_kink/__init__.py
new file mode 100644
index 00000000..f3fe33ea
--- /dev/null
+++ b/src/thesis/bootstrap_kink/__init__.py
@@ -0,0 +1 @@
+"""Bootstrap for non-differentiable or only directionally-differentiable functions."""
diff --git a/src/thesis/bootstrap_kink/funcs_kink.py b/src/thesis/bootstrap_kink/funcs_kink.py
new file mode 100644
index 00000000..09690f06
--- /dev/null
+++ b/src/thesis/bootstrap_kink/funcs_kink.py
@@ -0,0 +1,63 @@
+"""Functions for analyzing inference for simple kink model."""
+
+import numpy as np
+import pandas as pd # type: ignore[import-untyped]
+
+
+def simulation(n_sim, n_obs, n_boot, theta_0, phi, alpha, rng):
+ """Simulation."""
+ return pd.concat(
+ [
+ _experiment(n_obs, n_boot, theta_0, phi, alpha, rng, return_boot=False)
+ for _ in range(n_sim)
+ ],
+ axis=1,
+ ).T
+
+
+def _experiment(n_obs, n_boot, theta_0, phi, alpha, rng, return_boot):
+ x = rng.normal(theta_0, 1, n_obs)
+
+ mle = phi(x.mean())
+
+ boot_mle = np.zeros(n_boot)
+
+ for i in range(n_boot):
+ boot_idx = rng.choice(n_obs, n_obs, replace=True)
+ boot_x = x[boot_idx]
+ boot_mle[i] = phi(boot_x.mean())
+
+ boot_distr = np.sqrt(n_obs) * (boot_mle - mle)
+
+ boot_cval_lo = np.percentile(boot_distr, 100 * alpha / 2)
+ boot_cval_hi = np.percentile(boot_distr, 100 * (1 - alpha / 2))
+
+ ci_lo = mle - boot_cval_hi / np.sqrt(n_obs)
+ ci_hi = mle - boot_cval_lo / np.sqrt(n_obs)
+
+ if return_boot:
+ return boot_distr
+
+ return pd.Series(
+ {
+ "ci_lo": ci_lo,
+ "ci_hi": ci_hi,
+ "theta_0": theta_0,
+ "alpha": alpha,
+ "n_obs": n_obs,
+ "n_boot": n_boot,
+ },
+ )
+
+
+def _phi_max(theta: float, cutoff: float = 0):
+ return np.maximum(theta, cutoff)
+
+
+def _phi_kink(
+ theta: float,
+ kink: float,
+ slope_left: float = 0.5,
+ slope_right: float = 1,
+):
+ return slope_left * theta * (theta < kink) + slope_right * theta * (theta >= kink)
diff --git a/src/thesis/classes.py b/src/thesis/classes.py
new file mode 100644
index 00000000..d0eec08d
--- /dev/null
+++ b/src/thesis/classes.py
@@ -0,0 +1,82 @@
+"""Custom classes for thesis."""
+
+from collections.abc import Callable
+from dataclasses import dataclass
+from typing import NamedTuple
+
+import numpy as np
+
+
+@dataclass
+class Estimand:
+ """Target estimand."""
+
+ esttype: str
+ u_lo: float | None = None
+ u_hi: float | None = None
+ dz_cross: tuple[int, int] | None = None
+
+
+@dataclass
+class Instrument:
+ """Discrete instrument."""
+
+ support: np.ndarray
+ pmf: np.ndarray
+ pscores: np.ndarray
+
+
+@dataclass
+class DGP:
+ """Data Generating Process."""
+
+ m0: Callable
+ m1: Callable
+ support_z: np.ndarray
+ pmf_z: np.ndarray
+ pscores: np.ndarray
+ joint_pmf_dz: dict[int, dict[int, float]]
+
+ @property
+ def expectation_z(self):
+ """Expectation of instrument Z."""
+ return np.sum(self.support_z * self.pmf_z)
+
+ @property
+ def expectation_d(self):
+ """Expectation of binary treatment D."""
+ return np.sum(self.pscores * self.pmf_z)
+
+ @property
+ def variance_d(self):
+ """Variance of binary treatment D."""
+ return self.expectation_d * (1 - self.expectation_d)
+
+ @property
+ def covariance_dz(self):
+ """Covariance of binary treatment D and instrument Z."""
+ return np.sum(
+ [
+ self.joint_pmf_dz[d][z]
+ * (d - self.expectation_d)
+ * (z - self.expectation_z)
+ for d in [0, 1]
+ for z in self.support_z
+ ],
+ )
+
+
+class MonteCarloSetup(NamedTuple):
+ """Setup for Monte Carlo simulations."""
+
+ sample_size: int
+ repetitions: int
+ u_hi_range: np.ndarray | None = None
+
+
+class LocalATEs(NamedTuple):
+ """LATEs for subpopulations: never-taker, complier, always-taker."""
+
+ always_taker: float
+ complier: float
+ never_taker: float
diff --git a/src/thesis/config.py b/src/thesis/config.py
index 5367091c..abd4f859 100644
--- a/src/thesis/config.py
+++ b/src/thesis/config.py
@@ -1,6 +1,8 @@
"""All the general configuration of the project."""
from pathlib import Path
+import numpy as np
+
SRC = Path(__file__).parent.resolve()
BLD = SRC.joinpath("..", "..", "bld").resolve()
@@ -10,3 +12,5 @@
GROUPS = ["marital_status", "qualification"]
__all__ = ["BLD", "SRC", "TEST_DIR", "GROUPS"]
+
+RNG = np.random.default_rng()
diff --git a/src/thesis/data/data.csv b/src/thesis/data/data.csv
deleted file mode 100644
index fb565254..00000000
--- a/src/thesis/data/data.csv
+++ /dev/null
@@ -1,1692 +0,0 @@
-gender,age,marital_status,highest_qualification,nationality,ethnicity,gross_income,region,smoke,amt_weekends,amt_weekdays,type
-Male,38,Divorced,No Qualification,British,White,"2,600 to 5,200",The North,No,,,
-Female,42,Single,No Qualification,British,White,"Under 2,600",The North,Yes,12.0,12.0,Packets
-Male,40,Married,Degree,English,White,"28,600 to 36,400",The North,No,,,
-Female,40,Married,Degree,English,White,"10,400 to 15,600",The North,No,,,
-Female,39,Married,GCSE/O Level,British,White,"2,600 to 5,200",The North,No,,,
-Female,37,Married,GCSE/O Level,British,White,"15,600 to 20,800",The North,No,,,
-Male,53,Married,Degree,British,White,"Above 36,400",The North,Yes,6.0,6.0,Packets
-Male,44,Single,Degree,English,White,"10,400 to 15,600",The North,No,,,
-Male,40,Single,GCSE/CSE,English,White,"2,600 to 5,200",The North,Yes,8.0,8.0,Hand-Rolled
-Female,41,Married,No Qualification,English,White,"5,200 to 10,400",The North,Yes,15.0,12.0,Packets
-Male,72,Widowed,No Qualification,English,White,"10,400 to 15,600",The North,No,,,
-Male,49,Married,No Qualification,British,White,Refused,The North,No,,,
-Male,29,Married,Degree,English,White,"Above 36,400",The North,No,,,
-Female,79,Widowed,No Qualification,English,White,"10,400 to 15,600",The North,No,,,
-Male,25,Single,Degree,English,White,"15,600 to 20,800",The North,No,,,
-Female,27,Single,Degree,English,White,"15,600 to 20,800",The North,No,,,
-Female,30,Single,Degree,English,White,"20,800 to 28,600",The North,No,,,
-Male,47,Divorced,No Qualification,British,White,"10,400 to 15,600",The North,No,,,
-Female,69,Single,Other/Sub Degree,English,White,"2,600 to 5,200",The North,No,,,
-Male,55,Married,No Qualification,English,White,"20,800 to 28,600",The North,No,,,
-Female,34,Married,GCSE/CSE,British,White,"2,600 to 5,200",The North,Yes,6.0,12.0,Packets
-Female,36,Married,GCSE/O Level,English,White,"5,200 to 10,400",The North,Yes,5.0,2.0,Packets
-Female,56,Married,No Qualification,English,White,"2,600 to 5,200",The North,Yes,20.0,20.0,Packets
-Male,71,Divorced,No Qualification,English,White,Unknown,The North,No,,,
-Female,38,Married,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Female,79,Married,No Qualification,English,White,"Under 2,600",The North,No,,,
-Female,58,Divorced,No Qualification,English,White,"5,200 to 10,400",The North,Yes,25.0,20.0,Packets
-Female,69,Married,No Qualification,British,White,"Under 2,600",The North,No,,,
-Male,83,Widowed,No Qualification,English,White,"2,600 to 5,200",The North,No,,,
-Female,73,Widowed,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Female,42,Married,GCSE/O Level,British,White,Refused,The North,Yes,40.0,15.0,Packets
-Female,31,Married,GCSE/CSE,English,White,"Under 2,600",The North,Yes,15.0,15.0,Both/Mainly Packets
-Male,26,Single,GCSE/CSE,English,White,"2,600 to 5,200",The North,No,,,
-Female,27,Separated,Degree,English,White,"15,600 to 20,800",The North,No,,,
-Female,57,Married,Higher/Sub Degree,English,White,"10,400 to 15,600",The North,No,,,
-Female,30,Married,ONC/BTEC,English,White,"2,600 to 5,200",The North,No,,,
-Female,22,Single,A Levels,English,White,"5,200 to 10,400",The North,No,,,
-Female,78,Widowed,No Qualification,English,White,"Under 2,600",The North,Yes,20.0,20.0,Packets
-Female,49,Married,ONC/BTEC,English,Mixed,"15,600 to 20,800",The North,No,,,
-Male,74,Married,Other/Sub Degree,English,White,"15,600 to 20,800",The North,No,,,
-Male,85,Widowed,Degree,Scottish,White,"20,800 to 28,600",The North,No,,,
-Male,75,Married,Other/Sub Degree,English,White,Refused,The North,No,,,
-Female,80,Widowed,GCSE/O Level,British,White,"2,600 to 5,200",The North,No,,,
-Female,37,Married,Degree,British,White,"Under 2,600",The North,No,,,
-Female,33,Single,Degree,British,White,"10,400 to 15,600",The North,No,,,
-Male,41,Divorced,A Levels,English,White,"15,600 to 20,800",The North,No,,,
-Female,81,Widowed,No Qualification,British,White,"2,600 to 5,200",The North,No,,,
-Female,76,Widowed,No Qualification,English,White,"2,600 to 5,200",The North,Yes,6.0,6.0,Packets
-Female,58,Married,A Levels,British,White,"10,400 to 15,600",The North,No,,,
-Male,59,Married,Other/Sub Degree,English,White,"15,600 to 20,800",The North,Yes,25.0,25.0,Packets
-Female,40,Divorced,GCSE/O Level,English,White,"5,200 to 10,400",The North,Yes,4.0,4.0,Packets
-Female,54,Divorced,GCSE/O Level,British,White,"10,400 to 15,600",The North,No,,,
-Male,49,Married,Degree,Scottish,White,"Above 36,400",The North,No,,,
-Female,79,Widowed,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Male,28,Married,Other/Sub Degree,British,White,"20,800 to 28,600",The North,Yes,20.0,20.0,Packets
-Male,44,Married,Other/Sub Degree,English,White,"5,200 to 10,400",The North,No,,,
-Female,42,Single,GCSE/CSE,British,White,"10,400 to 15,600",The North,No,,,
-Female,36,Single,GCSE/CSE,British,White,"5,200 to 10,400",The North,No,,,
-Male,89,Widowed,No Qualification,British,White,"5,200 to 10,400",The North,No,,,
-Female,64,Married,No Qualification,British,White,"2,600 to 5,200",The North,No,,,
-Female,76,Married,No Qualification,English,White,"Under 2,600",The North,No,,,
-Female,76,Married,No Qualification,English,White,"Under 2,600",The North,No,,,
-Female,61,Married,No Qualification,English,White,"2,600 to 5,200",The North,No,,,
-Female,81,Widowed,GCSE/O Level,English,White,"5,200 to 10,400",The North,No,,,
-Female,20,Single,No Qualification,British,White,"2,600 to 5,200",The North,Yes,20.0,10.0,Packets
-Female,82,Widowed,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Female,23,Single,GCSE/O Level,British,White,"5,200 to 10,400",The North,Yes,20.0,10.0,Packets
-Male,41,Married,GCSE/O Level,English,White,"Above 36,400",The North,No,,,
-Female,31,Single,A Levels,British,White,"15,600 to 20,800",The North,No,,,
-Female,44,Married,A Levels,English,White,"20,800 to 28,600",The North,Yes,5.0,0.0,Packets
-Female,42,Married,Degree,Scottish,White,"10,400 to 15,600",The North,No,,,
-Male,67,Widowed,No Qualification,British,White,"5,200 to 10,400",The North,Yes,20.0,20.0,Packets
-Male,53,Married,Degree,English,White,"20,800 to 28,600",The North,No,,,
-Male,76,Married,No Qualification,English,Black,"10,400 to 15,600",The North,No,,,
-Male,40,Married,Other/Sub Degree,English,White,"2,600 to 5,200",The North,Yes,30.0,30.0,Hand-Rolled
-Male,44,Married,A Levels,British,White,"15,600 to 20,800",The North,No,,,
-Female,31,Separated,GCSE/O Level,British,White,"15,600 to 20,800",The North,Yes,20.0,20.0,Packets
-Male,43,Divorced,Other/Sub Degree,English,White,"2,600 to 5,200",The North,Yes,20.0,20.0,Hand-Rolled
-Female,36,Separated,A Levels,British,White,"10,400 to 15,600",The North,No,,,
-Female,34,Divorced,GCSE/O Level,British,White,"10,400 to 15,600",The North,No,,,
-Female,18,Single,GCSE/O Level,English,White,"2,600 to 5,200",The North,Yes,10.0,3.0,Packets
-Male,56,Married,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Female,73,Married,No Qualification,British,White,"Under 2,600",The North,No,,,
-Female,22,Single,GCSE/CSE,English,White,"2,600 to 5,200",The North,Yes,20.0,20.0,Packets
-Female,33,Single,GCSE/CSE,English,White,"15,600 to 20,800",The North,No,,,
-Male,27,Single,ONC/BTEC,British,White,"15,600 to 20,800",The North,Yes,10.0,0.0,Packets
-Female,33,Married,GCSE/O Level,British,White,"10,400 to 15,600",The North,No,,,
-Female,63,Married,No Qualification,English,White,"Under 2,600",The North,No,,,
-Female,64,Married,A Levels,British,White,"2,600 to 5,200",The North,No,,,
-Male,54,Married,Degree,British,White,"Above 36,400",The North,No,,,
-Female,34,Married,Degree,English,White,"Under 2,600",The North,No,,,
-Male,78,Married,No Qualification,English,White,"10,400 to 15,600",The North,No,,,
-Female,54,Married,No Qualification,British,White,"Under 2,600",The North,No,,,
-Male,56,Separated,GCSE/CSE,English,White,"28,600 to 36,400",The North,No,,,
-Male,78,Widowed,No Qualification,English,White,"2,600 to 5,200",The North,No,,,
-Female,37,Married,A Levels,British,White,"5,200 to 10,400",The North,No,,,
-Male,54,Married,Degree,British,White,"28,600 to 36,400",The North,No,,,
-Female,40,Married,GCSE/O Level,British,White,"20,800 to 28,600",The North,Yes,15.0,15.0,Hand-Rolled
-Female,64,Divorced,No Qualification,English,White,"2,600 to 5,200",The North,No,,,
-Female,42,Married,GCSE/O Level,Other,White,"5,200 to 10,400",The North,No,,,
-Female,34,Married,Higher/Sub Degree,British,White,"2,600 to 5,200",The North,No,,,
-Female,64,Married,No Qualification,English,White,"Under 2,600",The North,No,,,
-Female,50,Divorced,Other/Sub Degree,English,White,"15,600 to 20,800",The North,No,,,
-Female,31,Married,ONC/BTEC,English,White,"10,400 to 15,600",The North,No,,,
-Female,37,Married,Degree,English,White,"Above 36,400",The North,No,,,
-Male,81,Married,Degree,English,White,"10,400 to 15,600",The North,No,,,
-Male,57,Separated,No Qualification,Welsh,White,"5,200 to 10,400",The North,Yes,10.0,10.0,Hand-Rolled
-Female,59,Married,Higher/Sub Degree,English,White,"2,600 to 5,200",The North,No,,,
-Male,66,Married,Degree,English,White,"20,800 to 28,600",The North,No,,,
-Male,57,Married,No Qualification,English,White,"20,800 to 28,600",The North,No,,,
-Female,78,Widowed,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Male,49,Married,Degree,English,White,"20,800 to 28,600",The North,No,,,
-Female,62,Married,GCSE/O Level,English,White,"15,600 to 20,800",The North,No,,,
-Female,17,Single,GCSE/CSE,English,White,"2,600 to 5,200",The North,No,,,
-Female,36,Single,GCSE/O Level,English,White,"15,600 to 20,800",The North,No,,,
-Female,31,Single,No Qualification,English,White,Refused,The North,Yes,7.0,7.0,Packets
-Female,61,Married,Higher/Sub Degree,English,White,"15,600 to 20,800",The North,Yes,25.0,25.0,Packets
-Male,71,Widowed,No Qualification,English,White,"15,600 to 20,800",The North,Yes,40.0,40.0,Packets
-Female,75,Widowed,No Qualification,English,White,"5,200 to 10,400",The North,Yes,12.0,12.0,Packets
-Female,43,Married,Higher/Sub Degree,English,White,"Above 36,400",The North,No,,,
-Female,68,Widowed,No Qualification,English,White,Refused,The North,No,,,
-Female,65,Widowed,Degree,English,White,"15,600 to 20,800",The North,No,,,
-Female,85,Widowed,No Qualification,British,White,"5,200 to 10,400",The North,No,,,
-Female,35,Separated,GCSE/O Level,English,White,"5,200 to 10,400",The North,Yes,20.0,20.0,Both/Mainly Hand-Rolled
-Female,52,Married,Higher/Sub Degree,British,White,"15,600 to 20,800",The North,No,,,
-Female,28,Single,ONC/BTEC,English,White,"5,200 to 10,400",The North,Yes,15.0,15.0,Packets
-Male,52,Single,ONC/BTEC,English,White,"20,800 to 28,600",The North,No,,,
-Male,67,Widowed,No Qualification,Irish,White,"5,200 to 10,400",The North,Yes,25.0,25.0,Hand-Rolled
-Female,35,Divorced,No Qualification,English,White,"5,200 to 10,400",The North,Yes,30.0,30.0,Both/Mainly Hand-Rolled
-Female,68,Married,No Qualification,English,White,"2,600 to 5,200",The North,No,,,
-Female,34,Married,Higher/Sub Degree,Other,White,"10,400 to 15,600",The North,No,,,
-Female,57,Divorced,No Qualification,British,White,"2,600 to 5,200",The North,Yes,20.0,12.0,Hand-Rolled
-Male,37,Married,Higher/Sub Degree,English,White,"15,600 to 20,800",The North,Yes,8.0,8.0,Hand-Rolled
-Female,37,Divorced,No Qualification,English,White,"5,200 to 10,400",The North,Yes,15.0,15.0,Packets
-Male,43,Divorced,No Qualification,British,White,"10,400 to 15,600",The North,Yes,15.0,15.0,Packets
-Male,59,Widowed,GCSE/O Level,British,White,"2,600 to 5,200",The North,No,,,
-Female,28,Single,Degree,Scottish,White,"10,400 to 15,600",The North,No,,,
-Female,60,Married,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Male,16,Single,GCSE/O Level,British,White,"Under 2,600",The North,No,,,
-Male,37,Single,No Qualification,British,White,"2,600 to 5,200",The North,Yes,40.0,40.0,Hand-Rolled
-Male,34,Married,No Qualification,English,White,"15,600 to 20,800",The North,No,,,
-Female,73,Married,No Qualification,British,White,"5,200 to 10,400",The North,No,,,
-Male,24,Single,GCSE/O Level,English,White,"10,400 to 15,600",The North,No,,,
-Female,36,Single,Higher/Sub Degree,British,White,"15,600 to 20,800",The North,No,,,
-Male,39,Separated,GCSE/O Level,British,White,"10,400 to 15,600",The North,Yes,20.0,20.0,Both/Mainly Packets
-Female,74,Married,No Qualification,English,White,Refused,The North,No,,,
-Female,32,Married,GCSE/CSE,English,White,"Under 2,600",The North,Yes,15.0,15.0,Packets
-Female,30,Married,Degree,Other,White,"15,600 to 20,800",The North,No,,,
-Female,68,Married,GCSE/O Level,English,White,"15,600 to 20,800",The North,No,,,
-Female,49,Married,No Qualification,Refused,Refused,Refused,The North,No,,,
-Female,47,Married,Higher/Sub Degree,English,White,"2,600 to 5,200",The North,No,,,
-Male,30,Single,Higher/Sub Degree,British,White,"10,400 to 15,600",The North,Yes,30.0,30.0,Hand-Rolled
-Male,60,Divorced,No Qualification,English,White,"10,400 to 15,600",The North,Yes,15.0,8.0,Hand-Rolled
-Male,72,Single,Other/Sub Degree,English,White,"15,600 to 20,800",The North,No,,,
-Male,73,Married,Degree,English,White,"10,400 to 15,600",The North,No,,,
-Female,48,Widowed,GCSE/O Level,English,White,"10,400 to 15,600",The North,Yes,20.0,4.0,Packets
-Male,53,Divorced,Degree,English,White,"Above 36,400",The North,No,,,
-Male,17,Single,GCSE/O Level,English,White,Refused,The North,No,,,
-Female,58,Married,GCSE/O Level,English,White,"5,200 to 10,400",The North,No,,,
-Female,34,Married,ONC/BTEC,English,White,"10,400 to 15,600",The North,No,,,
-Male,44,Single,GCSE/O Level,British,White,"15,600 to 20,800",The North,No,,,
-Male,23,Single,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Male,49,Married,Degree,British,White,"20,800 to 28,600",The North,No,,,
-Male,30,Married,Degree,British,White,"15,600 to 20,800",The North,No,,,
-Female,61,Widowed,GCSE/O Level,English,White,"5,200 to 10,400",The North,No,,,
-Male,38,Separated,A Levels,British,White,"10,400 to 15,600",The North,Yes,9.0,9.0,Hand-Rolled
-Male,30,Single,Degree,British,White,"10,400 to 15,600",The North,No,,,
-Female,44,Married,A Levels,English,White,"15,600 to 20,800",The North,No,,,
-Male,91,Widowed,No Qualification,English,White,"Under 2,600",The North,No,,,
-Male,40,Divorced,GCSE/O Level,British,White,"Above 36,400",The North,No,,,
-Female,63,Married,Degree,British,White,"2,600 to 5,200",The North,No,,,
-Female,61,Divorced,No Qualification,British,White,"2,600 to 5,200",The North,Yes,30.0,30.0,Packets
-Female,40,Married,Higher/Sub Degree,English,White,"5,200 to 10,400",The North,No,,,
-Female,57,Married,No Qualification,British,White,"Under 2,600",The North,Yes,15.0,15.0,Both/Mainly Packets
-Female,41,Married,Degree,British,White,"28,600 to 36,400",The North,No,,,
-Female,32,Single,No Qualification,English,White,"2,600 to 5,200",The North,Yes,7.0,5.0,Hand-Rolled
-Male,76,Married,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Female,35,Divorced,GCSE/O Level,English,White,"10,400 to 15,600",The North,Yes,2.0,2.0,Packets
-Male,70,Married,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Female,75,Widowed,No Qualification,British,White,"5,200 to 10,400",The North,No,,,
-Female,74,Widowed,No Qualification,English,White,"2,600 to 5,200",The North,No,,,
-Male,49,Married,Degree,British,White,"Above 36,400",The North,No,,,
-Female,56,Married,GCSE/O Level,British,White,"Under 2,600",The North,No,,,
-Male,60,Married,Degree,English,White,"15,600 to 20,800",The North,No,,,
-Female,72,Widowed,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Male,17,Single,GCSE/O Level,English,White,"5,200 to 10,400",The North,No,,,
-Female,23,Single,Degree,English,White,"20,800 to 28,600",The North,No,,,
-Male,79,Married,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Female,65,Separated,No Qualification,British,White,"2,600 to 5,200",The North,No,,,
-Female,57,Married,Degree,English,White,"5,200 to 10,400",The North,No,,,
-Female,37,Married,GCSE/O Level,British,White,"15,600 to 20,800",The North,No,,,
-Male,79,Single,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Female,71,Single,No Qualification,English,White,"2,600 to 5,200",The North,No,,,
-Female,87,Widowed,No Qualification,Refused,Refused,Unknown,The North,No,,,
-Male,59,Married,Degree,English,White,"28,600 to 36,400",The North,Yes,40.0,40.0,Packets
-Male,21,Single,ONC/BTEC,English,White,"10,400 to 15,600",The North,Yes,20.0,8.0,Packets
-Female,66,Married,No Qualification,English,White,"Under 2,600",The North,No,,,
-Male,58,Married,Higher/Sub Degree,Welsh,White,"28,600 to 36,400",The North,No,,,
-Male,38,Married,GCSE/O Level,English,White,"Above 36,400",The North,Yes,30.0,15.0,Both/Mainly Packets
-Male,61,Widowed,Other/Sub Degree,Scottish,White,"15,600 to 20,800",The North,No,,,
-Male,37,Separated,Degree,English,White,"Above 36,400",The North,No,,,
-Female,38,Married,GCSE/O Level,English,White,"5,200 to 10,400",The North,No,,,
-Male,34,Married,Degree,English,White,"28,600 to 36,400",The North,No,,,
-Female,34,Married,Higher/Sub Degree,British,White,"10,400 to 15,600",The North,No,,,
-Female,20,Single,GCSE/O Level,English,White,"2,600 to 5,200",The North,No,,,
-Female,77,Widowed,No Qualification,English,White,"5,200 to 10,400",The North,Yes,20.0,20.0,Packets
-Female,31,Single,GCSE/O Level,English,White,"2,600 to 5,200",The North,Yes,20.0,15.0,Hand-Rolled
-Male,28,Single,No Qualification,English,White,"2,600 to 5,200",The North,Yes,2.0,2.0,Packets
-Female,36,Married,GCSE/CSE,British,White,"5,200 to 10,400",The North,No,,,
-Female,16,Single,GCSE/O Level,British,White,"5,200 to 10,400",The North,Yes,12.0,12.0,Packets
-Male,42,Married,GCSE/O Level,English,White,"5,200 to 10,400",The North,No,,,
-Female,47,Married,Higher/Sub Degree,English,White,"Under 2,600",The North,No,,,
-Male,42,Married,GCSE/CSE,British,White,"28,600 to 36,400",The North,No,,,
-Female,67,Widowed,No Qualification,British,White,"2,600 to 5,200",The North,No,,,
-Female,72,Single,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Male,40,Divorced,GCSE/O Level,English,White,"10,400 to 15,600",The North,Yes,25.0,30.0,Packets
-Female,34,Single,Degree,English,White,"Above 36,400",The North,No,,,
-Male,75,Married,No Qualification,English,White,"15,600 to 20,800",The North,No,,,
-Female,35,Married,GCSE/O Level,English,White,"5,200 to 10,400",The North,No,,,
-Female,40,Separated,No Qualification,British,White,"5,200 to 10,400",The North,Yes,10.0,10.0,Packets
-Female,46,Married,No Qualification,British,White,"15,600 to 20,800",The North,No,,,
-Male,23,Married,GCSE/O Level,English,White,"15,600 to 20,800",The North,No,,,
-Male,42,Married,GCSE/O Level,British,White,"10,400 to 15,600",The North,No,,,
-Male,63,Married,No Qualification,English,White,"2,600 to 5,200",The North,No,,,
-Female,33,Married,A Levels,British,White,"5,200 to 10,400",The North,No,,,
-Female,31,Married,GCSE/O Level,English,White,"15,600 to 20,800",The North,No,,,
-Male,54,Single,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Female,33,Single,ONC/BTEC,British,White,"15,600 to 20,800",The North,No,,,
-Female,24,Single,Degree,English,White,"15,600 to 20,800",The North,Yes,12.0,8.0,Hand-Rolled
-Female,80,Widowed,No Qualification,British,White,"5,200 to 10,400",The North,No,,,
-Female,77,Single,No Qualification,English,White,"2,600 to 5,200",The North,No,,,
-Male,51,Married,No Qualification,English,White,"10,400 to 15,600",The North,No,,,
-Male,16,Single,GCSE/O Level,English,White,Refused,The North,No,,,
-Male,27,Single,ONC/BTEC,English,White,Refused,The North,Yes,15.0,15.0,Packets
-Female,75,Single,No Qualification,English,White,"2,600 to 5,200",The North,No,,,
-Female,24,Single,Degree,Other,White,"5,200 to 10,400",The North,No,,,
-Male,78,Widowed,No Qualification,English,White,"5,200 to 10,400",The North,Yes,50.0,50.0,Both/Mainly Packets
-Female,20,Single,GCSE/O Level,British,White,"5,200 to 10,400",The North,No,,,
-Male,18,Single,GCSE/CSE,English,White,"5,200 to 10,400",The North,No,,,
-Female,25,Separated,ONC/BTEC,English,White,"10,400 to 15,600",The North,No,,,
-Female,55,Separated,No Qualification,English,Mixed,"5,200 to 10,400",The North,Yes,10.0,7.0,Both/Mainly Hand-Rolled
-Female,29,Married,Higher/Sub Degree,English,White,"10,400 to 15,600",The North,Yes,10.0,5.0,Packets
-Female,67,Divorced,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Female,79,Widowed,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Male,44,Single,No Qualification,British,White,"20,800 to 28,600",The North,No,,,
-Male,47,Married,Degree,British,White,"20,800 to 28,600",The North,No,,,
-Female,41,Divorced,GCSE/O Level,British,White,"10,400 to 15,600",The North,No,,,
-Female,31,Married,GCSE/O Level,British,White,"10,400 to 15,600",The North,Yes,5.0,0.0,Packets
-Male,32,Married,Other/Sub Degree,Scottish,White,"10,400 to 15,600",The North,No,,,
-Female,68,Widowed,No Qualification,English,White,Refused,The North,No,,,
-Female,84,Widowed,No Qualification,British,White,"Under 2,600",The North,No,,,
-Male,67,Married,No Qualification,English,White,"10,400 to 15,600",The North,No,,,
-Male,49,Married,No Qualification,British,White,"10,400 to 15,600",The North,No,,,
-Male,69,Married,No Qualification,English,White,"5,200 to 10,400",The North,Yes,6.0,8.0,Hand-Rolled
-Male,64,Married,Higher/Sub Degree,English,White,"28,600 to 36,400",The North,Yes,16.0,18.0,Packets
-Male,62,Married,No Qualification,English,White,"20,800 to 28,600",The North,No,,,
-Male,52,Divorced,No Qualification,British,White,"5,200 to 10,400",The North,No,,,
-Male,45,Married,ONC/BTEC,English,White,"28,600 to 36,400",The North,No,,,
-Female,28,Divorced,No Qualification,English,White,"10,400 to 15,600",The North,Yes,15.0,15.0,Packets
-Female,36,Single,GCSE/O Level,English,White,"10,400 to 15,600",The North,No,,,
-Female,73,Divorced,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Male,48,Widowed,No Qualification,English,White,"10,400 to 15,600",The North,No,,,
-Male,67,Widowed,No Qualification,British,White,"10,400 to 15,600",The North,Yes,10.0,10.0,Both/Mainly Hand-Rolled
-Female,38,Divorced,GCSE/O Level,British,White,"20,800 to 28,600",The North,No,,,
-Female,40,Divorced,GCSE/CSE,English,White,"10,400 to 15,600",The North,Yes,5.0,0.0,Packets
-Male,43,Single,Other/Sub Degree,British,White,"15,600 to 20,800",The North,No,,,
-Female,30,Single,Degree,English,White,"10,400 to 15,600",The North,No,,,
-Female,70,Widowed,No Qualification,British,White,"5,200 to 10,400",The North,No,,,
-Female,82,Widowed,No Qualification,British,White,"5,200 to 10,400",The North,No,,,
-Female,78,Widowed,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Male,21,Single,GCSE/O Level,English,White,"Under 2,600",The North,Yes,20.0,10.0,Packets
-Male,67,Widowed,No Qualification,English,White,"15,600 to 20,800",The North,No,,,
-Male,16,Single,GCSE/O Level,English,White,Refused,The North,No,,,
-Female,21,Single,GCSE/CSE,English,White,"5,200 to 10,400",The North,Yes,40.0,15.0,Packets
-Male,68,Separated,No Qualification,English,White,"5,200 to 10,400",The North,Yes,8.0,8.0,Packets
-Male,41,Married,No Qualification,British,White,"2,600 to 5,200",The North,Yes,20.0,10.0,Packets
-Male,33,Single,GCSE/O Level,British,White,"15,600 to 20,800",The North,No,,,
-Female,28,Married,No Qualification,British,White,"Under 2,600",The North,No,,,
-Female,75,Married,No Qualification,British,White,"2,600 to 5,200",The North,Yes,5.0,5.0,Packets
-Female,21,Single,Other/Sub Degree,English,White,"5,200 to 10,400",The North,Yes,5.0,10.0,Packets
-Female,69,Divorced,GCSE/CSE,English,White,"2,600 to 5,200",The North,No,,,
-Female,38,Married,GCSE/O Level,English,White,"5,200 to 10,400",The North,No,,,
-Female,33,Single,GCSE/O Level,English,White,"5,200 to 10,400",The North,Yes,10.0,4.0,Packets
-Female,47,Married,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Female,44,Single,GCSE/CSE,English,White,"2,600 to 5,200",The North,Yes,35.0,35.0,Hand-Rolled
-Female,74,Married,No Qualification,English,White,"Under 2,600",The North,No,,,
-Female,29,Married,GCSE/O Level,English,White,"Under 2,600",The North,No,,,
-Female,23,Married,A Levels,Other,Asian,"Under 2,600",The North,No,,,
-Female,72,Widowed,No Qualification,English,White,"2,600 to 5,200",The North,No,,,
-Female,31,Widowed,No Qualification,English,Asian,"5,200 to 10,400",The North,Yes,10.0,8.0,Packets
-Female,48,Married,No Qualification,British,Asian,"Under 2,600",The North,No,,,
-Female,29,Married,GCSE/O Level,English,White,"5,200 to 10,400",The North,No,,,
-Male,72,Married,Other/Sub Degree,English,Black,"5,200 to 10,400",The North,No,,,
-Female,52,Married,No Qualification,English,White,Unknown,The North,No,,,
-Female,68,Single,Degree,English,White,"5,200 to 10,400",The North,No,,,
-Male,48,Married,Other/Sub Degree,British,Chinese,"15,600 to 20,800",The North,Yes,40.0,40.0,Both/Mainly Packets
-Female,55,Married,GCSE/O Level,English,White,"10,400 to 15,600",The North,No,,,
-Female,23,Married,ONC/BTEC,British,White,"10,400 to 15,600",The North,Yes,7.0,7.0,Packets
-Female,66,Widowed,No Qualification,British,White,"2,600 to 5,200",The North,No,,,
-Female,33,Single,ONC/BTEC,British,White,"15,600 to 20,800",The North,No,,,
-Female,37,Married,GCSE/CSE,British,White,"Under 2,600",The North,Yes,10.0,10.0,Packets
-Male,42,Married,Degree,English,White,"20,800 to 28,600",The North,No,,,
-Female,46,Married,No Qualification,British,White,"10,400 to 15,600",The North,Yes,20.0,20.0,Hand-Rolled
-Male,57,Married,Other/Sub Degree,English,White,"28,600 to 36,400",The North,No,,,
-Female,36,Married,GCSE/O Level,English,White,"Under 2,600",The North,No,,,
-Female,51,Married,GCSE/O Level,Scottish,White,"20,800 to 28,600",The North,No,,,
-Female,18,Single,GCSE/O Level,British,White,Refused,The North,Yes,8.0,8.0,Hand-Rolled
-Female,69,Married,GCSE/O Level,English,White,"Under 2,600",The North,No,,,
-Male,30,Single,Degree,English,White,"15,600 to 20,800",The North,No,,,
-Male,51,Married,Higher/Sub Degree,English,White,"28,600 to 36,400",The North,No,,,
-Female,63,Married,No Qualification,British,Asian,"5,200 to 10,400",The North,No,,,
-Female,31,Single,Degree,English,White,"28,600 to 36,400",The North,No,,,
-Male,58,Single,No Qualification,British,White,"10,400 to 15,600",The North,No,,,
-Male,50,Married,GCSE/CSE,English,White,"20,800 to 28,600",The North,Yes,15.0,15.0,Packets
-Female,67,Divorced,No Qualification,English,White,"2,600 to 5,200",The North,Yes,20.0,20.0,Hand-Rolled
-Male,20,Single,A Levels,English,White,Refused,The North,No,,,
-Female,45,Single,Degree,British,White,"15,600 to 20,800",The North,No,,,
-Female,26,Married,GCSE/O Level,English,White,"5,200 to 10,400",The North,No,,,
-Male,30,Single,Degree,British,White,"28,600 to 36,400",The North,No,,,
-Female,43,Married,Degree,Other,White,Refused,The North,No,,,
-Male,73,Widowed,Other/Sub Degree,English,White,"2,600 to 5,200",The North,No,,,
-Female,52,Married,Higher/Sub Degree,British,White,"Under 2,600",The North,No,,,
-Male,18,Single,No Qualification,British,White,"10,400 to 15,600",The North,Yes,15.0,10.0,Both/Mainly Hand-Rolled
-Male,52,Married,No Qualification,English,White,"28,600 to 36,400",The North,No,,,
-Male,33,Divorced,GCSE/O Level,English,White,"15,600 to 20,800",The North,No,,,
-Female,56,Divorced,GCSE/O Level,English,White,"15,600 to 20,800",The North,No,,,
-Female,65,Married,No Qualification,English,White,"2,600 to 5,200",The North,No,,,
-Female,30,Single,No Qualification,British,White,"2,600 to 5,200",The North,No,,,
-Female,40,Married,No Qualification,British,White,"5,200 to 10,400",The North,Yes,10.0,1.0,Both/Mainly Hand-Rolled
-Female,26,Single,GCSE/CSE,English,White,"5,200 to 10,400",The North,Yes,20.0,20.0,Packets
-Male,74,Single,No Qualification,Refused,Refused,Refused,The North,No,,,
-Female,44,Married,Degree,Welsh,White,"28,600 to 36,400",The North,No,,,
-Female,80,Widowed,No Qualification,British,White,"2,600 to 5,200",The North,No,,,
-Female,67,Married,No Qualification,English,White,"Under 2,600",The North,No,,,
-Female,59,Married,No Qualification,British,White,Refused,The North,No,,,
-Female,28,Single,Higher/Sub Degree,English,White,"5,200 to 10,400",The North,Yes,15.0,10.0,Packets
-Female,31,Married,ONC/BTEC,British,White,"15,600 to 20,800",The North,No,,,
-Male,25,Single,GCSE/CSE,English,White,"15,600 to 20,800",The North,No,,,
-Female,64,Married,No Qualification,English,White,"Under 2,600",The North,No,,,
-Male,68,Married,Other/Sub Degree,English,White,"2,600 to 5,200",The North,No,,,
-Female,61,Widowed,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Female,50,Divorced,No Qualification,English,White,"10,400 to 15,600",The North,No,,,
-Male,43,Married,Higher/Sub Degree,British,Chinese,"Above 36,400",The North,No,,,
-Female,19,Single,No Qualification,Other,Asian,"Under 2,600",The North,No,,,
-Female,21,Married,GCSE/O Level,Other,Asian,"Under 2,600",The North,No,,,
-Male,51,Divorced,Degree,English,White,"2,600 to 5,200",The North,No,,,
-Male,65,Single,Other/Sub Degree,British,White,"5,200 to 10,400",The North,No,,,
-Female,50,Separated,No Qualification,Other,Asian,"2,600 to 5,200",The North,No,,,
-Female,38,Single,Other/Sub Degree,English,White,"2,600 to 5,200",The North,No,,,
-Female,78,Widowed,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Male,74,Widowed,No Qualification,Other,Asian,"2,600 to 5,200",The North,No,,,
-Female,22,Single,Degree,Other,Asian,"2,600 to 5,200",The North,No,,,
-Male,66,Married,No Qualification,British,White,"5,200 to 10,400",The North,No,,,
-Female,48,Single,No Qualification,English,White,"2,600 to 5,200",The North,No,,,
-Female,59,Married,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Female,69,Widowed,No Qualification,English,White,Unknown,The North,No,,,
-Female,76,Widowed,No Qualification,British,White,"5,200 to 10,400",The North,Yes,20.0,20.0,Packets
-Female,21,Single,ONC/BTEC,British,White,"2,600 to 5,200",The North,No,,,
-Female,47,Married,GCSE/O Level,British,White,"10,400 to 15,600",The North,No,,,
-Female,77,Widowed,No Qualification,British,White,"2,600 to 5,200",The North,No,,,
-Female,25,Single,GCSE/O Level,English,White,"10,400 to 15,600",The North,Yes,10.0,10.0,Packets
-Female,71,Widowed,No Qualification,English,White,"2,600 to 5,200",The North,No,,,
-Female,24,Single,GCSE/O Level,British,White,"2,600 to 5,200",The North,Yes,10.0,10.0,Packets
-Female,90,Widowed,No Qualification,English,White,"2,600 to 5,200",The North,No,,,
-Female,41,Divorced,GCSE/O Level,British,White,"5,200 to 10,400",The North,Yes,10.0,10.0,Packets
-Female,69,Married,No Qualification,English,White,"Under 2,600",The North,No,,,
-Female,35,Married,GCSE/O Level,British,White,"15,600 to 20,800",The North,No,,,
-Female,32,Single,No Qualification,English,White,"5,200 to 10,400",The North,Yes,12.0,7.0,Packets
-Female,48,Single,GCSE/O Level,British,White,"15,600 to 20,800",The North,No,,,
-Female,34,Married,No Qualification,English,White,"2,600 to 5,200",The North,No,,,
-Female,28,Married,GCSE/O Level,English,White,"5,200 to 10,400",The North,Yes,10.0,4.0,Packets
-Male,66,Married,No Qualification,English,White,Unknown,The North,No,,,
-Male,56,Married,GCSE/CSE,English,White,"20,800 to 28,600",The North,No,,,
-Male,51,Married,Other/Sub Degree,British,White,"10,400 to 15,600",The North,No,,,
-Female,63,Married,No Qualification,British,White,"2,600 to 5,200",The North,No,,,
-Male,44,Married,Degree,English,White,"28,600 to 36,400",The North,No,,,
-Female,45,Married,Higher/Sub Degree,British,White,"10,400 to 15,600",The North,No,,,
-Male,55,Married,No Qualification,British,White,"10,400 to 15,600",The North,Yes,20.0,20.0,Packets
-Female,55,Married,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Female,55,Married,A Levels,Scottish,White,Refused,The North,No,,,
-Female,57,Married,GCSE/O Level,British,White,"2,600 to 5,200",The North,No,,,
-Male,76,Married,A Levels,Scottish,White,"5,200 to 10,400",The North,No,,,
-Female,59,Married,No Qualification,British,White,"5,200 to 10,400",The North,No,,,
-Female,46,Married,No Qualification,British,White,"5,200 to 10,400",The North,No,,,
-Female,56,Married,No Qualification,British,White,"Under 2,600",The North,No,,,
-Male,67,Married,No Qualification,British,White,"5,200 to 10,400",The North,No,,,
-Female,73,Single,Other/Sub Degree,English,White,"10,400 to 15,600",The North,No,,,
-Female,82,Widowed,No Qualification,English,White,Refused,The North,No,,,
-Male,75,Married,ONC/BTEC,English,White,Refused,The North,No,,,
-Female,41,Married,Other/Sub Degree,English,White,"5,200 to 10,400",The North,No,,,
-Female,40,Married,A Levels,British,White,"15,600 to 20,800",The North,No,,,
-Male,37,Married,GCSE/O Level,English,White,"15,600 to 20,800",The North,No,,,
-Male,53,Married,ONC/BTEC,English,White,"20,800 to 28,600",The North,No,,,
-Female,35,Married,No Qualification,English,White,"5,200 to 10,400",The North,No,,,
-Female,39,Married,GCSE/O Level,English,White,"20,800 to 28,600",The North,No,,,
-Female,31,Separated,Other/Sub Degree,English,White,"2,600 to 5,200",The North,No,,,
-Male,38,Married,ONC/BTEC,British,White,"Above 36,400",The North,No,,,
-Female,34,Divorced,GCSE/O Level,British,White,"10,400 to 15,600",The North,Yes,25.0,10.0,Packets
-Female,36,Married,A Levels,English,White,"10,400 to 15,600",The North,Yes,20.0,20.0,Packets
-Male,31,Single,Degree,English,White,"20,800 to 28,600",The North,No,,,
-Female,31,Single,GCSE/CSE,British,White,"Under 2,600",The North,Yes,10.0,10.0,Hand-Rolled
-Male,35,Married,Degree,English,White,"20,800 to 28,600",The North,No,,,
-Male,42,Married,No Qualification,British,White,"20,800 to 28,600",The North,No,,,
-Male,30,Single,GCSE/CSE,English,White,"15,600 to 20,800",The North,No,,,
-Female,39,Divorced,GCSE/O Level,British,White,"10,400 to 15,600",The North,No,,,
-Female,47,Married,GCSE/O Level,Scottish,White,"10,400 to 15,600",The North,No,,,
-Male,38,Married,Higher/Sub Degree,English,White,"20,800 to 28,600",The North,No,,,
-Female,29,Divorced,A Levels,English,White,"15,600 to 20,800",The North,No,,,
-Male,75,Widowed,No Qualification,British,White,"5,200 to 10,400",The North,No,,,
-Female,47,Married,Higher/Sub Degree,British,White,Refused,The North,No,,,
-Male,55,Married,No Qualification,English,White,"15,600 to 20,800",The North,No,,,
-Male,42,Married,Higher/Sub Degree,Other,White,"Above 36,400",The North,No,,,
-Female,39,Married,Degree,English,White,"Above 36,400",The North,No,,,
-Male,64,Married,GCSE/O Level,English,White,"5,200 to 10,400",The North,No,,,
-Female,84,Widowed,No Qualification,English,White,"2,600 to 5,200",The North,No,,,
-Female,49,Married,Higher/Sub Degree,British,White,"5,200 to 10,400",The North,No,,,
-Male,24,Single,Higher/Sub Degree,English,White,"10,400 to 15,600",The North,No,,,
-Male,38,Single,GCSE/O Level,Scottish,White,"10,400 to 15,600",The North,Yes,30.0,30.0,Hand-Rolled
-Female,45,Married,Degree,Other,White,"15,600 to 20,800",The North,No,,,
-Female,47,Divorced,GCSE/O Level,British,White,"5,200 to 10,400",The North,No,,,
-Male,37,Divorced,Higher/Sub Degree,English,White,"28,600 to 36,400",The North,No,,,
-Male,59,Married,Higher/Sub Degree,English,White,"5,200 to 10,400",The North,No,,,
-Male,25,Single,Degree,Other,White,"28,600 to 36,400",The North,No,,,
-Male,56,Married,No Qualification,English,White,"15,600 to 20,800",The North,No,,,
-Female,71,Divorced,GCSE/CSE,British,White,"2,600 to 5,200",The North,No,,,
-Male,81,Widowed,Higher/Sub Degree,English,White,"10,400 to 15,600",The North,No,,,
-Male,55,Married,A Levels,English,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Male,42,Married,GCSE/CSE,English,White,"Above 36,400",Midlands & East Anglia,No,,,
-Male,73,Married,No Qualification,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,43,Married,A Levels,English,White,"28,600 to 36,400",Midlands & East Anglia,No,,,
-Female,57,Divorced,No Qualification,English,White,"10,400 to 15,600",Midlands & East Anglia,Yes,15.0,10.0,Packets
-Female,47,Married,Degree,English,White,"Above 36,400",Midlands & East Anglia,No,,,
-Female,29,Married,No Qualification,English,White,"10,400 to 15,600",Midlands & East Anglia,Yes,15.0,15.0,Packets
-Female,30,Widowed,GCSE/CSE,British,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,58,Married,No Qualification,English,White,"15,600 to 20,800",Midlands & East Anglia,Yes,30.0,25.0,Packets
-Female,58,Married,Degree,English,White,"28,600 to 36,400",Midlands & East Anglia,No,,,
-Male,70,Widowed,No Qualification,British,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,18,Single,A Levels,British,White,Refused,Midlands & East Anglia,No,,,
-Female,45,Single,No Qualification,British,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,37,Single,GCSE/O Level,English,White,"15,600 to 20,800",Midlands & East Anglia,Yes,15.0,10.0,Both/Mainly Packets
-Female,44,Separated,GCSE/O Level,British,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,46,Married,Degree,British,White,"Above 36,400",Midlands & East Anglia,No,,,
-Male,29,Married,Degree,British,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Male,50,Married,GCSE/O Level,English,White,"Above 36,400",Midlands & East Anglia,No,,,
-Female,51,Married,No Qualification,British,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,80,Widowed,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,85,Married,A Levels,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,49,Married,No Qualification,British,White,"Under 2,600",Midlands & East Anglia,No,,,
-Male,52,Married,No Qualification,British,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Female,58,Married,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,49,Married,Degree,British,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,39,Single,Degree,British,White,"28,600 to 36,400",Midlands & East Anglia,No,,,
-Female,53,Married,Degree,British,White,"5,200 to 10,400",Midlands & East Anglia,Yes,15.0,15.0,Both/Mainly Hand-Rolled
-Female,71,Married,GCSE/O Level,British,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,29,Single,Higher/Sub Degree,British,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Female,66,Married,No Qualification,British,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Male,75,Married,Other/Sub Degree,English,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Female,45,Divorced,Degree,British,White,"28,600 to 36,400",Midlands & East Anglia,No,,,
-Female,69,Married,No Qualification,English,White,"Under 2,600",Midlands & East Anglia,No,,,
-Female,49,Divorced,Degree,British,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Male,63,Married,No Qualification,English,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Male,61,Married,Other/Sub Degree,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,55,Married,Higher/Sub Degree,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,57,Married,Higher/Sub Degree,British,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,60,Married,Degree,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,58,Married,GCSE/O Level,English,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Female,76,Widowed,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,Yes,10.0,6.0,Packets
-Male,29,Single,Higher/Sub Degree,British,White,"20,800 to 28,600",Midlands & East Anglia,Yes,20.0,20.0,Packets
-Female,50,Married,GCSE/O Level,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,40,Married,GCSE/O Level,English,White,Refused,Midlands & East Anglia,No,,,
-Female,34,Married,Higher/Sub Degree,British,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,77,Widowed,No Qualification,Irish,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,47,Married,Degree,British,White,"Above 36,400",Midlands & East Anglia,No,,,
-Female,68,Married,No Qualification,British,White,"Under 2,600",Midlands & East Anglia,No,,,
-Male,31,Married,Higher/Sub Degree,English,White,"Above 36,400",Midlands & East Anglia,No,,,
-Male,41,Married,Degree,British,Asian,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Female,55,Married,Higher/Sub Degree,British,White,"5,200 to 10,400",Midlands & East Anglia,Yes,25.0,15.0,Packets
-Male,66,Married,A Levels,British,Asian,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,80,Married,No Qualification,British,White,"Under 2,600",Midlands & East Anglia,No,,,
-Female,45,Married,Degree,British,Asian,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,63,Married,Degree,English,White,"Above 36,400",Midlands & East Anglia,No,,,
-Male,62,Married,Degree,Scottish,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Female,49,Married,No Qualification,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,33,Married,ONC/BTEC,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,16,Single,GCSE/O Level,English,White,Refused,Midlands & East Anglia,No,,,
-Male,58,Married,Degree,British,White,"Above 36,400",Midlands & East Anglia,No,,,
-Female,33,Separated,ONC/BTEC,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,65,Married,Degree,British,White,"28,600 to 36,400",Midlands & East Anglia,No,,,
-Female,39,Single,Degree,Irish,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Female,57,Married,Higher/Sub Degree,British,White,"15,600 to 20,800",Midlands & East Anglia,Yes,6.0,4.0,Packets
-Female,31,Married,A Levels,British,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,79,Married,No Qualification,British,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,16,Single,GCSE/CSE,British,White,"Under 2,600",Midlands & East Anglia,Yes,2.0,2.0,Packets
-Female,54,Married,No Qualification,British,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,37,Divorced,GCSE/O Level,English,White,"10,400 to 15,600",Midlands & East Anglia,Yes,12.0,12.0,Packets
-Female,87,Widowed,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,32,Single,Degree,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,72,Married,No Qualification,British,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,20,Married,GCSE/O Level,English,Mixed,"10,400 to 15,600",Midlands & East Anglia,Yes,30.0,15.0,Hand-Rolled
-Male,77,Married,No Qualification,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,63,Married,No Qualification,English,White,"Under 2,600",Midlands & East Anglia,No,,,
-Male,56,Married,No Qualification,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,34,Single,GCSE/O Level,British,White,"15,600 to 20,800",Midlands & East Anglia,Yes,10.0,5.0,Hand-Rolled
-Female,24,Married,A Levels,English,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Female,42,Single,GCSE/O Level,British,White,"15,600 to 20,800",Midlands & East Anglia,Yes,10.0,3.0,Hand-Rolled
-Male,71,Married,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,48,Married,GCSE/O Level,English,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Male,38,Separated,ONC/BTEC,British,White,"28,600 to 36,400",Midlands & East Anglia,No,,,
-Female,55,Divorced,A Levels,English,White,"Under 2,600",Midlands & East Anglia,No,,,
-Male,50,Married,GCSE/CSE,English,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Male,30,Single,ONC/BTEC,English,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Female,72,Married,No Qualification,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,59,Married,Other/Sub Degree,English,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Female,68,Married,Higher/Sub Degree,English,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Male,48,Married,No Qualification,British,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Male,42,Widowed,GCSE/O Level,English,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Female,72,Divorced,Other/Sub Degree,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,54,Married,Other/Sub Degree,English,White,Refused,Midlands & East Anglia,No,,,
-Female,53,Divorced,GCSE/O Level,English,White,Unknown,Midlands & East Anglia,Yes,7.0,12.0,Packets
-Female,86,Married,No Qualification,English,White,"Under 2,600",Midlands & East Anglia,No,,,
-Female,57,Divorced,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,Yes,20.0,15.0,Both/Mainly Packets
-Female,30,Single,Degree,British,Chinese,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Male,35,Single,No Qualification,Other,White,Refused,Midlands & East Anglia,Yes,20.0,20.0,Hand-Rolled
-Female,25,Separated,GCSE/O Level,English,White,"10,400 to 15,600",Midlands & East Anglia,Yes,20.0,20.0,Packets
-Male,67,Married,Other/Sub Degree,British,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,33,Divorced,GCSE/O Level,English,White,"28,600 to 36,400",Midlands & East Anglia,No,,,
-Female,51,Married,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,Yes,15.0,3.0,Packets
-Female,54,Married,No Qualification,English,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Male,86,Married,No Qualification,English,White,Unknown,Midlands & East Anglia,No,,,
-Female,69,Married,No Qualification,British,White,"Under 2,600",Midlands & East Anglia,No,,,
-Female,23,Married,Higher/Sub Degree,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,33,Single,Degree,English,White,"15,600 to 20,800",Midlands & East Anglia,Yes,18.0,12.0,Packets
-Male,79,Married,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,21,Single,A Levels,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,37,Divorced,GCSE/O Level,British,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,40,Married,GCSE/O Level,English,White,"5,200 to 10,400",Midlands & East Anglia,Yes,20.0,20.0,Both/Mainly Packets
-Male,79,Single,No Qualification,English,White,Refused,Midlands & East Anglia,No,,,
-Female,34,Divorced,Higher/Sub Degree,English,White,"5,200 to 10,400",Midlands & East Anglia,Yes,10.0,10.0,Hand-Rolled
-Male,28,Single,GCSE/O Level,English,White,"10,400 to 15,600",Midlands & East Anglia,Yes,10.0,10.0,Hand-Rolled
-Female,44,Married,No Qualification,English,White,"2,600 to 5,200",Midlands & East Anglia,Yes,15.0,10.0,Packets
-Female,64,Married,GCSE/O Level,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,51,Separated,No Qualification,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,21,Single,ONC/BTEC,English,White,"10,400 to 15,600",Midlands & East Anglia,Yes,20.0,10.0,Packets
-Male,40,Single,GCSE/O Level,English,White,"2,600 to 5,200",Midlands & East Anglia,Yes,1.0,2.0,Hand-Rolled
-Female,42,Single,GCSE/O Level,British,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,47,Divorced,No Qualification,British,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,73,Widowed,Other/Sub Degree,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,18,Single,Higher/Sub Degree,British,White,"2,600 to 5,200",Midlands & East Anglia,Yes,15.0,15.0,Packets
-Male,24,Single,GCSE/O Level,British,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,69,Widowed,GCSE/O Level,British,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,61,Married,No Qualification,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,83,Single,No Qualification,Refused,Refused,Refused,Midlands & East Anglia,No,,,
-Female,80,Widowed,No Qualification,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,80,Married,No Qualification,English,White,"Under 2,600",Midlands & East Anglia,No,,,
-Female,26,Single,GCSE/O Level,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,34,Separated,GCSE/O Level,British,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,34,Married,Degree,British,White,"28,600 to 36,400",Midlands & East Anglia,No,,,
-Female,39,Single,GCSE/O Level,English,White,"15,600 to 20,800",Midlands & East Anglia,Yes,10.0,7.0,Hand-Rolled
-Male,33,Married,Other/Sub Degree,English,White,"5,200 to 10,400",Midlands & East Anglia,Yes,40.0,20.0,Packets
-Male,36,Married,GCSE/O Level,English,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Female,39,Divorced,No Qualification,British,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,35,Married,GCSE/O Level,English,White,"15,600 to 20,800",Midlands & East Anglia,Yes,10.0,7.0,Hand-Rolled
-Female,89,Widowed,No Qualification,British,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,23,Married,Degree,British,Asian,"5,200 to 10,400",Midlands & East Anglia,Yes,10.0,10.0,Packets
-Female,32,Divorced,GCSE/O Level,English,Asian,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,32,Married,No Qualification,Other,Asian,"5,200 to 10,400",Midlands & East Anglia,Yes,10.0,6.0,Packets
-Male,35,Married,GCSE/O Level,British,Asian,"2,600 to 5,200",Midlands & East Anglia,Yes,5.0,5.0,Packets
-Female,18,Single,A Levels,British,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,44,Divorced,Degree,English,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Female,75,Married,GCSE/CSE,Other,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,65,Married,GCSE/O Level,English,White,"Under 2,600",Midlands & East Anglia,No,,,
-Female,54,Married,ONC/BTEC,British,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Male,32,Married,GCSE/O Level,English,White,"28,600 to 36,400",Midlands & East Anglia,No,,,
-Female,57,Married,Higher/Sub Degree,British,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,50,Married,Other/Sub Degree,British,White,"10,400 to 15,600",Midlands & East Anglia,Yes,20.0,15.0,Packets
-Female,58,Married,Other/Sub Degree,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,30,Married,Degree,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,57,Married,Higher/Sub Degree,English,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Female,60,Married,No Qualification,English,White,"2,600 to 5,200",Midlands & East Anglia,Yes,15.0,15.0,Hand-Rolled
-Female,78,Separated,No Qualification,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,44,Married,No Qualification,English,White,"10,400 to 15,600",Midlands & East Anglia,Yes,20.0,20.0,Packets
-Male,32,Single,ONC/BTEC,British,White,"Above 36,400",Midlands & East Anglia,Yes,20.0,10.0,Packets
-Female,56,Separated,Higher/Sub Degree,English,White,"28,600 to 36,400",Midlands & East Anglia,No,,,
-Male,51,Married,Other/Sub Degree,Scottish,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Male,71,Married,Other/Sub Degree,British,Black,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,74,Widowed,Other/Sub Degree,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,56,Single,Degree,English,White,"15,600 to 20,800",Midlands & East Anglia,Yes,35.0,25.0,Packets
-Female,80,Widowed,No Qualification,English,White,"Under 2,600",Midlands & East Anglia,No,,,
-Female,51,Married,ONC/BTEC,English,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Female,53,Widowed,GCSE/O Level,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,80,Married,No Qualification,English,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Male,42,Married,Degree,British,White,"20,800 to 28,600",Midlands & East Anglia,Yes,0.0,2.0,Packets
-Female,82,Widowed,Other/Sub Degree,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,58,Married,A Levels,British,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,78,Married,Degree,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,61,Married,No Qualification,English,White,"20,800 to 28,600",Midlands & East Anglia,Yes,20.0,20.0,Packets
-Female,49,Single,No Qualification,British,White,"10,400 to 15,600",Midlands & East Anglia,Yes,20.0,15.0,Packets
-Female,51,Divorced,GCSE/CSE,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,48,Married,Other/Sub Degree,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,37,Married,GCSE/O Level,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,51,Married,GCSE/CSE,British,Asian,"10,400 to 15,600",Midlands & East Anglia,Yes,5.0,5.0,Packets
-Male,55,Married,GCSE/O Level,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,48,Married,No Qualification,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,82,Widowed,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,56,Married,No Qualification,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,71,Widowed,GCSE/O Level,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,30,Single,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,73,Widowed,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,35,Married,GCSE/O Level,English,White,"Under 2,600",Midlands & East Anglia,Yes,10.0,8.0,Packets
-Male,79,Married,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,75,Widowed,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,Yes,2.0,1.0,Packets
-Female,49,Married,ONC/BTEC,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,74,Married,No Qualification,English,White,"Under 2,600",Midlands & East Anglia,No,,,
-Male,33,Married,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,81,Widowed,No Qualification,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,63,Married,No Qualification,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,78,Widowed,No Qualification,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,68,Widowed,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,Yes,18.0,18.0,Packets
-Male,73,Married,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,34,Married,A Levels,English,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Female,36,Divorced,No Qualification,English,White,"10,400 to 15,600",Midlands & East Anglia,Yes,15.0,15.0,Hand-Rolled
-Female,16,Single,No Qualification,English,White,"Under 2,600",Midlands & East Anglia,No,,,
-Male,72,Widowed,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,66,Married,Higher/Sub Degree,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,41,Married,No Qualification,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,49,Married,Other/Sub Degree,English,White,"10,400 to 15,600",Midlands & East Anglia,Yes,20.0,15.0,Packets
-Male,42,Single,No Qualification,British,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Female,54,Separated,GCSE/CSE,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,70,Married,Higher/Sub Degree,British,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,29,Married,GCSE/CSE,British,Asian,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Male,43,Married,No Qualification,British,Asian,Refused,Midlands & East Anglia,Yes,5.0,10.0,Packets
-Female,19,Single,A Levels,Refused,Chinese,Refused,Midlands & East Anglia,No,,,
-Male,44,Married,Higher/Sub Degree,English,White,"28,600 to 36,400",Midlands & East Anglia,No,,,
-Male,27,Single,Degree,English,White,"20,800 to 28,600",Midlands & East Anglia,Yes,10.0,10.0,Packets
-Male,68,Married,No Qualification,English,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Female,80,Widowed,GCSE/O Level,English,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Male,85,Widowed,No Qualification,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,65,Married,No Qualification,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,88,Widowed,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,76,Widowed,No Qualification,English,White,Unknown,Midlands & East Anglia,No,,,
-Male,81,Married,GCSE/O Level,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,44,Separated,Degree,British,White,"5,200 to 10,400",Midlands & East Anglia,Yes,1.0,1.0,Packets
-Female,34,Married,Other/Sub Degree,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,55,Married,Higher/Sub Degree,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,77,Widowed,No Qualification,English,White,Refused,Midlands & East Anglia,No,,,
-Female,33,Married,Degree,Refused,Chinese,Refused,Midlands & East Anglia,No,,,
-Male,82,Single,No Qualification,British,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,77,Widowed,Higher/Sub Degree,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,19,Single,GCSE/O Level,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,70,Widowed,Other/Sub Degree,British,White,Refused,Midlands & East Anglia,No,,,
-Female,74,Widowed,No Qualification,British,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,57,Married,Other/Sub Degree,British,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,44,Divorced,GCSE/CSE,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,38,Single,Degree,British,White,Refused,Midlands & East Anglia,No,,,
-Male,43,Married,Degree,British,White,"Above 36,400",Midlands & East Anglia,No,,,
-Female,57,Separated,No Qualification,Welsh,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,54,Divorced,GCSE/O Level,Unknown,Chinese,"2,600 to 5,200",Midlands & East Anglia,Yes,20.0,20.0,Both/Mainly Packets
-Male,17,Single,GCSE/O Level,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,60,Married,No Qualification,British,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,50,Married,No Qualification,British,White,"Under 2,600",Midlands & East Anglia,No,,,
-Female,24,Single,GCSE/CSE,British,White,"10,400 to 15,600",Midlands & East Anglia,Yes,8.0,2.0,Packets
-Female,78,Widowed,No Qualification,British,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,60,Single,Other/Sub Degree,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,85,Widowed,Other/Sub Degree,English,White,"5,200 to 10,400",Midlands & East Anglia,Yes,6.0,6.0,Packets
-Male,59,Married,No Qualification,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,65,Married,Degree,English,White,"Above 36,400",Midlands & East Anglia,No,,,
-Female,55,Married,Other/Sub Degree,English,White,Refused,Midlands & East Anglia,No,,,
-Female,32,Married,ONC/BTEC,British,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Male,41,Single,Higher/Sub Degree,English,Mixed,"Under 2,600",Midlands & East Anglia,No,,,
-Female,31,Single,Higher/Sub Degree,British,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,45,Married,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,Yes,20.0,20.0,Both/Mainly Packets
-Female,30,Single,Degree,British,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Female,19,Single,A Levels,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,76,Widowed,GCSE/O Level,British,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,73,Married,No Qualification,British,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,19,Single,GCSE/O Level,English,White,Refused,Midlands & East Anglia,Yes,5.0,2.0,Packets
-Male,34,Married,Degree,English,White,"28,600 to 36,400",Midlands & East Anglia,No,,,
-Female,59,Married,No Qualification,British,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,22,Single,GCSE/CSE,English,White,"5,200 to 10,400",Midlands & East Anglia,Yes,40.0,10.0,Packets
-Female,27,Single,Degree,British,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,45,Married,A Levels,British,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Female,69,Widowed,Other/Sub Degree,Other,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,76,Married,Degree,Other,White,"Above 36,400",Midlands & East Anglia,No,,,
-Female,54,Married,Degree,British,White,"28,600 to 36,400",Midlands & East Anglia,No,,,
-Male,72,Married,Degree,British,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,89,Widowed,No Qualification,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,78,Widowed,No Qualification,Refused,Refused,Refused,Midlands & East Anglia,Yes,6.0,6.0,Packets
-Female,60,Married,GCSE/O Level,English,White,Refused,Midlands & East Anglia,No,,,
-Female,39,Married,A Levels,British,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,28,Single,GCSE/O Level,British,White,"5,200 to 10,400",Midlands & East Anglia,Yes,5.0,5.0,Both/Mainly Packets
-Male,23,Single,GCSE/CSE,English,White,"Under 2,600",Midlands & East Anglia,Yes,30.0,10.0,Both/Mainly Packets
-Male,26,Single,A Levels,British,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,52,Married,Other/Sub Degree,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,54,Married,Other/Sub Degree,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,20,Single,A Levels,British,White,"10,400 to 15,600",Midlands & East Anglia,Yes,10.0,10.0,Packets
-Female,59,Married,Other/Sub Degree,English,White,Refused,Midlands & East Anglia,No,,,
-Male,30,Married,ONC/BTEC,British,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Male,41,Married,GCSE/O Level,Scottish,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,26,Single,GCSE/O Level,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,27,Married,GCSE/O Level,Welsh,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,49,Married,No Qualification,Refused,Refused,Refused,Midlands & East Anglia,Yes,20.0,20.0,Packets
-Male,29,Single,ONC/BTEC,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,36,Divorced,GCSE/O Level,British,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,43,Divorced,GCSE/CSE,English,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Male,29,Single,ONC/BTEC,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,34,Separated,GCSE/O Level,English,White,"5,200 to 10,400",Midlands & East Anglia,Yes,15.0,10.0,Packets
-Female,81,Married,No Qualification,Refused,Chinese,Refused,Midlands & East Anglia,No,,,
-Female,30,Single,GCSE/O Level,British,White,"10,400 to 15,600",Midlands & East Anglia,Yes,5.0,10.0,Hand-Rolled
-Male,25,Single,Degree,English,White,"20,800 to 28,600",Midlands & East Anglia,Yes,20.0,20.0,Packets
-Male,59,Divorced,No Qualification,English,White,"10,400 to 15,600",Midlands & East Anglia,Yes,35.0,35.0,Packets
-Female,44,Widowed,No Qualification,British,Asian,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,64,Married,GCSE/O Level,Refused,Chinese,Refused,Midlands & East Anglia,No,,,
-Female,35,Married,A Levels,Refused,Chinese,Refused,Midlands & East Anglia,No,,,
-Male,40,Single,GCSE/O Level,English,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Female,65,Single,A Levels,Other,Black,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,17,Single,GCSE/O Level,British,Asian,Refused,Midlands & East Anglia,No,,,
-Male,72,Married,No Qualification,Other,Black,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Female,29,Married,Degree,British,Black,"Under 2,600",Midlands & East Anglia,No,,,
-Male,39,Married,No Qualification,British,Asian,"28,600 to 36,400",Midlands & East Anglia,No,,,
-Male,70,Widowed,No Qualification,British,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,73,Married,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,46,Single,GCSE/O Level,British,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Male,59,Married,ONC/BTEC,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,77,Widowed,No Qualification,British,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,42,Married,GCSE/O Level,British,White,"10,400 to 15,600",Midlands & East Anglia,Yes,20.0,20.0,Packets
-Male,41,Married,GCSE/O Level,British,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Male,49,Married,A Levels,British,White,"15,600 to 20,800",Midlands & East Anglia,Yes,20.0,15.0,Packets
-Male,67,Single,Degree,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,65,Single,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,40,Single,No Qualification,English,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Male,49,Separated,GCSE/O Level,English,White,"28,600 to 36,400",Midlands & East Anglia,No,,,
-Male,34,Single,GCSE/CSE,English,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Female,64,Married,No Qualification,English,White,"Under 2,600",Midlands & East Anglia,No,,,
-Female,44,Widowed,Degree,Other,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,72,Widowed,No Qualification,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,32,Married,A Levels,English,White,"20,800 to 28,600",Midlands & East Anglia,Yes,20.0,20.0,Packets
-Female,53,Married,No Qualification,British,White,Refused,Midlands & East Anglia,No,,,
-Female,50,Married,A Levels,British,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Female,63,Divorced,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,57,Married,Degree,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,40,Married,Degree,English,White,Refused,Midlands & East Anglia,No,,,
-Female,88,Divorced,Other/Sub Degree,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,57,Married,Degree,British,White,"Above 36,400",Midlands & East Anglia,No,,,
-Female,54,Married,Degree,British,White,"Above 36,400",Midlands & East Anglia,No,,,
-Female,58,Widowed,No Qualification,English,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Female,77,Divorced,Higher/Sub Degree,British,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,45,Divorced,GCSE/CSE,Other,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,31,Single,GCSE/O Level,British,White,"Under 2,600",Midlands & East Anglia,No,,,
-Male,31,Married,ONC/BTEC,English,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Male,80,Widowed,Degree,British,White,"28,600 to 36,400",Midlands & East Anglia,No,,,
-Male,67,Widowed,Degree,English,White,"20,800 to 28,600",Midlands & East Anglia,Yes,30.0,30.0,Packets
-Male,40,Single,GCSE/O Level,English,White,Refused,Midlands & East Anglia,Yes,20.0,10.0,Packets
-Female,70,Widowed,No Qualification,English,White,Refused,Midlands & East Anglia,No,,,
-Female,28,Single,GCSE/CSE,English,White,"Under 2,600",Midlands & East Anglia,Yes,30.0,10.0,Packets
-Male,30,Married,GCSE/CSE,Other,White,"Above 36,400",Midlands & East Anglia,No,,,
-Female,70,Divorced,Other/Sub Degree,English,White,Refused,Midlands & East Anglia,No,,,
-Male,57,Married,Degree,British,White,"Above 36,400",Midlands & East Anglia,No,,,
-Female,76,Widowed,GCSE/O Level,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,89,Widowed,No Qualification,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,81,Married,No Qualification,British,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,30,Single,ONC/BTEC,British,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Female,39,Single,Higher/Sub Degree,British,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,73,Married,No Qualification,English,White,"Under 2,600",Midlands & East Anglia,Yes,15.0,15.0,Packets
-Female,39,Married,A Levels,Other,White,Refused,Midlands & East Anglia,No,,,
-Female,76,Widowed,No Qualification,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,62,Single,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,66,Married,A Levels,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,53,Separated,Degree,British,White,"28,600 to 36,400",Midlands & East Anglia,No,,,
-Male,60,Married,Degree,British,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,36,Married,GCSE/O Level,British,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,40,Single,GCSE/CSE,British,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,57,Separated,No Qualification,English,White,"28,600 to 36,400",Midlands & East Anglia,Yes,15.0,15.0,Packets
-Male,16,Single,No Qualification,English,White,"Under 2,600",Midlands & East Anglia,Yes,25.0,20.0,Hand-Rolled
-Female,25,Single,GCSE/O Level,English,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Male,58,Single,No Qualification,English,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Female,49,Divorced,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,48,Single,Degree,English,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Female,76,Widowed,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,88,Widowed,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,35,Married,A Levels,British,Chinese,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,43,Married,Degree,British,Black,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Female,69,Widowed,No Qualification,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,64,Widowed,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,Yes,20.0,20.0,Packets
-Female,40,Divorced,GCSE/O Level,English,White,"10,400 to 15,600",Midlands & East Anglia,Yes,20.0,20.0,Packets
-Male,78,Married,Degree,British,White,Refused,Midlands & East Anglia,No,,,
-Male,58,Married,Degree,British,White,"Above 36,400",Midlands & East Anglia,No,,,
-Female,34,Married,Degree,British,White,"Under 2,600",Midlands & East Anglia,Yes,3.0,0.0,Packets
-Female,46,Married,Degree,British,Mixed,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Female,65,Married,No Qualification,English,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Female,34,Single,GCSE/O Level,British,White,"10,400 to 15,600",Midlands & East Anglia,Yes,20.0,10.0,Both/Mainly Packets
-Female,40,Separated,GCSE/CSE,British,Asian,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,79,Married,No Qualification,English,White,Refused,Midlands & East Anglia,No,,,
-Male,36,Married,No Qualification,British,Asian,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,39,Married,No Qualification,Other,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,24,Married,GCSE/CSE,English,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Female,28,Single,Other/Sub Degree,English,White,"2,600 to 5,200",Midlands & East Anglia,Yes,20.0,10.0,Both/Mainly Packets
-Female,26,Single,A Levels,English,Asian,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,45,Widowed,GCSE/CSE,English,White,"Under 2,600",Midlands & East Anglia,No,,,
-Female,24,Single,GCSE/O Level,English,White,"5,200 to 10,400",Midlands & East Anglia,Yes,18.0,10.0,Packets
-Male,77,Divorced,No Qualification,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,78,Married,Higher/Sub Degree,Welsh,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Male,25,Single,Degree,Scottish,White,Refused,Midlands & East Anglia,No,,,
-Male,36,Married,GCSE/CSE,British,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Male,17,Single,GCSE/O Level,English,White,Refused,Midlands & East Anglia,No,,,
-Male,55,Married,GCSE/O Level,British,White,"28,600 to 36,400",Midlands & East Anglia,No,,,
-Female,70,Married,Other/Sub Degree,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,43,Single,GCSE/O Level,English,White,"20,800 to 28,600",Midlands & East Anglia,Yes,15.0,8.0,Hand-Rolled
-Female,59,Divorced,No Qualification,British,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,32,Married,Other/Sub Degree,British,White,"Under 2,600",Midlands & East Anglia,No,,,
-Female,34,Married,Degree,British,White,"Under 2,600",Midlands & East Anglia,No,,,
-Male,58,Single,Degree,British,White,"28,600 to 36,400",Midlands & East Anglia,No,,,
-Male,44,Married,GCSE/O Level,British,White,"Above 36,400",Midlands & East Anglia,No,,,
-Male,64,Married,No Qualification,British,White,Refused,Midlands & East Anglia,Yes,0.0,0.0,Packets
-Female,34,Married,GCSE/CSE,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,32,Single,GCSE/CSE,English,White,Refused,Midlands & East Anglia,No,,,
-Male,38,Married,Degree,British,White,"Above 36,400",Midlands & East Anglia,No,,,
-Male,28,Single,GCSE/CSE,British,White,"28,600 to 36,400",Midlands & East Anglia,No,,,
-Male,42,Married,GCSE/CSE,English,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Female,34,Single,Degree,Other,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Male,23,Single,ONC/BTEC,English,White,"5,200 to 10,400",Midlands & East Anglia,Yes,15.0,5.0,Both/Mainly Packets
-Male,55,Separated,Degree,British,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Male,65,Married,Other/Sub Degree,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,62,Married,GCSE/O Level,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,66,Married,Higher/Sub Degree,English,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Female,73,Married,No Qualification,English,White,"Under 2,600",Midlands & East Anglia,No,,,
-Male,34,Single,Other/Sub Degree,English,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Female,33,Married,Degree,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,76,Widowed,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,55,Married,No Qualification,English,White,"2,600 to 5,200",Midlands & East Anglia,Yes,1.0,0.0,Packets
-Female,39,Married,Degree,English,White,"Under 2,600",Midlands & East Anglia,No,,,
-Female,26,Single,Degree,British,Asian,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Male,61,Married,GCSE/O Level,English,White,"15,600 to 20,800",Midlands & East Anglia,Yes,25.0,25.0,Packets
-Female,78,Married,Other/Sub Degree,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,42,Married,Degree,English,White,"Above 36,400",Midlands & East Anglia,No,,,
-Female,72,Married,No Qualification,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,62,Widowed,No Qualification,English,White,"20,800 to 28,600",Midlands & East Anglia,Yes,30.0,30.0,Both/Mainly Packets
-Female,46,Married,GCSE/O Level,British,White,"10,400 to 15,600",Midlands & East Anglia,Yes,20.0,20.0,Packets
-Male,24,Single,A Levels,British,White,"15,600 to 20,800",Midlands & East Anglia,No,,,
-Male,27,Single,Degree,British,Black,"10,400 to 15,600",Midlands & East Anglia,Yes,6.0,4.0,Both/Mainly Packets
-Female,64,Married,GCSE/O Level,British,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,86,Married,Degree,English,White,"28,600 to 36,400",Midlands & East Anglia,No,,,
-Female,46,Married,Degree,British,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Male,20,Single,ONC/BTEC,British,White,"10,400 to 15,600",Midlands & East Anglia,Yes,20.0,15.0,Both/Mainly Packets
-Male,35,Divorced,Higher/Sub Degree,Other,White,"28,600 to 36,400",Midlands & East Anglia,No,,,
-Female,46,Married,Higher/Sub Degree,Irish,White,"28,600 to 36,400",Midlands & East Anglia,Yes,30.0,30.0,Packets
-Female,39,Married,Higher/Sub Degree,Irish,White,"Under 2,600",Midlands & East Anglia,No,,,
-Female,28,Married,GCSE/O Level,English,White,"Above 36,400",Midlands & East Anglia,No,,,
-Female,33,Married,Higher/Sub Degree,Refused,Refused,Refused,Midlands & East Anglia,No,,,
-Female,39,Divorced,GCSE/O Level,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,45,Divorced,GCSE/CSE,British,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,37,Separated,Higher/Sub Degree,British,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Male,29,Married,ONC/BTEC,English,White,"15,600 to 20,800",Midlands & East Anglia,Yes,10.0,5.0,Packets
-Male,76,Widowed,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,26,Married,Degree,English,White,"20,800 to 28,600",Midlands & East Anglia,No,,,
-Female,47,Divorced,Degree,English,White,"20,800 to 28,600",Midlands & East Anglia,Yes,20.0,10.0,Packets
-Male,72,Divorced,No Qualification,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,72,Married,No Qualification,English,White,"Under 2,600",Midlands & East Anglia,No,,,
-Female,65,Widowed,No Qualification,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Female,69,Married,No Qualification,English,White,"Under 2,600",Midlands & East Anglia,No,,,
-Female,61,Married,A Levels,English,White,Unknown,Midlands & East Anglia,No,,,
-Female,63,Married,No Qualification,English,White,Refused,Midlands & East Anglia,No,,,
-Male,16,Single,No Qualification,British,White,Refused,Midlands & East Anglia,No,,,
-Male,61,Divorced,No Qualification,English,White,"2,600 to 5,200",Midlands & East Anglia,No,,,
-Male,18,Single,GCSE/O Level,English,White,"Under 2,600",Midlands & East Anglia,No,,,
-Male,76,Single,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,65,Married,GCSE/O Level,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Female,52,Married,No Qualification,English,White,"5,200 to 10,400",Midlands & East Anglia,No,,,
-Male,64,Widowed,No Qualification,English,White,"10,400 to 15,600",Midlands & East Anglia,No,,,
-Female,42,Married,GCSE/CSE,British,White,"Under 2,600",Midlands & East Anglia,No,,,
-Male,40,Married,Degree,Other,White,"Above 36,400",London,No,,,
-Female,28,Married,Other/Sub Degree,English,Mixed,"20,800 to 28,600",London,No,,,
-Male,40,Married,Degree,Welsh,White,"Above 36,400",London,No,,,
-Male,48,Married,GCSE/O Level,British,White,"28,600 to 36,400",London,No,,,
-Male,35,Separated,Degree,Other,White,"20,800 to 28,600",London,No,,,
-Female,30,Single,Degree,British,Black,"5,200 to 10,400",London,Yes,3.0,3.0,Packets
-Female,26,Single,Degree,British,White,"5,200 to 10,400",London,No,,,
-Male,81,Widowed,No Qualification,English,White,"5,200 to 10,400",London,No,,,
-Male,77,Divorced,A Levels,English,White,"10,400 to 15,600",London,No,,,
-Female,25,Single,Degree,British,White,"15,600 to 20,800",London,No,,,
-Female,66,Married,No Qualification,British,Asian,"Under 2,600",London,No,,,
-Male,18,Single,A Levels,British,White,"5,200 to 10,400",London,Yes,15.0,15.0,Packets
-Male,43,Married,GCSE/O Level,British,Black,"15,600 to 20,800",London,No,,,
-Male,49,Divorced,Other/Sub Degree,English,White,"5,200 to 10,400",London,Yes,20.0,6.0,Both/Mainly Packets
-Male,29,Married,Degree,English,White,"Above 36,400",London,No,,,
-Female,20,Single,No Qualification,English,White,"5,200 to 10,400",London,No,,,
-Female,75,Widowed,Other/Sub Degree,English,White,"Under 2,600",London,Yes,25.0,25.0,Packets
-Male,40,Married,No Qualification,Other,Chinese,"20,800 to 28,600",London,No,,,
-Female,64,Single,Higher/Sub Degree,Irish,White,"5,200 to 10,400",London,No,,,
-Male,66,Single,Degree,British,Chinese,Refused,London,No,,,
-Female,34,Single,Degree,British,White,"5,200 to 10,400",London,No,,,
-Female,76,Divorced,Degree,British,White,"28,600 to 36,400",London,No,,,
-Male,30,Married,Degree,English,White,"Above 36,400",London,Yes,1.0,0.0,Packets
-Male,55,Married,Degree,Other,Mixed,"Above 36,400",London,No,,,
-Female,49,Single,GCSE/O Level,English,White,"5,200 to 10,400",London,No,,,
-Male,38,Separated,Degree,English,Chinese,"10,400 to 15,600",London,No,,,
-Male,23,Single,A Levels,Other,Refused,Refused,London,Yes,10.0,10.0,Packets
-Male,77,Married,Degree,Welsh,White,"15,600 to 20,800",London,No,,,
-Male,46,Single,Degree,Other,White,"10,400 to 15,600",London,Yes,8.0,8.0,Hand-Rolled
-Female,26,Separated,Higher/Sub Degree,British,White,"Under 2,600",London,No,,,
-Female,26,Single,Degree,Other,White,"28,600 to 36,400",London,Yes,10.0,8.0,Packets
-Male,45,Single,Degree,Irish,White,"Above 36,400",London,Yes,7.0,2.0,Packets
-Female,45,Single,No Qualification,Other,Black,"2,600 to 5,200",London,No,,,
-Female,61,Married,No Qualification,English,White,Refused,London,No,,,
-Female,47,Married,GCSE/O Level,English,White,"5,200 to 10,400",London,No,,,
-Female,75,Widowed,Other/Sub Degree,English,White,"10,400 to 15,600",London,Yes,10.0,10.0,Packets
-Female,36,Divorced,No Qualification,English,White,"2,600 to 5,200",London,Yes,30.0,20.0,Packets
-Male,26,Single,Degree,British,Chinese,"2,600 to 5,200",London,No,,,
-Male,71,Widowed,No Qualification,English,White,"2,600 to 5,200",London,Yes,25.0,25.0,Packets
-Male,69,Married,No Qualification,British,White,"5,200 to 10,400",London,Yes,10.0,10.0,Hand-Rolled
-Male,38,Single,A Levels,Irish,White,"28,600 to 36,400",London,Yes,10.0,6.0,Hand-Rolled
-Female,21,Single,GCSE/O Level,British,Black,Refused,London,No,,,
-Male,26,Single,No Qualification,British,Chinese,"10,400 to 15,600",London,No,,,
-Female,83,Widowed,No Qualification,English,White,Unknown,London,No,,,
-Female,56,Divorced,No Qualification,Other,White,"5,200 to 10,400",London,No,,,
-Male,34,Single,Degree,English,White,"Above 36,400",London,Yes,20.0,20.0,Packets
-Female,66,Widowed,No Qualification,English,White,"5,200 to 10,400",London,No,,,
-Female,32,Single,GCSE/O Level,English,White,"5,200 to 10,400",London,Yes,3.0,3.0,Hand-Rolled
-Female,77,Single,No Qualification,English,White,"5,200 to 10,400",London,No,,,
-Female,70,Married,No Qualification,English,White,"Under 2,600",London,No,,,
-Male,24,Single,GCSE/O Level,English,White,"Under 2,600",London,Yes,10.0,10.0,Both/Mainly Packets
-Female,81,Widowed,No Qualification,British,White,"5,200 to 10,400",London,No,,,
-Male,37,Separated,Other/Sub Degree,Other,White,"2,600 to 5,200",London,No,,,
-Male,90,Married,No Qualification,English,White,"2,600 to 5,200",London,No,,,
-Female,29,Single,GCSE/O Level,Other,Black,"5,200 to 10,400",London,Yes,6.0,0.0,Hand-Rolled
-Female,24,Single,A Levels,Other,White,"5,200 to 10,400",London,No,,,
-Male,81,Widowed,No Qualification,English,White,"2,600 to 5,200",London,No,,,
-Female,38,Separated,ONC/BTEC,Other,Black,"5,200 to 10,400",London,No,,,
-Male,22,Single,GCSE/O Level,Irish,White,"20,800 to 28,600",London,Yes,0.0,7.0,Packets
-Male,31,Married,GCSE/CSE,British,Asian,"5,200 to 10,400",London,No,,,
-Female,77,Widowed,No Qualification,Other,White,"2,600 to 5,200",London,No,,,
-Female,61,Divorced,No Qualification,Other,White,"10,400 to 15,600",London,Yes,5.0,4.0,Packets
-Male,54,Single,No Qualification,English,White,"2,600 to 5,200",London,No,,,
-Female,72,Widowed,No Qualification,British,White,"5,200 to 10,400",London,No,,,
-Male,66,Single,No Qualification,British,White,"5,200 to 10,400",London,Yes,30.0,30.0,Packets
-Male,47,Single,ONC/BTEC,British,White,"20,800 to 28,600",London,No,,,
-Female,76,Widowed,No Qualification,English,White,"5,200 to 10,400",London,No,,,
-Male,41,Married,A Levels,English,White,"20,800 to 28,600",London,No,,,
-Female,80,Widowed,No Qualification,English,White,"5,200 to 10,400",London,No,,,
-Female,63,Married,No Qualification,English,White,"2,600 to 5,200",London,No,,,
-Male,29,Married,No Qualification,English,White,"15,600 to 20,800",London,No,,,
-Female,40,Single,GCSE/O Level,Other,White,"15,600 to 20,800",London,No,,,
-Female,54,Married,No Qualification,English,White,"10,400 to 15,600",London,No,,,
-Male,66,Married,Degree,British,Asian,"10,400 to 15,600",London,Yes,5.0,55.0,Packets
-Male,69,Married,Other/Sub Degree,Other,Asian,"10,400 to 15,600",London,No,,,
-Male,51,Married,No Qualification,British,White,"15,600 to 20,800",London,No,,,
-Male,70,Widowed,No Qualification,English,White,"5,200 to 10,400",London,No,,,
-Female,36,Married,GCSE/O Level,British,White,"2,600 to 5,200",London,Yes,10.0,1.0,Packets
-Female,73,Married,No Qualification,English,White,"Under 2,600",London,No,,,
-Male,72,Married,Other/Sub Degree,British,White,"2,600 to 5,200",London,No,,,
-Female,18,Single,GCSE/O Level,English,White,"Under 2,600",London,No,,,
-Female,57,Married,Other/Sub Degree,British,White,"Under 2,600",London,No,,,
-Male,21,Single,A Levels,British,White,Refused,London,No,,,
-Male,64,Married,Degree,British,Asian,"5,200 to 10,400",London,No,,,
-Female,39,Married,GCSE/CSE,English,White,"20,800 to 28,600",London,No,,,
-Female,37,Divorced,GCSE/CSE,British,White,"5,200 to 10,400",London,No,,,
-Female,36,Married,GCSE/CSE,British,White,"Under 2,600",London,Yes,5.0,1.0,Packets
-Female,37,Married,Degree,English,White,"10,400 to 15,600",London,No,,,
-Male,79,Married,No Qualification,English,White,"2,600 to 5,200",London,No,,,
-Female,49,Married,GCSE/CSE,English,White,"5,200 to 10,400",London,No,,,
-Female,30,Married,GCSE/O Level,English,White,"5,200 to 10,400",London,No,,,
-Female,27,Single,Degree,Welsh,White,"20,800 to 28,600",London,No,,,
-Male,16,Single,GCSE/O Level,English,White,"Under 2,600",London,No,,,
-Male,40,Married,GCSE/O Level,English,White,"Above 36,400",London,No,,,
-Female,45,Separated,GCSE/O Level,English,White,"15,600 to 20,800",London,No,,,
-Female,78,Widowed,Other/Sub Degree,English,White,"5,200 to 10,400",London,No,,,
-Female,59,Married,Other/Sub Degree,English,White,"5,200 to 10,400",London,No,,,
-Female,93,Widowed,Other/Sub Degree,English,White,"5,200 to 10,400",London,Yes,0.0,3.0,Packets
-Female,33,Married,A Levels,English,White,"10,400 to 15,600",London,No,,,
-Female,57,Divorced,No Qualification,British,White,"15,600 to 20,800",London,No,,,
-Female,53,Single,No Qualification,English,White,"2,600 to 5,200",London,Yes,4.0,4.0,Packets
-Female,54,Divorced,Higher/Sub Degree,English,White,"2,600 to 5,200",London,No,,,
-Female,33,Married,A Levels,Irish,White,"Above 36,400",London,No,,,
-Female,52,Single,Other/Sub Degree,Irish,White,"2,600 to 5,200",London,No,,,
-Male,31,Single,Higher/Sub Degree,English,White,"10,400 to 15,600",London,No,,,
-Female,54,Married,Degree,British,White,"28,600 to 36,400",London,No,,,
-Female,41,Married,Higher/Sub Degree,British,White,"20,800 to 28,600",London,Yes,10.0,10.0,Packets
-Female,23,Single,GCSE/O Level,English,White,"10,400 to 15,600",London,No,,,
-Female,81,Widowed,GCSE/CSE,British,White,"Under 2,600",London,No,,,
-Female,17,Single,A Levels,British,White,Refused,London,No,,,
-Male,73,Married,No Qualification,British,White,"Above 36,400",London,No,,,
-Male,62,Married,No Qualification,English,White,"20,800 to 28,600",London,No,,,
-Female,73,Married,Degree,Scottish,White,"10,400 to 15,600",London,Yes,16.0,16.0,Packets
-Male,22,Single,ONC/BTEC,English,White,"20,800 to 28,600",London,Yes,15.0,15.0,Both/Mainly Packets
-Female,18,Single,A Levels,English,White,"Under 2,600",London,No,,,
-Female,86,Widowed,GCSE/O Level,Irish,White,"10,400 to 15,600",London,Yes,7.0,7.0,Packets
-Female,54,Married,Degree,British,White,"20,800 to 28,600",London,Yes,20.0,20.0,Packets
-Female,55,Married,Degree,British,White,"20,800 to 28,600",London,No,,,
-Female,22,Single,Degree,English,Mixed,"20,800 to 28,600",London,Yes,15.0,15.0,Packets
-Female,30,Married,ONC/BTEC,British,Asian,"5,200 to 10,400",London,No,,,
-Female,25,Single,Degree,Other,Black,"2,600 to 5,200",London,No,,,
-Female,54,Divorced,Other/Sub Degree,British,Chinese,"Under 2,600",London,Yes,15.0,10.0,Packets
-Male,36,Single,GCSE/O Level,English,White,"15,600 to 20,800",London,No,,,
-Female,40,Married,Other/Sub Degree,Other,White,"Under 2,600",London,No,,,
-Female,51,Divorced,GCSE/O Level,British,White,"2,600 to 5,200",London,Yes,10.0,2.0,Packets
-Male,44,Married,Degree,British,White,"10,400 to 15,600",London,No,,,
-Female,26,Single,Degree,Other,White,"Under 2,600",London,Yes,5.0,0.0,Packets
-Male,73,Married,ONC/BTEC,English,White,"5,200 to 10,400",London,No,,,
-Female,36,Married,GCSE/O Level,English,White,"15,600 to 20,800",London,No,,,
-Male,69,Widowed,No Qualification,Irish,White,"5,200 to 10,400",London,No,,,
-Female,41,Married,Degree,British,White,"10,400 to 15,600",London,No,,,
-Male,40,Married,Degree,Other,White,"Above 36,400",London,No,,,
-Male,82,Married,Other/Sub Degree,Other,White,"2,600 to 5,200",London,Yes,3.0,3.0,Packets
-Male,27,Single,Degree,British,White,"15,600 to 20,800",London,No,,,
-Female,35,Single,Degree,English,White,"20,800 to 28,600",London,No,,,
-Male,35,Married,Degree,English,White,"Above 36,400",London,No,,,
-Male,33,Married,Other/Sub Degree,Other,White,"28,600 to 36,400",London,No,,,
-Female,41,Divorced,No Qualification,British,White,"2,600 to 5,200",London,Yes,20.0,20.0,Packets
-Female,26,Separated,Other/Sub Degree,British,Black,"5,200 to 10,400",London,No,,,
-Female,55,Married,No Qualification,English,White,Refused,London,No,,,
-Male,18,Single,GCSE/O Level,Other,Black,"Under 2,600",London,No,,,
-Female,68,Married,GCSE/O Level,British,Black,"5,200 to 10,400",London,No,,,
-Female,43,Single,GCSE/O Level,Other,Black,"10,400 to 15,600",London,No,,,
-Male,58,Married,No Qualification,English,Mixed,"20,800 to 28,600",London,No,,,
-Female,39,Married,GCSE/O Level,British,White,"5,200 to 10,400",London,No,,,
-Female,54,Separated,No Qualification,English,White,"2,600 to 5,200",London,No,,,
-Female,62,Married,No Qualification,British,White,"28,600 to 36,400",London,No,,,
-Female,40,Married,A Levels,English,White,"10,400 to 15,600",London,Yes,2.0,0.0,Both/Mainly Packets
-Female,59,Single,GCSE/O Level,English,White,"2,600 to 5,200",London,No,,,
-Female,57,Divorced,GCSE/O Level,British,White,"20,800 to 28,600",London,No,,,
-Female,42,Single,GCSE/O Level,Other,Black,"5,200 to 10,400",London,Yes,25.0,10.0,Packets
-Female,48,Separated,GCSE/O Level,Scottish,White,"20,800 to 28,600",London,Yes,25.0,25.0,Packets
-Female,45,Divorced,GCSE/O Level,British,Black,"20,800 to 28,600",London,Yes,15.0,10.0,Packets
-Female,24,Single,GCSE/CSE,British,White,"2,600 to 5,200",London,No,,,
-Female,66,Widowed,GCSE/CSE,British,Black,"10,400 to 15,600",London,No,,,
-Male,63,Single,Higher/Sub Degree,English,White,"10,400 to 15,600",London,Yes,20.0,20.0,Packets
-Male,28,Single,GCSE/O Level,Refused,Refused,Refused,London,No,,,
-Male,59,Divorced,No Qualification,English,White,"20,800 to 28,600",London,No,,,
-Male,50,Married,Higher/Sub Degree,Other,Black,"20,800 to 28,600",London,No,,,
-Female,41,Separated,GCSE/CSE,English,White,"10,400 to 15,600",London,No,,,
-Female,76,Single,No Qualification,British,White,"5,200 to 10,400",London,No,,,
-Male,24,Single,ONC/BTEC,British,White,"15,600 to 20,800",London,Yes,15.0,20.0,Packets
-Female,25,Single,Other/Sub Degree,British,Chinese,"20,800 to 28,600",London,Yes,5.0,2.0,Packets
-Female,33,Widowed,Degree,Other,Chinese,"5,200 to 10,400",London,No,,,
-Male,31,Single,ONC/BTEC,Other,Black,"28,600 to 36,400",London,No,,,
-Female,33,Single,Degree,British,Black,"5,200 to 10,400",London,Yes,10.0,8.0,Packets
-Female,37,Single,No Qualification,Other,White,"5,200 to 10,400",London,No,,,
-Male,81,Divorced,No Qualification,British,Black,"5,200 to 10,400",London,No,,,
-Female,36,Single,No Qualification,Other,Black,"20,800 to 28,600",London,No,,,
-Female,64,Married,No Qualification,British,White,"10,400 to 15,600",London,Yes,15.0,7.0,Hand-Rolled
-Male,51,Married,Other/Sub Degree,British,Asian,"5,200 to 10,400",London,No,,,
-Female,19,Single,A Levels,British,Mixed,"10,400 to 15,600",London,No,,,
-Female,18,Single,A Levels,British,Black,Refused,London,No,,,
-Male,31,Married,Degree,English,White,"Above 36,400",London,Yes,10.0,5.0,Both/Mainly Packets
-Female,24,Single,ONC/BTEC,English,White,"5,200 to 10,400",London,No,,,
-Male,18,Single,No Qualification,Other,Chinese,"Under 2,600",London,Yes,20.0,10.0,Packets
-Male,22,Single,Degree,Other,Asian,"2,600 to 5,200",London,No,,,
-Female,26,Single,Other/Sub Degree,Other,Black,"10,400 to 15,600",London,No,,,
-Female,33,Single,Degree,British,Black,"2,600 to 5,200",London,No,,,
-Male,17,Single,GCSE/CSE,British,Asian,"Under 2,600",London,Yes,20.0,10.0,Packets
-Male,37,Single,ONC/BTEC,British,Black,"10,400 to 15,600",London,Yes,20.0,10.0,Packets
-Male,60,Married,No Qualification,British,Chinese,"2,600 to 5,200",London,No,,,
-Male,80,Married,No Qualification,British,White,"2,600 to 5,200",South East,No,,,
-Male,68,Married,ONC/BTEC,British,White,"10,400 to 15,600",South East,Yes,7.0,7.0,Packets
-Female,65,Married,GCSE/O Level,English,White,"5,200 to 10,400",South East,No,,,
-Male,42,Married,No Qualification,British,Chinese,"10,400 to 15,600",South East,No,,,
-Female,48,Married,GCSE/O Level,British,White,"15,600 to 20,800",South East,No,,,
-Male,46,Married,A Levels,Scottish,White,"Above 36,400",South East,No,,,
-Male,44,Married,ONC/BTEC,English,White,"28,600 to 36,400",South East,No,,,
-Female,29,Married,A Levels,English,White,"10,400 to 15,600",South East,Yes,10.0,5.0,Packets
-Male,38,Married,Higher/Sub Degree,English,White,"15,600 to 20,800",South East,No,,,
-Female,52,Married,No Qualification,Irish,White,Refused,South East,No,,,
-Male,48,Divorced,Degree,English,White,"Above 36,400",South East,No,,,
-Male,65,Married,GCSE/CSE,British,White,"10,400 to 15,600",South East,No,,,
-Male,27,Single,Degree,English,White,"10,400 to 15,600",South East,No,,,
-Female,37,Married,GCSE/O Level,English,White,"2,600 to 5,200",South East,No,,,
-Female,42,Married,GCSE/O Level,British,White,"Under 2,600",South East,No,,,
-Male,53,Single,No Qualification,British,White,"5,200 to 10,400",South East,No,,,
-Female,66,Widowed,No Qualification,English,White,Refused,South East,Yes,20.0,20.0,Packets
-Female,20,Single,ONC/BTEC,English,White,"15,600 to 20,800",South East,No,,,
-Female,62,Married,No Qualification,Refused,Refused,Refused,South East,No,,,
-Male,18,Single,Other/Sub Degree,English,White,"2,600 to 5,200",South East,No,,,
-Male,69,Married,No Qualification,English,White,"5,200 to 10,400",South East,No,,,
-Male,63,Divorced,ONC/BTEC,English,White,"Above 36,400",South East,Yes,2.0,1.0,Packets
-Male,41,Married,Degree,British,Chinese,"28,600 to 36,400",South East,No,,,
-Male,74,Married,Higher/Sub Degree,English,White,"Above 36,400",South East,No,,,
-Female,20,Single,Other/Sub Degree,Other,White,"2,600 to 5,200",South East,No,,,
-Female,18,Single,GCSE/O Level,British,White,"2,600 to 5,200",South East,No,,,
-Male,28,Single,GCSE/O Level,English,Black,"5,200 to 10,400",South East,Yes,10.0,10.0,Hand-Rolled
-Male,49,Married,Degree,British,Chinese,"Above 36,400",South East,No,,,
-Male,64,Married,Higher/Sub Degree,British,White,"5,200 to 10,400",South East,No,,,
-Female,69,Widowed,GCSE/O Level,English,White,"5,200 to 10,400",South East,No,,,
-Female,33,Separated,No Qualification,British,White,"5,200 to 10,400",South East,No,,,
-Female,22,Single,GCSE/CSE,English,White,"10,400 to 15,600",South East,Yes,15.0,7.0,Packets
-Female,63,Married,No Qualification,English,White,"10,400 to 15,600",South East,No,,,
-Female,93,Widowed,No Qualification,Welsh,White,"2,600 to 5,200",South East,No,,,
-Female,65,Married,No Qualification,English,White,"Under 2,600",South East,No,,,
-Female,33,Married,GCSE/O Level,British,White,"10,400 to 15,600",South East,Yes,20.0,10.0,Packets
-Female,38,Married,No Qualification,British,White,"2,600 to 5,200",South East,No,,,
-Female,29,Single,GCSE/O Level,English,White,"Under 2,600",South East,Yes,10.0,10.0,Packets
-Male,59,Married,A Levels,British,White,"Above 36,400",South East,No,,,
-Male,37,Single,Degree,English,Mixed,"Above 36,400",South East,No,,,
-Male,51,Single,No Qualification,British,White,"20,800 to 28,600",South East,Yes,20.0,20.0,Packets
-Female,33,Married,GCSE/O Level,English,White,Refused,South East,No,,,
-Female,80,Single,Higher/Sub Degree,English,White,"2,600 to 5,200",South East,No,,,
-Male,49,Married,A Levels,English,White,"15,600 to 20,800",South East,No,,,
-Female,38,Married,Degree,English,White,"20,800 to 28,600",South East,No,,,
-Male,45,Single,GCSE/O Level,English,White,"Above 36,400",South East,Yes,10.0,20.0,Packets
-Female,58,Married,Higher/Sub Degree,Scottish,White,"20,800 to 28,600",South East,No,,,
-Female,63,Married,Degree,English,White,"2,600 to 5,200",South East,No,,,
-Female,69,Married,Other/Sub Degree,English,White,"2,600 to 5,200",South East,No,,,
-Male,86,Widowed,Other/Sub Degree,British,White,"20,800 to 28,600",South East,No,,,
-Female,66,Widowed,Higher/Sub Degree,English,White,"20,800 to 28,600",South East,No,,,
-Male,61,Married,No Qualification,British,Unknown,"5,200 to 10,400",South East,No,,,
-Female,62,Separated,Higher/Sub Degree,English,White,"15,600 to 20,800",South East,No,,,
-Female,89,Widowed,No Qualification,British,White,"2,600 to 5,200",South East,No,,,
-Female,72,Widowed,Degree,British,White,"20,800 to 28,600",South East,No,,,
-Male,64,Married,Other/Sub Degree,British,White,"Above 36,400",South East,No,,,
-Female,58,Married,A Levels,English,White,"28,600 to 36,400",South East,No,,,
-Female,80,Widowed,No Qualification,English,White,"10,400 to 15,600",South East,No,,,
-Female,61,Married,Higher/Sub Degree,English,White,"5,200 to 10,400",South East,No,,,
-Female,34,Married,Higher/Sub Degree,Irish,White,"5,200 to 10,400",South East,No,,,
-Female,36,Married,GCSE/O Level,British,White,"Under 2,600",South East,Yes,5.0,3.0,Packets
-Female,34,Divorced,GCSE/O Level,British,White,"10,400 to 15,600",South East,Yes,20.0,10.0,Packets
-Female,40,Single,A Levels,English,White,"Above 36,400",South East,No,,,
-Male,53,Single,No Qualification,English,White,"20,800 to 28,600",South East,No,,,
-Female,31,Single,Degree,English,White,"20,800 to 28,600",South East,No,,,
-Female,86,Separated,No Qualification,English,White,"2,600 to 5,200",South East,No,,,
-Female,46,Married,No Qualification,English,White,"5,200 to 10,400",South East,Yes,20.0,20.0,Packets
-Male,34,Single,GCSE/O Level,Scottish,White,"15,600 to 20,800",South East,No,,,
-Female,44,Single,Degree,English,White,"28,600 to 36,400",South East,No,,,
-Female,69,Widowed,A Levels,English,White,"5,200 to 10,400",South East,No,,,
-Female,56,Widowed,No Qualification,British,White,"15,600 to 20,800",South East,No,,,
-Male,79,Single,No Qualification,English,White,"2,600 to 5,200",South East,No,,,
-Female,41,Separated,GCSE/CSE,English,White,"5,200 to 10,400",South East,Yes,30.0,15.0,Packets
-Male,34,Divorced,ONC/BTEC,English,White,"Above 36,400",South East,Yes,30.0,15.0,Packets
-Male,47,Single,GCSE/CSE,English,White,"2,600 to 5,200",South East,Yes,40.0,40.0,Packets
-Male,46,Married,No Qualification,British,White,"15,600 to 20,800",South East,No,,,
-Female,30,Single,No Qualification,English,White,"10,400 to 15,600",South East,No,,,
-Female,19,Single,A Levels,English,White,"5,200 to 10,400",South East,No,,,
-Male,45,Married,Degree,Welsh,White,"28,600 to 36,400",South East,No,,,
-Female,24,Single,Higher/Sub Degree,Other,White,"10,400 to 15,600",South East,No,,,
-Male,42,Married,GCSE/O Level,English,White,"20,800 to 28,600",South East,No,,,
-Male,32,Married,Other/Sub Degree,English,White,"15,600 to 20,800",South East,No,,,
-Male,32,Single,GCSE/CSE,English,White,"10,400 to 15,600",South East,Yes,20.0,20.0,Packets
-Female,28,Single,A Levels,British,White,"Under 2,600",South East,Yes,5.0,5.0,Packets
-Female,78,Widowed,No Qualification,British,White,"10,400 to 15,600",South East,No,,,
-Male,42,Divorced,GCSE/O Level,English,White,"15,600 to 20,800",South East,Yes,20.0,20.0,Hand-Rolled
-Female,27,Married,Degree,English,White,"28,600 to 36,400",South East,No,,,
-Female,57,Married,Other/Sub Degree,English,White,Refused,South East,No,,,
-Female,24,Single,Degree,Refused,Refused,Refused,South East,No,,,
-Female,71,Married,GCSE/O Level,English,White,"2,600 to 5,200",South East,No,,,
-Male,51,Married,GCSE/O Level,English,White,"20,800 to 28,600",South East,No,,,
-Male,66,Widowed,No Qualification,English,White,"10,400 to 15,600",South East,No,,,
-Female,73,Separated,No Qualification,British,White,"2,600 to 5,200",South East,No,,,
-Male,18,Single,No Qualification,Refused,Refused,Refused,South East,Yes,10.0,10.0,Packets
-Male,50,Divorced,A Levels,English,White,"20,800 to 28,600",South East,Yes,10.0,5.0,Packets
-Female,79,Widowed,No Qualification,English,White,"5,200 to 10,400",South East,Yes,5.0,5.0,Packets
-Female,22,Single,GCSE/O Level,English,White,"Under 2,600",South East,Yes,10.0,5.0,Packets
-Male,45,Divorced,No Qualification,English,White,"10,400 to 15,600",South East,No,,,
-Female,53,Married,No Qualification,English,White,Refused,South East,No,,,
-Male,77,Married,No Qualification,English,White,"2,600 to 5,200",South East,Yes,15.0,15.0,Packets
-Female,64,Widowed,No Qualification,English,White,"5,200 to 10,400",South East,No,,,
-Male,39,Married,A Levels,British,White,"Under 2,600",South East,No,,,
-Male,54,Widowed,No Qualification,English,White,"5,200 to 10,400",South East,Yes,10.0,20.0,Both/Mainly Packets
-Male,17,Single,GCSE/CSE,English,White,Refused,South East,No,,,
-Male,78,Married,No Qualification,English,White,"5,200 to 10,400",South East,No,,,
-Male,50,Single,Degree,British,White,"20,800 to 28,600",South East,Yes,12.0,6.0,Packets
-Male,41,Married,Higher/Sub Degree,English,White,"Above 36,400",South East,No,,,
-Female,48,Divorced,No Qualification,Irish,Mixed,"2,600 to 5,200",South East,Yes,40.0,20.0,Packets
-Male,25,Married,GCSE/O Level,English,White,"15,600 to 20,800",South East,No,,,
-Male,18,Married,ONC/BTEC,British,White,"2,600 to 5,200",South East,No,,,
-Female,58,Married,GCSE/O Level,English,White,"10,400 to 15,600",South East,No,,,
-Female,79,Widowed,Higher/Sub Degree,English,White,"20,800 to 28,600",South East,No,,,
-Male,62,Married,No Qualification,English,White,"2,600 to 5,200",South East,Yes,10.0,10.0,Hand-Rolled
-Female,36,Married,GCSE/CSE,British,White,"Under 2,600",South East,No,,,
-Female,38,Divorced,No Qualification,English,White,"5,200 to 10,400",South East,No,,,
-Male,19,Single,Other/Sub Degree,British,White,"10,400 to 15,600",South East,No,,,
-Female,45,Separated,No Qualification,English,White,"5,200 to 10,400",South East,No,,,
-Male,54,Married,Degree,English,White,"Above 36,400",South East,No,,,
-Female,69,Widowed,GCSE/O Level,Scottish,White,"15,600 to 20,800",South East,No,,,
-Female,65,Widowed,Higher/Sub Degree,British,White,"10,400 to 15,600",South East,No,,,
-Male,31,Married,GCSE/CSE,English,White,"20,800 to 28,600",South East,No,,,
-Female,31,Married,Degree,British,White,"2,600 to 5,200",South East,No,,,
-Female,46,Single,No Qualification,English,White,"2,600 to 5,200",South East,Yes,20.0,20.0,Packets
-Male,37,Single,ONC/BTEC,Other,White,"20,800 to 28,600",South East,No,,,
-Female,72,Married,No Qualification,English,White,Unknown,South East,No,,,
-Male,59,Married,GCSE/O Level,English,White,"20,800 to 28,600",South East,No,,,
-Female,82,Widowed,GCSE/O Level,English,White,Refused,South East,No,,,
-Male,81,Widowed,Degree,English,White,"28,600 to 36,400",South East,No,,,
-Female,65,Widowed,GCSE/O Level,English,White,"2,600 to 5,200",South East,No,,,
-Female,48,Divorced,No Qualification,English,White,"15,600 to 20,800",South East,Yes,5.0,5.0,Hand-Rolled
-Male,49,Divorced,ONC/BTEC,British,White,"Above 36,400",South East,Yes,15.0,10.0,Packets
-Male,35,Single,GCSE/O Level,British,White,"2,600 to 5,200",South East,Yes,60.0,40.0,Packets
-Female,73,Married,No Qualification,English,White,"5,200 to 10,400",South East,No,,,
-Female,80,Divorced,No Qualification,British,White,"5,200 to 10,400",South East,No,,,
-Male,55,Married,ONC/BTEC,English,White,"28,600 to 36,400",South East,No,,,
-Male,78,Widowed,No Qualification,English,White,Refused,South East,No,,,
-Female,45,Single,GCSE/O Level,English,White,Refused,South East,Yes,20.0,20.0,Packets
-Male,39,Married,A Levels,English,White,"20,800 to 28,600",South East,Yes,20.0,10.0,Packets
-Male,75,Married,No Qualification,English,White,"5,200 to 10,400",South East,No,,,
-Male,65,Married,Other/Sub Degree,English,Chinese,"5,200 to 10,400",South East,No,,,
-Male,79,Divorced,No Qualification,Refused,Refused,"2,600 to 5,200",South East,No,,,
-Male,75,Married,GCSE/O Level,English,Chinese,"20,800 to 28,600",South East,No,,,
-Female,69,Married,No Qualification,British,White,"5,200 to 10,400",South East,No,,,
-Female,57,Single,No Qualification,British,White,"15,600 to 20,800",South East,No,,,
-Female,29,Single,Degree,English,White,"20,800 to 28,600",South East,No,,,
-Male,54,Married,No Qualification,English,White,"20,800 to 28,600",South East,Yes,30.0,30.0,Packets
-Male,29,Married,Degree,British,White,"20,800 to 28,600",South East,No,,,
-Male,42,Divorced,No Qualification,British,White,"10,400 to 15,600",South East,No,,,
-Male,28,Single,Degree,British,White,"15,600 to 20,800",South East,Yes,5.0,5.0,Hand-Rolled
-Female,65,Widowed,No Qualification,British,White,"15,600 to 20,800",South East,No,,,
-Male,37,Married,Degree,English,White,"Above 36,400",South East,Yes,0.0,4.0,Packets
-Female,35,Single,Degree,English,White,"5,200 to 10,400",South East,No,,,
-Female,33,Single,Degree,English,Mixed,"15,600 to 20,800",South East,Yes,10.0,6.0,Both/Mainly Packets
-Male,52,Married,Degree,Other,White,"5,200 to 10,400",South East,Yes,40.0,40.0,Packets
-Male,57,Single,Higher/Sub Degree,British,White,"20,800 to 28,600",South East,No,,,
-Female,31,Single,Degree,English,White,"15,600 to 20,800",South East,No,,,
-Male,73,Married,No Qualification,British,White,"5,200 to 10,400",South East,No,,,
-Male,43,Married,Degree,British,White,"Above 36,400",South East,No,,,
-Male,38,Single,Degree,English,Black,"28,600 to 36,400",South East,No,,,
-Female,43,Married,No Qualification,British,White,"Above 36,400",South East,Yes,12.0,12.0,Packets
-Female,55,Married,Higher/Sub Degree,English,White,"20,800 to 28,600",South East,No,,,
-Male,59,Married,Degree,British,White,"Above 36,400",South East,No,,,
-Female,33,Married,Degree,British,White,"20,800 to 28,600",South East,No,,,
-Female,41,Married,GCSE/O Level,British,White,"5,200 to 10,400",South East,Yes,20.0,20.0,Packets
-Female,46,Married,GCSE/CSE,British,White,"5,200 to 10,400",South East,Yes,20.0,20.0,Packets
-Male,57,Married,Degree,British,White,"Above 36,400",South East,Yes,35.0,35.0,Packets
-Male,40,Single,A Levels,English,White,"15,600 to 20,800",South East,No,,,
-Male,57,Married,GCSE/O Level,English,White,"5,200 to 10,400",South East,No,,,
-Female,39,Married,GCSE/O Level,British,White,"10,400 to 15,600",South East,Yes,10.0,7.0,Packets
-Male,67,Married,Other/Sub Degree,British,White,"5,200 to 10,400",South East,Yes,5.0,5.0,Hand-Rolled
-Female,46,Single,No Qualification,English,White,"2,600 to 5,200",South East,No,,,
-Male,39,Married,Higher/Sub Degree,British,White,"28,600 to 36,400",South East,No,,,
-Female,55,Married,GCSE/O Level,British,White,Refused,South East,No,,,
-Male,50,Married,Other/Sub Degree,English,White,"15,600 to 20,800",South East,Yes,15.0,10.0,Hand-Rolled
-Female,70,Widowed,ONC/BTEC,English,White,"5,200 to 10,400",South East,No,,,
-Female,44,Married,Higher/Sub Degree,Other,White,"Under 2,600",South East,No,,,
-Female,33,Married,ONC/BTEC,English,White,"Under 2,600",South East,No,,,
-Male,69,Divorced,Other/Sub Degree,English,White,"5,200 to 10,400",South East,No,,,
-Female,70,Married,No Qualification,English,White,"Under 2,600",South East,Yes,10.0,10.0,Packets
-Female,62,Widowed,No Qualification,British,White,Refused,South East,No,,,
-Female,31,Married,GCSE/O Level,British,White,"Under 2,600",South East,No,,,
-Male,70,Married,Other/Sub Degree,English,White,"10,400 to 15,600",South East,No,,,
-Male,32,Married,No Qualification,British,White,"28,600 to 36,400",South East,No,,,
-Female,75,Divorced,A Levels,British,White,Refused,South East,No,,,
-Male,47,Married,Higher/Sub Degree,British,White,"20,800 to 28,600",South East,No,,,
-Male,57,Married,No Qualification,English,White,"20,800 to 28,600",South East,No,,,
-Male,86,Widowed,No Qualification,English,White,"5,200 to 10,400",South East,No,,,
-Female,32,Single,GCSE/O Level,English,White,"2,600 to 5,200",South East,No,,,
-Male,46,Single,GCSE/O Level,British,White,"10,400 to 15,600",South East,Yes,15.0,15.0,Packets
-Male,51,Single,No Qualification,English,White,"Under 2,600",South East,Yes,10.0,10.0,Packets
-Female,69,Married,GCSE/O Level,English,White,"10,400 to 15,600",South East,No,,,
-Female,73,Married,GCSE/O Level,British,White,"2,600 to 5,200",South East,No,,,
-Female,32,Single,Higher/Sub Degree,English,White,"15,600 to 20,800",South East,Yes,20.0,20.0,Packets
-Female,75,Married,GCSE/O Level,English,White,Unknown,South East,No,,,
-Female,54,Widowed,GCSE/CSE,English,White,"20,800 to 28,600",South East,Yes,25.0,25.0,Packets
-Female,51,Married,Higher/Sub Degree,English,White,"10,400 to 15,600",South East,No,,,
-Female,35,Married,Degree,British,White,"10,400 to 15,600",South East,No,,,
-Male,38,Married,Degree,English,White,"Above 36,400",South East,No,,,
-Female,72,Married,GCSE/O Level,English,White,"5,200 to 10,400",South East,No,,,
-Female,66,Married,A Levels,English,White,Refused,South East,No,,,
-Female,59,Married,GCSE/CSE,English,White,"2,600 to 5,200",South East,No,,,
-Male,70,Married,Degree,British,White,"20,800 to 28,600",South East,No,,,
-Male,44,Single,GCSE/CSE,English,White,Refused,South East,No,,,
-Female,67,Married,GCSE/CSE,English,White,"5,200 to 10,400",South East,No,,,
-Male,35,Married,GCSE/O Level,English,White,"20,800 to 28,600",South East,No,,,
-Female,33,Married,GCSE/O Level,English,White,"5,200 to 10,400",South East,No,,,
-Male,17,Single,GCSE/CSE,English,White,"2,600 to 5,200",South East,No,,,
-Female,45,Married,GCSE/O Level,British,White,"Under 2,600",South East,No,,,
-Female,57,Married,GCSE/O Level,British,White,"5,200 to 10,400",South East,No,,,
-Female,18,Single,A Levels,British,White,"Under 2,600",South East,No,,,
-Male,68,Separated,A Levels,English,White,"15,600 to 20,800",South East,No,,,
-Female,27,Single,Higher/Sub Degree,Other,White,"15,600 to 20,800",South East,Yes,12.0,15.0,Hand-Rolled
-Female,40,Married,GCSE/CSE,British,White,"Under 2,600",South East,Yes,30.0,18.0,Packets
-Male,77,Single,GCSE/CSE,English,White,"5,200 to 10,400",South East,No,,,
-Female,77,Married,GCSE/O Level,British,White,"2,600 to 5,200",South East,No,,,
-Male,75,Married,ONC/BTEC,English,White,"Above 36,400",South East,No,,,
-Male,39,Married,ONC/BTEC,British,White,"15,600 to 20,800",South East,Yes,15.0,15.0,Packets
-Female,21,Single,GCSE/O Level,British,White,"2,600 to 5,200",South East,No,,,
-Female,59,Divorced,Degree,British,White,"5,200 to 10,400",South East,No,,,
-Male,27,Single,GCSE/CSE,British,White,"20,800 to 28,600",South East,No,,,
-Female,67,Married,No Qualification,English,White,"2,600 to 5,200",South East,No,,,
-Female,50,Single,Higher/Sub Degree,English,White,"20,800 to 28,600",South East,No,,,
-Female,64,Divorced,GCSE/O Level,English,White,"5,200 to 10,400",South East,No,,,
-Female,79,Widowed,No Qualification,English,Chinese,"5,200 to 10,400",South East,No,,,
-Male,31,Single,GCSE/O Level,English,White,"10,400 to 15,600",South East,Yes,10.0,20.0,Both/Mainly Packets
-Female,19,Single,A Levels,British,Chinese,"10,400 to 15,600",South East,No,,,
-Male,42,Married,Degree,British,White,"28,600 to 36,400",South East,No,,,
-Male,36,Married,GCSE/CSE,English,White,"5,200 to 10,400",South East,No,,,
-Male,36,Married,GCSE/CSE,English,White,"20,800 to 28,600",South East,No,,,
-Female,64,Widowed,GCSE/O Level,British,White,"2,600 to 5,200",South East,No,,,
-Female,53,Single,No Qualification,English,White,"5,200 to 10,400",South East,No,,,
-Female,39,Married,GCSE/CSE,Irish,White,"15,600 to 20,800",South East,Yes,20.0,20.0,Packets
-Female,39,Married,Degree,English,White,"28,600 to 36,400",South East,No,,,
-Female,28,Married,Degree,British,White,"5,200 to 10,400",South East,Yes,7.0,7.0,Packets
-Male,81,Married,No Qualification,English,White,"5,200 to 10,400",South East,No,,,
-Female,79,Married,No Qualification,English,White,"Under 2,600",South East,No,,,
-Female,72,Married,No Qualification,English,White,"5,200 to 10,400",South East,No,,,
-Female,18,Single,GCSE/O Level,English,White,"2,600 to 5,200",South East,Yes,25.0,25.0,Both/Mainly Hand-Rolled
-Female,76,Married,No Qualification,British,White,"2,600 to 5,200",South East,No,,,
-Female,68,Widowed,No Qualification,British,White,"5,200 to 10,400",South East,Yes,8.0,8.0,Packets
-Female,28,Single,GCSE/CSE,English,White,"10,400 to 15,600",South East,Yes,20.0,15.0,Packets
-Female,42,Divorced,GCSE/O Level,English,White,"2,600 to 5,200",South East,No,,,
-Female,26,Single,GCSE/O Level,British,White,"5,200 to 10,400",South East,Yes,15.0,15.0,Both/Mainly Packets
-Female,19,Single,No Qualification,Welsh,White,"2,600 to 5,200",South East,Yes,5.0,5.0,Packets
-Female,41,Divorced,Other/Sub Degree,Welsh,White,"5,200 to 10,400",South East,Yes,30.0,30.0,Packets
-Female,82,Widowed,No Qualification,English,White,"5,200 to 10,400",South East,No,,,
-Male,36,Married,Degree,British,Asian,"15,600 to 20,800",South East,No,,,
-Male,47,Single,No Qualification,English,White,"5,200 to 10,400",South East,Yes,25.0,25.0,Both/Mainly Hand-Rolled
-Male,77,Widowed,Other/Sub Degree,Scottish,White,"5,200 to 10,400",South East,No,,,
-Female,66,Divorced,No Qualification,English,White,"Under 2,600",South East,No,,,
-Male,50,Divorced,Other/Sub Degree,English,White,"5,200 to 10,400",South East,Yes,15.0,15.0,Hand-Rolled
-Female,55,Divorced,No Qualification,English,White,"5,200 to 10,400",South East,No,,,
-Female,36,Married,A Levels,English,White,"Under 2,600",South West,Yes,10.0,0.0,Packets
-Male,79,Married,No Qualification,Irish,White,Unknown,South West,No,,,
-Female,67,Married,No Qualification,English,White,"2,600 to 5,200",South West,No,,,
-Female,42,Single,A Levels,British,White,Refused,South West,No,,,
-Female,76,Married,No Qualification,British,White,"Under 2,600",South West,No,,,
-Female,44,Married,Other/Sub Degree,British,White,"15,600 to 20,800",South West,No,,,
-Female,75,Married,No Qualification,English,White,"2,600 to 5,200",South West,No,,,
-Female,24,Married,Other/Sub Degree,British,White,"10,400 to 15,600",South West,No,,,
-Male,78,Married,Other/Sub Degree,English,White,"5,200 to 10,400",South West,No,,,
-Female,38,Married,ONC/BTEC,British,White,"5,200 to 10,400",South West,No,,,
-Male,22,Single,Other/Sub Degree,English,White,"5,200 to 10,400",South West,No,,,
-Female,62,Married,Other/Sub Degree,English,White,"2,600 to 5,200",South West,No,,,
-Female,40,Divorced,No Qualification,British,White,"5,200 to 10,400",South West,Yes,10.0,5.0,Packets
-Male,35,Married,Degree,British,White,"20,800 to 28,600",South West,No,,,
-Female,45,Married,ONC/BTEC,British,White,"5,200 to 10,400",South West,No,,,
-Female,66,Married,No Qualification,British,White,"2,600 to 5,200",South West,No,,,
-Female,53,Divorced,Higher/Sub Degree,English,White,"5,200 to 10,400",South West,No,,,
-Female,43,Divorced,GCSE/O Level,English,White,"10,400 to 15,600",South West,Yes,5.0,0.0,Packets
-Female,72,Widowed,No Qualification,British,White,"2,600 to 5,200",South West,Yes,3.0,3.0,Packets
-Female,72,Widowed,No Qualification,English,White,"5,200 to 10,400",South West,No,,,
-Female,67,Married,Higher/Sub Degree,English,White,"10,400 to 15,600",South West,No,,,
-Female,37,Divorced,GCSE/O Level,British,White,"5,200 to 10,400",South West,No,,,
-Male,38,Married,ONC/BTEC,English,White,"10,400 to 15,600",South West,No,,,
-Female,39,Married,GCSE/O Level,English,White,"10,400 to 15,600",South West,Yes,10.0,6.0,Both/Mainly Packets
-Male,64,Divorced,No Qualification,English,White,"5,200 to 10,400",South West,No,,,
-Male,72,Married,No Qualification,English,White,"2,600 to 5,200",South West,No,,,
-Male,54,Married,GCSE/O Level,English,White,"10,400 to 15,600",South West,Yes,20.0,20.0,Packets
-Female,59,Widowed,No Qualification,English,White,"5,200 to 10,400",South West,No,,,
-Female,50,Married,ONC/BTEC,English,White,"10,400 to 15,600",South West,No,,,
-Male,33,Married,GCSE/O Level,English,White,"Above 36,400",South West,No,,,
-Female,45,Married,Higher/Sub Degree,English,White,"20,800 to 28,600",South West,No,,,
-Female,73,Widowed,No Qualification,English,White,"5,200 to 10,400",South West,No,,,
-Male,68,Married,Degree,British,White,"15,600 to 20,800",South West,No,,,
-Female,33,Married,GCSE/O Level,British,White,"10,400 to 15,600",South West,No,,,
-Female,21,Single,ONC/BTEC,British,White,"2,600 to 5,200",South West,No,,,
-Female,32,Married,Degree,English,White,"20,800 to 28,600",South West,No,,,
-Male,37,Married,Other/Sub Degree,British,White,"15,600 to 20,800",South West,No,,,
-Female,48,Single,Degree,English,White,Refused,South West,Yes,10.0,10.0,Hand-Rolled
-Male,25,Single,No Qualification,British,White,"15,600 to 20,800",South West,Yes,40.0,20.0,Packets
-Male,61,Married,No Qualification,English,White,"5,200 to 10,400",South West,Yes,30.0,10.0,Hand-Rolled
-Female,86,Widowed,No Qualification,English,White,"2,600 to 5,200",South West,No,,,
-Female,45,Married,No Qualification,English,White,"5,200 to 10,400",South West,No,,,
-Female,60,Divorced,GCSE/O Level,English,White,"10,400 to 15,600",South West,No,,,
-Female,40,Single,Degree,British,White,"15,600 to 20,800",South West,No,,,
-Male,55,Married,No Qualification,British,White,Refused,South West,No,,,
-Male,53,Separated,GCSE/O Level,Welsh,White,"10,400 to 15,600",South West,No,,,
-Male,41,Married,GCSE/O Level,English,White,"2,600 to 5,200",South West,No,,,
-Female,17,Single,GCSE/O Level,English,White,"Under 2,600",South West,Yes,20.0,10.0,Both/Mainly Packets
-Female,42,Divorced,Higher/Sub Degree,English,White,"15,600 to 20,800",South West,No,,,
-Male,44,Married,GCSE/O Level,English,White,"20,800 to 28,600",South West,No,,,
-Female,43,Married,A Levels,English,White,"10,400 to 15,600",South West,No,,,
-Female,39,Married,Other/Sub Degree,Other,White,"2,600 to 5,200",South West,Yes,20.0,20.0,Packets
-Male,67,Married,No Qualification,English,White,"10,400 to 15,600",South West,No,,,
-Female,73,Married,No Qualification,British,White,"Under 2,600",South West,No,,,
-Female,16,Single,GCSE/O Level,English,White,"2,600 to 5,200",South West,No,,,
-Female,73,Widowed,No Qualification,British,White,"10,400 to 15,600",South West,No,,,
-Male,52,Married,Degree,Scottish,White,"Above 36,400",South West,No,,,
-Male,72,Single,No Qualification,English,White,"5,200 to 10,400",South West,No,,,
-Male,79,Married,No Qualification,British,White,"10,400 to 15,600",South West,No,,,
-Female,90,Widowed,No Qualification,English,White,"2,600 to 5,200",South West,No,,,
-Male,27,Single,Higher/Sub Degree,English,White,"20,800 to 28,600",South West,Yes,30.0,10.0,Packets
-Male,32,Divorced,Degree,English,White,"Above 36,400",South West,No,,,
-Female,38,Divorced,GCSE/O Level,English,White,"5,200 to 10,400",South West,No,,,
-Female,21,Single,Degree,British,White,"Under 2,600",South West,No,,,
-Male,40,Married,A Levels,British,White,"Above 36,400",South West,No,,,
-Male,28,Married,Degree,British,White,"Above 36,400",South West,No,,,
-Male,47,Married,A Levels,English,White,"Above 36,400",South West,Yes,40.0,40.0,Packets
-Female,45,Married,Degree,British,White,"5,200 to 10,400",South West,No,,,
-Female,95,Widowed,No Qualification,British,White,"5,200 to 10,400",South West,No,,,
-Male,18,Single,A Levels,English,White,"2,600 to 5,200",South West,No,,,
-Female,89,Widowed,GCSE/O Level,English,White,Unknown,South West,No,,,
-Female,82,Widowed,No Qualification,English,White,"5,200 to 10,400",South West,No,,,
-Male,65,Married,A Levels,English,White,"28,600 to 36,400",South West,No,,,
-Female,87,Widowed,GCSE/O Level,English,White,"5,200 to 10,400",South West,No,,,
-Male,30,Single,Higher/Sub Degree,English,White,"20,800 to 28,600",South West,Yes,4.0,1.0,Packets
-Female,77,Widowed,Degree,English,White,"28,600 to 36,400",South West,No,,,
-Female,55,Divorced,Degree,British,White,"Above 36,400",South West,No,,,
-Female,33,Married,Degree,British,Asian,"5,200 to 10,400",South West,No,,,
-Female,56,Married,No Qualification,British,Black,"5,200 to 10,400",South West,No,,,
-Female,68,Divorced,No Qualification,English,White,"2,600 to 5,200",South West,Yes,15.0,10.0,Packets
-Female,73,Married,No Qualification,British,White,"2,600 to 5,200",South West,No,,,
-Female,66,Single,GCSE/O Level,English,White,"10,400 to 15,600",South West,No,,,
-Female,73,Married,Other/Sub Degree,English,White,"5,200 to 10,400",South West,No,,,
-Male,42,Married,Higher/Sub Degree,English,White,"20,800 to 28,600",South West,Yes,12.0,0.0,Packets
-Male,41,Single,No Qualification,Welsh,White,Refused,South West,No,,,
-Female,35,Married,GCSE/CSE,British,White,"10,400 to 15,600",South West,No,,,
-Female,53,Married,No Qualification,English,White,"2,600 to 5,200",South West,No,,,
-Female,74,Divorced,No Qualification,English,White,"5,200 to 10,400",South West,No,,,
-Male,46,Married,No Qualification,English,White,"10,400 to 15,600",South West,Yes,25.0,25.0,Hand-Rolled
-Female,22,Single,GCSE/CSE,English,White,"2,600 to 5,200",South West,No,,,
-Male,27,Married,A Levels,English,White,"15,600 to 20,800",South West,No,,,
-Female,71,Married,GCSE/O Level,British,White,"5,200 to 10,400",South West,No,,,
-Male,78,Widowed,Other/Sub Degree,English,White,"5,200 to 10,400",South West,No,,,
-Female,38,Married,GCSE/O Level,English,White,"5,200 to 10,400",South West,No,,,
-Female,31,Married,A Levels,British,White,"10,400 to 15,600",South West,No,,,
-Female,54,Married,Degree,English,White,"2,600 to 5,200",South West,No,,,
-Female,31,Single,GCSE/CSE,English,White,"10,400 to 15,600",South West,No,,,
-Male,53,Married,ONC/BTEC,English,Mixed,"Above 36,400",South West,No,,,
-Female,36,Single,GCSE/O Level,English,White,"2,600 to 5,200",South West,Yes,20.0,20.0,Both/Mainly Packets
-Male,41,Married,Other/Sub Degree,English,White,"15,600 to 20,800",South West,No,,,
-Male,78,Widowed,No Qualification,British,White,"5,200 to 10,400",South West,Yes,15.0,12.0,Packets
-Female,54,Divorced,GCSE/O Level,British,White,"2,600 to 5,200",South West,Yes,10.0,10.0,Packets
-Female,72,Divorced,No Qualification,British,White,"2,600 to 5,200",South West,No,,,
-Male,28,Married,Other/Sub Degree,English,White,"20,800 to 28,600",South West,Yes,20.0,20.0,Hand-Rolled
-Female,41,Separated,GCSE/O Level,British,White,"5,200 to 10,400",South West,Yes,20.0,15.0,Both/Mainly Packets
-Female,78,Married,No Qualification,English,White,"Under 2,600",South West,No,,,
-Male,71,Widowed,No Qualification,Welsh,White,"5,200 to 10,400",South West,No,,,
-Female,57,Married,Other/Sub Degree,English,White,"5,200 to 10,400",South West,No,,,
-Male,31,Married,GCSE/O Level,British,White,"15,600 to 20,800",South West,Yes,1.0,5.0,Packets
-Female,64,Widowed,No Qualification,Irish,White,"5,200 to 10,400",South West,No,,,
-Male,65,Married,No Qualification,English,White,"5,200 to 10,400",South West,No,,,
-Male,61,Married,No Qualification,English,White,"5,200 to 10,400",South West,No,,,
-Male,33,Married,GCSE/CSE,English,White,"20,800 to 28,600",South West,No,,,
-Female,60,Married,ONC/BTEC,English,White,Refused,South West,Yes,20.0,20.0,Packets
-Male,40,Married,Higher/Sub Degree,British,White,"15,600 to 20,800",South West,No,,,
-Female,68,Married,Other/Sub Degree,English,White,"5,200 to 10,400",South West,No,,,
-Female,36,Married,No Qualification,English,White,"15,600 to 20,800",South West,No,,,
-Female,35,Married,GCSE/O Level,English,White,"Under 2,600",South West,No,,,
-Male,22,Single,GCSE/CSE,English,White,"20,800 to 28,600",South West,Yes,20.0,5.0,Packets
-Female,43,Married,GCSE/CSE,English,White,"2,600 to 5,200",South West,No,,,
-Male,84,Widowed,No Qualification,English,White,"5,200 to 10,400",South West,No,,,
-Female,24,Single,No Qualification,British,White,"5,200 to 10,400",South West,No,,,
-Male,68,Married,No Qualification,English,White,"5,200 to 10,400",South West,Yes,25.0,12.0,Hand-Rolled
-Male,35,Single,Other/Sub Degree,British,White,"Under 2,600",South West,Yes,10.0,10.0,Packets
-Female,51,Divorced,Other/Sub Degree,English,White,Unknown,South West,Yes,25.0,25.0,Packets
-Male,45,Married,ONC/BTEC,British,White,"28,600 to 36,400",South West,No,,,
-Female,45,Single,No Qualification,English,White,Refused,South West,Yes,5.0,5.0,Packets
-Male,71,Married,No Qualification,English,White,"10,400 to 15,600",South West,No,,,
-Female,47,Married,No Qualification,British,White,"5,200 to 10,400",South West,Yes,20.0,15.0,Packets
-Female,39,Married,Higher/Sub Degree,British,White,"10,400 to 15,600",South West,No,,,
-Male,62,Married,Higher/Sub Degree,English,White,"2,600 to 5,200",South West,No,,,
-Female,37,Single,Degree,British,White,"15,600 to 20,800",South West,No,,,
-Female,71,Married,No Qualification,English,White,Refused,South West,No,,,
-Male,49,Divorced,Degree,English,White,"28,600 to 36,400",South West,No,,,
-Female,22,Single,GCSE/CSE,British,White,"2,600 to 5,200",South West,Yes,30.0,20.0,Both/Mainly Packets
-Male,61,Married,No Qualification,English,White,"Under 2,600",South West,No,,,
-Male,34,Single,GCSE/O Level,British,White,"20,800 to 28,600",South West,Yes,10.0,5.0,Packets
-Female,40,Separated,Degree,British,White,"Above 36,400",South West,No,,,
-Female,27,Separated,No Qualification,English,White,"2,600 to 5,200",South West,Yes,20.0,20.0,Both/Mainly Packets
-Male,28,Single,ONC/BTEC,English,White,"10,400 to 15,600",South West,Yes,12.0,12.0,Packets
-Female,28,Single,A Levels,British,White,"10,400 to 15,600",South West,No,,,
-Male,36,Married,GCSE/CSE,English,White,"20,800 to 28,600",South West,Yes,20.0,30.0,Both/Mainly Packets
-Female,34,Single,GCSE/O Level,English,White,"5,200 to 10,400",South West,Yes,20.0,20.0,Hand-Rolled
-Male,19,Single,A Levels,English,White,"10,400 to 15,600",South West,No,,,
-Female,31,Divorced,GCSE/O Level,English,White,"15,600 to 20,800",South West,Yes,20.0,20.0,Packets
-Male,49,Divorced,No Qualification,English,White,Refused,South West,Yes,20.0,20.0,Hand-Rolled
-Male,42,Divorced,A Levels,Irish,White,"2,600 to 5,200",South West,Yes,50.0,50.0,Hand-Rolled
-Female,38,Married,No Qualification,English,White,"Under 2,600",South West,No,,,
-Male,16,Single,No Qualification,English,White,"Under 2,600",South West,Yes,6.0,6.0,Both/Mainly Packets
-Male,73,Married,No Qualification,British,White,"5,200 to 10,400",South West,No,,,
-Male,36,Married,GCSE/O Level,English,Asian,"15,600 to 20,800",South West,No,,,
-Female,81,Married,No Qualification,Welsh,White,"Under 2,600",South West,No,,,
-Female,97,Widowed,No Qualification,English,White,"10,400 to 15,600",South West,No,,,
-Male,38,Married,No Qualification,English,White,"10,400 to 15,600",South West,No,,,
-Female,46,Married,Other/Sub Degree,British,White,"5,200 to 10,400",South West,Yes,12.0,7.0,Packets
-Male,79,Married,No Qualification,English,White,"5,200 to 10,400",South West,No,,,
-Female,76,Widowed,No Qualification,English,White,"2,600 to 5,200",South West,Yes,4.0,4.0,Packets
-Female,54,Widowed,No Qualification,Irish,White,"2,600 to 5,200",Wales,No,,,
-Male,59,Married,Degree,English,White,"Above 36,400",Wales,No,,,
-Male,52,Separated,Degree,British,White,"Above 36,400",Wales,No,,,
-Male,58,Divorced,No Qualification,Welsh,White,"5,200 to 10,400",Wales,Yes,20.0,20.0,Both/Mainly Packets
-Male,69,Divorced,Degree,Welsh,White,"15,600 to 20,800",Wales,No,,,
-Male,67,Widowed,Degree,Welsh,White,"5,200 to 10,400",Wales,No,,,
-Female,76,Married,Other/Sub Degree,Welsh,White,Refused,Wales,No,,,
-Male,33,Married,No Qualification,British,Asian,"5,200 to 10,400",Wales,No,,,
-Male,61,Separated,No Qualification,Welsh,White,Refused,Wales,Yes,30.0,30.0,Hand-Rolled
-Female,82,Widowed,No Qualification,Welsh,White,Refused,Wales,No,,,
-Male,62,Married,No Qualification,Welsh,White,"5,200 to 10,400",Wales,No,,,
-Male,66,Divorced,No Qualification,Welsh,White,Refused,Wales,No,,,
-Female,69,Widowed,No Qualification,Welsh,White,"Under 2,600",Wales,No,,,
-Female,25,Married,No Qualification,Other,Asian,Refused,Wales,No,,,
-Male,29,Single,Degree,Welsh,White,"20,800 to 28,600",Wales,No,,,
-Male,28,Married,Degree,British,White,"10,400 to 15,600",Wales,No,,,
-Female,52,Single,Degree,Other,White,"28,600 to 36,400",Wales,No,,,
-Female,81,Widowed,No Qualification,Welsh,White,Refused,Wales,No,,,
-Female,44,Married,GCSE/O Level,Welsh,White,"10,400 to 15,600",Wales,Yes,20.0,20.0,Packets
-Female,62,Married,GCSE/O Level,Welsh,White,Refused,Wales,No,,,
-Male,45,Married,No Qualification,Welsh,White,"2,600 to 5,200",Wales,No,,,
-Female,71,Widowed,No Qualification,Welsh,White,"5,200 to 10,400",Wales,No,,,
-Female,72,Married,No Qualification,Welsh,White,"2,600 to 5,200",Wales,No,,,
-Female,69,Widowed,No Qualification,English,White,Unknown,Wales,Yes,15.0,15.0,Packets
-Female,72,Widowed,No Qualification,Welsh,White,"2,600 to 5,200",Wales,Yes,10.0,10.0,Packets
-Male,61,Married,No Qualification,Welsh,White,"5,200 to 10,400",Wales,No,,,
-Female,29,Married,A Levels,British,White,"2,600 to 5,200",Wales,Yes,5.0,2.0,Packets
-Female,37,Single,Higher/Sub Degree,British,White,"5,200 to 10,400",Wales,Yes,30.0,30.0,Packets
-Male,29,Single,GCSE/O Level,Welsh,White,"15,600 to 20,800",Wales,Yes,20.0,20.0,Packets
-Male,57,Married,No Qualification,English,White,"10,400 to 15,600",Wales,No,,,
-Male,60,Married,GCSE/O Level,Welsh,White,"5,200 to 10,400",Wales,No,,,
-Male,38,Married,Higher/Sub Degree,Welsh,White,"28,600 to 36,400",Wales,No,,,
-Male,74,Married,No Qualification,Welsh,White,"5,200 to 10,400",Wales,No,,,
-Male,38,Married,Higher/Sub Degree,Welsh,White,"10,400 to 15,600",Wales,No,,,
-Female,51,Divorced,GCSE/O Level,Welsh,White,Refused,Wales,No,,,
-Male,68,Married,No Qualification,Welsh,White,"2,600 to 5,200",Wales,Yes,15.0,15.0,Hand-Rolled
-Male,67,Married,Higher/Sub Degree,Welsh,White,"10,400 to 15,600",Wales,No,,,
-Male,32,Single,GCSE/O Level,Welsh,White,"10,400 to 15,600",Wales,No,,,
-Female,25,Married,ONC/BTEC,British,Asian,"5,200 to 10,400",Wales,No,,,
-Male,53,Married,No Qualification,Welsh,White,"15,600 to 20,800",Wales,No,,,
-Female,52,Divorced,No Qualification,Welsh,Unknown,"5,200 to 10,400",Wales,Yes,5.0,5.0,Packets
-Female,21,Single,No Qualification,Welsh,White,"2,600 to 5,200",Wales,No,,,
-Female,71,Divorced,No Qualification,Welsh,White,"5,200 to 10,400",Wales,No,,,
-Female,30,Married,GCSE/O Level,British,White,"Under 2,600",Wales,No,,,
-Male,23,Single,GCSE/O Level,Welsh,White,"15,600 to 20,800",Wales,No,,,
-Female,28,Single,Higher/Sub Degree,Welsh,White,"10,400 to 15,600",Wales,Yes,0.0,1.0,Packets
-Female,37,Married,GCSE/O Level,Welsh,White,"5,200 to 10,400",Wales,No,,,
-Female,84,Widowed,No Qualification,Welsh,White,"2,600 to 5,200",Wales,No,,,
-Female,61,Married,No Qualification,English,White,"2,600 to 5,200",Wales,No,,,
-Female,18,Single,GCSE/O Level,Welsh,White,"2,600 to 5,200",Wales,No,,,
-Female,55,Married,GCSE/O Level,English,White,"5,200 to 10,400",Wales,No,,,
-Male,52,Single,Higher/Sub Degree,British,White,"20,800 to 28,600",Wales,No,,,
-Female,46,Married,No Qualification,Welsh,White,"2,600 to 5,200",Wales,Yes,25.0,25.0,Packets
-Female,46,Married,Other/Sub Degree,British,White,"10,400 to 15,600",Wales,No,,,
-Male,23,Single,No Qualification,Welsh,White,"2,600 to 5,200",Wales,No,,,
-Male,58,Married,Degree,British,White,"20,800 to 28,600",Wales,No,,,
-Male,16,Single,GCSE/O Level,Welsh,White,"Under 2,600",Wales,No,,,
-Male,71,Married,No Qualification,Welsh,White,"15,600 to 20,800",Wales,No,,,
-Female,47,Married,No Qualification,British,White,"2,600 to 5,200",Wales,Yes,10.0,10.0,Packets
-Male,34,Married,Degree,Welsh,White,"20,800 to 28,600",Wales,No,,,
-Female,55,Single,No Qualification,British,White,"Above 36,400",Wales,No,,,
-Female,78,Widowed,Other/Sub Degree,British,White,"20,800 to 28,600",Wales,No,,,
-Male,35,Single,No Qualification,Welsh,White,"2,600 to 5,200",Wales,Yes,40.0,40.0,Hand-Rolled
-Male,68,Married,No Qualification,English,White,"20,800 to 28,600",Wales,No,,,
-Male,76,Single,No Qualification,British,White,"5,200 to 10,400",Wales,No,,,
-Male,28,Single,GCSE/CSE,Welsh,White,"10,400 to 15,600",Wales,No,,,
-Female,36,Divorced,GCSE/CSE,British,White,"5,200 to 10,400",Wales,Yes,20.0,20.0,Both/Mainly Packets
-Female,57,Divorced,ONC/BTEC,English,White,"5,200 to 10,400",Wales,No,,,
-Female,29,Single,Higher/Sub Degree,Welsh,White,"10,400 to 15,600",Wales,Yes,20.0,10.0,Both/Mainly Packets
-Female,27,Single,GCSE/O Level,Welsh,White,"5,200 to 10,400",Wales,Yes,5.0,5.0,Packets
-Female,64,Widowed,No Qualification,English,White,"2,600 to 5,200",Wales,No,,,
-Female,89,Single,No Qualification,Welsh,White,Refused,Wales,No,,,
-Female,26,Single,GCSE/CSE,Welsh,White,"2,600 to 5,200",Wales,Yes,15.0,15.0,Packets
-Male,27,Single,Higher/Sub Degree,Welsh,White,"20,800 to 28,600",Wales,No,,,
-Male,66,Single,Higher/Sub Degree,English,White,"10,400 to 15,600",Wales,No,,,
-Female,84,Widowed,No Qualification,British,White,"5,200 to 10,400",Wales,No,,,
-Male,41,Single,Higher/Sub Degree,British,White,"20,800 to 28,600",Wales,Yes,20.0,12.0,Packets
-Male,27,Single,Degree,British,White,"15,600 to 20,800",Wales,No,,,
-Female,40,Divorced,GCSE/O Level,English,White,"10,400 to 15,600",Wales,No,,,
-Female,79,Widowed,No Qualification,British,White,"10,400 to 15,600",Wales,No,,,
-Male,49,Married,No Qualification,English,White,"20,800 to 28,600",Wales,Yes,20.0,20.0,Packets
-Female,79,Widowed,No Qualification,British,White,Unknown,Wales,No,,,
-Female,54,Married,No Qualification,Welsh,White,"Under 2,600",Wales,No,,,
-Male,25,Single,GCSE/O Level,Scottish,White,"5,200 to 10,400",Scotland,Yes,10.0,10.0,Packets
-Female,45,Single,Degree,Scottish,White,"28,600 to 36,400",Scotland,Yes,5.0,5.0,Packets
-Female,24,Single,Degree,Scottish,White,"5,200 to 10,400",Scotland,Yes,5.0,5.0,Packets
-Female,54,Divorced,No Qualification,Scottish,White,"10,400 to 15,600",Scotland,Yes,20.0,10.0,Packets
-Female,78,Widowed,No Qualification,Scottish,White,"15,600 to 20,800",Scotland,No,,,
-Male,50,Married,No Qualification,Scottish,White,"10,400 to 15,600",Scotland,Yes,25.0,25.0,Hand-Rolled
-Female,42,Married,Degree,Scottish,White,"20,800 to 28,600",Scotland,No,,,
-Female,32,Single,A Levels,Scottish,White,"10,400 to 15,600",Scotland,Yes,10.0,10.0,Packets
-Male,75,Single,No Qualification,Scottish,White,"5,200 to 10,400",Scotland,No,,,
-Female,25,Single,GCSE/O Level,Scottish,White,"Under 2,600",Scotland,Yes,15.0,15.0,Packets
-Male,50,Separated,Higher/Sub Degree,Scottish,White,"20,800 to 28,600",Scotland,No,,,
-Male,24,Single,Higher/Sub Degree,Scottish,White,"28,600 to 36,400",Scotland,Yes,15.0,15.0,Packets
-Male,35,Single,Higher/Sub Degree,British,White,"28,600 to 36,400",Scotland,No,,,
-Female,52,Married,No Qualification,Scottish,White,"10,400 to 15,600",Scotland,No,,,
-Female,42,Married,Degree,British,White,"10,400 to 15,600",Scotland,No,,,
-Male,44,Separated,GCSE/O Level,Scottish,White,"15,600 to 20,800",Scotland,No,,,
-Male,67,Widowed,No Qualification,Scottish,White,"5,200 to 10,400",Scotland,Yes,20.0,12.0,Hand-Rolled
-Female,69,Widowed,No Qualification,Scottish,White,"2,600 to 5,200",Scotland,No,,,
-Female,91,Single,No Qualification,Scottish,White,"2,600 to 5,200",Scotland,No,,,
-Male,61,Divorced,No Qualification,Scottish,White,"2,600 to 5,200",Scotland,No,,,
-Female,50,Single,Degree,British,White,"20,800 to 28,600",Scotland,No,,,
-Male,37,Single,Higher/Sub Degree,Scottish,White,"20,800 to 28,600",Scotland,No,,,
-Female,35,Single,Higher/Sub Degree,Scottish,White,"15,600 to 20,800",Scotland,Yes,15.0,15.0,Packets
-Male,24,Single,Higher/Sub Degree,British,White,"10,400 to 15,600",Scotland,Yes,20.0,15.0,Packets
-Female,39,Single,No Qualification,Scottish,White,"5,200 to 10,400",Scotland,No,,,
-Female,79,Divorced,No Qualification,Scottish,White,"2,600 to 5,200",Scotland,No,,,
-Male,40,Married,Degree,Scottish,White,"Above 36,400",Scotland,No,,,
-Male,60,Married,Degree,British,White,"Above 36,400",Scotland,No,,,
-Male,42,Married,No Qualification,Scottish,White,"20,800 to 28,600",Scotland,No,,,
-Male,64,Widowed,Other/Sub Degree,Scottish,White,"5,200 to 10,400",Scotland,No,,,
-Female,55,Married,GCSE/O Level,Scottish,White,"15,600 to 20,800",Scotland,Yes,2.0,10.0,Packets
-Male,54,Married,Other/Sub Degree,Scottish,White,"10,400 to 15,600",Scotland,No,,,
-Female,61,Married,Higher/Sub Degree,British,White,"10,400 to 15,600",Scotland,No,,,
-Female,62,Divorced,Higher/Sub Degree,Scottish,White,"5,200 to 10,400",Scotland,No,,,
-Female,59,Divorced,Degree,Scottish,White,Unknown,Scotland,No,,,
-Female,79,Widowed,GCSE/CSE,Scottish,White,Refused,Scotland,No,,,
-Female,37,Married,Degree,British,White,"10,400 to 15,600",Scotland,No,,,
-Female,55,Married,Other/Sub Degree,Scottish,White,"2,600 to 5,200",Scotland,Yes,12.0,8.0,Packets
-Female,88,Widowed,Higher/Sub Degree,Scottish,White,"28,600 to 36,400",Scotland,No,,,
-Female,39,Divorced,Degree,British,White,"15,600 to 20,800",Scotland,No,,,
-Male,34,Single,Degree,British,White,"Above 36,400",Scotland,No,,,
-Male,42,Married,Degree,Scottish,White,"28,600 to 36,400",Scotland,No,,,
-Female,45,Married,Degree,British,White,"2,600 to 5,200",Scotland,No,,,
-Male,59,Married,ONC/BTEC,Scottish,White,"Above 36,400",Scotland,No,,,
-Male,58,Married,GCSE/O Level,British,White,"10,400 to 15,600",Scotland,Yes,30.0,30.0,Packets
-Male,74,Widowed,Higher/Sub Degree,British,White,"5,200 to 10,400",Scotland,No,,,
-Male,45,Married,GCSE/O Level,Scottish,White,"20,800 to 28,600",Scotland,No,,,
-Male,64,Married,No Qualification,Scottish,White,"10,400 to 15,600",Scotland,No,,,
-Male,53,Single,Higher/Sub Degree,Scottish,White,"15,600 to 20,800",Scotland,No,,,
-Male,40,Married,GCSE/O Level,Scottish,White,"15,600 to 20,800",Scotland,No,,,
-Male,30,Married,ONC/BTEC,Scottish,White,"15,600 to 20,800",Scotland,Yes,10.0,10.0,Hand-Rolled
-Male,70,Married,No Qualification,Scottish,White,"10,400 to 15,600",Scotland,No,,,
-Female,28,Separated,ONC/BTEC,Scottish,White,"5,200 to 10,400",Scotland,No,,,
-Female,78,Widowed,No Qualification,Scottish,White,"2,600 to 5,200",Scotland,No,,,
-Male,41,Married,A Levels,Scottish,White,"20,800 to 28,600",Scotland,Yes,24.0,24.0,Packets
-Female,69,Divorced,No Qualification,British,White,"5,200 to 10,400",Scotland,No,,,
-Male,54,Married,GCSE/O Level,Scottish,White,"20,800 to 28,600",Scotland,No,,,
-Male,59,Married,ONC/BTEC,Scottish,White,"15,600 to 20,800",Scotland,Yes,20.0,20.0,Hand-Rolled
-Female,23,Single,GCSE/O Level,Scottish,White,"5,200 to 10,400",Scotland,Yes,15.0,15.0,Packets
-Male,40,Married,No Qualification,Scottish,White,"15,600 to 20,800",Scotland,Yes,30.0,15.0,Packets
-Male,17,Single,GCSE/O Level,Scottish,White,"5,200 to 10,400",Scotland,Yes,15.0,6.0,Packets
-Male,54,Single,No Qualification,British,White,"5,200 to 10,400",Scotland,No,,,
-Female,49,Divorced,No Qualification,Scottish,White,"5,200 to 10,400",Scotland,No,,,
-Male,55,Married,Higher/Sub Degree,Scottish,White,"5,200 to 10,400",Scotland,Yes,45.0,45.0,Hand-Rolled
-Male,51,Widowed,ONC/BTEC,Scottish,White,"5,200 to 10,400",Scotland,Yes,20.0,20.0,Packets
-Female,73,Married,No Qualification,Scottish,White,"Under 2,600",Scotland,No,,,
-Female,41,Married,No Qualification,Scottish,White,"2,600 to 5,200",Scotland,No,,,
-Female,70,Married,GCSE/O Level,British,White,"20,800 to 28,600",Scotland,No,,,
-Female,44,Separated,No Qualification,Scottish,White,"10,400 to 15,600",Scotland,Yes,15.0,10.0,Packets
-Male,51,Divorced,No Qualification,Scottish,White,"Under 2,600",Scotland,No,,,
-Male,68,Married,Degree,Irish,White,"28,600 to 36,400",Scotland,Yes,1.0,0.0,Packets
-Female,75,Separated,No Qualification,Scottish,White,"2,600 to 5,200",Scotland,No,,,
-Male,60,Married,No Qualification,Scottish,White,"5,200 to 10,400",Scotland,Yes,20.0,15.0,Packets
-Male,56,Married,No Qualification,Scottish,White,"2,600 to 5,200",Scotland,No,,,
-Male,64,Widowed,No Qualification,Scottish,White,"5,200 to 10,400",Scotland,No,,,
-Female,27,Single,Degree,Scottish,White,"10,400 to 15,600",Scotland,No,,,
-Female,73,Widowed,No Qualification,Scottish,White,"5,200 to 10,400",Scotland,Yes,20.0,12.0,Packets
-Male,34,Single,GCSE/O Level,Scottish,White,"15,600 to 20,800",Scotland,Yes,10.0,2.0,Packets
-Male,47,Married,GCSE/O Level,British,White,"10,400 to 15,600",Scotland,Yes,15.0,10.0,Hand-Rolled
-Male,32,Single,A Levels,Scottish,White,"10,400 to 15,600",Scotland,No,,,
-Male,63,Married,No Qualification,British,White,"15,600 to 20,800",Scotland,No,,,
-Female,23,Single,Degree,Scottish,White,"5,200 to 10,400",Scotland,No,,,
-Female,63,Married,No Qualification,Scottish,White,"5,200 to 10,400",Scotland,No,,,
-Female,46,Divorced,A Levels,Scottish,White,"15,600 to 20,800",Scotland,No,,,
-Male,48,Single,A Levels,Scottish,White,"10,400 to 15,600",Scotland,No,,,
-Male,68,Married,No Qualification,Scottish,White,"5,200 to 10,400",Scotland,No,,,
-Male,52,Separated,No Qualification,Scottish,White,"15,600 to 20,800",Scotland,Yes,20.0,20.0,Packets
-Female,29,Separated,No Qualification,Scottish,White,"15,600 to 20,800",Scotland,Yes,10.0,6.0,Packets
-Female,16,Single,No Qualification,Scottish,White,Refused,Scotland,No,,,
-Male,41,Divorced,A Levels,Scottish,White,"15,600 to 20,800",Scotland,No,,,
-Female,48,Widowed,No Qualification,Scottish,White,"5,200 to 10,400",Scotland,Yes,20.0,20.0,Packets
-Male,24,Single,A Levels,Scottish,White,"5,200 to 10,400",Scotland,No,,,
-Male,72,Widowed,No Qualification,Scottish,White,"5,200 to 10,400",Scotland,Yes,40.0,40.0,Hand-Rolled
-Male,45,Married,GCSE/O Level,British,White,"15,600 to 20,800",Scotland,No,,,
-Female,28,Married,Degree,British,White,"28,600 to 36,400",Scotland,No,,,
-Male,20,Single,A Levels,Scottish,White,"5,200 to 10,400",Scotland,No,,,
-Female,69,Widowed,No Qualification,Scottish,White,"5,200 to 10,400",Scotland,No,,,
-Female,70,Married,No Qualification,Scottish,White,"Under 2,600",Scotland,No,,,
-Male,50,Married,GCSE/CSE,Scottish,White,"15,600 to 20,800",Scotland,Yes,25.0,20.0,Both/Mainly Packets
-Male,63,Married,A Levels,Scottish,White,"5,200 to 10,400",Scotland,Yes,20.0,20.0,Packets
-Male,44,Married,GCSE/O Level,British,White,"15,600 to 20,800",Scotland,No,,,
-Female,83,Widowed,No Qualification,Scottish,White,"5,200 to 10,400",Scotland,No,,,
-Female,17,Single,GCSE/CSE,Scottish,White,"5,200 to 10,400",Scotland,Yes,20.0,15.0,Packets
-Female,61,Married,Other/Sub Degree,Scottish,White,"5,200 to 10,400",Scotland,No,,,
-Female,47,Married,GCSE/O Level,Scottish,White,"5,200 to 10,400",Scotland,Yes,15.0,20.0,Packets
-Male,57,Married,Degree,Scottish,White,"28,600 to 36,400",Scotland,No,,,
-Female,44,Married,GCSE/O Level,Scottish,White,"5,200 to 10,400",Scotland,Yes,15.0,10.0,Packets
-Male,52,Single,GCSE/CSE,Irish,White,"15,600 to 20,800",Scotland,Yes,25.0,20.0,Both/Mainly Packets
-Male,82,Married,No Qualification,Scottish,White,"5,200 to 10,400",Scotland,No,,,
-Male,63,Married,A Levels,Scottish,White,"28,600 to 36,400",Scotland,No,,,
-Female,72,Separated,No Qualification,Scottish,White,"2,600 to 5,200",Scotland,No,,,
-Male,39,Married,Higher/Sub Degree,Scottish,White,"Above 36,400",Scotland,No,,,
-Male,16,Single,No Qualification,Scottish,White,Refused,Scotland,No,,,
-Female,35,Divorced,Degree,Scottish,White,"15,600 to 20,800",Scotland,No,,,
-Female,39,Married,Degree,Scottish,White,"10,400 to 15,600",Scotland,No,,,
-Male,56,Separated,No Qualification,Scottish,White,Refused,Scotland,Yes,10.0,10.0,Hand-Rolled
-Female,31,Single,Other/Sub Degree,Scottish,White,"5,200 to 10,400",Scotland,Yes,60.0,30.0,Packets
-Male,61,Married,No Qualification,Scottish,White,"Under 2,600",Scotland,No,,,
-Male,23,Separated,GCSE/O Level,Scottish,White,"Under 2,600",Scotland,Yes,5.0,0.0,Packets
-Male,34,Single,No Qualification,Scottish,White,"10,400 to 15,600",Scotland,Yes,35.0,18.0,Both/Mainly Hand-Rolled
-Male,24,Single,Degree,Scottish,White,"15,600 to 20,800",Scotland,No,,,
-Male,82,Single,Other/Sub Degree,Scottish,White,"5,200 to 10,400",Scotland,No,,,
-Male,27,Married,A Levels,Scottish,White,"15,600 to 20,800",Scotland,No,,,
-Female,62,Divorced,GCSE/CSE,Scottish,White,"2,600 to 5,200",Scotland,Yes,18.0,18.0,Packets
-Male,39,Married,Higher/Sub Degree,British,White,"20,800 to 28,600",Scotland,No,,,
-Male,44,Separated,No Qualification,Scottish,White,"2,600 to 5,200",Scotland,No,,,
-Female,43,Single,No Qualification,British,White,"10,400 to 15,600",Scotland,Yes,25.0,20.0,Packets
-Male,31,Single,GCSE/O Level,Scottish,White,"2,600 to 5,200",Scotland,Yes,20.0,15.0,Both/Mainly Packets
-Male,77,Divorced,Other/Sub Degree,English,White,"5,200 to 10,400",Scotland,Yes,20.0,10.0,Packets
-Female,20,Single,GCSE/O Level,English,White,"5,200 to 10,400",Scotland,No,,,
-Female,81,Widowed,Other/Sub Degree,Scottish,White,"5,200 to 10,400",Scotland,No,,,
-Female,31,Single,GCSE/O Level,Scottish,White,"15,600 to 20,800",Scotland,Yes,20.0,10.0,Packets
-Female,78,Married,Other/Sub Degree,Scottish,White,"2,600 to 5,200",Scotland,No,,,
-Male,35,Married,A Levels,Scottish,White,"10,400 to 15,600",Scotland,No,,,
-Female,67,Widowed,No Qualification,Scottish,White,"Under 2,600",Scotland,No,,,
-Male,65,Divorced,Other/Sub Degree,Scottish,White,"10,400 to 15,600",Scotland,No,,,
-Male,61,Married,Higher/Sub Degree,Scottish,White,"10,400 to 15,600",Scotland,No,,,
-Male,24,Single,No Qualification,Scottish,White,"Under 2,600",Scotland,Yes,15.0,15.0,Hand-Rolled
-Male,53,Single,No Qualification,Scottish,White,"20,800 to 28,600",Scotland,No,,,
-Female,63,Married,No Qualification,British,White,Refused,Scotland,No,,,
-Male,35,Married,No Qualification,Scottish,White,"10,400 to 15,600",Scotland,Yes,3.0,12.0,Packets
-Male,78,Widowed,No Qualification,Scottish,White,Refused,Scotland,No,,,
-Female,31,Single,Other/Sub Degree,Scottish,White,"5,200 to 10,400",Scotland,No,,,
-Male,22,Single,No Qualification,Scottish,White,"2,600 to 5,200",Scotland,No,,,
-Female,49,Divorced,Other/Sub Degree,English,White,"2,600 to 5,200",Scotland,Yes,20.0,20.0,Hand-Rolled
-Male,45,Married,Other/Sub Degree,Scottish,White,"5,200 to 10,400",Scotland,No,,,
-Female,51,Married,No Qualification,English,White,"2,600 to 5,200",Scotland,Yes,20.0,20.0,Packets
-Male,31,Married,Degree,Scottish,White,"10,400 to 15,600",Scotland,No,,,
diff --git a/src/thesis/data_management/__init__.py b/src/thesis/data_management/__init__.py
deleted file mode 100644
index c61fcadb..00000000
--- a/src/thesis/data_management/__init__.py
+++ /dev/null
@@ -1,5 +0,0 @@
-"""Functions for managing data."""
-
-from thesis.data_management.clean_data import clean_data
-
-__all__ = [clean_data]
diff --git a/src/thesis/data_management/clean_data.py b/src/thesis/data_management/clean_data.py
deleted file mode 100644
index e4612408..00000000
--- a/src/thesis/data_management/clean_data.py
+++ /dev/null
@@ -1,36 +0,0 @@
-"""Function(s) for cleaning the data set(s)."""
-
-import pandas as pd
-
-
-def clean_data(data, data_info):
- """Clean data set.
-
- Information on data columns is stored in ``data_management/data_info.yaml``.
-
- Args:
- data (pandas.DataFrame): The data set.
- data_info (dict): Information on data set stored in data_info.yaml. The
- following keys can be accessed:
- - 'outcome': Name of dependent variable column in data
- - 'outcome_numerical': Name to be given to the numerical version of outcome
- - 'columns_to_drop': Names of columns that are dropped in data cleaning step
- - 'categorical_columns': Names of columns that are converted to categorical
- - 'column_rename_mapping': Old and new names of columns to be renamend,
- stored in a dictionary with design: {'old_name': 'new_name'}
- - 'url': URL to data set
-
- Returns:
- pandas.DataFrame: The cleaned data set.
-
- """
- data = data.drop(columns=data_info["columns_to_drop"])
- data = data.dropna()
- for cat_col in data_info["categorical_columns"]:
- data[cat_col] = data[cat_col].astype("category")
- data = data.rename(columns=data_info["column_rename_mapping"])
-
- numerical_outcome = pd.Categorical(data[data_info["outcome"]]).codes
- data[data_info["outcome_numerical"]] = numerical_outcome
-
- return data
diff --git a/src/thesis/data_management/data_info.yaml b/src/thesis/data_management/data_info.yaml
deleted file mode 100644
index e5aea16b..00000000
--- a/src/thesis/data_management/data_info.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
----
-data_name: data.csv
-columns_to_drop:
- - nationality
- - ethnicity
- - region
- - amt_weekends
- - amt_weekdays
- - type
- - gross_income
-categorical_columns: [gender, marital_status, highest_qualification, smoke]
-column_rename_mapping:
- highest_qualification: qualification
-outcome: smoke
-outcome_numerical: smoke_numerical # created during the clean_data step
diff --git a/src/thesis/data_management/task_data_management.py b/src/thesis/data_management/task_data_management.py
deleted file mode 100644
index 1de5e902..00000000
--- a/src/thesis/data_management/task_data_management.py
+++ /dev/null
@@ -1,26 +0,0 @@
-"""Tasks for managing the data."""
-
-from pathlib import Path
-
-import pandas as pd
-
-from thesis.config import BLD, SRC
-from thesis.data_management import clean_data
-from thesis.utilities import read_yaml
-
-clean_data_deps = {
- "scripts": Path("clean_data.py"),
- "data_info": SRC / "data_management" / "data_info.yaml",
- "data": SRC / "data" / "data.csv",
-}
-
-
-def task_clean_data_python(
- depends_on=clean_data_deps,
- produces=BLD / "python" / "data" / "data_clean.csv",
-):
- """Clean the data (Python version)."""
- data_info = read_yaml(depends_on["data_info"])
- data = pd.read_csv(depends_on["data"])
- data = clean_data(data, data_info)
- data.to_csv(produces, index=False)
diff --git a/src/thesis/final/__init__.py b/src/thesis/final/__init__.py
deleted file mode 100644
index f33f1cf1..00000000
--- a/src/thesis/final/__init__.py
+++ /dev/null
@@ -1,5 +0,0 @@
-"""Functions for formatting results."""
-
-from thesis.final.plot import plot_regression_by_age
-
-__all__ = [plot_regression_by_age]
diff --git a/src/thesis/final/plot.py b/src/thesis/final/plot.py
deleted file mode 100644
index 53a9ebda..00000000
--- a/src/thesis/final/plot.py
+++ /dev/null
@@ -1,56 +0,0 @@
-"""Functions plotting results."""
-
-import plotly.express as px
-import plotly.graph_objects as go
-
-
-def plot_regression_by_age(data, data_info, predictions, group):
- """Plot regression results by age.
-
- Args:
- data (pandas.DataFrame): The data set.
- data_info (dict): Information on data set stored in data_info.yaml. The
- following keys can be accessed:
- - 'outcome': Name of dependent variable column in data
- - 'outcome_numerical': Name to be given to the numerical version of outcome
- - 'columns_to_drop': Names of columns that are dropped in data cleaning step
- - 'categorical_columns': Names of columns that are converted to categorical
- - 'column_rename_mapping': Old and new names of columns to be renamend,
- stored in a dictionary with design: {'old_name': 'new_name'}
- - 'url': URL to data set
- predictions (pandas.DataFrame): Model predictions for different age values.
- group (str): Categorical column in data set. We create predictions for each
- unique value in column data[group]. Cannot be 'age' or 'smoke'.
-
- Returns:
- plotly.graph_objects.Figure: The figure.
-
- """
- plot_data = predictions.melt(
- id_vars="age",
- value_vars=predictions.columns,
- value_name="prediction",
- var_name=group,
- )
-
- outcomes = data[data_info["outcome_numerical"]]
-
- fig = px.line(
- plot_data,
- x="age",
- y="prediction",
- color=group,
- labels={"age": "Age", "prediction": "Probability of Smoking"},
- )
-
- fig.add_traces(
- go.Scatter(
- x=data["age"],
- y=outcomes,
- mode="markers",
- marker_color="black",
- marker_opacity=0.1,
- name="Data",
- ),
- )
- return fig
diff --git a/src/thesis/final/task_final.py b/src/thesis/final/task_final.py
deleted file mode 100644
index 71356701..00000000
--- a/src/thesis/final/task_final.py
+++ /dev/null
@@ -1,40 +0,0 @@
-"""Tasks running the results formatting (tables, figures)."""
-
-import pandas as pd
-import pytask
-
-from thesis.analysis.model import load_model
-from thesis.config import BLD, GROUPS, SRC
-from thesis.final import plot_regression_by_age
-from thesis.utilities import read_yaml
-
-for group in GROUPS:
- deps = {
- "predictions": BLD / "python" / "predictions" / f"{group}.csv",
- "data_info": SRC / "data_management" / "data_info.yaml",
- "data": BLD / "python" / "data" / "data_clean.csv",
- }
-
- @pytask.task(id=group)
- def task_plot_results_by_age_python(
- group=group,
- depends_on=deps,
- produces=BLD / "python" / "figures" / f"smoking_by_{group}.png",
- ):
- """Plot the regression results by age (Python version)."""
- data_info = read_yaml(depends_on["data_info"])
- data = pd.read_csv(depends_on["data"])
- predictions = pd.read_csv(depends_on["predictions"])
- fig = plot_regression_by_age(data, data_info, predictions, group)
- fig.write_image(produces)
-
-
-def task_create_results_table_python(
- depends_on=BLD / "python" / "models" / "model.pickle",
- produces=BLD / "python" / "tables" / "estimation_results.tex",
-):
- """Store a table in LaTeX format with the estimation results (Python version)."""
- model = load_model(depends_on)
- table = model.summary().as_latex()
- with open(produces, "w") as f:
- f.writelines(table)
diff --git a/src/thesis/imbens_manski/__init__.py b/src/thesis/imbens_manski/__init__.py
new file mode 100644
index 00000000..4919f5df
--- /dev/null
+++ b/src/thesis/imbens_manski/__init__.py
@@ -0,0 +1 @@
+"""Code for simulations related to Imbens and Manski (2004) ECMA paper."""
diff --git a/src/thesis/imbens_manski/_task_plot_imbens_manski.py b/src/thesis/imbens_manski/_task_plot_imbens_manski.py
new file mode 100644
index 00000000..d32cacd4
--- /dev/null
+++ b/src/thesis/imbens_manski/_task_plot_imbens_manski.py
@@ -0,0 +1,103 @@
+"""Run simulations for Imbens and Manski (2004) ECMA."""
+
+from pathlib import Path
+from typing import Annotated
+
+import pandas as pd # type: ignore[import-untyped]
+import plotly.graph_objects as go # type: ignore[import-untyped]
+from pytask import Product
+
+from thesis.config import BLD
+from thesis.imbens_manski._task_sim_imbens_manski import (
+ ID_TO_KWARGS,
+ N_BOOT,
+ N_SIMS,
+ _Arguments,
+)
+
+PATHS_PLOTS = {
+ "analytical": BLD / "imbens_manski" / "imbens_manski_analytical.png",
+ "bootstrap": BLD / "imbens_manski" / "imbens_manski_bootstrap.png",
+}
+
+
+def task_plot_sim_results(
+ id_to_kwargs: dict[str, _Arguments] = ID_TO_KWARGS,
+ paths_plots: Annotated[dict[str, Path], Product] = PATHS_PLOTS,
+) -> None:
+ """Plot simulation results."""
+ # Combine all results
+ datasets = []
+ for res in id_to_kwargs.values():
+ _df = pd.read_pickle(res.path_to_results) # noqa: S301
+ _df["p"] = res.p
+ _df["n_obs"] = res.n_obs
+ _df["ci_type"] = res.ci_type
+ _df["alpha"] = res.alpha
+ datasets.append(_df)
+
+ data = pd.concat(datasets)
+
+ alpha = data["alpha"].unique()
+
+ # Compute mean coverage probability
+ cols_groupby = ["p", "n_obs", "ci_type", "alpha"]
+
+ mean_coverage_probability = data.groupby(cols_groupby).mean().reset_index()
+
+ n_obs_to_line_type = {100: "solid", 250: "dash", 1000: "dot"}
+
+ for ci_type in ["analytical", "bootstrap"]:
+ fig = go.Figure()
+
+ for n_obs in data["n_obs"].unique():
+ query = f"n_obs == {n_obs} and ci_type == '{ci_type}'"
+ sub_df = mean_coverage_probability.query(query)
+
+ fig.add_trace(
+ go.Scatter(
+ x=sub_df["p"],
+ y=sub_df["ci_idset_covers"],
+ mode="lines+markers",
+ name=f"n_obs = {n_obs}",
+ legendgroup="idset_ci",
+ legendgrouptitle={"text": "CI for Identified Set"},
+ line_dash=n_obs_to_line_type.get(n_obs, "solid"),
+ line={"color": "blue"},
+ ),
+ )
+
+ fig.add_trace(
+ go.Scatter(
+ x=sub_df["p"],
+ y=sub_df["ci_param_covers"],
+ mode="lines+markers",
+ name=f"n_obs = {n_obs}",
+ legendgroup="param_ci",
+ legendgrouptitle={"text": "CI for Parameter"},
+ line_dash=n_obs_to_line_type.get(n_obs, "solid"),
+ line={"color": "red"},
+ ),
+ )
+
+ fig.update_layout(
+ title=(
+ "CI for Parameter is not Uniformly Valid over p"
+ f"
CI Type: {ci_type}, alpha: {alpha}, "
+ f"Number of simulations: {N_SIMS} "
+ ),
+ xaxis_title="p",
+ yaxis_title="Coverage probability",
+ )
+
+ if ci_type == "bootstrap":
+ fig.add_annotation(
+ x=1,
+ y=-0.1,
+ xref="paper",
+ yref="paper",
+ text=f"Number of bootstrap samples: {N_BOOT}",
+ showarrow=False,
+ )
+
+ fig.write_image(paths_plots[ci_type])
diff --git a/src/thesis/imbens_manski/_task_sim_imbens_manski.py b/src/thesis/imbens_manski/_task_sim_imbens_manski.py
new file mode 100644
index 00000000..6b195540
--- /dev/null
+++ b/src/thesis/imbens_manski/_task_sim_imbens_manski.py
@@ -0,0 +1,73 @@
+"""Run simulations for Imbens and Manski (2004) ECMA."""
+
+from pathlib import Path
+from typing import Annotated, NamedTuple
+
+import numpy as np
+import pytask
+from pytask import Product, task
+
+from thesis.config import BLD, RNG
+from thesis.imbens_manski.im_funcs import simulation
+
+
+class _Arguments(NamedTuple):
+ p: float
+ n_obs: int
+ n_sims: int
+ path_to_results: Path
+ ci_type: str
+ alpha: float = 0.95
+ beta_params: tuple[float, float] = (0.5, 0.5)
+
+
+P_VALUES = np.linspace(0.9, 0.99, 25)
+N_OBS_VALUES = [100, 250, 1000]
+N_SIMS = 1_000
+N_BOOT = 1_000
+CI_TYPES = [
+ "analytical",
+ "bootstrap",
+]
+
+ID_TO_KWARGS = {
+ f"p_{p}_n_obs_{n_obs}_{ci_type}": _Arguments(
+ p=p,
+ n_obs=n_obs,
+ n_sims=N_SIMS,
+ ci_type=ci_type,
+ path_to_results=BLD
+ / "imbens_manski"
+ / "results"
+ / f"p_{p}_n_obs_{n_obs}_{ci_type}.pkl",
+ )
+ for p in P_VALUES
+ for n_obs in N_OBS_VALUES
+ for ci_type in CI_TYPES
+}
+
+for id_, kwargs in ID_TO_KWARGS.items():
+
+ @pytask.mark.skip()
+ @task(id=id_, kwargs=kwargs) # type: ignore[arg-type]
+ def task_manski_imbens_sims(
+ p: float,
+ n_obs: int,
+ n_sims: int,
+ alpha: float,
+ ci_type: str,
+ beta_params: tuple[float, float],
+ path_to_results: Annotated[Path, Product],
+ ) -> None:
+ """Run simulations for Imbens and Manski (2004) ECMA."""
+ res = simulation(
+ n_sims,
+ n_obs,
+ p,
+ alpha=alpha,
+ ci_type=ci_type,
+ beta_params=beta_params,
+ n_boot=N_BOOT,
+ rng=RNG,
+ )
+ res.to_pickle(path_to_results)
diff --git a/src/thesis/imbens_manski/im_funcs.py b/src/thesis/imbens_manski/im_funcs.py
new file mode 100644
index 00000000..5c47ba07
--- /dev/null
+++ b/src/thesis/imbens_manski/im_funcs.py
@@ -0,0 +1,172 @@
+"""Functions for Imbens and Manski (2004) ECMA simulations."""
+
+import numpy as np
+import pandas as pd # type: ignore[import-untyped]
+from scipy.stats import norm # type: ignore[import-untyped]
+
+from thesis.config import RNG
+
+
+def simulation(
+ n_sims: int,
+ n_obs: int,
+ p: float,
+ alpha: float,
+ ci_type: str,
+ rng: np.random.Generator = RNG,
+ beta_params: tuple[float, float] = (1, 1),
+ n_boot: int = 1_000,
+) -> pd.DataFrame:
+ """Run simulation."""
+ columns = [
+ "cilo_idset",
+ "cihi_idset",
+ "cilo_param",
+ "cihi_param",
+ "mu_bar",
+ "sigma_sq_bar",
+ ]
+
+ res = pd.DataFrame(
+ [
+ _experiment(
+ n_obs,
+ p,
+ alpha,
+ beta_params=beta_params,
+ rng=rng,
+ ci_type=ci_type,
+ n_boot=n_boot,
+ )
+ for _ in range(n_sims)
+ ],
+ columns=columns,
+ )
+
+ mean = beta_params[0] / (beta_params[0] + beta_params[1])
+
+ res["ci_idset_covers"] = (res["cilo_idset"] <= mean) & (mean <= res["cihi_idset"])
+ res["ci_param_covers"] = (res["cilo_param"] <= mean) & (mean <= res["cihi_param"])
+
+ return res
+
+
+def _experiment(
+ n_obs,
+ p: float,
+ alpha: float,
+ beta_params: tuple[float, float],
+ ci_type: str,
+ n_boot: int = 1_000,
+ rng: np.random.Generator = RNG,
+):
+ y, w = _draw_data(n_obs, p, beta_params=beta_params, rng=rng)
+
+ n1 = np.sum(w)
+
+ if n1 == 0:
+ return 0, 1
+
+ mu_bar = np.sum(y * w) / n1
+ sigma_sq_bar = np.sum(w * (y - mu_bar) ** 2) / np.clip(
+ (n1 - 1),
+ a_min=1,
+ a_max=None,
+ )
+
+ if ci_type == "analytical":
+ cilo_idset, cihi_idset = _compute_ci(
+ alpha,
+ p,
+ mu_bar,
+ sigma_sq_bar,
+ n_obs,
+ target="idset",
+ )
+ cilo_param, cihi_param = _compute_ci(
+ alpha,
+ p,
+ mu_bar,
+ sigma_sq_bar,
+ n_obs,
+ target="param",
+ )
+
+ elif ci_type == "bootstrap":
+ cilo_idset, cihi_idset = _compute_bootstrap_ci(
+ n_boot=n_boot,
+ y=y,
+ w=w,
+ alpha=alpha,
+ p=p,
+ target="idset",
+ )
+ cilo_param, cihi_param = _compute_bootstrap_ci(
+ n_boot=n_boot,
+ y=y,
+ w=w,
+ alpha=alpha,
+ p=p,
+ target="param",
+ )
+
+ return cilo_idset, cihi_idset, cilo_param, cihi_param, mu_bar, sigma_sq_bar
+
+
+def _draw_data(
+ n_obs: int,
+ p: float,
+ beta_params: tuple[float, float],
+ rng: np.random.Generator = RNG,
+) -> tuple[np.ndarray, np.ndarray]:
+ """Draw data."""
+ y = rng.beta(beta_params[0], beta_params[1], n_obs)
+ w = rng.binomial(1, p, n_obs)
+
+ return y, w
+
+
+def _compute_ci(
+ alpha: float,
+ p: float,
+ mu_bar: float,
+ sigma_sq_bar: float,
+ n_obs: int,
+ target: str,
+) -> tuple[float, float]:
+ if target == "idset":
+ z = norm.ppf((alpha + 1) / 2)
+ elif target == "param":
+ z = norm.ppf(alpha)
+
+ lower = (mu_bar - z * (np.sqrt(sigma_sq_bar) / np.sqrt(p * n_obs))) * p
+ upper = (mu_bar + z * (np.sqrt(sigma_sq_bar) / np.sqrt(p * n_obs))) * p + 1 - p
+
+ return lower, upper
+
+
+def _compute_bootstrap_ci(
+ n_boot: int,
+ y: np.ndarray,
+ w: np.ndarray,
+ alpha: float,
+ p: float,
+ target: str,
+ rng: np.random.Generator = RNG,
+) -> tuple[float, float]:
+ boot_mu_bar = np.zeros(n_boot)
+ y_nomiss = y[w == 1]
+
+ for b in range(n_boot):
+ boot_y_nomiss = rng.choice(y_nomiss, size=len(y_nomiss), replace=True)
+ boot_mu_bar[b] = np.mean(boot_y_nomiss)
+
+ if target == "idset":
+ mu_lo, mu_hi = np.quantile(boot_mu_bar, [(1 - alpha) / 2, 1 - (1 - alpha) / 2])
+ elif target == "param":
+ mu_lo, mu_hi = np.quantile(boot_mu_bar, [1 - alpha, alpha])
+
+ lower = mu_lo * p
+ upper = mu_hi * p + 1 - p
+
+ return lower, upper
diff --git a/src/thesis/misc/__init__.py b/src/thesis/misc/__init__.py
new file mode 100644
index 00000000..cdaaa2e9
--- /dev/null
+++ b/src/thesis/misc/__init__.py
@@ -0,0 +1 @@
+"""Miscaellaneaous scripts and notebooks for the thesis."""
diff --git a/src/thesis/misc/estimate_avars.py b/src/thesis/misc/estimate_avars.py
new file mode 100644
index 00000000..d180047c
--- /dev/null
+++ b/src/thesis/misc/estimate_avars.py
@@ -0,0 +1,87 @@
+"""Estimate asymptotic variances for some key estimators."""
+
+import numpy as np
+
+from thesis.classes import Instrument, LocalATEs
+from thesis.config import RNG
+from thesis.simple_model.funcs import (
+ _estimate_pscores,
+ _late,
+)
+from thesis.utilities import draw_data
+
+# --------------------------------------------------------------------------------------
+# Parameters
+# --------------------------------------------------------------------------------------
+
+n_obs = 100_000
+n_sim = 10_000
+late_complier = -0.5
+u_hi = 0.2
+rn = np.sqrt(n_obs)
+
+# --------------------------------------------------------------------------------------
+# Preliminary calculations
+# --------------------------------------------------------------------------------------
+
+local_ates = LocalATEs(
+ always_taker=0,
+ complier=late_complier,
+ never_taker=np.min((1, 1 + late_complier)),
+)
+
+instrument = Instrument(
+ support=np.array([0, 1]),
+ pmf=np.array([0.5, 0.5]),
+ pscores=np.array([0.4, 0.6]),
+)
+
+
+true_w = (instrument.pscores[1] - instrument.pscores[0]) / (
+ instrument.pscores[1] + u_hi - instrument.pscores[0]
+)
+
+true_late = late_complier
+true_idset = true_w * late_complier + (1 - true_w) * local_ates.always_taker
+
+# --------------------------------------------------------------------------------------
+# Asymptotic variance of LATE, w * LATE, w_hat * LATE and id_set
+# --------------------------------------------------------------------------------------
+
+sim_late = np.zeros(n_sim)
+sim_hat_w = np.zeros(n_sim)
+sim_late_true_w = np.zeros(n_sim)
+sim_late_hat_w = np.zeros(n_sim)
+sim_id_set = np.zeros(n_sim)
+sim_id_set_slope_1 = np.zeros(n_sim)
+
+for i in range(n_sim):
+ data = draw_data(
+ n_obs=n_obs,
+ local_ates=local_ates,
+ instrument=instrument,
+ rng=RNG,
+ )
+ _pscores = _estimate_pscores(data)
+ hat_w = (_pscores[1] - _pscores[0]) / (_pscores[1] + u_hi - _pscores[0])
+ sim_hat_w[i] = hat_w
+
+ sim_late[i] = _late(data)
+ sim_late_true_w[i] = _late(data) * true_w
+ sim_late_hat_w[i] = _late(data) * hat_w
+ sim_id_set[i] = _late(data) * hat_w + (1 - hat_w)
+ sim_id_set_slope_1[i] = _late(data) + (1 - hat_w)
+
+avar_late = (rn * (sim_late - true_late)).var()
+avar_late_true_w = (rn * (sim_late_true_w - true_late * true_w)).var()
+avar_late_hat_w = (rn * (sim_late_hat_w - true_late * true_w)).var()
+avar_id_set = (rn * (sim_id_set - (true_late * true_w + (1 - true_w)))).var()
+avar_id_set_slope_1 = (rn * (sim_id_set_slope_1)).var()
+
+avar_hat_w = (rn * sim_hat_w).var()
+
+cov_hat_w_late = np.cov(rn * sim_hat_w, rn * sim_late_hat_w)[0, 1]
+
+avar_id_set_2 = avar_late_hat_w + avar_hat_w - 2 * cov_hat_w_late
+
+assert np.isclose(avar_id_set, avar_id_set_2, rtol=1e-3)
diff --git a/src/thesis/pyvmte/__init__.py b/src/thesis/pyvmte/__init__.py
new file mode 100644
index 00000000..6254b9a5
--- /dev/null
+++ b/src/thesis/pyvmte/__init__.py
@@ -0,0 +1 @@
+"""Using pyvmte package for simple model."""
diff --git a/src/thesis/pyvmte/task_plot_simple_model_sharp.py b/src/thesis/pyvmte/task_plot_simple_model_sharp.py
new file mode 100644
index 00000000..6ebbde7c
--- /dev/null
+++ b/src/thesis/pyvmte/task_plot_simple_model_sharp.py
@@ -0,0 +1,196 @@
+"""Plot the simple model for a specification resulting in sharp bounds."""
+
+from pathlib import Path
+from typing import Annotated, NamedTuple
+
+import pandas as pd # type: ignore[import-untyped]
+import plotly.graph_objects as go # type: ignore[import-untyped]
+from pytask import Product, task
+
+from thesis.config import BLD
+
+
+# --------------------------------------------------------------------------------------
+# Task definitions
+# --------------------------------------------------------------------------------------
+class _Arguments(NamedTuple):
+ param_to_vary: str
+ path_to_plot: Annotated[Path, Product]
+ shape_constraint: tuple[str, str] = ("decreasing", "decreasing")
+ path_to_data: Path = BLD / "data" / "solutions_simple_model_sharp.pkl"
+
+
+ID_TO_KWARGS = {
+ "y1_c": _Arguments(
+ param_to_vary="y1_c",
+ path_to_plot=BLD / "figures" / "simple_model_sharp_by_y1_ct.png",
+ ),
+ "y0_c": _Arguments(
+ param_to_vary="y0_c",
+ path_to_plot=BLD / "figures" / "simple_model_sharp_by_y0_ct.png",
+ ),
+}
+
+for kwargs in ID_TO_KWARGS.values():
+
+ @task(kwargs=kwargs) # type: ignore[arg-type]
+ def task_plot_simple_model_solution_sharp(
+ param_to_vary: str,
+ path_to_plot: Annotated[Path, Product],
+ shape_constraint: tuple[str, str],
+ path_to_data: Path,
+ ) -> None:
+ """Plot solution to the simple model over a grid of parameter values."""
+ # Solve the program for a range of y1_c values from 0 to 1
+
+ results = pd.read_pickle(path_to_data) # noqa: S301
+
+ results = results[results["shape_constraint"] == shape_constraint]
+
+ y1_at = results["y1_at"][0]
+ y0_at = results["y0_at"][0]
+ y1_nt = results["y1_nt"][0]
+ y0_nt = results["y0_nt"][0]
+
+ # Get value of y1_c and y0_c closest to 0.5
+ if param_to_vary == "y1_c":
+ _idx_closest = results["y0_c"].sub(0.5).abs().idxmin()
+ _closest = results["y0_c"][_idx_closest]
+ results = results[results["y0_c"] == _closest]
+
+ elif param_to_vary == "y0_c":
+ _idx_closest = results["y1_c"].sub(0.5).abs().idxmin()
+ _closest = results["y1_c"][_idx_closest]
+ results = results[results["y1_c"] == _closest]
+
+ # ------------------------------------------------------------------------------
+ # Plot the results
+ # ------------------------------------------------------------------------------
+
+ fig = go.Figure()
+
+ fig.add_trace(
+ go.Scatter(
+ x=results[param_to_vary],
+ y=results["upper_bound"],
+ mode="lines",
+ name="Upper bound",
+ ),
+ )
+
+ fig.add_trace(
+ go.Scatter(
+ x=results[param_to_vary],
+ y=results["lower_bound"],
+ mode="lines",
+ name="Lower bound",
+ ),
+ )
+
+ # Add nan as red crosses
+ _idx_nan = results["upper_bound"].isna()
+ fig.add_trace(
+ go.Scatter(
+ x=results[param_to_vary][_idx_nan],
+ y=results[param_to_vary][_idx_nan] * 0,
+ mode="markers",
+ marker={"color": "red", "symbol": "x", "opacity": 0.1},
+ name="Empty identified set",
+ ),
+ )
+
+ fig.update_layout(
+ title=(
+ f"Bounds on the LATE for different values of {param_to_vary}"
+ f"
Other at 0.5, y1 at = {y1_at}, "
+ f"y0 at = {y0_at}, y1 nt = {y1_nt}, y0 nt = {y0_nt}
"
+ ),
+ xaxis_title=f"{param_to_vary}",
+ yaxis_title="Bounds on Target",
+ )
+
+ fig.write_image(path_to_plot)
+
+
+# --------------------------------------------------------------------------------------
+# Plot for changing both parameters simultaneously
+# --------------------------------------------------------------------------------------
+
+
+def task_plot_simple_model_solution_sharp_both(
+ path_to_plot_upper: Annotated[Path, Product] = BLD
+ / "figures"
+ / "simple_model_sharp_by_y0_y1_ct_upper.png",
+ path_to_plot_lower: Annotated[Path, Product] = BLD
+ / "figures"
+ / "simple_model_sharp_by_y0_y1_ct_lower.png",
+ path_to_data: Path = BLD / "data" / "solutions_simple_model_sharp.pkl",
+) -> None:
+ """Plot solution to the simple model over a meshgrid of parameter values."""
+ results = pd.read_pickle(path_to_data) # noqa: S301
+
+ y1_at = results["y1_at"][0]
+ y0_at = results["y0_at"][0]
+ y1_nt = results["y1_nt"][0]
+ y0_nt = results["y0_nt"][0]
+
+ # ----------------------------------------------------------------------------------
+ # Plot the results
+ # ----------------------------------------------------------------------------------
+
+ for bound in ["upper_bound", "lower_bound"]:
+ fig = go.Figure()
+
+ _idx_nan = results[bound].isna()
+
+ # Make scatter with coloring by res_upper but only for non-nan values
+ fig.add_trace(
+ go.Scatter(
+ x=results["y1_c"][~_idx_nan],
+ y=results["y0_c"][~_idx_nan],
+ mode="markers",
+ marker={
+ "color": results[bound][~_idx_nan],
+ "colorscale": "Viridis",
+ "colorbar": {
+ "title": f"ID set: {bound.replace('_', ' ').capitalize()}",
+ },
+ },
+ name="Solution region",
+ ),
+ )
+
+ # Plot the nan values
+ fig.add_trace(
+ go.Scatter(
+ x=results["y1_c"][_idx_nan],
+ y=results["y0_c"][_idx_nan],
+ mode="markers",
+ marker={"color": "red", "symbol": "x", "opacity": 0.1},
+ name="Empty identified set",
+ ),
+ )
+
+ fig.update_layout(
+ legend={
+ "x": 0.8, # Position the legend at the right side
+ "y": -0.2, # Center the legend vertically
+ "traceorder": "normal",
+ },
+ )
+
+ # Title the axes
+ fig.update_layout(
+ title=(
+ f"Bounds on the LATE for different values of complier outcomes"
+ f"
Decreasing MTR functions. y1 at = {y1_at}, "
+ f"y0 at = {y0_at}, y1 nt = {y1_nt}, y0 nt = {y0_nt}
"
+ ),
+ xaxis_title="$E[Y(1)|complier]$",
+ yaxis_title="$E[Y(0)|complier]$",
+ )
+
+ if bound == "upper_bound":
+ fig.write_image(path_to_plot_upper)
+ else:
+ fig.write_image(path_to_plot_lower)
diff --git a/src/thesis/pyvmte/task_solve_simple_model_sharp.py b/src/thesis/pyvmte/task_solve_simple_model_sharp.py
new file mode 100644
index 00000000..0c0a81e2
--- /dev/null
+++ b/src/thesis/pyvmte/task_solve_simple_model_sharp.py
@@ -0,0 +1,142 @@
+"""Solve the simple model for a specification resulting in sharp bounds."""
+
+from pathlib import Path
+from typing import Annotated
+
+import numpy as np
+import pandas as pd # type: ignore[import-untyped]
+from pytask import Product
+from pyvmte.classes import Estimand, Instrument # type: ignore[import-untyped]
+from pyvmte.identification import identification # type: ignore[import-untyped]
+
+from thesis.config import BLD
+
+# --------------------------------------------------------------------------------------
+# Preliminary parameters
+# --------------------------------------------------------------------------------------
+num_gridpoints = 25
+
+u_hi = 0.2
+
+target = Estimand(
+ "late",
+ u_lo=0.4,
+ u_hi=0.6 + u_hi,
+)
+
+identified = [
+ Estimand(esttype="cross", dz_cross=(d, z)) for d in [0, 1] for z in [0, 1]
+]
+
+pscore_lo = 0.4
+pscore_hi = 0.6
+
+instrument = Instrument(
+ support=np.array([0, 1]),
+ pmf=np.array([0.5, 0.5]),
+ pscores=np.array([pscore_lo, pscore_hi]),
+)
+
+bfunc_1 = {"type": "constant", "u_lo": 0.0, "u_hi": pscore_lo}
+bfunc_2 = {"type": "constant", "u_lo": pscore_lo, "u_hi": pscore_hi}
+bfunc_3 = {"type": "constant", "u_lo": pscore_hi, "u_hi": pscore_hi + u_hi}
+bfunc_4 = {"type": "constant", "u_lo": pscore_hi + u_hi, "u_hi": 1.0}
+
+bfuncs = [bfunc_1, bfunc_2, bfunc_3, bfunc_4]
+
+u_partition = np.array([0, pscore_lo, pscore_hi, pscore_hi + u_hi, 1])
+
+
+def _at(u: float) -> bool:
+ return u <= pscore_lo
+
+
+def _c(u: float) -> bool:
+ return pscore_lo <= u and pscore_hi > u
+
+
+def _nt(u: float) -> bool:
+ return u >= pscore_hi
+
+
+# The following MTR functions are decreasing and imply a decreasing MTE function.
+y1_at = 0.9
+y0_at = 0
+
+y1_nt = 0.5
+y0_nt = 0.4
+
+
+# Define function factories to avoid late binding
+# See https://stackoverflow.com/a/3431699
+def _make_m0(y0_c):
+ def _m0(u):
+ return y0_at * _at(u) + y0_c * _c(u) + y0_nt * _nt(u)
+
+ return _m0
+
+
+def _make_m1(y1_c):
+ def _m1(u):
+ return y1_at * _at(u) + y1_c * _c(u) + y1_nt * _nt(u)
+
+ return _m1
+
+
+def task_solve_simple_model_sharp(
+ num_gridpoints: int = num_gridpoints,
+ path_to_data: Annotated[Path, Product] = BLD
+ / "data"
+ / "solutions_simple_model_sharp.pkl",
+ shape_constraint: tuple[str, str] = ("decreasing", "decreasing"),
+) -> None:
+ """Solve the simple model for a range of parameter values."""
+ param_grid = np.linspace(0, 1, num_gridpoints)
+
+ # Generate solution for a meshgrid of parameter values
+ y1_c, y0_c = np.meshgrid(param_grid, param_grid)
+
+ # Flatten the meshgrid
+ y1_c_flat = y1_c.flatten()
+ y0_c_flat = y0_c.flatten()
+
+ results = []
+
+ for y1_c_val, y0_c_val in zip(y1_c_flat, y0_c_flat, strict=True):
+ _m1 = _make_m1(y1_c_val)
+ _m0 = _make_m0(y0_c_val)
+
+ # The identified set might be empty for some parameter value combinations.
+ try:
+ res = identification(
+ target=target,
+ identified_estimands=identified,
+ basis_funcs=bfuncs,
+ instrument=instrument,
+ u_partition=u_partition,
+ m0_dgp=_m0,
+ m1_dgp=_m1,
+ shape_constraints=shape_constraint,
+ )
+ except TypeError:
+ res = {"upper_bound": np.nan, "lower_bound": np.nan}
+
+ results.append(res)
+
+ # Put into pandas DataFrame and save to disk
+
+ df_res = pd.DataFrame(
+ {
+ "y1_c": y1_c_flat,
+ "y0_c": y0_c_flat,
+ "upper_bound": [r["upper_bound"] for r in results],
+ "lower_bound": [r["lower_bound"] for r in results],
+ "shape_constraint": [shape_constraint for _ in results],
+ "y1_at": y1_at,
+ "y0_at": y0_at,
+ "y1_nt": y1_nt,
+ "y0_nt": y0_nt,
+ },
+ )
+
+ df_res.to_pickle(path_to_data)
diff --git a/src/thesis/r_ivmte/.Rhistory b/src/thesis/r_ivmte/.Rhistory
new file mode 100644
index 00000000..d209f554
--- /dev/null
+++ b/src/thesis/r_ivmte/.Rhistory
@@ -0,0 +1,96 @@
+library("AER")
+library("ivmte")
+knitr::kable(head(AE, n = 10))
+lm(data = AE, worked ~ morekids)
+lm(data = AE, morekids ~ samesex)
+ivreg(data = AE, worked ~ morekids | samesex )
+# Simple bounds for ATT using moment approach
+results <- ivmte(data = AE,
+target = "att",
+m0 = ~ u + yob,
+m1 = ~ u + yob,
+ivlike = worked ~ morekids + samesex + morekids*samesex,
+propensity = morekids ~ samesex + yob,
+noisy = TRUE)
+# Simple bounds for ATT using moment approach
+results <- ivmte(data = AE,
+target = "att",
+m0 = ~ u + yob,
+m1 = ~ u + yob,
+ivlike = worked ~ morekids + samesex + morekids*samesex,
+propensity = morekids ~ samesex + yob,
+noisy = TRUE)
+library("AER")
+library("ivmte")
+# install.packages("ivmte")
+# install.packages("splines2 ")
+install.packages("lpSolveAPI ")
+library("AER")
+library("ivmte")
+knitr::kable(head(AE, n = 10))
+lm(data = AE, worked ~ morekids)
+lm(data = AE, morekids ~ samesex)
+ivreg(data = AE, worked ~ morekids | samesex )
+# Simple bounds for ATT using moment approach
+results <- ivmte(data = AE,
+target = "att",
+m0 = ~ u + yob,
+m1 = ~ u + yob,
+ivlike = worked ~ morekids + samesex + morekids*samesex,
+propensity = morekids ~ samesex + yob,
+noisy = TRUE)
+library(lpSolveAPI)
+# install.packages("ivmte")
+# install.packages("splines2 ")
+install.packages("lpSolveAPI ")
+# install.packages("ivmte")
+# install.packages("splines2 ")
+install.packages("lpSolveAPI")
+# install.packages("ivmte")
+install.packages("splines2")
+library("AER")
+library("ivmte")
+knitr::kable(head(AE, n = 10))
+lm(data = AE, worked ~ morekids)
+lm(data = AE, morekids ~ samesex)
+ivreg(data = AE, worked ~ morekids | samesex )
+# Simple bounds for ATT using moment approach
+results <- ivmte(data = AE,
+target = "att",
+m0 = ~ u + yob,
+m1 = ~ u + yob,
+ivlike = worked ~ morekids + samesex + morekids*samesex,
+propensity = morekids ~ samesex + yob,
+noisy = TRUE)
+library(lpSolveAPI)
+# Simple bounds for ATT using moment approach
+results <- ivmte(data = AE,
+target = "att",
+m0 = ~ u + yob,
+m1 = ~ u + yob,
+ivlike = worked ~ morekids + samesex + morekids*samesex,
+propensity = morekids ~ samesex + yob,
+noisy = TRUE)
+# Parametric restrictions on m0, m1
+args <- list(data = AE,
+ivlike = worked ~ morekids + samesex + morekids*samesex,
+target = "att",
+m0 = ~ u + I(u^2) + yob + u*yob,
+m1 = ~ u + I(u^2) + I(u^3) + yob + u*yob,
+propensity = morekids ~ samesex + yob)
+r <- do.call(ivmte, args)
+r
+AE
+length(AE)
+len(AE)
+shape(AE)
+a = AE
+# Parametric restrictions on m0, m1
+args <- list(data = AE,
+ivlike = worked ~ morekids + samesex + morekids*samesex,
+target = "att",
+m0 = ~ u + I(u^2) + yob + u*yob,
+m1 = ~ u + I(u^2) + I(u^3) + yob + u*yob,
+propensity = morekids ~ samesex + yob)
+r <- do.call(ivmte, args)
+r
diff --git a/src/thesis/r_ivmte/__init__.py b/src/thesis/r_ivmte/__init__.py
new file mode 100644
index 00000000..bbdbc89b
--- /dev/null
+++ b/src/thesis/r_ivmte/__init__.py
@@ -0,0 +1 @@
+"""Explore R ivmte package: https://github.com/jkcshea/ivmte/."""
diff --git a/src/thesis/r_ivmte/ivmte_simple_model.r b/src/thesis/r_ivmte/ivmte_simple_model.r
new file mode 100644
index 00000000..e3ab010f
--- /dev/null
+++ b/src/thesis/r_ivmte/ivmte_simple_model.r
@@ -0,0 +1,34 @@
+# install.packages("ivmte")
+# install.packages("splines2")
+# install.packages("lpSolveAPI")
+# install.packages("lsei ")
+# install.packages("AER")
+
+library("AER")
+library("ivmte")
+knitr::kable(head(AE, n = 10))
+
+lm(data = AE, worked ~ morekids)
+
+lm(data = AE, morekids ~ samesex)
+
+ivreg(data = AE, worked ~ morekids | samesex )
+
+# Simple bounds for ATT using moment approach
+results <- ivmte(data = AE,
+ target = "att",
+ m0 = ~ u + yob,
+ m1 = ~ u + yob,
+ ivlike = worked ~ morekids + samesex + morekids*samesex,
+ propensity = morekids ~ samesex + yob,
+ noisy = TRUE)
+
+# Parametric restrictions on m0, m1
+args <- list(data = AE,
+ ivlike = worked ~ morekids + samesex + morekids*samesex,
+ target = "att",
+ m0 = ~ u + I(u^2) + yob + u*yob,
+ m1 = ~ u + I(u^2) + I(u^3) + yob + u*yob,
+ propensity = morekids ~ samesex + yob)
+r <- do.call(ivmte, args)
+r
diff --git a/src/thesis/simple_model/__init__.py b/src/thesis/simple_model/__init__.py
new file mode 100644
index 00000000..ad905c7d
--- /dev/null
+++ b/src/thesis/simple_model/__init__.py
@@ -0,0 +1 @@
+"""Code demonstrating the failure of the bootstrap in simple examples."""
diff --git a/src/thesis/simple_model/funcs.py b/src/thesis/simple_model/funcs.py
new file mode 100644
index 00000000..2088088f
--- /dev/null
+++ b/src/thesis/simple_model/funcs.py
@@ -0,0 +1,581 @@
+"""Functions for bootstrap sampling experiment."""
+
+from collections.abc import Callable
+from functools import partial
+from math import comb
+
+import numpy as np
+import pandas as pd # type: ignore[import-untyped]
+from numpy.typing import ArrayLike
+from statsmodels.api import add_constant # type: ignore[import-untyped]
+from statsmodels.sandbox.regression.gmm import IV2SLS # type: ignore[import-untyped]
+
+from thesis.classes import Instrument, LocalATEs
+from thesis.utilities import draw_data
+
+
+def simulation_bootstrap(
+ n_sims: int,
+ n_obs: int,
+ n_boot: int,
+ u_hi: float,
+ local_ates: LocalATEs,
+ instrument: Instrument,
+ alpha: float,
+ constraint_mtr: str,
+ bootstrap_method: str,
+ rng: np.random.Generator,
+ bootstrap_params: dict[str, Callable] | None = None,
+) -> pd.DataFrame:
+ """Simulate the bootstrap experiment.
+
+ Args:
+ n_sims: Number of simulations.
+ n_obs: Number of observations.
+ n_boot: Number of bootstrap samples.
+ local_ates: Local average treatment effects by complier type.
+ u_hi: Upper bound of target parameter.
+ instrument: Instrument object containing properties of the instrument.
+ alpha: Bootstrap percentile for confidence interval (1-alpha for upper).
+ constraint_mtr: Constraint on the marginal treatment response functions.
+ rng: Random number generator.
+ bootstrap_method: Method to compute the bootstrap confidence interval.
+ bootstrap_params: Additional parameters for the bootstrap method depending on
+ the bootstrap_method.
+
+ Returns:
+ DataFrame with the results of the simulation.
+
+ """
+ if bootstrap_params is None:
+ bootstrap_params = {}
+ _check_constraint_supported(constraint_mtr)
+ _check_bootsrap_method_supported(bootstrap_method)
+ _check_instrument_and_u_hi_consistent(u_hi, instrument)
+ _check_complier_never_taker_consistent(local_ates, constraint_mtr)
+ _check_bootstrap_params_supplied(bootstrap_method, bootstrap_params)
+
+ results = np.zeros((n_sims, 2))
+
+ # TODO(@buddejul): Remove after debugging.
+ beta_lo = np.zeros(n_sims)
+ beta_hi = np.zeros(n_sims)
+
+ for i in range(n_sims):
+ data = draw_data(n_obs, local_ates=local_ates, instrument=instrument, rng=rng)
+
+ beta_lo[i], beta_hi[i] = _idset(
+ b_late=_late(data),
+ u_hi=u_hi,
+ pscores_hat=_estimate_pscores(data),
+ constraint_mtr=constraint_mtr,
+ )
+
+ results[i] = _bootstrap_ci(
+ n_boot=n_boot,
+ bootstrap_method=bootstrap_method,
+ bootstrap_params=bootstrap_params,
+ alpha=alpha,
+ u_hi=u_hi,
+ data=data,
+ constraint_mtr=constraint_mtr,
+ rng=rng,
+ )
+
+ out = pd.DataFrame(results, columns=["lo", "hi"])
+ out["true"] = _true_late(u_hi, instrument=instrument, local_ates=local_ates)
+ out["beta_lo"] = beta_lo
+ out["beta_hi"] = beta_hi
+
+ return out
+
+
+def _bootstrap_ci(
+ bootstrap_method: str,
+ alpha: float,
+ u_hi: float,
+ data: np.ndarray,
+ n_boot: int,
+ constraint_mtr: str,
+ rng: np.random.Generator,
+ bootstrap_params: dict[str, Callable] | None = None,
+) -> tuple[ArrayLike, ...]:
+ """Compute bootstrap confidence interval.
+
+ The function allows for different type of bootstrap confidence intervals:
+ - standard: Calculates phi(beta_s) for every bootstrap drawn. Then it returns the
+ quantiles of the bootstrap distribution.
+ - numerical delta: Separately bootstraps the distribution of beta_s and estimates a
+ a derivative phi'(beta_s) using a numerical approximation.
+ Based on Hong and Li (2018).
+ - analytical delta: Separately bootstraps the distribution of beta_s and estimates a
+ a derivative phi'(beta_s) utilizing the analytical structure of the derivative.
+ Based on Fang and Santos (2017).
+
+ """
+ # Note that we currently implement this by separately bootstrapping the data for
+ # each method. This is not the most efficient way to do this, but keeps the code
+ # easy to organize. If it turns we out we spend a significant time on resampling
+ # we might want to consider a more efficient implementation.
+
+ if bootstrap_params is None:
+ bootstrap_params = {}
+ if bootstrap_method == "standard":
+ return _ci_standard_bootstrap(
+ n_boot=n_boot,
+ data=data,
+ alpha=alpha,
+ u_hi=u_hi,
+ constraint_mtr=constraint_mtr,
+ rng=rng,
+ )
+ if bootstrap_method == "numerical_delta":
+ return _ci_numerical_delta_bootstrap(
+ n_boot=n_boot,
+ data=data,
+ alpha=alpha,
+ u_hi=u_hi,
+ constraint_mtr=constraint_mtr,
+ rng=rng,
+ eps_fun=bootstrap_params["eps_fun"],
+ )
+ if bootstrap_method == "analytical_delta":
+ return _ci_analytical_delta_bootstrap(
+ n_boot=n_boot,
+ data=data,
+ alpha=alpha,
+ u_hi=u_hi,
+ constraint_mtr=constraint_mtr,
+ rng=rng,
+ kappa_fun=bootstrap_params["kappa_fun"],
+ )
+
+ # In principle, this should not be needed because we check the supported methods
+ # after input. However, mypy does not seem to recognize this. Or maybe this function
+ # is called from somewhere else.
+ msg = f"Bootstrap method '{bootstrap_method}' not supported."
+ raise ValueError(msg)
+
+
+def _ci_standard_bootstrap(
+ n_boot: int,
+ data: np.ndarray,
+ alpha: float,
+ u_hi: float,
+ constraint_mtr: str,
+ rng: np.random.Generator,
+) -> tuple[float, float]:
+ """Compute the standard bootstrap confidence interval.
+
+ The standard, non-parametric approach calculates phi(X_hat) for each bootstrap. In
+ our case this amounts to computing the identified set based on the bootstrap sample.
+ Then quantiles of this bootstrap distribution are used to construct CIs.
+
+ """
+ boot_lo = np.zeros(n_boot)
+ boot_hi = np.zeros(n_boot)
+
+ n_obs = data.shape[0]
+
+ for i in range(n_boot):
+ boot_data, pscores_boot = _draw_bootstrap_data(data=data, n_obs=n_obs, rng=rng)
+
+ # TODO(@buddejul): This might be more efficiently implemented by vectorizing the
+ # _idset call on the _late and _estimate_pscores bootstrap estimates.
+ boot_lo[i], boot_hi[i] = _idset(
+ b_late=_late(boot_data),
+ u_hi=u_hi,
+ pscores_hat=pscores_boot,
+ constraint_mtr=constraint_mtr,
+ )
+
+ # Explicitly make this a float to avoid static typing issues. np.quantile returns a
+ # np.float64 type, which is not compatible with the float type hint, although this
+ # seems to be addressed in https://github.com/numpy/numpy/pull/27334.
+ # Note we also take (1 - ) alpha/2 quantiles. This is to keep to code consistent.
+ # Following the usual Imbens and Manski argument, when covering the parameter we
+ # would only use (1 - ) alpha quantiles.
+ return float(np.quantile(boot_lo, alpha / 2)), float(
+ np.quantile(boot_hi, 1 - alpha / 2),
+ )
+
+
+def _ci_numerical_delta_bootstrap(
+ n_boot: int,
+ data: np.ndarray,
+ alpha: float,
+ u_hi: float,
+ constraint_mtr: str,
+ rng: np.random.Generator,
+ eps_fun: Callable = lambda n: n ** (-1 / 6),
+) -> tuple[float, float]:
+ """Compute the numerical delta bootstrap confidence interval.
+
+ Based on Hong and Li (2018), for details see p. 382.
+
+ The stepsize is set to n^(-1/6) by default, which is at least theoretically
+ consistent with eps -> zero and rn * eps -> infty, i.e. eps converges more slowly.
+
+ """
+ n_obs = data.shape[0]
+
+ eps = eps_fun(n_obs)
+
+ rn = np.sqrt(n_obs)
+
+ late = _late(data)
+ _id_data = _idset(late, u_hi, _estimate_pscores(data), constraint_mtr)
+
+ boot_delta_lo = np.zeros(n_boot)
+ boot_delta_hi = np.zeros(n_boot)
+
+ for i in range(n_boot):
+ # Step 1: Draw Z_s from the bootstrap distribution.
+ boot_data, pscores_boot = _draw_bootstrap_data(data=data, n_obs=n_obs, rng=rng)
+
+ z_s = rn * (_late(boot_data) - late)
+
+ # Step 2: Compute the numerical derivative.
+ # Note that in our case we need to do this for both the lower and upper bound.
+
+ _id_plus_eps_boot = _idset(
+ b_late=late + eps * z_s,
+ u_hi=u_hi,
+ pscores_hat=pscores_boot,
+ constraint_mtr=constraint_mtr,
+ )
+
+ boot_delta_lo[i], boot_delta_hi[i] = (1 / eps) * (_id_plus_eps_boot - _id_data)
+
+ # Construct 1 - alpha two-sided equal-tailed confidence interval for phi(beta_s).
+ # Note: Based on the Imbens and Manski argument for covering the parameter - instead
+ # of the identified set - we might only need to take "alpha" critical values here,
+ # instead of alpha. To achieve this, the user may supply alpha * 2 as the alpha.
+
+ # Compute the lower CI bound for the lower bound of the identified set.
+ ci_lo = _id_data[0] - (1 / rn) * np.quantile(boot_delta_lo, 1 - alpha / 2)
+
+ # Compute the upper CI bound for the upper bound of the identified set.
+ ci_hi = _id_data[1] - (1 / rn) * np.quantile(boot_delta_hi, alpha / 2)
+
+ return ci_lo, ci_hi
+
+
+def _ci_analytical_delta_bootstrap(
+ n_boot: int,
+ data: np.ndarray,
+ alpha: float,
+ u_hi: float,
+ constraint_mtr: str,
+ rng: np.random.Generator,
+ kappa_fun: Callable,
+) -> tuple[float, float]:
+ """Compute the analytical delta bootstrap confidence interval.
+
+ Based on Fang and Santos (2017), adapted from Example 2.1 equation (26). kappa_fun
+ is the tuning parameter used for the pretest to estimate the derivative.
+
+ """
+ n_obs = data.shape[0]
+
+ kappa_n = kappa_fun(n_obs)
+
+ rn = np.sqrt(n_obs)
+
+ # Estimate late using 2SLS to get the standard error for estimating the derivative.
+ late, se_late = _late_2sls(data)
+
+ # Note se_late is the standard error of the LATE estimate, i.e. the estimate of the
+ # asymptotic variance divided by rn. Hence we rescale to the asymptotic variance.
+ sigma_hat = rn * se_late
+
+ pscores = _estimate_pscores(data)
+
+ boot_late_scaled = np.zeros(n_boot)
+ boot_w_scaled = np.zeros(n_boot)
+
+ w = (pscores[1] - pscores[0]) / (u_hi + pscores[1] - pscores[0])
+
+ # Step 1: Bootstrap quantiles for the identified parameter beta_s.
+ for i in range(n_boot):
+ # Step 1: Draw Z_s from the bootstrap distribution of beta_s.
+ boot_data, _ = _draw_bootstrap_data(data=data, n_obs=n_obs, rng=rng)
+
+ boot_pscores = _estimate_pscores(boot_data)
+
+ boot_late_scaled[i] = rn * (_late(boot_data) - late)
+ boot_w = (boot_pscores[1] - boot_pscores[0]) / (
+ u_hi + boot_pscores[1] - boot_pscores[0]
+ )
+
+ boot_w_scaled[i] = rn * (boot_w - w)
+
+ # Step 2: Estimate the derivative.
+ # We need two separate derivatives, since the upper and lower bound have
+ # different solutions.
+ # We apply the derivative also to the bootstrapped w, since this is a nuisance
+ # parameter. Otherwise, we understate the variance.
+ _kwargs = {
+ "b": boot_late_scaled,
+ "w": boot_w_scaled,
+ "beta_late": late,
+ "sigma_hat": sigma_hat,
+ "kappa_n": kappa_n,
+ "rn": rn,
+ }
+
+ _d_phi = partial(_d_phi_kink, **_kwargs)
+
+ # Note the solution for the upper bound has a "+(1-w)" and for the lower bound a
+ # "-(1-w)" term. Hence the slopes are -1 and 1, respectively.
+
+ if constraint_mtr == "none":
+ boot_d_phi_upper = _d_phi(
+ slope_left=boot_w,
+ slope_right=boot_w,
+ slope_w=-1,
+ )
+ boot_d_phi_lower = _d_phi(
+ slope_left=boot_w,
+ slope_right=boot_w,
+ slope_w=1,
+ )
+
+ elif constraint_mtr == "increasing":
+ boot_d_phi_upper = _d_phi(slope_left=1, slope_right=boot_w, slope_w=-1)
+ boot_d_phi_lower = _d_phi(slope_left=boot_w, slope_right=1, slope_w=1)
+
+ # Step 3: Construct confidence intervals
+ id_lo, id_hi = _idset(
+ b_late=late,
+ u_hi=u_hi,
+ pscores_hat=pscores,
+ constraint_mtr=constraint_mtr,
+ )
+
+ _c_1_minus_alpha_half = np.quantile(boot_d_phi_lower, 1 - alpha / 2)
+ boot_ci_lo = id_lo - _c_1_minus_alpha_half / rn
+
+ _c_alpha_half = np.quantile(boot_d_phi_upper, alpha / 2)
+ boot_ci_hi = id_hi - _c_alpha_half / rn
+
+ return boot_ci_lo, boot_ci_hi
+
+
+def _idset(
+ b_late: float,
+ u_hi: float,
+ pscores_hat: tuple[float, float],
+ constraint_mtr: str,
+) -> np.ndarray:
+ w = (pscores_hat[1] - pscores_hat[0]) / (u_hi + pscores_hat[1] - pscores_hat[0])
+
+ if constraint_mtr == "none":
+ lo = w * b_late - (1 - w)
+ hi = w * b_late + (1 - w)
+
+ # Restricting the MTRs to be increasing changes the solution to have a kink when
+ # viewed as a function of the identified parameter.
+ elif constraint_mtr == "increasing":
+ lo = _phi_kink(theta=b_late, kink=0, slope_left=w, slope_right=1) - (1 - w)
+ hi = _phi_kink(theta=b_late, kink=0, slope_left=1, slope_right=w) + (1 - w)
+
+ return np.array([lo, hi])
+
+
+def _late(data: np.ndarray) -> float:
+ """Estimate the LATE using the Wald estimator."""
+ yz1 = data[data[:, 2] == 1, 0].mean()
+ yz0 = data[data[:, 2] == 0, 0].mean()
+
+ dz1 = data[data[:, 2] == 1, 1].mean()
+ dz0 = data[data[:, 2] == 0, 1].mean()
+
+ return (yz1 - yz0) / (dz1 - dz0)
+
+
+def _late_2sls(data: np.ndarray) -> tuple[float, float]:
+ """Estimate the LATE using 2SLS; also returns standard error."""
+ # Data order is y, d, z, u.
+
+ # In Statsmodels endog refers to the dependent variable, while exog refers to the
+ # explanatory variables, including all controls and the - endogenous - treatment
+ # variable.
+ model = IV2SLS(
+ endog=data[:, 0],
+ exog=add_constant(data[:, 1]),
+ instrument=add_constant(data[:, 2]),
+ )
+
+ res = model.fit()
+
+ return res.params[1], res.bse[1]
+
+
+def _draw_bootstrap_data(
+ data: np.ndarray,
+ n_obs: int,
+ rng: np.random.Generator,
+ max_iter: int = 100,
+) -> tuple[np.ndarray, tuple[float, float]]:
+ """Draw data for the bootstrap; redraw if propensity scores are equal."""
+ boot_data = data[rng.choice(n_obs, size=n_obs, replace=True)]
+
+ pscores_boot = _estimate_pscores(boot_data)
+
+ if pscores_boot[0] != pscores_boot[1]:
+ return boot_data, pscores_boot
+
+ # If the pscores are not the same we redraw the data. We set a maximum number of
+ # iterations to avoid infinite loops.
+ i = 0
+ while pscores_boot[0] == pscores_boot[1]:
+ boot_data = data[rng.choice(n_obs, size=n_obs, replace=True)]
+ pscores_boot = _estimate_pscores(boot_data)
+ i = i + 1
+
+ if i == max_iter:
+ msg = (
+ f"Bootstrap failed to generate different propensity scores"
+ f"({max_iter} draws)."
+ )
+ raise ValueError(msg)
+
+ return boot_data, pscores_boot
+
+
+def _m0(u: float | np.ndarray) -> float | np.ndarray:
+ """Compute the m0 function."""
+ return 0.6 * _bern(0, 2, u) + 0.4 * _bern(1, 2, u) + 0.3 * _bern(2, 2, u)
+
+
+def _m1(u: float | np.ndarray) -> float | np.ndarray:
+ """Compute the m1 function."""
+ return 0.75 * _bern(0, 2, u) + 0.5 * _bern(1, 2, u) + 0.25 * _bern(2, 2, u)
+
+
+def _bern(v: int, n: int, x: float | np.ndarray) -> float | np.ndarray:
+ """Compute the Bernstein polynomial of degree n and index i at x."""
+ return comb(n, v) * x**v * (1 - x) ** (n - v)
+
+
+def _true_late(u_hi: float, instrument: Instrument, local_ates: LocalATEs) -> float:
+ _target_pop_size = u_hi + instrument.pscores[1] - instrument.pscores[0]
+ _complier_share = (instrument.pscores[1] - instrument.pscores[0]) / _target_pop_size
+
+ return (
+ _complier_share * local_ates.complier
+ + (1 - _complier_share) * local_ates.never_taker
+ )
+
+
+def _estimate_pscores(data: np.ndarray) -> tuple[float, float]:
+ """Estimate the propensity score."""
+ return (data[data[:, 2] == 0, 1].mean(), data[data[:, 2] == 1, 1].mean())
+
+
+def _phi_max(theta: float, cutoff: float = 0):
+ return np.maximum(theta, cutoff)
+
+
+def _phi_kink(
+ theta: float,
+ kink: float,
+ slope_left: float,
+ slope_right: float,
+) -> float:
+ return slope_left * theta * (theta < kink) + slope_right * theta * (theta >= kink)
+
+
+def _d_phi_kink(
+ b: float,
+ w: float,
+ beta_late: float,
+ sigma_hat: float,
+ kappa_n: float,
+ rn: float,
+ slope_left: float,
+ slope_right: float,
+ slope_w: float,
+ kink: float = 0,
+) -> float:
+ """Estimator for the derivative of the identified set."""
+ # TODO(@buddejul): Wrifte more general version allowing for different kink point.
+ # Currently we do this for a kink at zero; this should show up in the pre-test.
+
+ cond_right = rn * (beta_late - kink) / sigma_hat > kappa_n
+ cond_left = rn * (beta_late - kink) / sigma_hat < -kappa_n
+ cond_mid = (not cond_right) & (not cond_left)
+
+ # Note we need to add the variability resulting from estimating the propensity score
+ # in the derivative of the function: w * b +- (1 - w)
+ # TODO(@buddejul): This should probably also include a product-rule term.
+ return (
+ cond_right * b * slope_right
+ + cond_left * b * slope_left
+ + cond_mid * ((b < 0) * b * slope_left + (b >= 0) * b * slope_right)
+ + w * slope_w
+ + beta_late * w * (b >= 0)
+ )
+
+
+def _check_constraint_supported(constraint_mtr: str) -> None:
+ supported = ["none", "increasing"]
+ if constraint_mtr not in supported:
+ msg = (
+ f"Constraint '{constraint_mtr}' not supported.\n"
+ f"Supported constraints: {supported}"
+ )
+ raise ValueError(msg)
+
+
+def _check_bootsrap_method_supported(bootstrap_method: str) -> None:
+ supported = ["standard", "numerical_delta", "analytical_delta"]
+ if bootstrap_method not in supported:
+ msg = (
+ f"Bootstrap method '{bootstrap_method}' not supported.\n"
+ f"Supported bootstrap methods: {supported}"
+ )
+ raise ValueError(msg)
+
+
+def _check_instrument_and_u_hi_consistent(u_hi: float, instrument: Instrument) -> None:
+ if u_hi + instrument.pscores[1] > 1:
+ msg = (
+ f"Upper bound u_hi + pscores[1] = {u_hi + instrument.pscores[1]} "
+ f"exceeds 1. This is not allowed."
+ )
+ raise ValueError(msg)
+
+
+def _check_complier_never_taker_consistent(local_ates, constraint_mtr):
+ if constraint_mtr == "increasing" and (
+ local_ates.complier < 0 and local_ates.never_taker >= 1
+ ):
+ # To be consistent with increasing MTR funcs, the never-taker ATE can be at
+ # most min(1, 1 + complier ate). Hence, if the complier ATE is negative, the
+ # never-taker ATE needs to be smaller than 1.
+ msg = (
+ "Whenever late_complier < 0, the largest possible never-taker ATE is "
+ "1 + later_complier < 1."
+ )
+ raise ValueError(msg)
+
+
+def _check_bootstrap_params_supplied(
+ bootstrap_method: str,
+ bootstrap_params: dict[str, Callable],
+) -> None:
+ if bootstrap_method == "numerical_delta" and "eps_fun" not in bootstrap_params:
+ msg = (
+ "Numerical delta bootstrap method requires the user to supply an epsilon "
+ "function via bootstrap_params under key 'eps_fun'."
+ )
+ raise ValueError(msg)
+
+ if bootstrap_method == "analytical_delta" and "kappa_fun" not in bootstrap_params:
+ msg = (
+ "Analytical delta bootstrap method requires the user to supply a kappa "
+ "function via bootstrap_params under key 'kappa_fun'."
+ )
+ raise ValueError(msg)
diff --git a/src/thesis/simple_model/task_combine_sim_results.py b/src/thesis/simple_model/task_combine_sim_results.py
new file mode 100644
index 00000000..2e06c07e
--- /dev/null
+++ b/src/thesis/simple_model/task_combine_sim_results.py
@@ -0,0 +1,72 @@
+"""Task for combining simulation results into dataset."""
+
+from pathlib import Path
+from typing import Annotated
+
+import pandas as pd # type: ignore[import-untyped] # type: ignore[import-untyped]
+from pytask import Product
+
+from thesis.config import BLD
+from thesis.simple_model.task_simple_model_sims import (
+ EPS_FUNS_NUMERICAL_DELTA,
+ ID_TO_KWARGS,
+ _Arguments,
+)
+from thesis.utilities import get_func_as_string
+
+EPS_FUNS_STRINGS = [get_func_as_string(eps_fun) for eps_fun in EPS_FUNS_NUMERICAL_DELTA]
+KAPPA_FUNS_STRINGS = [
+ get_func_as_string(kappa_fun) for kappa_fun in EPS_FUNS_NUMERICAL_DELTA
+]
+
+
+def task_combine_sim_results(
+ id_to_kwargs: dict[str, _Arguments] = ID_TO_KWARGS,
+ path_to_data: Annotated[Path, Product] = Path(
+ BLD / "simple_model" / "sim_results_combined.pkl",
+ ),
+) -> None:
+ """Combine all simulation results into single dataframe."""
+ coverage = []
+
+ for kwargs in id_to_kwargs.values():
+ res = pd.read_pickle(kwargs.path_to_data) # noqa: S301
+ res["ci_covers_true_param"] = (res["lo"] <= res["true"]) & (
+ res["hi"] >= res["true"]
+ )
+ res["ci_length"] = res["hi"] - res["lo"]
+ coverage.append(
+ (
+ kwargs.u_hi,
+ kwargs.n_obs,
+ kwargs.local_ates.complier,
+ kwargs.bootstrap_method,
+ get_func_as_string(kwargs.bootstrap_params["eps_fun"]),
+ get_func_as_string(kwargs.bootstrap_params["kappa_fun"]),
+ kwargs.constraint_mtr,
+ res["true"].mean(),
+ res["ci_covers_true_param"].mean(),
+ res["ci_length"].mean(),
+ res["lo"].mean(),
+ res["hi"].mean(),
+ ),
+ )
+
+ cols = [
+ "u_hi",
+ "n_obs",
+ "late_complier",
+ "bootstrap_method",
+ "eps_fun",
+ "kappa_fun",
+ "constraint_mtr",
+ "true",
+ "coverage",
+ "length",
+ "ci_lo",
+ "ci_hi",
+ ]
+
+ data = pd.DataFrame(coverage, columns=cols)
+
+ data.to_pickle(path_to_data)
diff --git a/src/thesis/simple_model/task_plot_simple_model_sims.py b/src/thesis/simple_model/task_plot_simple_model_sims.py
new file mode 100644
index 00000000..82cdc394
--- /dev/null
+++ b/src/thesis/simple_model/task_plot_simple_model_sims.py
@@ -0,0 +1,248 @@
+"""Task for running bootstrap simulations."""
+
+from pathlib import Path
+from typing import Annotated
+
+import pandas as pd # type: ignore[import-untyped]
+import plotly.graph_objects as go # type: ignore[import-untyped]
+from pytask import Product
+
+from thesis.config import BLD
+from thesis.simple_model.task_simple_model_sims import (
+ EPS_FUNS_NUMERICAL_DELTA,
+)
+from thesis.utilities import get_func_as_string
+
+EPS_FUNS_STRINGS = [get_func_as_string(eps_fun) for eps_fun in EPS_FUNS_NUMERICAL_DELTA]
+KAPPA_FUNS_STRINGS = [
+ get_func_as_string(kappa_fun) for kappa_fun in EPS_FUNS_NUMERICAL_DELTA
+]
+
+
+# TODO(@buddejul): Put graphs below in a loop, currently there is a lot of copy/paste.
+# TODO(@buddejul): Include true parameters in plots.
+# TODO(@buddejul): Split tasks, function is too complex, see noqa below.
+def task_plot_simple_model_sims( # noqa: C901, PLR0912
+ path_to_data: Path = BLD / "simple_model" / "sim_results_combined.pkl",
+ path_to_plot_coverage: Annotated[Path, Product] = Path(
+ BLD / "simple_model" / "figures" / "coverage.png",
+ ),
+ path_to_plot_length: Annotated[Path, Product] = Path(
+ BLD / "simple_model" / "figures" / "length.png",
+ ),
+ path_to_plot_means_hi: Annotated[Path, Product] = Path(
+ BLD / "simple_model" / "figures" / "means_hi.png",
+ ),
+ path_to_plot_means_lo: Annotated[Path, Product] = Path(
+ BLD / "simple_model" / "figures" / "means_lo.png",
+ ),
+ path_to_plot_coverage_by_eps_fun: Annotated[Path, Product] = Path(
+ BLD / "simple_model" / "figures" / "coverage_eps_fun.png",
+ ),
+ path_to_plot_coverage_by_kappa_fun: Annotated[Path, Product] = Path(
+ BLD / "simple_model" / "figures" / "coverage_kappa_fun.png",
+ ),
+) -> None:
+ """Plot the coverage probability of the confidence interval."""
+ data = pd.read_pickle(path_to_data) # noqa: S301
+
+ # TODO(@buddejul): Do this properly and loop over mtr constraints.
+ data = data[data.constraint_mtr == "increasing"]
+
+ data = data.sort_values("late_complier")
+
+ color_by_bootstrap_method = {
+ "standard": "blue",
+ "numerical_delta": "red",
+ "analytical_delta": "green",
+ }
+
+ color_by_eps_fun = {
+ "npow(-1div1)": "black",
+ "npow(-1div2)": "blue",
+ "npow(-1div3)": "red",
+ "npow(-1div4)": "green",
+ "npow(-1div5)": "orange",
+ "npow(-1div6)": "purple",
+ }
+
+ color_by_kappa_fun = {
+ "npow(1div2)": "blue",
+ "npow(1div3)": "red",
+ "npow(1div6)": "purple",
+ "np.log(n)pow(1div2)": "green",
+ "(2xnp.log(np.log(n)))pow(1div2)": "orange",
+ }
+
+ line_type_by_n_obs = {
+ 250: "solid",
+ 1_000: "dash",
+ 10_000: "dot",
+ }
+
+ for col_to_plot in ["coverage", "length"]:
+ fig = go.Figure()
+ for n_obs in data.n_obs.unique():
+ for bootstrap_method in ["standard", "numerical_delta", "analytical_delta"]:
+ data_sub = data[
+ (data.n_obs == n_obs) & (data.bootstrap_method == bootstrap_method)
+ ]
+ if bootstrap_method == "numerical_delta":
+ data_sub = data_sub[data_sub["eps_fun"] == "npow(-1div2)"]
+ if bootstrap_method == "analytical_delta":
+ data_sub = data_sub[data_sub["kappa_fun"] == "np.log(n)pow(1div2)"]
+
+ fig.add_trace(
+ go.Scatter(
+ x=data_sub.late_complier,
+ y=data_sub[col_to_plot],
+ name=f"n_obs={n_obs}",
+ legendgroup=f"{bootstrap_method}",
+ legendgrouptitle_text=(
+ f"{bootstrap_method.replace('_', ' ').capitalize()} "
+ "Bootstrap"
+ ),
+ line={
+ "color": color_by_bootstrap_method[bootstrap_method],
+ "dash": line_type_by_n_obs[int(n_obs)],
+ "width": 2,
+ },
+ ),
+ )
+
+ fig.update_layout(
+ title=f"{col_to_plot.capitalize()} of CI for true parameter",
+ xaxis_title="LATE Complier",
+ yaxis_title=f"{col_to_plot.capitalize()}",
+ )
+
+ if col_to_plot == "coverage":
+ path_to_plot = path_to_plot_coverage
+ elif col_to_plot == "length":
+ path_to_plot = path_to_plot_length
+
+ fig.write_image(path_to_plot)
+
+ # ==================================================================================
+ # Plot Means
+ # ==================================================================================
+ params_to_stat = {
+ "ci_lo": {"title": "Means of CI Lower Bounds", "path": path_to_plot_means_lo},
+ "ci_hi": {"title": "Means of CI Upper Bounds", "path": path_to_plot_means_hi},
+ }
+
+ for stat in ["ci_hi", "ci_lo"]:
+ fig = go.Figure()
+ for n_obs in data.n_obs.unique():
+ for bootstrap_method in ["standard", "numerical_delta", "analytical_delta"]:
+ data_sub = data[
+ (data.n_obs == n_obs) & (data.bootstrap_method == bootstrap_method)
+ ]
+ if bootstrap_method == "numerical_delta":
+ data_sub = data_sub[data_sub["eps_fun"] == "npow(-1div2)"]
+ if bootstrap_method == "analytical_delta":
+ data_sub = data_sub[data_sub["kappa_fun"] == "npow(1div2)"]
+ fig.add_trace(
+ go.Scatter(
+ x=data_sub.late_complier,
+ y=data_sub[stat],
+ name=f"n_obs={n_obs}",
+ legendgroup=f"{bootstrap_method}",
+ legendgrouptitle_text=(
+ f"{bootstrap_method.replace('_', ' ').capitalize()}"
+ "Bootstrap"
+ ),
+ line={
+ "color": color_by_bootstrap_method[bootstrap_method],
+ "dash": line_type_by_n_obs[int(n_obs)],
+ },
+ ),
+ )
+
+ fig.update_layout(
+ title=params_to_stat[stat]["title"],
+ xaxis_title="late_complier",
+ yaxis_title="Mean CI Bounds",
+ )
+
+ fig.write_image(params_to_stat[stat]["path"])
+
+ # ==================================================================================
+ # Plot coverage by eps_fun
+ # ==================================================================================
+ fig = go.Figure()
+
+ for eps_fun in data.eps_fun.unique():
+ eps_fun_to_print = (
+ eps_fun.replace("npow", "n^")
+ .replace("div", "/")
+ .replace("(", "")
+ .replace(")", "")
+ )
+
+ for n_obs in data.n_obs.unique():
+ data_sub = data[data.eps_fun == eps_fun]
+ data_sub = data_sub[data_sub.n_obs == n_obs]
+ data_sub = data_sub[data_sub["bootstrap_method"] == "numerical_delta"]
+ fig.add_trace(
+ go.Scatter(
+ x=data_sub.late_complier,
+ y=data_sub.coverage,
+ name=f"n_obs={n_obs}",
+ legendgroup=f"{eps_fun}",
+ legendgrouptitle_text=(f"eps(n) = {eps_fun_to_print}"),
+ line={
+ "dash": line_type_by_n_obs[int(n_obs)],
+ "width": 2,
+ "color": color_by_eps_fun[eps_fun],
+ },
+ ),
+ )
+
+ fig.update_layout(
+ title="Coverage by eps_fun",
+ xaxis_title="late_complier",
+ yaxis_title="Coverage",
+ )
+
+ fig.write_image(path_to_plot_coverage_by_eps_fun)
+
+ # ==================================================================================
+ # Plot coverage by kappa_fun
+ # ==================================================================================
+ fig = go.Figure()
+
+ for kappa_fun in data.kappa_fun.unique():
+ kappa_fun_to_print = (
+ kappa_fun.replace("npow", "n^")
+ .replace("div", "/")
+ .replace("(", "")
+ .replace(")", "")
+ )
+
+ for n_obs in data.n_obs.unique():
+ data_sub = data[data.kappa_fun == kappa_fun]
+ data_sub = data_sub[data_sub.n_obs == n_obs]
+ data_sub = data_sub[data_sub["bootstrap_method"] == "analytical_delta"]
+ fig.add_trace(
+ go.Scatter(
+ x=data_sub.late_complier,
+ y=data_sub.coverage,
+ name=f"n_obs={n_obs}",
+ legendgroup=f"{kappa_fun}",
+ legendgrouptitle_text=(f"eps(n) = {kappa_fun_to_print}"),
+ line={
+ "dash": line_type_by_n_obs[int(n_obs)],
+ "width": 2,
+ "color": color_by_kappa_fun[kappa_fun],
+ },
+ ),
+ )
+
+ fig.update_layout(
+ title="Coverage by kappa_fun",
+ xaxis_title="late_complier",
+ yaxis_title="Coverage",
+ )
+
+ fig.write_image(path_to_plot_coverage_by_kappa_fun)
diff --git a/src/thesis/simple_model/task_plot_solution.py b/src/thesis/simple_model/task_plot_solution.py
new file mode 100644
index 00000000..458bedf7
--- /dev/null
+++ b/src/thesis/simple_model/task_plot_solution.py
@@ -0,0 +1,104 @@
+"""Plot the true solution for the simple model setting."""
+
+from pathlib import Path
+from typing import Annotated, NamedTuple
+
+import numpy as np
+import pandas as pd # type: ignore[import-untyped]
+import plotly.graph_objects as go # type: ignore[import-untyped]
+from pytask import Product, task
+
+from thesis.config import BLD
+from thesis.simple_model.funcs import _idset
+
+
+class _Arguments(NamedTuple):
+ constraint_mtr: str
+ path_to_plot: Annotated[Path, Product]
+ u_hi: float = 0.2
+ grid_points: int = 250
+ endpoints: tuple[float, float] = (-0.1, 0.1)
+ pscores: tuple[float, float] = (0.4, 0.6)
+
+
+constr_mtr_to_plot = ["none", "increasing"]
+
+ID_TO_KWARGS = {
+ f"plot_constraint_mtr_{constraint_mtr}": _Arguments(
+ constraint_mtr=constraint_mtr,
+ path_to_plot=Path(
+ BLD
+ / "simple_model"
+ / "solutions"
+ / f"solution_constraint_mtr_{constraint_mtr}.png",
+ ),
+ )
+ for constraint_mtr in constr_mtr_to_plot
+}
+
+for id_, kwargs in ID_TO_KWARGS.items():
+
+ @task(id=id_, kwargs=kwargs) # type: ignore[arg-type]
+ def task_plot_solution(
+ u_hi: float,
+ grid_points: int,
+ endpoints: tuple[float, float],
+ pscores: tuple[float, float],
+ constraint_mtr: str,
+ path_to_plot: Annotated[Path, Product],
+ ) -> None:
+ """Plot the solution for the simple model."""
+ beta_grid = np.linspace(endpoints[0], endpoints[1], grid_points)
+
+ solutions = np.zeros((grid_points, 2))
+
+ for i, beta in enumerate(beta_grid):
+ solutions[i, :] = _idset(
+ b_late=beta,
+ u_hi=u_hi,
+ pscores_hat=pscores,
+ constraint_mtr=constraint_mtr,
+ )
+
+ data = pd.DataFrame(solutions, columns=["lower_bound", "upper_bound"])
+
+ fig = go.Figure()
+
+ fig.add_trace(
+ go.Scatter(
+ x=beta_grid,
+ y=data["lower_bound"],
+ mode="lines",
+ name="Lower Bound",
+ ),
+ )
+
+ fig.add_trace(
+ go.Scatter(
+ x=beta_grid,
+ y=data["upper_bound"],
+ mode="lines",
+ name="Upper Bound",
+ ),
+ )
+
+ fig.update_layout(
+ title=(
+ "Solution for the Simple Model: "
+ f"MTR Constraint = {constraint_mtr.capitalize()}"
+ ),
+ xaxis_title="Beta",
+ yaxis_title="Value",
+ )
+
+ # Add a note with all parameters
+ fig.add_annotation(
+ text=f"u_hi = {u_hi}, pscores = {pscores}",
+ xref="paper",
+ yref="paper",
+ x=0.5,
+ y=-0.1,
+ showarrow=False,
+ )
+
+ fig.write_image(path_to_plot)
diff --git a/src/thesis/simple_model/task_simple_model_sims.py b/src/thesis/simple_model/task_simple_model_sims.py
new file mode 100644
index 00000000..ffdcd7b7
--- /dev/null
+++ b/src/thesis/simple_model/task_simple_model_sims.py
@@ -0,0 +1,144 @@
+"""Task for running bootstrap simulations."""
+
+from collections.abc import Callable
+from pathlib import Path
+from typing import Annotated, NamedTuple
+
+import numpy as np
+import pytask
+from pytask import Product, task
+
+from thesis.classes import Instrument, LocalATEs
+from thesis.config import BLD, RNG
+from thesis.simple_model.funcs import simulation_bootstrap
+
+
+class _Arguments(NamedTuple):
+ u_hi: float
+ pscore_low: float
+ n_obs: int
+ local_ates: LocalATEs
+ constraint_mtr: str
+ bootstrap_method: str
+ bootstrap_params: dict[str, Callable]
+ path_to_data: Path | None = None
+ pscore_hi: float = 0.6
+ alpha: float = 0.05
+ n_boot: int = 500
+ n_sims: int = 2_000
+ rng: np.random.Generator = RNG
+
+
+U_HI = [0.2]
+N_OBS = [1_000, 10_000]
+PSCORES_LOW = [0.4]
+CONSTRAINTS_MTR = ["increasing"]
+BOOTSTRAP_METHODS = ["standard", "numerical_delta", "analytical_delta"]
+LATES_COMPLIER = np.concat((np.linspace(-0.4, 0.4, num=20), np.zeros(1)))
+EPS_FUNS_NUMERICAL_DELTA = [
+ lambda n: n ** (-1 / 2),
+]
+KAPPA_FUNS_ANALYTICAL_DELTA = [
+ lambda n: np.log(n) ** (1 / 2),
+]
+
+
+# TODO(@buddejul): An alternative way to do this would be to simply give this a
+# non-meaningful name (e.g. simple_models_sims_`i`) and store all params in the dataset
+# or a separate dict (probably more efficient). We can then retrieve the params from the
+# dataset or dict after saving. We can then query the settings dict before merging.
+KWARGS = [
+ _Arguments(
+ u_hi=u_hi,
+ n_obs=n_obs,
+ pscore_low=pscore_low,
+ local_ates=LocalATEs(
+ always_taker=0,
+ complier=late_complier,
+ # The following ensures that the model is always correctly specified under
+ # increasing MTR functions: Whenever late_complier < 0, the largest possible
+ # never-taker ATE is 1 + later_complier < 1.
+ never_taker=np.min((1, 1 + late_complier)),
+ ),
+ constraint_mtr=constraint_mtr,
+ bootstrap_method=bootstrap_method,
+ bootstrap_params={"eps_fun": eps_fun, "kappa_fun": kappa_fun},
+ )
+ for u_hi in U_HI
+ for n_obs in N_OBS
+ for pscore_low in PSCORES_LOW
+ for late_complier in LATES_COMPLIER
+ for constraint_mtr in CONSTRAINTS_MTR
+ for bootstrap_method in BOOTSTRAP_METHODS
+ for eps_fun in EPS_FUNS_NUMERICAL_DELTA
+ for kappa_fun in KAPPA_FUNS_ANALYTICAL_DELTA
+ # For standard bootstrap, we only need to run the simulation once
+ if not (
+ bootstrap_method == "standard"
+ and (
+ eps_fun is not EPS_FUNS_NUMERICAL_DELTA[0]
+ or kappa_fun is not KAPPA_FUNS_ANALYTICAL_DELTA[0]
+ )
+ )
+ # For the analytical delta method, we only need to run the simulation once for each
+ # kappa_fun, but not for different eps_fun
+ if not (
+ bootstrap_method == "analytical_delta"
+ and eps_fun is not EPS_FUNS_NUMERICAL_DELTA[0]
+ )
+ # For the numerical delta method, we only need to run the simulation once for each
+ # eps_fun, but not for different kappa_fun
+ if not (
+ bootstrap_method == "numerical_delta"
+ and kappa_fun is not KAPPA_FUNS_ANALYTICAL_DELTA[0]
+ )
+]
+
+ID_TO_KWARGS = {str(id_): kwargs for id_, kwargs in enumerate(KWARGS)}
+
+for id_, kwargs in ID_TO_KWARGS.items():
+ ID_TO_KWARGS[id_] = kwargs._replace(
+ path_to_data=BLD / "simple_model" / kwargs.bootstrap_method / f"{id_}.pkl",
+ )
+
+for id_, kwargs in ID_TO_KWARGS.items():
+
+ @pytask.mark.skip()
+ @task(id=id_, kwargs=kwargs) # type: ignore[arg-type]
+ def task_bootstrap_sims(
+ n_sims: int,
+ n_obs: int,
+ n_boot: int,
+ u_hi: float,
+ alpha: float,
+ pscore_low: float,
+ pscore_hi: float,
+ local_ates: LocalATEs,
+ constraint_mtr: str,
+ bootstrap_method: str,
+ bootstrap_params: dict[str, Callable],
+ rng: np.random.Generator,
+ path_to_data: Annotated[Path, Product],
+ ) -> None:
+ """Task for running bootstrap simulations."""
+ instrument = Instrument(
+ support=np.array([0, 1]),
+ pmf=np.array([0.5, 0.5]),
+ pscores=np.array([pscore_low, pscore_hi]),
+ )
+
+ res = simulation_bootstrap(
+ n_sims=n_sims,
+ n_obs=n_obs,
+ n_boot=n_boot,
+ u_hi=u_hi,
+ local_ates=local_ates,
+ alpha=alpha,
+ instrument=instrument,
+ constraint_mtr=constraint_mtr,
+ rng=rng,
+ bootstrap_method=bootstrap_method,
+ bootstrap_params=bootstrap_params,
+ )
+
+ res.to_pickle(path_to_data)
diff --git a/src/thesis/utilities.py b/src/thesis/utilities.py
index 3ef10780..c2b83a55 100644
--- a/src/thesis/utilities.py
+++ b/src/thesis/utilities.py
@@ -1,6 +1,13 @@
"""Utilities used in various parts of the project."""
-import yaml
+import inspect
+from collections.abc import Callable
+from pathlib import Path
+
+import numpy as np
+import yaml # type: ignore[import-untyped]
+
+from thesis.classes import Instrument, LocalATEs
def read_yaml(path):
@@ -13,7 +20,7 @@ def read_yaml(path):
dict: The parsed YAML file.
"""
- with open(path) as stream:
+ with Path.open(path) as stream:
try:
out = yaml.safe_load(stream)
except yaml.YAMLError as error:
@@ -23,3 +30,45 @@ def read_yaml(path):
)
raise ValueError(info) from error
return out
+
+
+def get_func_as_string(func: Callable) -> str:
+ """Inspects a function and converts the first body line into a string."""
+ func_str = str(inspect.getsourcelines(func)[0])
+ func_str = func_str.split(":")[1].strip()
+ func_str = func_str.removesuffix(")\\n']")
+ func_str = func_str.removesuffix(",\\n']")
+ func_str = func_str.replace(" ", "")
+ func_str = func_str.replace("**", "pow")
+ func_str = func_str.replace("*", "x")
+ return func_str.replace("/", "div")
+
+
+def draw_data(
+ n_obs: int,
+ local_ates: LocalATEs,
+ instrument: Instrument,
+ rng: np.random.Generator,
+) -> np.ndarray:
+ """Draw data for given local ates and instrument."""
+ z = rng.choice(instrument.support, size=n_obs, p=instrument.pmf)
+
+ u = rng.uniform(low=0, high=1, size=n_obs)
+
+ d = np.where(z == 1, u <= instrument.pscores[1], u <= instrument.pscores[0])
+
+ _never_takers = u <= instrument.pscores[0]
+ _compliers = (u > instrument.pscores[0]) & (u <= instrument.pscores[1])
+ _always_takers = u > instrument.pscores[1]
+
+ y0 = np.zeros(n_obs)
+
+ y1 = (
+ _never_takers * local_ates.never_taker
+ + _compliers * local_ates.complier
+ + _always_takers * local_ates.always_taker
+ )
+
+ y = d * y1 + (1 - d) * y0 + rng.normal(scale=0.1, size=n_obs)
+
+ return np.column_stack((y, d, z, u))
diff --git a/tests/analysis/__init__.py b/tests/analysis/__init__.py
deleted file mode 100644
index 5f03a7a4..00000000
--- a/tests/analysis/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-"""Tests for the analysis module."""
diff --git a/tests/analysis/test_model.py b/tests/analysis/test_model.py
deleted file mode 100644
index 80625614..00000000
--- a/tests/analysis/test_model.py
+++ /dev/null
@@ -1,36 +0,0 @@
-"""Tests for the regression model."""
-
-import numpy as np
-import pandas as pd
-import pytest
-from thesis.analysis.model import fit_logit_model
-
-DESIRED_PRECISION = 10e-2
-
-
-@pytest.fixture()
-def data():
- np.random.default_rng(seed=0)
- x = np.random.Generator.normal(size=100_000)
- coef = 2.0
- prob = 1 / (1 + np.exp(-coef * x))
- return pd.DataFrame(
- {"outcome_numerical": np.random.Generator.binomial(1, prob), "covariate": x},
- )
-
-
-@pytest.fixture()
-def data_info():
- return {"outcome": "outcome", "outcome_numerical": "outcome_numerical"}
-
-
-def test_fit_logit_model_recover_coefficients(data, data_info):
- model = fit_logit_model(data, data_info, model_type="linear")
- params = model.params
- assert np.abs(params["Intercept"]) < DESIRED_PRECISION
- assert np.abs(params["covariate"] - 2.0) < DESIRED_PRECISION
-
-
-def test_fit_logit_model_error_model_type(data, data_info):
- with pytest.raises(ValueError): # noqa: PT011
- assert fit_logit_model(data, data_info, model_type="quadratic")
diff --git a/tests/analysis/test_predict.py b/tests/analysis/test_predict.py
deleted file mode 100644
index 0c8070ec..00000000
--- a/tests/analysis/test_predict.py
+++ /dev/null
@@ -1,46 +0,0 @@
-"""Tests for the prediction model."""
-
-import pandas as pd
-import pytest
-from pandas.testing import assert_frame_equal
-from thesis.analysis.predict import predict_prob_by_age
-
-
-@pytest.fixture()
-def data():
- out = pd.DataFrame([1, 2, 3], columns=["age"])
- out["education"] = ["high-school", "high-school", "university"]
- out["income"] = ["high", "low", "low"]
- return out
-
-
-@pytest.fixture()
-def model():
- class ModelClass:
- @staticmethod
- def predict(data):
- if "high-school" in data["education"].to_numpy():
- prob = 0.1 if "low" in data["income"].to_numpy() else 0.2
- else:
- prob = 0.3 if "low" in data["income"].to_numpy() else 0.4
- return prob * data["age"]
-
- return ModelClass
-
-
-@pytest.mark.parametrize("group", ["education", "income"])
-def test_predict_prob_over_age(data, model, group):
- got = predict_prob_by_age(data, model, group)
-
- if group == "education":
- expected = pd.DataFrame(
- [[1, 0.1, 0.3], [2, 0.2, 0.6], [3, 0.3, 0.9]],
- columns=["age", "high-school", "university"],
- )
- else:
- expected = pd.DataFrame(
- [[1, 0.2, 0.1], [2, 0.4, 0.2], [3, 0.6, 0.3]],
- columns=["age", "high", "low"],
- )
-
- assert_frame_equal(got, expected)
diff --git a/tests/bootstrap/__init__.py b/tests/bootstrap/__init__.py
new file mode 100644
index 00000000..0ec0c327
--- /dev/null
+++ b/tests/bootstrap/__init__.py
@@ -0,0 +1 @@
+"""Tests for bootstrap procedures."""
diff --git a/tests/bootstrap/test_bootstrap_off_kink.py b/tests/bootstrap/test_bootstrap_off_kink.py
new file mode 100644
index 00000000..8d37a54a
--- /dev/null
+++ b/tests/bootstrap/test_bootstrap_off_kink.py
@@ -0,0 +1,81 @@
+"""Test bootstrap confidence intervals have right coverage far off the kink."""
+
+import numpy as np
+import pytest
+from thesis.classes import Instrument, LocalATEs
+from thesis.config import RNG
+from thesis.simple_model.funcs import simulation_bootstrap
+
+
+@pytest.fixture()
+def setup():
+ complier_late = -0.5
+
+ local_ates = LocalATEs(
+ always_taker=0,
+ complier=complier_late,
+ never_taker=np.min((1, 1 + complier_late)),
+ )
+
+ instrument = Instrument(
+ support=np.array([0, 1]),
+ pmf=np.array([0.5, 0.5]),
+ pscores=np.array([0.4, 0.6]),
+ )
+
+ return local_ates, instrument, complier_late
+
+
+def _bic(n):
+ """BIC."""
+ return np.sqrt(np.log(n))
+
+
+@pytest.mark.parametrize("method", ["numerical_delta"])
+def test_bootstrap_coverage(setup, method):
+ local_ates, instrument, complier_late = setup
+
+ expected = 0.95
+
+ n_obs = 10_000
+ n_boot = 500
+ n_sims = 100
+
+ bootstrap_params = {
+ "eps_fun": np.sqrt,
+ "kappa_fun": _bic,
+ }
+
+ res = simulation_bootstrap(
+ n_sims=n_sims,
+ n_obs=n_obs,
+ n_boot=n_boot,
+ u_hi=0.2,
+ local_ates=local_ates,
+ instrument=instrument,
+ alpha=0.05,
+ constraint_mtr="increasing",
+ bootstrap_method=method,
+ rng=RNG,
+ bootstrap_params=bootstrap_params,
+ )
+
+ res["covers"] = (res["lo"] <= res["true"]) & (res["hi"] >= res["true"])
+ res["covers_hi"] = res["hi"] >= res["true"]
+ res["covers_lo"] = res["lo"] <= res["true"]
+
+ # Calculate critical values of CI
+ res["c_hi"] = res["hi"] - res["beta_hi"]
+ res["c_lo"] = res["lo"] - res["beta_lo"]
+
+ # Assert they are always >= 0
+ assert np.all(res["c_hi"] >= 0)
+ assert np.all(res["c_lo"] <= 0)
+
+ # Coverage should be determined by upper bound
+ assert res["covers_lo"].mean() == 1
+ assert np.all(res["covers_hi"] == res["covers"])
+
+ actual = res.mean()["covers"]
+
+ assert actual == pytest.approx(expected, abs=0.051)
diff --git a/tests/data_management/__init__.py b/tests/data_management/__init__.py
deleted file mode 100644
index 90c3ffa0..00000000
--- a/tests/data_management/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-"""Tests for the data management module."""
diff --git a/tests/data_management/data_fixture.csv b/tests/data_management/data_fixture.csv
deleted file mode 100644
index 99a2bd2f..00000000
--- a/tests/data_management/data_fixture.csv
+++ /dev/null
@@ -1,5 +0,0 @@
-col0,col1,col2,col3
-yes,a,3.0,4
-no,a,4.5,2
-no,b,-1.0,3
-NaN,b,2.0,10
diff --git a/tests/data_management/data_info_fixture.yaml b/tests/data_management/data_info_fixture.yaml
deleted file mode 100644
index c26773cb..00000000
--- a/tests/data_management/data_info_fixture.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
----
-columns_to_drop: [col2]
-categorical_columns: [col0, col1]
-column_rename_mapping:
- col1: column_1
-outcome: col0
-outcome_numerical: col0_numerical
diff --git a/tests/data_management/test_clean_data.py b/tests/data_management/test_clean_data.py
deleted file mode 100644
index 419cc3bc..00000000
--- a/tests/data_management/test_clean_data.py
+++ /dev/null
@@ -1,50 +0,0 @@
-import numpy as np
-import pandas as pd
-import pytest
-from thesis.config import TEST_DIR
-from thesis.data_management import clean_data
-from thesis.utilities import read_yaml
-
-
-@pytest.fixture()
-def data():
- return pd.read_csv(TEST_DIR / "data_management" / "data_fixture.csv")
-
-
-@pytest.fixture()
-def data_info():
- return read_yaml(TEST_DIR / "data_management" / "data_info_fixture.yaml")
-
-
-def test_clean_data_drop_columns(data, data_info):
- data_clean = clean_data(data, data_info)
- assert not set(data_info["columns_to_drop"]).intersection(set(data_clean.columns))
-
-
-def test_clean_data_dropna(data, data_info):
- data_clean = clean_data(data, data_info)
- assert not data_clean.isna().any(axis=None)
-
-
-def test_clean_data_categorical_columns(data, data_info):
- data_clean = clean_data(data, data_info)
- for cat_col in data_info["categorical_columns"]:
- renamed_col = data_info["column_rename_mapping"].get(cat_col, cat_col)
- assert data_clean[renamed_col].dtype == "category"
-
-
-def test_clean_data_column_rename(data, data_info):
- data_clean = clean_data(data, data_info)
- old_names = set(data_info["column_rename_mapping"].keys())
- new_names = set(data_info["column_rename_mapping"].values())
- assert not old_names.intersection(set(data_clean.columns))
- assert new_names.intersection(set(data_clean.columns)) == new_names
-
-
-def test_convert_outcome_to_numerical(data, data_info):
- data_clean = clean_data(data, data_info)
- outcome_name = data_info["outcome"]
- outcome_numerical_name = data_info["outcome_numerical"]
- assert outcome_numerical_name in data_clean.columns
- assert data_clean[outcome_name].dtype == "category"
- assert data_clean[outcome_numerical_name].dtype == np.int8
diff --git a/tests/imbens_manski/__init__.py b/tests/imbens_manski/__init__.py
new file mode 100644
index 00000000..85a77a1b
--- /dev/null
+++ b/tests/imbens_manski/__init__.py
@@ -0,0 +1 @@
+"""Tests for Imbens Manski simulations."""
diff --git a/tests/imbens_manski/test_im_funcs.py b/tests/imbens_manski/test_im_funcs.py
new file mode 100644
index 00000000..9c319140
--- /dev/null
+++ b/tests/imbens_manski/test_im_funcs.py
@@ -0,0 +1,7 @@
+"""Tests for Imbens Manski simulations."""
+from thesis.config import RNG
+
+
+def test_compute_bootstrap_ci() -> None:
+ """Test compute_bootstrap_ci."""
+ RNG.normal(0, 1, 100)
diff --git a/tests/simple_model/__init__.py b/tests/simple_model/__init__.py
new file mode 100644
index 00000000..461f7e2a
--- /dev/null
+++ b/tests/simple_model/__init__.py
@@ -0,0 +1 @@
+"""Tests for the simple_model functions."""
diff --git a/tests/simple_model/test_debug_sm_cis.py b/tests/simple_model/test_debug_sm_cis.py
new file mode 100644
index 00000000..abfd78d2
--- /dev/null
+++ b/tests/simple_model/test_debug_sm_cis.py
@@ -0,0 +1,41 @@
+"""Tests for debugging simple model CIs."""
+
+import numpy as np
+from thesis.classes import Instrument, LocalATEs
+from thesis.config import RNG
+from thesis.simple_model.funcs import simulation_bootstrap
+
+
+def test_debug_sm():
+ n_sims = 2
+ n_obs = 10_000
+ n_boot = 2_000
+ u_hi = 0.2
+ alpha = 0.05
+ constraint_mtr = "none"
+ bootstrap_method = "standard"
+
+ instrument = Instrument(
+ support=np.array([0, 1]),
+ pmf=np.array([0.5, 0.5]),
+ pscores=np.array([0.4, 0.6]),
+ )
+
+ local_ates = LocalATEs(
+ always_taker=0,
+ complier=-0.1,
+ never_taker=1,
+ )
+
+ simulation_bootstrap(
+ n_sims=n_sims,
+ n_obs=n_obs,
+ n_boot=n_boot,
+ u_hi=u_hi,
+ alpha=alpha,
+ rng=RNG,
+ constraint_mtr=constraint_mtr,
+ bootstrap_method=bootstrap_method,
+ instrument=instrument,
+ local_ates=local_ates,
+ )
diff --git a/tests/simple_model/test_funcs.py b/tests/simple_model/test_funcs.py
new file mode 100644
index 00000000..a2ded3f9
--- /dev/null
+++ b/tests/simple_model/test_funcs.py
@@ -0,0 +1,284 @@
+"""Test functions for simple_model."""
+
+from functools import partial
+
+import numpy as np
+import pytest
+from thesis.classes import Instrument, LocalATEs
+from thesis.config import RNG
+from thesis.simple_model.funcs import (
+ _estimate_pscores,
+ _idset,
+ _late,
+ _late_2sls,
+ _true_late,
+ simulation_bootstrap,
+)
+from thesis.utilities import draw_data
+
+
+@pytest.fixture()
+def instrument() -> Instrument:
+ return Instrument(
+ support=np.array([0, 1]),
+ pmf=np.array([0.5, 0.5]),
+ pscores=np.array([0.4, 0.6]),
+ )
+
+
+@pytest.fixture()
+def local_ates_zero() -> LocalATEs:
+ return LocalATEs(
+ always_taker=0,
+ complier=0,
+ never_taker=0,
+ )
+
+
+@pytest.fixture()
+def local_ates_nonzero() -> LocalATEs:
+ return LocalATEs(
+ always_taker=0,
+ complier=0.5,
+ never_taker=1,
+ )
+
+
+# Setting where true parameter is at upper boundary of ID set and at kink of solution
+@pytest.fixture()
+def local_ates_boundary_hi() -> LocalATEs:
+ return LocalATEs(
+ always_taker=0,
+ complier=0,
+ never_taker=1,
+ )
+
+
+# Setting where true parameter is at lower boundary of ID set and at kink of solution
+@pytest.fixture()
+def local_ates_boundary_lo() -> LocalATEs:
+ return LocalATEs(
+ always_taker=0,
+ complier=0,
+ never_taker=-1,
+ )
+
+
+@pytest.fixture()
+def local_ates_complier_negative() -> LocalATEs:
+ return LocalATEs(
+ always_taker=0,
+ complier=-0.5,
+ never_taker=1,
+ )
+
+
+@pytest.fixture()
+def sim_boot():
+ return partial(
+ simulation_bootstrap,
+ n_sims=2,
+ n_obs=10_000,
+ n_boot=2_000,
+ u_hi=0.1,
+ alpha=0.05,
+ rng=RNG,
+ )
+
+
+def test_compute_pscores(instrument, local_ates_nonzero) -> None:
+ n_obs = 1_000_000
+
+ data = draw_data(
+ n_obs,
+ rng=RNG,
+ instrument=instrument,
+ local_ates=local_ates_nonzero,
+ )
+
+ expected = (0.4, 0.6)
+
+ actual = _estimate_pscores(data)
+
+ assert expected == pytest.approx(actual, abs=3 / np.sqrt(n_obs))
+
+
+def test_simulation_runs(local_ates_boundary_hi, instrument, sim_boot) -> None:
+ for boot_met in ["analytical_delta", "standard", "numerical_delta"]:
+ for const_mtr in ["increasing", "none"]:
+ if boot_met == "numerical_delta":
+ bootstrap_params = {"eps_fun": lambda n: n ** (-1 / 3)}
+ elif boot_met == "analytical_delta":
+ bootstrap_params = {"kappa_fun": lambda n: n ** (1 / 3)}
+ else:
+ bootstrap_params = {}
+
+ sim_boot(
+ local_ates=local_ates_boundary_hi,
+ instrument=instrument,
+ constraint_mtr=const_mtr,
+ bootstrap_method=boot_met,
+ bootstrap_params=bootstrap_params,
+ )
+
+
+def test_check_invalid_constraint_mtr(instrument, local_ates_nonzero, sim_boot) -> None:
+ with pytest.raises(ValueError, match="Constraint 'invalid' not supported."):
+ sim_boot(
+ local_ates=local_ates_nonzero,
+ instrument=instrument,
+ constraint_mtr="invalid",
+ bootstrap_method="standard",
+ )
+
+
+def test_check_invalid_bootstrap_method(
+ instrument,
+ local_ates_nonzero,
+ sim_boot,
+) -> None:
+ with pytest.raises(ValueError, match="Bootstrap method 'invalid' not supported."):
+ sim_boot(
+ local_ates=local_ates_nonzero,
+ instrument=instrument,
+ constraint_mtr="increasing",
+ bootstrap_method="invalid",
+ )
+
+
+def test_true_late():
+ instrument = Instrument(
+ support=np.array([0, 1]),
+ pmf=np.array([0.5, 0.5]),
+ pscores=np.array([0.4, 0.6]),
+ )
+
+ u_hi = 0.2
+
+ complier_lates = np.linspace(-0.1, 0.1, num=10)
+
+ # With u_hi = 0.2, the weights are 0.5 (both relevant populations have mass 0.2).
+ expected = 0.5 * complier_lates + 0.5 * 1
+
+ local_ates = [
+ LocalATEs(
+ always_taker=0,
+ complier=cp_late,
+ never_taker=1,
+ )
+ for cp_late in complier_lates
+ ]
+
+ actual = [
+ _true_late(instrument=instrument, local_ates=la, u_hi=u_hi) for la in local_ates
+ ]
+
+ np.testing.assert_allclose(actual, expected, atol=1e-10)
+
+
+def test_id_set_consistent() -> None:
+ instrument = Instrument(
+ support=np.array([0, 1]),
+ pmf=np.array([0.5, 0.5]),
+ pscores=np.array([0.4, 0.6]),
+ )
+
+ u_hi = 0.2
+
+ local_ates = LocalATEs(
+ always_taker=0,
+ complier=-0.1,
+ never_taker=1,
+ )
+
+ constraint_mtr = "none"
+
+ n_obs = 10_000
+ n_sims = 1_000
+
+ res = np.zeros((n_sims, 2))
+
+ for i in range(n_sims):
+ data = draw_data(
+ n_obs=n_obs,
+ local_ates=local_ates,
+ instrument=instrument,
+ rng=RNG,
+ )
+
+ res[i] = _idset(
+ b_late=_late(data),
+ u_hi=u_hi,
+ pscores_hat=_estimate_pscores(data),
+ constraint_mtr=constraint_mtr,
+ )
+
+ # Take means over columns
+
+ actual = res.mean(axis=0)
+
+ actual[0]
+ mean_hi = actual[1]
+
+ expected_hi = 0.45
+ assert mean_hi == pytest.approx(expected_hi, abs=3 / np.sqrt(n_obs))
+
+
+def test_inconsistent_complier_and_never_taker_ate(
+ sim_boot,
+ local_ates_complier_negative,
+ instrument,
+) -> None:
+ # In settings with no constraint on the MTR functions it is possible to have a
+ # negative complier ATE and a never-taker ATE of 1.
+ sim_boot(
+ local_ates=local_ates_complier_negative,
+ instrument=instrument,
+ constraint_mtr="none",
+ bootstrap_method="standard",
+ )
+
+ # With increasing MTR functions this is not possible, as the largest possible value
+ # given a negative complier ATE is 1 + complier ATE < 1.
+
+ with pytest.raises(ValueError, match="largest possible never-taker ATE"):
+ sim_boot(
+ local_ates=local_ates_complier_negative,
+ instrument=instrument,
+ constraint_mtr="increasing",
+ bootstrap_method="standard",
+ )
+
+
+def test_bootstrap_params_supplied(sim_boot, local_ates_nonzero, instrument) -> None:
+ with pytest.raises(ValueError, match="Numerical delta bootstrap method requires"):
+ sim_boot(
+ local_ates=local_ates_nonzero,
+ instrument=instrument,
+ constraint_mtr="increasing",
+ bootstrap_method="numerical_delta",
+ bootstrap_params={},
+ )
+ with pytest.raises(ValueError, match="Analytical delta bootstrap method requires"):
+ sim_boot(
+ local_ates=local_ates_nonzero,
+ instrument=instrument,
+ constraint_mtr="increasing",
+ bootstrap_method="analytical_delta",
+ bootstrap_params={},
+ )
+
+
+def test_late_and_late_2sls_equivalent(local_ates_nonzero, instrument) -> None:
+ data = draw_data(
+ n_obs=10_000,
+ local_ates=local_ates_nonzero,
+ instrument=instrument,
+ rng=RNG,
+ )
+
+ late = _late(data)
+
+ late_2sls, _ = _late_2sls(data)
+
+ assert late == pytest.approx(late_2sls)
diff --git a/tests/utilities/__init__.py b/tests/utilities/__init__.py
new file mode 100644
index 00000000..874535a0
--- /dev/null
+++ b/tests/utilities/__init__.py
@@ -0,0 +1 @@
+"""Tests for utilities."""
diff --git a/tests/utilities/test_utilities.py b/tests/utilities/test_utilities.py
new file mode 100644
index 00000000..845dbe28
--- /dev/null
+++ b/tests/utilities/test_utilities.py
@@ -0,0 +1,119 @@
+"""Tests for the utilities module."""
+
+import numpy as np
+import pandas as pd # type: ignore[import-untyped]
+import pytest
+from thesis.classes import Instrument, LocalATEs
+from thesis.config import RNG
+from thesis.simple_model.funcs import _late, _true_late
+from thesis.utilities import draw_data
+
+
+@pytest.fixture()
+def instrument() -> Instrument:
+ return Instrument(
+ support=np.array([0, 1]),
+ pmf=np.array([0.5, 0.5]),
+ pscores=np.array([0.4, 0.6]),
+ )
+
+
+@pytest.fixture()
+def local_ates_zero() -> LocalATEs:
+ return LocalATEs(
+ always_taker=0,
+ complier=0,
+ never_taker=0,
+ )
+
+
+@pytest.fixture()
+def local_ates_nonzero() -> LocalATEs:
+ return LocalATEs(
+ always_taker=0,
+ complier=0.5,
+ never_taker=1,
+ )
+
+
+# Setting where true parameter is at upper boundary of ID set and at kink of solution
+@pytest.fixture()
+def local_ates_boundary_hi() -> LocalATEs:
+ return LocalATEs(
+ always_taker=0,
+ complier=0,
+ never_taker=1,
+ )
+
+
+# Setting where true parameter is at lower boundary of ID set and at kink of solution
+@pytest.fixture()
+def local_ates_boundary_lo() -> LocalATEs:
+ return LocalATEs(
+ always_taker=0,
+ complier=0,
+ never_taker=-1,
+ )
+
+
+@pytest.fixture()
+def local_ates_complier_negative() -> LocalATEs:
+ return LocalATEs(
+ always_taker=0,
+ complier=-0.5,
+ never_taker=1,
+ )
+
+
+def test_data_moments_boundary(instrument, local_ates_zero) -> None:
+ n_obs = 100_000
+
+ expected = pd.DataFrame(
+ {
+ "y_given_z": [0.0, 0.0],
+ "d_given_z": instrument.pscores,
+ },
+ index=[0, 1],
+ )
+
+ data = pd.DataFrame(
+ draw_data(n_obs, rng=RNG, instrument=instrument, local_ates=local_ates_zero),
+ columns=["y", "d", "z", "u"],
+ )
+
+ actual = pd.DataFrame(
+ {
+ "y_given_z": data.groupby("z")["y"].mean(),
+ "d_given_z": data.groupby("z")["d"].mean(),
+ },
+ index=[0, 1],
+ )
+
+ pd.testing.assert_frame_equal(actual, expected, atol=0.01)
+
+
+def test_generate_late(instrument):
+ n_obs = 1_000_000
+
+ complier_lates = [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1]
+
+ actual = np.zeros(len(complier_lates))
+ expected = np.zeros(len(complier_lates))
+
+ for i, complier_late in enumerate(complier_lates):
+ local_ates = LocalATEs(
+ always_taker=0,
+ complier=complier_late,
+ never_taker=np.min((1, 1 + complier_late)),
+ )
+ data = draw_data(
+ n_obs=n_obs,
+ local_ates=local_ates,
+ instrument=instrument,
+ rng=RNG,
+ )
+
+ actual[i] = _late(data)
+ expected[i] = _true_late(u_hi=0, instrument=instrument, local_ates=local_ates)
+
+ assert actual == pytest.approx(expected, abs=20 / np.sqrt(n_obs))