Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

The Overton Paradox, Updated: Technical Appendix

Allen Downey

Click here to run this notebook on Colab

This notebook shows the whole analysis behind the articles in this series, from the survey responses to the results: how the questions were selected, how conservatism is measured, and the models of age, period, and cohort. To run the same models with your own choice of questions, try The Overtron, which runs them in your browser. It’s self-contained: every function it uses is defined here. It downloads a data excerpt (about 2.5 MB) and runs in about 15 minutes on Colab.

Where the production analysis did something the notebook can’t (it started from the full GSS file, and ran longer MCMC samples), the notebook loads the saved results and checks that its own computations agree with them.

On Colab, upgrade the sampling stack before importing PyMC: Colab ships an older PyMC, and installing nutpie alone can pull in ArviZ 1.x and break the imports.

The data

The data come from the General Social Survey (GSS) cumulative file, 1972 to 2024, release 3a. The excerpt contains one row per respondent and one column for each of the 170 candidate questions described below, plus year, age, year of birth, self-identified ideology (polviews), and the analysis weight.

The sample excludes 2021, and in 2022 and 2024 it includes only respondents interviewed in person or by phone. In those years the GSS also used a web questionnaire, and the answers people give on the web differ systematically from the ones they give to an interviewer. The weights in 2022 and 2024 are raked to match the margins of the full sample. I’ll call this the interviewer sample.

The excerpt is stored as a column store: raw little-endian columns, gzipped, described by a manifest.

(68005, 195)

questions.json has the metadata for each question: its description, wording, and value labels, and, for checking, the results of each step of the selection as the production code computed them.

188

Self-identified ideology is the 7-point polviews scale. I group it into liberal (1–3), moderate (4), and conservative (5–7).

Loading...

Selecting the questions

The articles describe the selection in nine steps. Steps 1 and 2 started from the full GSS file, which has 6,943 variables; the excerpt contains only the 170 questions that came out of them, so for those steps I describe what was done. Steps 3 to 9 run here.

Steps 1 and 2: gates and scope

  1. Gates. Keep variables asked in at least 8 survey years (counting years with at least 100 respondents who also answered polviews), spanning at least 15 years, with 2 to 7 distinct answers, and not administrative. Run on the interviewer sample, 429 variables pass.

  2. Scope. Keep questions that ask for an opinion about a public issue or a social norm. Exclude demographics, behaviors and experiences, personal circumstances, knowledge, religious belief and practice, and party identification and vote. 170 questions remain.

These are the 170 candidates:

170

Step 3: identify the conservative answers

For each question, the conservative answers are the ones conservatives choose more often than liberals. Specifically:

  • for each answer and each survey year, compute the weighted percentage of conservatives who chose it, minus the percentage of liberals;

  • average those differences over the years, weighted by the number of respondents;

  • split the ordered answer scale at the point where the cumulative difference is largest; the side conservatives choose more is the conservative side.

Volunteered answers that aren’t positions on the question (“other”, “depends”, “neither”) never count as conservative. For two questions, the answer codes aren’t in scale order, so the order is given explicitly. And for the 15 questions used in Probably Overthinking It, I kept the codings used in the book.

For example, here are the differences for the question about the death penalty, cappun (“Do you favor or oppose the death penalty for persons convicted of murder?”):

cappun 1.0 20.0 2.0 -20.0 dtype: float64

Averaged over the years, conservatives are 20 points more likely than liberals to say “favor” (1), so that’s the conservative answer.

Check: the codings agree with the production code’s.

With the codings, each answer becomes 1 (conservative), 0 (not conservative), or missing (not asked, or no answer).

Step 4: measure how well each question separates conservatives and liberals

For each question, a weighted logistic regression of the coded answer on survey year, year of birth, and polviews (all linear). The coefficient of polviews measures how strongly the conservative answer is associated with self-identified ideology, controlling for year and cohort. It’s used only to choose among questions on the same topic (step 7); there’s no cutoff.

homosex 0.442 helppoor 0.390 helpnot 0.375 pillok 0.348 sexeduc 0.345 helpsick 0.345 wrkwayup 0.338 natarmsy 0.331 abany 0.331 goveqinc 0.329 dtype: float64

Check: the coefficients agree with the production code’s.

Step 5: coverage

A question has to be asked in at least half of the 32 survey years in the interviewer sample, counting years with at least 100 respondents who answered it and polviews.

np.int64(121)

Step 6: stability

For each question and decade, the gap is the weighted percentage of conservatives who gave the conservative answer, minus the percentage of liberals, with a 90% interval based on Kish’s effective sample size. Decades with fewer than 200 liberal and conservative respondents are left out.

A question is excluded if the gap reverses: in one decade, conservatives are credibly more likely to give the conservative answer, by at least 5 percentage points, and in another decade liberals are credibly more likely, by at least 5 points. (“Credibly” means the 90% interval excludes 0.)

A gap that shrinks is allowed. When nearly everyone gives the same answer, the gap between any two groups has to shrink, even if the question means the same thing.

['confed']

Only one question reverses: confidence in the executive branch, whose gap follows the party in the White House. It’s also excluded by step 8.

Loading...

Check: the gaps agree with the production code’s (which reports them rounded to whole points).

Step 7: no duplicates

Some questions were asked with two different wordings on different versions of the survey (the spending questions whose names end in y); I keep the original wording. And when several questions cover the same topic, like the seven versions of the abortion question, I keep the one with the largest polviews coefficient (step 4). The topics were assigned by hand, before any results; the topic field has them.

topic abortion 7 affirmative action 2 causes of racial inequality 4 child-rearing values 5 civil liberties: Muslim clergy 3 civil liberties: anti-religionist 3 civil liberties: communist 3 civil liberties: homosexual 3 civil liberties: militarist 3 civil liberties: racist 3 end of life 2 government help for Black Americans 3 health care 3 income inequality 2 police use of force 5 racial stereotypes 6 social trust 3 suicide 3 teen sexuality 2 welfare and the poor 3 women in politics 2 working mothers 2 dtype: int64

Step 8: no confidence questions

The 13 questions about confidence in institutions are left out, because confidence in an institution can reflect approval of its current decisions, perceived legitimacy, trust in expertise, or partisan alignment, rather than a position on an issue. This rule was adopted after seeing results that included them; the articles say so, and report the results with them as a sensitivity analysis.

np.int64(13)

Applying steps 5 to 8:

55

Step 9: check for drift

A question drifts if its connection to conservatism changes over time. To check, for each question:

  • estimate each respondent’s conservatism from the other questions (a leave-one-out score; the measurement model is described in the next section);

  • fit a weighted logistic regression of the answer to this question on that score and one indicator per decade, with the decade effects measured relative to their average;

  • list the question for review if some decade’s effect is credibly different from 0 and at least 0.5 on the logit scale (for a question that half of people answer conservatively, that’s about 12 percentage points).

The listed questions were reviewed one at a time, and each decision recorded. Most were questions where opinion moved faster or slower than on the others, which is real change, so they were kept. The functions this step uses are defined in the next section, so the drift check runs there.

Measuring conservatism

Respondents in different years answered different subsets of the questions: questions were added and dropped over the decades, and the GSS asks many of them on only some versions of the survey. So a simple count of conservative answers isn’t comparable from one respondent to another. Instead, I use a one-parameter item response (Rasch) model:

  • each respondent has a latent conservatism, θ\theta;

  • each question has a difficulty, dd;

  • the probability of a conservative answer is expit(θ−d)\mathrm{expit}(\theta - d).

A question with a high difficulty is one that only the most conservative respondents answer conservatively.

The latent conservatism is represented on a grid of 21 values from −6 to 6, with a wide normal prior (standard deviation 4).

Difficulties

The difficulties are estimated once, from all years combined, by weighted marginal maximum likelihood, using the EM algorithm of Bock and Aitkin:

  • E step: each respondent’s posterior on the grid, given the current difficulties;

  • M step: for each question, the difficulty at which the weighted expected share of conservative answers, among the respondents who were asked, equals the observed weighted share.

The calibration uses respondents who answered at least 5 of the questions. It stops when no difficulty changes by more than 0.0001, or after 100 iterations.

Step 9, continued: the drift check

14

These are the listed questions, with their decade effects on the logit scale and the slope on the leave-one-out score:

Loading...

Each was reviewed with its wording, coded answer, and decade effects. Twelve were kept: the departures look like opinion moving faster or slower than on the other questions, not a change in what the answer means. Two were excluded:

  • librac, removing a racist’s book from a public library: removing such a book is no longer a distinctively conservative position.

  • natspac, spending on the space program: besides the drift, its answer is slightly negatively related to the other questions (the slope on the leave-one-out score is below 0).

With librac out, the topic rule (step 7) gives its place to colrac, allowing a racist to teach in a college. Its drift check shows the same pattern as librac, so it was excluded too, along with the rest of the topic (spkrac isn’t brought in). There were no further rounds.

Loading...
53

Check: this is the selection the articles use.

These are the 53 primary questions, in rank order:

Loading...

So, precisely, the measure is how closely someone’s answers match a fixed collection of positions that have been associated with self-identified conservatism over the last 50 years.

Connectivity

Scores of people surveyed decades apart are comparable only through the questions asked in both periods. This table shows how many of the primary questions were asked in each pair of decades.

Loading...
Loading...

Scores

With the difficulties calibrated on the primary questions, each respondent’s score is the posterior mean of the number of the 53 questions they would answer conservatively if they were asked all of them, expressed as a percentage: “percent conservative responses”. The analysis uses respondents who answered at least 5 of the questions and whose age and year of birth are known.

64632

Weighted average by year:

<Figure size 640x480 with 1 Axes>

Check: the scores agree with the production code’s, for a sample of respondents, and so do the yearly means.

Models of age, period, and cohort

The models work with the weighted mean score in groups of respondents (cells): by survey year, by age, or by year of birth × survey year. Each cell has a weighted mean, a weighted variance, and Kish’s effective sample size.

The random walk prior

The trends over time, age, and year of birth are smooth curves with a second-order random walk (RW2) prior: each step’s change in slope is normal with standard deviation σ\sigma, and the curve is centered at 0 so it doesn’t compete with the intercept. Sampling the RW2 directly (as a double cumulative sum of innovations) is slow, because the innovations are strongly correlated in the posterior. The same prior can be written in other coordinates: the eigenvectors of the centered RW2 covariance, with the linear trend split out as a separate parameter. The two forms define the same model; the second samples several times faster. The derivation is in a separate note.

The models

  • Period (and age): a smooth curve over survey years (or ages), plus independent noise for each year, on the logit scale, fit to the cell means. This model is used for each group of self-identified ideology, and for each decade of birth.

  • Period–cohort: a smooth curve over years of birth (the cohort component) plus a smooth curve over survey years (the period component), plus year noise, fit to the cells of year of birth × survey year. The period component is the change within cohorts: how the beliefs of people born in the same year change over time. Following a birth cohort, age and period move together, so this change combines the effects of aging and of the times; the model doesn’t try to separate them.

The prior scales are the same as in You Would Choose Now.

Sampling

The production fits used 4 chains with 4,000 tuning steps and 6,000 draws each. To keep this notebook quick, it uses 2 chains with 1,000 tuning steps and 1,000 draws, and then compares the results with the production fits. It uses the nutpie sampler if it’s installed, and PyMC’s default sampler otherwise.

A series fit returns the curve (on the percent scale) and its linear slope per decade, over the values that have data.

The period–cohort fit returns the two components and the decomposition of the total change:

  • the period component, standardized to the mix of birth years in the whole sample, and its linear slope per decade: the within-cohort change, the first metric in the articles;

  • the cohort component, at the average period;

  • the decomposition of the change from the first survey year to the last into generational replacement (the mix of birth years changes, period held fixed) and changing minds (the period changes, mix held fixed), averaging the two orders of the decomposition.

The production results, for comparison:

Results

Older people are more conservative

Within each decade of surveys, the age model shows how conservatism varies with age.

NUTS[nutpie]: [alpha, sigma_t, trend_w, trend_trend, sigma_short, epsilon_z]
NUTS[nutpie]: [alpha, sigma_t, trend_w, trend_trend, sigma_short, epsilon_z]
NUTS[nutpie]: [alpha, sigma_t, trend_w, trend_trend, sigma_short, epsilon_z]
NUTS[nutpie]: [alpha, sigma_t, trend_w, trend_trend, sigma_short, epsilon_z]
NUTS[nutpie]: [alpha, sigma_t, trend_w, trend_trend, sigma_short, epsilon_z]
NUTS[nutpie]: [alpha, sigma_t, trend_w, trend_trend, sigma_short, epsilon_z]
<Figure size 640x480 with 1 Axes>

The slope of each curve, in percentage points per decade of age, compared with the production fits:

Loading...

Conservatism by self-identified ideology

NUTS[nutpie]: [alpha, sigma_t, trend_w, trend_trend, sigma_short, epsilon_z]
NUTS[nutpie]: [alpha, sigma_t, trend_w, trend_trend, sigma_short, epsilon_z]
NUTS[nutpie]: [alpha, sigma_t, trend_w, trend_trend, sigma_short, epsilon_z]
<Figure size 640x480 with 1 Axes>

Change within cohorts

NUTS[nutpie]: [alpha, sigma_c, cohort_w, cohort_trend, sigma_t, period_w, period_trend, sigma_short, epsilon_z, sigma_resid]
<Figure size 1000x400 with 2 Axes>

The within-cohort slope and the decomposition, compared with the production fit:

Loading...

By decade of birth

The same period–cohort model, fit separately within each decade of birth, shows whether every group follows the same pattern. These fits are loaded from the production results.

Loading...

Relative position

The Overton paradox is about relative position: a liberal of the 1970s would be right of center today. To measure it, I estimate the distribution of latent conservatism in each group of respondents (each survey year, and each year × self-identified ideology), and express a position as a percentile of a year’s distribution.

The distributions are normal on the logit scale, estimated by weighted EM on a fine grid with the difficulties held fixed. Unlike the individual scores, they aren’t widened by measurement noise or pulled toward the prior, so their spread doesn’t depend on how many questions people answered. Positions are medians, so the percentiles are the same on any monotone scale.

The reference point is the median liberal of 1974, the first year with enough self-identified liberals among the scored respondents. In each year, its percentile is the share of that year’s distribution below it.

<Figure size 640x480 with 1 Axes>

In 2024, with 90% intervals from a bootstrap of respondents in the two years involved (the bootstrap ignores the GSS’s clustered design, so the intervals are somewhat too narrow):

Loading...

Check, against the production results:

Sensitivity

How much do the choices matter? These comparisons were run after the choices were made, and reported whatever they showed; they weren’t used to choose. All are production fits, loaded:

  • primary: the decided specification, 53 questions;

  • with_confidence: the same, keeping the confidence questions that pass the stability rule (65 questions);

  • p48: the previous selection, 48 questions, under the earlier stability rule (which also excluded questions whose gap faded);

  • social and econA: the social questions and the core economic questions among the primary questions.

Loading...

Diagnostics

Every production fit, with its largest R-hat, smallest bulk effective sample size, and number of divergent transitions:

Loading...
rhat_max 1.009 ess_bulk_min 809.914 divergences 597.000 fits_with_divergences 20.000 dtype: float64

And the fits in this notebook. With shorter runs, the effective sample sizes are smaller, but the estimates agree with the production fits, as shown above:

Loading...