# State-level MRP opinion estimates -- codebook

Generated: 2026-11-12

## Method

Multilevel regression and poststratification (MRP).

For each measure a multilevel model is fit on engaged survey respondents (`surveys.us_labelled`, `engaged = 1`), **unweighted**:

```
outcome ~ C(age_band) + C(race) + C(educ) + C(gender) + prior_vote + C(party) [+ ces] + (1 | state)
```

* `party` is pid7 collapsed to Democrat / Independent / Republican, leaners counted as partisans. A measure asked of, or defined for, some parties only is fit on them and poststratified over their cells: `maga_share` over Republicans (one party, so no party term), `affpol` over Democrats and Republicans.
* Measures marked *CES pooled* in the table below add the respondents of the Cooperative Election Study (Tufts / YouGov, CC0; 2024 and 2025 Common Content) who answered the matching CES question, with an indicator `ces` = 1 for them; the estimate is predicted at `ces` = 0, so it describes our survey's question. `n_ces` counts the CES respondents in each state; `n_respondents` counts ours. The CES items and their wording are in `state-opinion-ces-benchmark.json`.

* Continuous outcomes: random state intercept by two-stage empirical Bayes -- OLS fixed effects, state variance from the state means of the OLS residuals (DerSimonian-Laird method of moments, floored at 0), and each state's mean residual shrunk toward 0 by tau^2 / (tau^2 + sigma^2 / n_state).
* Binary outcomes: `BinomialBayesMixedGLM.fit_vb` (variational-Bayes random state intercept), linear-probability empirical-Bayes fallback. VB gives only marginal standard deviations, so the interval draws use the joint covariance of the fixed and state effects from a Laplace approximation at the VB posterior means (the inverse Hessian of the log posterior, state sd held at its VB estimate).
* Each empirical-Bayes measure lists its estimated state standard deviation (tau, in the measure's units) in the table below. A state sd of 0 means the survey shows no state differences beyond what the model's demographics and the 2024 vote predict: every state effect is 0, and each state's estimate differs from another's only through its demographic mix and its 2024 vote.
* `prior_vote` = 2024 presidential two-party Democratic vote share of the respondent's state (`dem_two_party_2024` in `state-opinion-predictors.csv`), a Ghitza-Gelman state-level predictor: Democratic / (Democratic + Republican) share of the state's 2024 presidential vote, computed from the file's `dem_pct_2024` and `rep_pct_2024` (percent of the state's presidential vote). The source of those percentages was not recorded when the file was added (2026-07-12), but in every state the two-party share is within 0.0001 of the share computed from the certified results in the FEC's *Official 2024 Presidential General Election Results* (https://www.fec.gov/documents/5645/2024presgeresults.xlsx).

Fitted cell predictions are poststratified against the population frame (`state-opinion-frame.csv`, a copy of `data/frame.csv`; cells = state x gender x age-band x race x educ x party) by weighting each cell's prediction by its population and aggregating within state. Confidence intervals are simulation-based (2.5/97.5 percentiles over 200 draws from the fitted fixed-effect and random-effect (co)variances).

Poststratification frame source: **acs5_pums_2024+ces_party_2024+2025**.

## Files

* `state-opinion-estimates.csv` / `.json`: the estimates, one row per state x measure (`estimate`, 95% simulation interval `ci_low`/`ci_high`, `n_respondents`, `n_ces`).
* `state-opinion-frame.csv`: the poststratification frame, one row per state x gender x age band x race x education x party cell with its adult population (`pop`) and the frame's `source`. The demographic cells are ACS 5-year PUMS person weights; each is split by party in the shares a weighted multinomial logit fit on the CES predicts for it (state, the four demographics and their main interactions; `party_frame.py`).
* `state-opinion-predictors.csv`: the state-level predictor file (`dem_pct_2024`, `rep_pct_2024`, `dem_two_party_2024`).

## Category crosswalk (survey -> harmonized -> ACS)

| dimension | harmonized levels | survey source | ACS source |
|---|---|---|---|
| age_band | 18-29 / 30-44 / 45-64 / 65+ | year - birthyr | AGEP |
| gender | Female / Male | gender | SEX |
| race | White / Black / Hispanic / Other | race (+hispanic); Hispanic ethnicity takes precedence | RAC1P + HISP |
| educ | HS-or-less / Some-college / College / Postgrad | educ (6->4) | SCHL |
| party | Democrat / Independent / Republican | pid7 (leaners as partisans) | CES pid7 model |

educ collapse: {No HS, High school graduate}->HS-or-less; {Some college, 2-year}->Some-college; {4-year}->College; {Post-grad}->Postgrad.

## Measures

Binary/index estimates are proportions in [0,1]; affpol is on the thermometer-difference scale.

| measure | type | model | unit | definition |
|---|---|---|---|---|
| `affpol` | continuous | EB random intercept; repeat interviews weighted (rho 0.75) (state sd 1.41) | thermometer points (in-party minus out-party, 0-100 scale) | Affective polarization: in-party minus out-party feeling-thermometer difference, signed by pid7 (Democrats: dem-rep; Republicans: rep-dem; leaners counted as partisans; pure independents dropped). |
| `violence_index` | continuous | EB random intercept; repeat interviews weighted (rho 0.32) (state sd 0.00412) | proportion of the six violent acts supported (0-1) | Political-violence index: mean over the six violence items of support = (Strongly support / Support). The items are a skip ladder (each act shown only to those who supported the one before), so an act not shown after a non-supported one counts as not supported. |
| `norms_index` | continuous | EB random intercept; repeat interviews weighted (rho 0.59) (state sd 0.0199) | proportion of anti-democratic norms endorsed (0-1) | Democratic-norm-erosion index: mean over the 5 norm items of endorse = (Strongly agree / Agree). |
| `violence1_support` | binary | BinomialBayesMixedGLM(VB); repeat interviews weighted (rho 0.39) | proportion of all respondents supporting (0-1) | Support (Strongly support / Support) for violence1. |
| `violence2_support` | binary | BinomialBayesMixedGLM(VB); repeat interviews weighted (rho 0.25) | proportion of all respondents supporting (0-1) | Support (Strongly support / Support) for violence2; respondents not shown it because they did not support the act before count as not supporting (skip ladder). |
| `violence3_support` | binary | BinomialBayesMixedGLM(VB); repeat interviews weighted (rho 0.24) | proportion of all respondents supporting (0-1) | Support (Strongly support / Support) for violence3; respondents not shown it because they did not support the act before count as not supporting (skip ladder). |
| `violence4_support` | binary | BinomialBayesMixedGLM(VB); repeat interviews weighted (rho 0.23) | proportion of all respondents supporting (0-1) | Support (Strongly support / Support) for violence4; respondents not shown it because they did not support the act before count as not supporting (skip ladder). |
| `violence5_support` | binary | BinomialBayesMixedGLM(VB); repeat interviews weighted (rho 0.22) | proportion of all respondents supporting (0-1) | Support (Strongly support / Support) for violence5; respondents not shown it because they did not support the act before count as not supporting (skip ladder). |
| `violence6_support` | binary | BinomialBayesMixedGLM(VB); repeat interviews weighted (rho 0.21) | proportion of all respondents supporting (0-1) | Support (Strongly support / Support) for violence6; respondents not shown it because they did not support the act before count as not supporting (skip ladder). |
| `norm_judges_agree` | binary | BinomialBayesMixedGLM(VB); repeat interviews weighted (rho 0.41) | proportion endorsing (0-1) | Endorsement (Strongly agree / Agree) of norm_judges. |
| `norm_polling_agree` | binary | BinomialBayesMixedGLM(VB); repeat interviews weighted (rho 0.41) | proportion endorsing (0-1) | Endorsement (Strongly agree / Agree) of norm_polling. |
| `norm_executive_agree` | binary | BinomialBayesMixedGLM(VB); repeat interviews weighted (rho 0.39) | proportion endorsing (0-1) | Endorsement (Strongly agree / Agree) of norm_executive. |
| `norm_censorship_agree` | binary | BinomialBayesMixedGLM(VB); repeat interviews weighted (rho 0.37) | proportion endorsing (0-1) | Endorsement (Strongly agree / Agree) of norm_censorship. |
| `norm_loyalty_agree` | binary | BinomialBayesMixedGLM(VB); repeat interviews weighted (rho 0.42) | proportion endorsing (0-1) | Endorsement (Strongly agree / Agree) of norm_loyalty. |
| `ideo_extreme` | binary | BinomialBayesMixedGLM(VB) + CES pooled; repeat interviews weighted (rho 0.78) | proportion ideologically extreme (0-1) | Ideological extremity: ideo5 in {Very liberal, Very conservative} vs {Liberal, Moderate, Conservative}; Not sure dropped. |
| `maga_share` | binary | BinomialBayesMixedGLM(VB) + CES pooled; repeat interviews weighted (rho 0.71) | proportion self-identifying MAGA Republican (0-1) | Share selecting 'MAGA Republican' vs {Never Trumper, Neither} (among those asked the maga item). |
| `immigration_good` | binary | BinomialBayesMixedGLM(VB); repeat interviews weighted (rho 0.63) | proportion saying immigration is good (0-1) | immigration1 = 'Good thing' vs 'Bad thing' (Don't know dropped). |
| `immigration_deport` | binary | BinomialBayesMixedGLM(VB); repeat interviews weighted (rho 0.54) | proportion supporting deportation (0-1) | immigration2 agree (Strongly/Somewhat) that immigrants here illegally should be deported. |
| `immigration_amnesty` | binary | BinomialBayesMixedGLM(VB); repeat interviews weighted (rho 0.51) | proportion supporting amnesty (0-1) | immigration3 agree (Strongly/Somewhat) that undocumented immigrants should be granted amnesty. |
| `economy_optimism` | binary | BinomialBayesMixedGLM(VB); repeat interviews weighted (rho 0.50) | proportion optimistic about next 6 months (0-1) | economy1 = economy will perform (Very/Somewhat) good over next 6 months. |
| `economy_retro` | binary | BinomialBayesMixedGLM(VB); repeat interviews weighted (rho 0.27) | proportion rating past 6 months good (0-1) | economy2 = economy performed (Very/Somewhat) good over past 6 months. |
| `tariffs_manufacturing` | binary | BinomialBayesMixedGLM(VB); repeat interviews weighted (rho 0.48) | proportion agreeing (0-1) | tariffs1 agree (Strongly/Somewhat) that America would be better off with more manufacturing jobs. |
| `tariffs_support` | binary | BinomialBayesMixedGLM(VB) + CES pooled; repeat interviews weighted (rho 0.50) | proportion supporting new tariffs (0-1) | tariffs2a support (Strongly/Somewhat) for new tariffs on foreign goods. |
| `tariffs_support_priced` | binary | BinomialBayesMixedGLM(VB); repeat interviews weighted (rho 0.49) | proportion supporting tariffs despite higher prices (0-1) | tariffs2b support for new tariffs even if they raise store prices. |
| `freespeech_secure` | binary | BinomialBayesMixedGLM(VB); repeat interviews weighted (rho 0.39) | proportion viewing free speech as secure (0-1) | freespeech = free speech is (Very/Completely) secure in America vs (Not at all/Somewhat) secure. |

## Checking against the CES (`state-opinion-ces-benchmark.csv` / `.json`)

For each measure that the Cooperative Election Study (CES, Tufts / YouGov,
CC0) also asks,
the CES's own weighted estimate for each state (`commonweight`, pooled over
the CES years that carry the item by effective sample size) beside ours.

| column | meaning |
|---|---|
| `state`, `measure` | as in the estimates file |
| `our_estimate` | our MRP estimate |
| `ces_estimate`, `ces_se` | the CES weighted share and its binomial standard error on the effective n |
| `ces_n`, `ces_neff` | CES respondents and their effective (Kish) sample size |
| `ces_years` | CES Common Content years pooled |
| `gap` | `our_estimate - ces_estimate` |
| `compared` | true when `ces_neff` >= 100; only these states enter the summary |

The JSON gives, per measure, the states compared, the correlation across
them, the mean and mean absolute gap, and both questions' wording. The
wordings differ for every item but ideology, so the correlation (whether the
states rank alike) is the test; a level gap is expected where the questions
differ. A measure marked `pooled` in the JSON has the CES's respondents in
its model, so its comparison describes the fit and is no longer an
independent check; the held-out comparison is in the methods paper.
