Skip to contents

ps_trunc() bounds extreme propensity scores to fixed limits, replacing out-of-range values with the boundary value (a form of winsorizing). The result is a vector or matrix of the same length and dimensions as .propensity, with no observations removed. This contrasts with ps_trim(), which sets extreme values to NA (effectively removing those observations from analysis).

Usage

ps_trunc(
  .propensity,
  method = c("ps", "adaptive", "pctl", "cr"),
  lower = NULL,
  upper = NULL,
  .exposure = NULL,
  .focal_level = NULL,
  .reference_level = NULL,
  ...,
  .treated = NULL,
  .untreated = NULL,
  ps = lifecycle::deprecated()
)

Arguments

.propensity

A numeric vector of propensity scores in (0, 1) (binary exposures), or a matrix/data.frame where each column contains propensity scores for one level of a categorical exposure. A data frame truncated for a binary exposure is reduced to a single column: the second column of a two column data frame, which is the probability of the second level in the layout model predictions come in, and the first column otherwise. The column taken is announced; options(propensity.quiet = TRUE) silences the announcement. A vector is held to the open interval, so a score of exactly 0 or 1 is refused. A matrix is held to the closed interval instead: a cell of exactly 0 or 1 is bounded rather than refused, which is how a separated multinomial fit is repaired, and what comes back lies strictly inside (0, 1). Anything outside \([0, 1]\) is refused either way, and so is a truncation whose own computed bounds leave a score at an endpoint; see Propensity scores at 0 and 1 in wt_ate(). A data frame holding a .pred_class column, which a fitted tidymodels classification model returns when no prediction type is named, carries predicted levels rather than probabilities and is refused with an error of class propensity_df_class_column_error.

method

One of "ps", "adaptive", "pctl", or "cr":

  • "ps" (default): Truncate directly on propensity score values. Values outside [lower, upper] are set to the nearest bound. For categorical exposures, applies symmetric truncation using lower as the threshold (delta) and renormalizes rows to sum to 1.

  • "adaptive": Truncate at \([1/c, 1 - 1/c]\) with \(c = \sqrt{n} \log(n) / 5\) (Gruber et al., 2022), where \(n\) is the number of propensity scores present (binary exposures only). See The adaptive bound below.

  • "pctl": Truncate at quantiles of the propensity score distribution. The lower and upper arguments specify quantile probabilities. For categorical exposures, quantiles are computed across all columns.

  • "cr": Truncate to the common range of propensity scores across exposure groups (binary exposures only). Bounds are [min(.propensity[focal]), max(.propensity[reference])]. Requires .exposure. When those bounds cross, so that the two distributions do not overlap at all, there is no common range to bound the scores to and the call errors with class propensity_no_overlap_error. That differs deliberately from ps_trim(), which trims every observed unit in the same situation, a truthful record of an empty overlap region.

For categorical exposures, only "ps" and "pctl" are supported.

lower, upper

Bounds for truncation. Interpretation depends on method:

  • method = "adaptive": Not used. Supplying either is ignored with a warning.

  • method = "ps": Propensity score values. See The "ps" bounds below.

  • method = "pctl": Quantile probabilities (defaults: 0.05 and 0.95; categorical defaults: 0.01 and 0.99).

  • method = "cr": Ignored; bounds are determined by the data.

.exposure

An exposure vector. Required for method "cr" (binary exposure vector) and for categorical exposures (factor or character vector) with any method.

.focal_level

The value of .exposure representing the focal (treated) group, used by "cr". Every binary coding honors it: 0/1 numeric, logical, two-level factor, and two-level character exposures are all coded with the named level as focal, and a level the exposure never takes is an error. With no level named, a binary exposure defaults to its higher level, which is 1 for a 0/1 exposure, TRUE for a logical one, and the second of the two levels a factor or character exposure takes. Levels a factor declares but never takes are not candidates. Naming any other level reverses the coding, so .propensity must then hold the probability of the named level.

.reference_level

The value of .exposure representing the reference (control) group. Naming it makes the exposure's other level focal, with the same consequence for .propensity, and a level the exposure never takes is an error. Automatically detected if not supplied.

...

Additional arguments passed to methods.

.treated

[Deprecated] Use .focal_level instead.

.untreated

[Deprecated] Use .reference_level instead.

ps

[Deprecated] Use .propensity instead. A call that names ps must name the arguments after it as well, since a positional argument binds to .propensity.

Value

A ps_trunc object (a numeric vector for binary exposures, or a matrix for categorical exposures). Use ps_trunc_meta() to inspect metadata including method, lower_bound, upper_bound, truncated_idx (positions of modified values), and n_obs (the number of observations those positions describe).

Details

Unlike ps_trim(), truncation preserves all observations. No NA values are introduced; out-of-range scores are replaced with the boundary value.

For binary exposures, each propensity score \(e_i\) is bounded:

  • If \(e_i < l\), set \(e_i = l\) (the lower bound).

  • If \(e_i > u\), set \(e_i = u\) (the upper bound).

For categorical exposures, values below the threshold are set to the threshold and each row is renormalized to sum to 1.

The "ps" bounds

With method = "ps", both paths default to a floor of 0.1. For a binary exposure, each bound supplied must be a single number strictly between 0 and 1, and a bound supplied alone implies its mirror: lower alone bounds the scores at [lower, 1 - lower], and upper alone at [1 - upper, upper]. With neither supplied, the bounds are [0.1, 0.9]; with both supplied, both are used as written, so an asymmetric bound must be given in full. The mirror follows from the weights: an untreated unit's ATE weight is \(1/(1 - e)\), so a floor on \(e\) alone would bound the treated weights and leave the untreated ones unbounded. A lone lower of 0.5 or more, or a lone upper of 0.5 or less, meets or crosses its own mirror and is an error of class propensity_range_error. lower = 1/c gives the bounds method = "adaptive" computes for the same \(c\).

For a categorical exposure, lower is the threshold \(\delta\) (default 0.1) applied to every column, after which each row is renormalized, and upper is not read and is recorded as NA. With k exposure levels, a threshold of 1/k or larger cannot be met by every column of a row that sums to one and is an error, so an exposure with ten or more levels needs an explicit lower below 1/k.

The adaptive bound

method = "adaptive" bounds a binary propensity score at \([1/c, 1 - 1/c]\), where \(c = \sqrt{n} \log(n) / 5\) is the weight bound of Gruber et al. (2022) and \(n\) counts the scores that are present, as in wt_trunc(). At \(n = 1000\), \(c\) is 43.7 and the bounds are 0.023 and 0.977. The bound adapts to the sample size alone, loosening as \(n\) grows, and leaves the estimand alone. ps_trim(method = "adaptive") instead adapts to the estimated scores, tightening under poor overlap, and changes the estimand to the population it keeps.

For unstabilized ATE weights the bound is the weight bound written on the score scale. A treated unit's weight is \(1/e\), so the floor \(1/c\) caps the treated weights at \(c\); an untreated unit's weight is \(1/(1 - e)\), so the ceiling \(1 - 1/c\) caps the untreated weights at \(c\). wt_ate() on the result therefore gives the weights wt_trunc(wt_ate(ps, .exposure), method = "adaptive") gives, with one difference, provided every unit's exposure is observed. (A unit with a present score and a missing exposure counts toward \(n\) here but has no weight for wt_trunc() to count, so the two bounds then differ.) The difference is this: a treated unit above \(1 - 1/c\) or an untreated unit below \(1/c\), whose weight is already close to 1, is moved to that bound too, and its weight rises to \(c / (c - 1)\). Every weight above \(c / (c - 1)\) is the same under either route. A lower bound alone would not do the same work, since no floor on \(e\) bounds an untreated unit's weight.

For stabilized ATE weights the equivalence does not carry over as it stands. Stabilizing multiplies each arm's weights by that arm's prevalence, so the treated weights are capped at \(P(A = 1) c\) and the untreated weights at \(P(A = 0) c\), which agree only when the two arms are equally common.

The bounds cross when \(c < 2\), which holds for fewer than 15 scores, so "adaptive" needs at least 15 scores present and raises an error of class propensity_range_error otherwise. It is refused for categorical exposures with an error of class propensity_method_error: truncating a categorical score renormalizes each row, which moves scores the bound never reached, so the bound no longer caps the weights. A two-level nnet::multinom() fit is read as one binary score and accepted.

Arithmetic behavior: Arithmetic operations on ps_trunc objects return plain numeric vectors. Once propensity scores are transformed (e.g., into weights), the result is no longer a propensity score.

Combining behavior: Combining ps_trunc objects with c() requires matching truncation parameters. Mismatched parameters produce a warning and return a plain numeric vector.

Fitted models

.propensity can be the fitted propensity score model instead of the scores it reports. A binomial stats::glm() and a two-level nnet::multinom() are read as one score per unit; a nnet::multinom() of three or more levels is read as one column per level. Those are the shapes predict(fit, type = "response") and fitted(fit) give, and truncating a fit bounds exactly what bounding those values would. A model of a continuous exposure's conditional mean, such as a stats::lm() fit or a gaussian stats::glm(), has no propensity score to bound and raises an error of class propensity_model_family_error. A dose has two routes instead: trim the dose model with ps_trim(method = "density"), which sets aside the units whose dose is implausible under the model, or build the weights with wt_ate() and bound them with wt_trunc(), which keeps every unit. There is no density method here: a floor on a dose's conditional density is a bound on its unstabilized weights, the ones wt_ate(stabilize = FALSE) builds, which wt_trunc() already applies.

The methods that read an exposure ("cr", and every method on the categorical route) take it from the model when .exposure is not supplied, announcing the variable they read; options(propensity.quiet = TRUE) silences the announcement. An .exposure you supply is used instead, and a categorical model's columns are matched to its levels by name, so an exposure whose levels are ordered differently is still bounded against the right column.

Missing values

A propensity score that arrives missing is not one this function pinned to a bound, so it takes no part in the truncation record: it never joins truncated_idx, and is_unit_truncated() reports FALSE for it. The value stays missing in the result. For a matrix of categorical propensity scores, a row with a missing cell comes back missing throughout, because renormalizing a row divides by its sum and that sum is unknown.

The methods that read their bounds off the data read them off the data they have. "pctl" and "cr" work their bounds out from the complete scores, and the categorical "pctl" bounds come from the cells of the complete rows, so the bounds are the ones the same call would produce with the missing observations dropped.

A missing exposure is a different matter, and "cr" refuses one with an error of class propensity_missing_value_error. Its bounds come from the exposure groups, and a unit that belongs to neither leaves them undefined. Remove or impute the missing exposure values first.

The truncation record

A ps_trunc records which units were winsorized as positions among the observations it was written for, along with how many observations that was. Operations that hand this package the subscript re-index those positions onto the result: subsetting with [, sort(), and rep() all return a record written for what they return, and a subscript naming a position more than once reports that unit at every place it now holds. unique() does the same when it can, as described below.

Operations that change how many observations there are without supplying a subscript cannot re-index the record, and it is dropped rather than worked out from the values, since a score that arrived equal to a bound is indistinguishable from one this function pinned there. vctrs::vec_slice(), which is how filtering, joining, and grouped verbs in dplyr reach a column, is the usual route, and combining two or more vectors with c() is another, because concatenation appends one set of observations to another. The record is dropped without comment on every route, since most of these length changes build vectors the caller never holds, such as the pieces a grouped verb slices a column into. The values, the class, and the method and its bounds are untouched.

A combine drops the positions even when it is handed a single vector. c() of one ps_trunc returns it unchanged, record included, but vctrs::vec_c(x), dplyr::bind_rows(df), vctrs::vec_rbind(df), and an ungrouped dplyr::reframe() rebuild the column, so a later is_unit_truncated() on the result refuses it.

unique() keeps one element for each distinct value, or one row for each distinct row of a matrix of scores, and that element or row stands for every unit holding the same scores. A matrix comes back as a matrix of the same class with its column names. The record is re-indexed onto the result when all of the merged units share one status, and dropped otherwise: a score or row that arrived where truncation would put it and one truncation moved there merge into a single element or row that neither status describes.

as.data.frame() and tibble::as_tibble() turn a matrix of scores into a data frame whose columns are ps_trunc vectors carrying the matrix's record, one row per unit, so the weight functions read the data frame as they read the matrix and a subset of its rows re-indexes the record. A data frame whose columns are not all truncated with the same record is refused by the weight functions with an error of class propensity_matrix_type_error. A matrix without column names gives columns named V1, V2, and so on, which the weight functions then refuse because they name no exposure level, so name the columns after the exposure levels before converting.

A record can also outlive the observations it describes, because it travels by routes vctrs does not see: growing a ps_trunc by subassignment carries it across the length change. is_unit_truncated() therefore checks that the record covers the object it is given and raises an error of class propensity_missing_meta_error when it does not, rather than name truncated units at stale positions.

That check compares how many observations the record was written for against how many the object holds, which a reordering does not change, so a route that could reorder the observations without saying how drops the positions instead, at any length: vctrs::vec_slice(), dplyr::arrange(), dplyr::filter(), and vctrs::vec_assign() and the helpers built on it return a ps_trunc whose record keeps its method and bounds and names no units, and is_unit_truncated() refuses it. Subsetting with [, sort(), unique(), and rep() know where the units went and re-index the record, and [<- and is.na<- move no unit and keep it, so reorder with [, or put the propensity scores in the order you want before truncating them.

Casting a numeric vector into a ps_trunc with vctrs::vec_cast() is a type operation and not a truncation. The result is described by the method and bounds of the target and records that none of the arriving values was pinned to a bound, so it can hold scores outside those bounds, including 0 and 1. Call ps_trunc() on the scores to truncate them.

References

Crump, R. K., Hotz, V. J., Imbens, G. W., & Mitnik, O. A. (2009). Dealing with limited overlap in estimation of average treatment effects. Biometrika, 96(1), 187–199.

Gruber, S., Phillips, R. V., Lee, H., & van der Laan, M. J. (2022). Data-adaptive selection of the propensity score truncation level for inverse-probability-weighted and targeted maximum likelihood estimators of marginal point treatment effects. American Journal of Epidemiology, 191(9), 1640–1651.

Walker, A. M., Patrick, A. R., Lauer, M. S., et al. (2013). A tool for assessing the feasibility of comparative effectiveness research. Comparative Effectiveness Research, 3, 11–20.

See also

ps_trim() for removing (rather than bounding) extreme values, ps_refit() for refitting the propensity model after trimming, is_ps_truncated(), is_unit_truncated(), ps_trunc_meta()

Examples

set.seed(2)
n <- 200
x <- rnorm(n)
z <- rbinom(n, 1, plogis(1.2 * x))
fit <- glm(z ~ x, family = binomial)
ps <- predict(fit, type = "response")

# Truncate to [0.1, 0.9]
ps_t <- ps_trunc(ps, method = "ps", lower = 0.1, upper = 0.9)
ps_t
#> <ps_trunc{[0.1,0.9], method=ps}[200]>
#>         1         2         3         4         5         6         7         8 
#> 0.2913095 0.5707511 0.8590561 0.2418820 0.4993060 0.5567591 0.7011122 0.4561604 
#>         9        10        11        12        13        14        15        16 
#> 0.9000000 0.4834312 0.6312438 0.7594616 0.4153613 0.2603881 0.8827176 0.1000000 
#>        17        18        19        20        21        22        23        24 
#> 0.7384237 0.5307543 0.7655683 0.6349278 0.9000000 0.2283122 0.8592915 0.9000000 
#>        25        26        27        28        29        30        31        32 
#> 0.5224033 0.1000000 0.6461643 0.3628349 0.7199125 0.5983593 0.7081103 0.6059826 
#>        33        34        35        36        37        38        39        40 
#> 0.7776782 0.4442186 0.3189618 0.3630602 0.1432262 0.2900408 0.3722937 0.4543264 
#>        41        42        43        44        45        46        47        48 
#> 0.4177638 0.1148898 0.3038315 0.8956760 0.6813225 0.9000000 0.4385124 0.4964323 
#>        49        50        51        52        53        54        55        56 
#> 0.4711469 0.2285337 0.3046166 0.9000000 0.3714863 0.8128663 0.2587396 0.1141443 
#>        57        58        59        60        61        62        63        64 
#> 0.4338453 0.7502468 0.7892860 0.8697086 0.1351329 0.9000000 0.3365403 0.5636424 
#>        65        66        67        68        69        70        71        72 
#> 0.6533256 0.3088378 0.1105762 0.3927369 0.5438044 0.2916295 0.2858818 0.6089556 
#>        73        74        75        76        77        78        79        80 
#> 0.4826524 0.6355772 0.5065031 0.2890305 0.8174117 0.7154239 0.7732114 0.1906359 
#>        81        82        83        84        85        86        87        88 
#> 0.7622717 0.1472971 0.3788316 0.1970403 0.1000000 0.8871259 0.3486998 0.4440783 
#>        89        90        91        92        93        94        95        96 
#> 0.4168763 0.6233901 0.8606965 0.8708768 0.2314475 0.1994226 0.1740398 0.2183041 
#>        97        98        99       100       101       102       103       104 
#> 0.9000000 0.5231365 0.3036226 0.3616811 0.7773582 0.5907629 0.4361655 0.3253709 
#>       105       106       107       108       109       110       111       112 
#> 0.2991480 0.9000000 0.7510710 0.9000000 0.4078242 0.4264336 0.2629645 0.4532487 
#>       113       114       115       116       117       118       119       120 
#> 0.6448288 0.8262184 0.6674247 0.6408723 0.8053646 0.7907095 0.5498332 0.3173982 
#>       121       122       123       124       125       126       127       128 
#> 0.8071017 0.5584826 0.8744315 0.4053976 0.2594361 0.6609892 0.3447196 0.6851535 
#>       129       130       131       132       133       134       135       136 
#> 0.1434914 0.1410494 0.6969682 0.6090883 0.7368408 0.1087328 0.8022199 0.8001309 
#>       137       138       139       140       141       142       143       144 
#> 0.7692948 0.7186431 0.9000000 0.1834141 0.3662171 0.6292392 0.3118604 0.5441734 
#>       145       146       147       148       149       150       151       152 
#> 0.7097460 0.3486308 0.6894218 0.6639810 0.3119192 0.2693231 0.7582975 0.4751339 
#>       153       154       155       156       157       158       159       160 
#> 0.7043397 0.3032090 0.8131267 0.2020948 0.7139969 0.6429234 0.5927018 0.6918371 
#>       161       162       163       164       165       166       167       168 
#> 0.6263848 0.3524856 0.4486289 0.6165339 0.2074392 0.2942181 0.9000000 0.1003085 
#>       169       170       171       172       173       174       175       176 
#> 0.2210197 0.7611782 0.7800143 0.7302196 0.5364399 0.6072561 0.2895753 0.3489979 
#>       177       178       179       180       181       182       183       184 
#> 0.4500408 0.2829251 0.7262051 0.1573127 0.2623292 0.2166743 0.6247878 0.2416706 
#>       185       186       187       188       189       190       191       192 
#> 0.6625838 0.7959533 0.5278943 0.6555095 0.3485232 0.6526880 0.2148033 0.5002503 
#>       193       194       195       196       197       198       199       200 
#> 0.2017086 0.4490035 0.7798096 0.6994296 0.4022315 0.3161761 0.3003833 0.3261363 

# Bound at [1/c, 1 - 1/c], c = sqrt(n) log(n) / 5, which caps the
# unstabilized ATE weights at c
ps_trunc(ps, method = "adaptive")
#> <ps_trunc{[0.0667,0.933], method=adaptive}[200]>
#>          1          2          3          4          5          6          7 
#> 0.29130945 0.57075108 0.85905610 0.24188201 0.49930600 0.55675915 0.70111217 
#>          8          9         10         11         12         13         14 
#> 0.45616038 0.90360175 0.48343119 0.63124383 0.75946160 0.41536130 0.26038808 
#>         15         16         17         18         19         20         21 
#> 0.88271762 0.08138334 0.73842370 0.53075430 0.76556830 0.63492785 0.91319684 
#>         22         23         24         25         26         27         28 
#> 0.22831221 0.85929151 0.90074563 0.52240328 0.07067804 0.64616425 0.36283492 
#>         29         30         31         32         33         34         35 
#> 0.71991248 0.59835929 0.70811028 0.60598256 0.77767823 0.44421858 0.31896179 
#>         36         37         38         39         40         41         42 
#> 0.36306016 0.14322616 0.29004079 0.37229372 0.45432638 0.41776382 0.11488981 
#>         43         44         45         46         47         48         49 
#> 0.30383148 0.89567605 0.68132246 0.90420941 0.43851237 0.49643227 0.47114693 
#>         50         51         52         53         54         55         56 
#> 0.22853370 0.30461661 0.91106441 0.37148631 0.81286633 0.25873963 0.11414425 
#>         57         58         59         60         61         62         63 
#> 0.43384531 0.75024682 0.78928598 0.86970860 0.13513287 0.90793306 0.33654034 
#>         64         65         66         67         68         69         70 
#> 0.56364237 0.65332558 0.30883775 0.11057616 0.39273691 0.54380444 0.29162947 
#>         71         72         73         74         75         76         77 
#> 0.28588182 0.60895559 0.48265241 0.63557725 0.50650310 0.28903045 0.81741170 
#>         78         79         80         81         82         83         84 
#> 0.71542388 0.77321135 0.19063586 0.76227170 0.14729713 0.37883165 0.19704028 
#>         85         86         87         88         89         90         91 
#> 0.09015377 0.88712592 0.34869977 0.44407832 0.41687627 0.62339015 0.86069650 
#>         92         93         94         95         96         97         98 
#> 0.87087680 0.23144748 0.19942258 0.17403983 0.21830407 0.90120123 0.52313647 
#>         99        100        101        102        103        104        105 
#> 0.30362260 0.36168114 0.77735817 0.59076288 0.43616553 0.32537093 0.29914802 
#>        106        107        108        109        110        111        112 
#> 0.90944557 0.75107099 0.90586644 0.40782418 0.42643359 0.26296452 0.45324867 
#>        113        114        115        116        117        118        119 
#> 0.64482877 0.82621843 0.66742471 0.64087231 0.80536462 0.79070955 0.54983317 
#>        120        121        122        123        124        125        126 
#> 0.31739822 0.80710169 0.55848264 0.87443145 0.40539762 0.25943605 0.66098916 
#>        127        128        129        130        131        132        133 
#> 0.34471960 0.68515350 0.14349137 0.14104940 0.69696824 0.60908833 0.73684079 
#>        134        135        136        137        138        139        140 
#> 0.10873282 0.80221987 0.80013088 0.76929476 0.71864309 0.91483892 0.18341413 
#>        141        142        143        144        145        146        147 
#> 0.36621714 0.62923918 0.31186040 0.54417339 0.70974601 0.34863085 0.68942181 
#>        148        149        150        151        152        153        154 
#> 0.66398099 0.31191915 0.26932313 0.75829753 0.47513393 0.70433975 0.30320896 
#>        155        156        157        158        159        160        161 
#> 0.81312672 0.20209485 0.71399686 0.64292343 0.59270175 0.69183710 0.62638476 
#>        162        163        164        165        166        167        168 
#> 0.35248559 0.44862887 0.61653394 0.20743922 0.29421813 0.91200893 0.10030845 
#>        169        170        171        172        173        174        175 
#> 0.22101971 0.76117824 0.78001428 0.73021961 0.53643993 0.60725607 0.28957526 
#>        176        177        178        179        180        181        182 
#> 0.34899794 0.45004081 0.28292513 0.72620510 0.15731275 0.26232917 0.21667429 
#>        183        184        185        186        187        188        189 
#> 0.62478778 0.24167062 0.66258378 0.79595325 0.52789428 0.65550948 0.34852324 
#>        190        191        192        193        194        195        196 
#> 0.65268801 0.21480329 0.50025030 0.20170859 0.44900347 0.77980959 0.69942959 
#>        197        198        199        200 
#> 0.40223149 0.31617609 0.30038326 0.32613630 

# Truncate at the 1st and 99th percentiles
ps_trunc(ps, method = "pctl", lower = 0.01, upper = 0.99)
#> <ps_trunc{[0.0901,0.912], method=pctl}[200]>
#>          1          2          3          4          5          6          7 
#> 0.29130945 0.57075108 0.85905610 0.24188201 0.49930600 0.55675915 0.70111217 
#>          8          9         10         11         12         13         14 
#> 0.45616038 0.90360175 0.48343119 0.63124383 0.75946160 0.41536130 0.26038808 
#>         15         16         17         18         19         20         21 
#> 0.88271762 0.09006607 0.73842370 0.53075430 0.76556830 0.63492785 0.91202081 
#>         22         23         24         25         26         27         28 
#> 0.22831221 0.85929151 0.90074563 0.52240328 0.09006607 0.64616425 0.36283492 
#>         29         30         31         32         33         34         35 
#> 0.71991248 0.59835929 0.70811028 0.60598256 0.77767823 0.44421858 0.31896179 
#>         36         37         38         39         40         41         42 
#> 0.36306016 0.14322616 0.29004079 0.37229372 0.45432638 0.41776382 0.11488981 
#>         43         44         45         46         47         48         49 
#> 0.30383148 0.89567605 0.68132246 0.90420941 0.43851237 0.49643227 0.47114693 
#>         50         51         52         53         54         55         56 
#> 0.22853370 0.30461661 0.91106441 0.37148631 0.81286633 0.25873963 0.11414425 
#>         57         58         59         60         61         62         63 
#> 0.43384531 0.75024682 0.78928598 0.86970860 0.13513287 0.90793306 0.33654034 
#>         64         65         66         67         68         69         70 
#> 0.56364237 0.65332558 0.30883775 0.11057616 0.39273691 0.54380444 0.29162947 
#>         71         72         73         74         75         76         77 
#> 0.28588182 0.60895559 0.48265241 0.63557725 0.50650310 0.28903045 0.81741170 
#>         78         79         80         81         82         83         84 
#> 0.71542388 0.77321135 0.19063586 0.76227170 0.14729713 0.37883165 0.19704028 
#>         85         86         87         88         89         90         91 
#> 0.09015377 0.88712592 0.34869977 0.44407832 0.41687627 0.62339015 0.86069650 
#>         92         93         94         95         96         97         98 
#> 0.87087680 0.23144748 0.19942258 0.17403983 0.21830407 0.90120123 0.52313647 
#>         99        100        101        102        103        104        105 
#> 0.30362260 0.36168114 0.77735817 0.59076288 0.43616553 0.32537093 0.29914802 
#>        106        107        108        109        110        111        112 
#> 0.90944557 0.75107099 0.90586644 0.40782418 0.42643359 0.26296452 0.45324867 
#>        113        114        115        116        117        118        119 
#> 0.64482877 0.82621843 0.66742471 0.64087231 0.80536462 0.79070955 0.54983317 
#>        120        121        122        123        124        125        126 
#> 0.31739822 0.80710169 0.55848264 0.87443145 0.40539762 0.25943605 0.66098916 
#>        127        128        129        130        131        132        133 
#> 0.34471960 0.68515350 0.14349137 0.14104940 0.69696824 0.60908833 0.73684079 
#>        134        135        136        137        138        139        140 
#> 0.10873282 0.80221987 0.80013088 0.76929476 0.71864309 0.91202081 0.18341413 
#>        141        142        143        144        145        146        147 
#> 0.36621714 0.62923918 0.31186040 0.54417339 0.70974601 0.34863085 0.68942181 
#>        148        149        150        151        152        153        154 
#> 0.66398099 0.31191915 0.26932313 0.75829753 0.47513393 0.70433975 0.30320896 
#>        155        156        157        158        159        160        161 
#> 0.81312672 0.20209485 0.71399686 0.64292343 0.59270175 0.69183710 0.62638476 
#>        162        163        164        165        166        167        168 
#> 0.35248559 0.44862887 0.61653394 0.20743922 0.29421813 0.91200893 0.10030845 
#>        169        170        171        172        173        174        175 
#> 0.22101971 0.76117824 0.78001428 0.73021961 0.53643993 0.60725607 0.28957526 
#>        176        177        178        179        180        181        182 
#> 0.34899794 0.45004081 0.28292513 0.72620510 0.15731275 0.26232917 0.21667429 
#>        183        184        185        186        187        188        189 
#> 0.62478778 0.24167062 0.66258378 0.79595325 0.52789428 0.65550948 0.34852324 
#>        190        191        192        193        194        195        196 
#> 0.65268801 0.21480329 0.50025030 0.20170859 0.44900347 0.77980959 0.69942959 
#>        197        198        199        200 
#> 0.40223149 0.31617609 0.30038326 0.32613630 

# Use truncated scores to calculate weights
wt_ate(ps_t, .exposure = z)
#> ℹ Treating `.exposure` as binary
#> <psw{estimand = ate; truncated}[200]>
#>   [1] 1.411053 2.329651 1.164068 1.319056 2.002780 2.256110 1.426305 1.838777
#>   [9] 1.111111 2.068547 2.711819 1.316722 1.710458 1.352060 1.132865 1.111111
#>  [17] 1.354236 1.884111 1.306219 2.739185 1.111111 1.295861 1.163749 1.111111
#>  [25] 2.093817 1.111111 1.547594 1.569452 1.389058 1.671237 1.412209 2.537959
#>  [33] 4.497985 1.799268 1.468346 2.754364 1.167169 3.447791 2.686051 1.832597
#>  [41] 2.393697 1.129803 3.291298 1.116475 1.467734 1.111111 2.280437 2.014373
#>  [49] 1.890884 1.296233 1.438056 1.111111 1.591055 1.230215 3.864889 1.128852
#>  [57] 1.766302 4.003953 4.745769 1.149810 7.400124 1.111111 1.507251 1.774175
#>  [65] 1.530630 3.237946 1.124323 2.546234 1.838896 1.411691 1.400328 1.642156
#>  [73] 1.932936 1.573373 1.974322 3.459843 1.223374 1.397773 1.293307 1.235538
#>  [81] 1.311868 1.172741 2.639695 1.245392 1.111111 1.127236 2.867797 1.798815
#>  [89] 2.398793 2.655268 1.161850 1.148268 1.301147 5.014477 1.210712 1.279270
#>  [97] 1.111111 2.097036 1.436003 1.566615 1.286408 1.692727 1.773570 1.482296
#> [105] 1.426835 1.111111 1.331432 1.111111 2.452037 2.345031 1.356787 1.828985
#> [113] 1.550799 1.210334 1.498296 1.560373 5.137812 1.264687 1.818733 3.150616
#> [121] 5.184079 1.790566 1.143600 2.466714 1.350322 1.512884 1.526064 3.176151
#> [129] 1.167531 7.089715 1.434786 2.558123 3.799981 1.121998 1.246541 1.249796
#> [137] 1.299892 1.391511 1.111111 5.452143 1.577827 2.697157 1.453194 2.193817
#> [145] 1.408955 1.535228 1.450491 2.976022 1.453318 3.713012 4.137318 1.905248
#> [153] 1.419769 1.435150 5.351220 1.253282 1.400566 1.555395 1.687189 3.245037
#> [161] 2.676550 1.544367 2.229014 1.621971 4.820689 1.416868 1.111111 1.111492
#> [169] 4.524483 1.313753 4.545750 1.369451 2.157218 2.546188 1.407609 2.865346
#> [177] 2.222021 1.394555 1.377021 1.186680 1.355618 1.276608 2.665158 1.318688
#> [185] 1.509243 1.256355 1.894319 1.525531 2.869249 2.879256 1.273566 2.001002
#> [193] 1.252675 1.814893 1.282364 3.327007 1.672888 1.462365 1.429354 1.483980

# Inspect which observations were truncated
is_unit_truncated(ps_t)
#>   [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE
#>  [13] FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE  TRUE
#>  [25] FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#>  [37] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE
#>  [49] FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#>  [61] FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#>  [73] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#>  [85]  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#>  [97]  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE  TRUE
#> [109] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [121] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [133] FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE
#> [145] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [157] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE
#> [169] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [181] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [193] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE

# Bound the scores a fitted model reports, reading the exposure off the model
ps_trunc(fit, method = "cr")
#> ℹ Using exposure variable "z" from the propensity score model
#> <ps_trunc{[0.135,0.813], method=cr}[200]>
#>         1         2         3         4         5         6         7         8 
#> 0.2913095 0.5707511 0.8131267 0.2418820 0.4993060 0.5567591 0.7011122 0.4561604 
#>         9        10        11        12        13        14        15        16 
#> 0.8131267 0.4834312 0.6312438 0.7594616 0.4153613 0.2603881 0.8131267 0.1351329 
#>        17        18        19        20        21        22        23        24 
#> 0.7384237 0.5307543 0.7655683 0.6349278 0.8131267 0.2283122 0.8131267 0.8131267 
#>        25        26        27        28        29        30        31        32 
#> 0.5224033 0.1351329 0.6461643 0.3628349 0.7199125 0.5983593 0.7081103 0.6059826 
#>        33        34        35        36        37        38        39        40 
#> 0.7776782 0.4442186 0.3189618 0.3630602 0.1432262 0.2900408 0.3722937 0.4543264 
#>        41        42        43        44        45        46        47        48 
#> 0.4177638 0.1351329 0.3038315 0.8131267 0.6813225 0.8131267 0.4385124 0.4964323 
#>        49        50        51        52        53        54        55        56 
#> 0.4711469 0.2285337 0.3046166 0.8131267 0.3714863 0.8128663 0.2587396 0.1351329 
#>        57        58        59        60        61        62        63        64 
#> 0.4338453 0.7502468 0.7892860 0.8131267 0.1351329 0.8131267 0.3365403 0.5636424 
#>        65        66        67        68        69        70        71        72 
#> 0.6533256 0.3088378 0.1351329 0.3927369 0.5438044 0.2916295 0.2858818 0.6089556 
#>        73        74        75        76        77        78        79        80 
#> 0.4826524 0.6355772 0.5065031 0.2890305 0.8131267 0.7154239 0.7732114 0.1906359 
#>        81        82        83        84        85        86        87        88 
#> 0.7622717 0.1472971 0.3788316 0.1970403 0.1351329 0.8131267 0.3486998 0.4440783 
#>        89        90        91        92        93        94        95        96 
#> 0.4168763 0.6233901 0.8131267 0.8131267 0.2314475 0.1994226 0.1740398 0.2183041 
#>        97        98        99       100       101       102       103       104 
#> 0.8131267 0.5231365 0.3036226 0.3616811 0.7773582 0.5907629 0.4361655 0.3253709 
#>       105       106       107       108       109       110       111       112 
#> 0.2991480 0.8131267 0.7510710 0.8131267 0.4078242 0.4264336 0.2629645 0.4532487 
#>       113       114       115       116       117       118       119       120 
#> 0.6448288 0.8131267 0.6674247 0.6408723 0.8053646 0.7907095 0.5498332 0.3173982 
#>       121       122       123       124       125       126       127       128 
#> 0.8071017 0.5584826 0.8131267 0.4053976 0.2594361 0.6609892 0.3447196 0.6851535 
#>       129       130       131       132       133       134       135       136 
#> 0.1434914 0.1410494 0.6969682 0.6090883 0.7368408 0.1351329 0.8022199 0.8001309 
#>       137       138       139       140       141       142       143       144 
#> 0.7692948 0.7186431 0.8131267 0.1834141 0.3662171 0.6292392 0.3118604 0.5441734 
#>       145       146       147       148       149       150       151       152 
#> 0.7097460 0.3486308 0.6894218 0.6639810 0.3119192 0.2693231 0.7582975 0.4751339 
#>       153       154       155       156       157       158       159       160 
#> 0.7043397 0.3032090 0.8131267 0.2020948 0.7139969 0.6429234 0.5927018 0.6918371 
#>       161       162       163       164       165       166       167       168 
#> 0.6263848 0.3524856 0.4486289 0.6165339 0.2074392 0.2942181 0.8131267 0.1351329 
#>       169       170       171       172       173       174       175       176 
#> 0.2210197 0.7611782 0.7800143 0.7302196 0.5364399 0.6072561 0.2895753 0.3489979 
#>       177       178       179       180       181       182       183       184 
#> 0.4500408 0.2829251 0.7262051 0.1573127 0.2623292 0.2166743 0.6247878 0.2416706 
#>       185       186       187       188       189       190       191       192 
#> 0.6625838 0.7959533 0.5278943 0.6555095 0.3485232 0.6526880 0.2148033 0.5002503 
#>       193       194       195       196       197       198       199       200 
#> 0.2017086 0.4490035 0.7798096 0.6994296 0.4022315 0.3161761 0.3003833 0.3261363 

if (rlang::is_installed("nnet")) {
  trt <- factor(sample(c("a", "b", "c"), n, replace = TRUE))
  multinomial_fit <- nnet::multinom(trt ~ x, trace = FALSE)
  ps_trunc(multinomial_fit, method = "ps")
}
#> ℹ Using exposure variable "trt" from the propensity score model
#> <ps_trunc_matrix[200 x 3]; truncated 0 of 200; method=ps[0.1000,Inf]>
#>            a         b         c
#> 1  0.3368013 0.3782680 0.2849307
#> 2  0.3285505 0.3863809 0.2850686
#> 3  0.3179718 0.3969403 0.2850880
#> 4  0.3385922 0.3765210 0.2848868
#> 5  0.3305651 0.3843902 0.2850447
#> 6  0.3289485 0.3859871 0.2850644
#> 7  0.3245896 0.3903135 0.2850969
#> 8  0.3317791 0.3831936 0.2850273
#> 9  0.3150074 0.3999317 0.2850609
#> 10 0.3310106 0.3839508 0.2850386
#> # ... with 190 more rows