Survey Weighting in Python: Adjustment and Calibration Methods
Nonresponse adjustment, poststratification, calibration, raking, and trimming
Tutorials
Survey Weighting
Calibration
Python
Learn how to calculate and adjust survey weights in Python using the svy library. Covers nonresponse adjustment, poststratification, calibration, raking, trimming, and replicate weights for variance estimation.
Sample weighting allows analysts to generalize results from a survey sample to the target population. Design weights (also called base weights) are derived as the inverse of the final probability of selection. In large-scale surveys, these design weights are often further adjusted to correct for nonresponse, extreme values, or to align auxiliary variables with known population controls.
This tutorial covers weight adjustment techniques — nonresponse adjustment, poststratification, calibration, raking, and trimming — to improve representativeness and reduce bias.
For more on sample-weight adjustments, see Valliant and Dever (2018), which provides a step-by-step guide to calculating survey weights.
Setting Up the Sample Data
This tutorial uses the World Bank (2023) synthetic sample data.
import numpy as npimport polars as plfrom rich importprintas rprintimport svyhld_data = svy.datasets.load(name="hld_sample_wb_2023")print(f"The number of records in the household sample data is {hld_data.shape[0]}")
The number of records in the household sample data is 8000
Weight Adjustment Methods
In practice, base weights derived from selection probabilities are routinely adjusted to:
Correct for nonresponse and unknown eligibility
Temper the influence of extreme or large weights
Align the weighted sample with known auxiliary controls
This section demonstrates the key methods available in the svy library:
Method
Function
Purpose
Nonresponse adjustment
adjust()
Account for unit nonresponse and unknown eligibility
Poststratification
poststratify()
Match weights to known control totals
Calibration
calibrate()
Adjust weights using GREG framework with auxiliary variables
Raking
rake()
Rescale weights to match multiple marginal totals
Trimming
trim()
Cap extreme weights to reduce variance
Normalization
normalize()
Rescale weights to sum to a chosen constant
Creating the Design Weight
The design weight (or base weight) represents the inverse of the overall probability of selection—the product of first-stage and second-stage selection probabilities, as explained in the Sample Selection tutorial.
# Define the sampling designhld_design = svy.Design(stratum=("geo1", "urbrur"), psu="ea", wgt="hhweight")# Create the samplehld_sample = svy.Sample(data=hld_data, design=hld_design)
The dataset includes a household-level base weight variable named hhweight. Let’s rename it to base_wgt for clarity:
╭────────────── Sample ───────────────╮│Survey Data││ Rows : 8000 ││ Columns : 52 ││ Strata : 19 ││ PSUs : 320 ││││Survey Design││ Row index svy_row_index ││ Stratum (geo1, urbrur) ││ PSU ea ││ SSU None ││ Weight base_wgt ││ With replacement False ││ Prob None ││ Hit None ││ MOS None ││ Population size None ││ Replicate weights None │╰─────────────────────────────────────╯
Replicate Weights
The preferred workflow is to create replicate weights early — from the design weights — and then apply the same adjustment steps (nonresponse, calibration, raking, trimming) to both the full-sample weight and the replicate weights in the same pass. This ensures that replication-based standard errors reflect all sources of variability, not just sampling variability.
The replicate weights are named automatically from the active weight (e.g., base_wgt1, …, base_wgt500). You can customize the prefix with rep_prefix=....
Tip
When replicate weights are provided by the data producer (e.g., in DHS or ACS public-use files), register them directly via the RepWeights configuration in svy.Design() instead of creating them.
Understanding Response Status Categories
The core idea of nonresponse adjustment is to redistribute the survey weights of eligible non-respondents to eligible respondents within defined adjustment classes.
svy classifies records into four response categories:
Code
Meaning
rr
Respondent
nr
Nonrespondent
uk
Unknown eligible
in
Ineligible
If your dataset uses different labels, provide a mapping to the canonical values:
The adjust() method computes adjusted weights and stores them in the sample object. Adjustment classes are defined by the by parameter — they don’t have to match the sampling strata.
Setting respondents_only=True (the default) retains only respondents after adjustment, since the adjusted weights already account for all excluded units.
If you don’t specify wgt_name, svy creates the adjusted weight automatically as svy_adjusted_<base_weight_name>
Set replace=True to replace the pre-adjusted variable with the adjusted one
Replicate weights are adjusted in the same pass (e.g., nr_wgt1, …, nr_wgt500)
Poststratification
Poststratification compensates for under- or over-representation in the sample by adjusting weights so that weighted sums within poststratification classes match known control totals from reliable sources.
Poststratification classes need not mirror the sampling design—they can be formed from additional variables. Common choices include age group, gender, race/ethnicity, and education.
Warning
Use current, reliable controls: Poststratifying to out-of-date or unreliable totals may introduce bias rather than reduce it. Document your sources and reference dates.
Let’s assume we have reliable control totals (e.g., from a recent census) for households per administrative region:
Calibration adjusts sample weights so that certain totals align with known population values. The Generalized Regression (GREG) approach is a model-assisted version that assumes the survey variable of interest relates to auxiliary variables through a regression-type relationship. See Deville and Särndal (1992) for the foundational work on calibration, and Särndal, Swensson, and Wretman (1992) for a thorough treatment of model-assisted survey sampling.
GREG calibration finds weights that:
Stay as close as possible to the original design weights
Make the weighted totals of auxiliary variables match their known population values
Unlike poststratification, which adjusts weights within discrete cells, GREG can incorporate continuous auxiliary information, leveraging the linear relationship between the covariate and the study variable to potentially improve efficiency.
The controls dictionary uses svy.Cat() to wrap categorical variables and a plain string key for continuous ones:
Calibration can also be performed within domains via the by parameter, which is useful when domain-level control totals are available and the relationship between auxiliary variables and the survey outcome varies across domains.
Raking (Iterative Proportional Fitting)
Raking (also called iterative proportional fitting or IPF) adjusts survey weights so that weighted sample distributions match known population margins for several categorical variables.
Unlike calibration, which aligns multiple totals simultaneously, raking updates weights iteratively—adjusting one margin at a time until all specified margins agree with population controls within tolerance.
Raking is especially useful when only marginal totals are available (e.g., totals by tenure status and totals by electricity access, but not their cross-tabulation).
shape: (10, 5)
┌─────────────┬───────────────────┬─────────────┬────────────┬────────────┐
│ hid ┆ statocc ┆ electricity ┆ cal_wgt ┆ rake_wgt │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ str ┆ f64 ┆ f64 │
╞═════════════╪═══════════════════╪═════════════╪════════════╪════════════╡
│ 930a60bcf98 ┆ Occupied for free ┆ No ┆ 565.1162 ┆ 563.180626 │
│ 4b47172f406 ┆ Occupied for free ┆ No ┆ 289.307288 ┆ 288.316384 │
│ 9974467f1f0 ┆ Occupied for free ┆ No ┆ 346.981908 ┆ 345.793463 │
│ 7f4e816138f ┆ Occupied for free ┆ No ┆ 322.19723 ┆ 321.093675 │
│ 760d1268159 ┆ Occupied for free ┆ No ┆ 278.987393 ┆ 278.031835 │
│ 3bdc6e31f82 ┆ Occupied for free ┆ No ┆ 411.697994 ┆ 410.287891 │
│ d3e45b7d943 ┆ Occupied for free ┆ No ┆ 241.838078 ┆ 241.00976 │
│ 7a7bc823c36 ┆ Occupied for free ┆ No ┆ 449.707983 ┆ 448.167692 │
│ 0c4a30de8c4 ┆ Occupied for free ┆ No ┆ 401.670634 ┆ 400.294875 │
│ f5364673803 ┆ Occupied for free ┆ No ┆ 458.555042 ┆ 456.98445 │
└─────────────┴───────────────────┴─────────────┴────────────┴────────────┘
Weight Trimming
Trimming caps extreme survey weights that can arise from nonresponse adjustment, calibration, or unequal selection probabilities. Untrimmed extreme weights inflate the variance of survey estimates and can cause individual observations to dominate weighted totals. Trimming reduces this instability at the cost of introducing a small bias.
svy provides Cap for specifying stat-based thresholds:
Threshold
Description
svy.Cap("median", k)
k × median
svy.Cap("mean", k)
k × mean
svy.Cap("sd", k)
k × standard deviation
svy.Cap("iqr", k)
k × interquartile range (Q75 − Q25)
Float in (0, 1]
Quantile (e.g., 0.99 for the 99th percentile)
Float > 1
Absolute threshold
Cap supports composition via +, -, and * operators, which is useful for common rules like the boxplot threshold (median + k × IQR):
When redistribute=True (the default), mass removed from trimmed units is redistributed proportionally to the remaining units so the total weighted sample size is preserved.
Standalone trimming is a one-shot operation: weights are capped but the raking or calibration constraints may no longer hold. svy addresses this through the trimming parameter available on rake(), poststratify(), and calibrate().
When trimming is specified, the method runs an iterative cycle: rake (or calibrate) the weights, then trim, then repeat until both constraints are satisfied simultaneously. The final step is always a raking (or calibration) pass so that the margin constraints take priority.
The TrimConfig object encapsulates all trimming parameters (threshold, redistribution, convergence tolerance, maximum iterations) into a single specification that can be passed to any weighting method.
Weight Normalization
Surveys sometimes normalize weights to a convenient constant (e.g., the sample size or 1,000) so results are easier to compare across analyses.
Normalization multiplies every weight by the same factor. It does not change weighted means, proportions, or regression coefficients (the factor cancels), but it does change level estimates such as totals by the same factor.
This tutorial covered the essential techniques for survey weight adjustment:
Replicate weights with create_bs_wgts() / create_jk_wgts() / create_brr_wgts() — create replicate weights early so all adjustments propagate automatically
Nonresponse adjustment with adjust() — redistributes weights from non-respondents to respondents
Poststratification with poststratify() — aligns weights to known population totals within cells
Calibration with calibrate() — uses GREG framework with both categorical and continuous auxiliary variables
Raking with rake() — iteratively matches multiple marginal distributions
Trimming with trim() — caps extreme weights; also available as integrated trimming via TrimConfig
Normalization with normalize() — scales weights to a convenient total
Tip
The recommended workflow order is: design weights → replicate weights → nonresponse adjustment → calibration or raking (with optional integrated trimming) → normalization. Each step automatically updates both the full-sample weight and the replicate weights.
Next Steps
Now that you understand how to create and adjust survey weights, continue to the estimation tutorial to learn how to produce design-adjusted estimates.
Ready to estimate? Learn about design-adjusted estimation in Survey Estimation →
References
Deville, J.-C., & Särndal, C.-E. (1992). Calibration estimators in survey sampling. Journal of the American Statistical Association, 87(418), 376–382.
Särndal, C.-E., Swensson, B., & Wretman, J. (1992). Model Assisted Survey Sampling. Springer.
Valliant, R., Dever, J. A., & Kreuter, F. (2018). Practical Tools for Designing and Weighting Survey Samples (2nd ed.). Springer.
References
Deville, Jean-Claude, and Carl-Erik Särndal. 1992. “Calibration Estimators in Survey Sampling.”J. Amer. Statist. Assoc. 87 (418): 376–82. https://doi.org/10.1080/01621459.1992.10475217.