Statistical and graphical tools for exploring group differences
and possible discrimination related to sensitive attributes such as race,
gender, and age. Includes adjusted group comparisons, matching, distribution
plots, and prediction methods that constrain sensitive-attribute influence
or deweight selected predictors. Cross-validation supports comparison of
prediction error and sensitive-attribute associations. Methods and teaching
examples are described by Mittal et al. (2025)
Statistical and graphical tools for exploring possible discrimination and bias in data, with an R package and Python interfaces.
Discrimination is an important social issue, and many datasets can help us investigate it. DSLD brings together statistical models, visualizations, and fair machine learning methods to make these investigations more accessible.
The package supports teaching and research in statistics and the social sciences, organizational analysis, and other studies of differences between groups. The companion book explains the statistical principles behind the methods and works through practical examples.
DSLD is currently archived on CRAN. Version 2.0.0 is available from this repository and has not yet been submitted to CRAN. Download or clone the repository, then run the following in R with the repository root as your working directory:
install.packages("remotes") # Run once if needed.
remotes::install_local(".", dependencies = TRUE, upgrade = "never",
build_vignettes = TRUE)
If the dependencies are already installed, run R CMD build . followed by
R CMD INSTALL dsld_2.0.0.tar.gz from the repository root. Building first also
prepares the vignettes. Optional methods use bnlearn, Matching, randomForest,
cccp, or CVXR; their functions explain which package is needed if it is absent.
After installation:
library(dsld)
vignette("Quick_Start", package = "dsld")
vignette("Function_List", package = "dsld")
See the Python installation guide to use the same R implementations from Python.
DSLD distinguishes between estimating group differences and building predictive models that limit the influence of sensitive attributes.
| Statistical analysis | Fair machine learning | |
|---|---|---|
| Main question | How does an outcome differ between groups after adjustment? | How can predictions limit the influence of sensitive attributes and their proxies? |
| Sensitive attributes | Included to estimate group differences | Excluded or constrained, depending on the method |
| Other variables | Adjust for selected confounders | Control the influence of possible proxies |
| Main outputs | Estimates, standard errors, and group comparisons | Predictions, prediction error, and group correlations |
We use Y for the outcome, S for a sensitive attribute, C for confounders, and X for other predictors. A statistical association after adjustment depends on the variables and model chosen; it does not by itself establish a causal explanation.
The bundled svcensus dataset records age, education, occupation, wage income,
weeks worked, and gender for workers in engineering occupations. We start with
age, wage income, and gender to keep the example focused.
data(svcensus, package = "dsld")
wages <- svcensus[, c("age", "wageinc", "gender")]
head(wages)
wage_model <- dsldLinear(wages, "wageinc", "gender", interactions = FALSE)
summary(wage_model)
Without interactions, the model estimates a common age slope and a constant
gender difference in expected wage income. summary() reports the coefficients,
the adjusted group comparison, and its standard error. useSandwich = TRUE
requests a sandwich covariance estimate.
Allowing interactions fits a separate model for each group. We can then compare predicted wages at specific ages:
comparison_points <- data.frame(age = c(36, 43))
interaction_model <- dsldLinear(
wages, "wageinc", "gender",
interactions = TRUE, sComparisonPts = comparison_points
)
summary(interaction_model)
predict(interaction_model, comparison_points)
dsldLogit() provides analogous comparisons for a binary outcome; specify its
positive label with yesYVal. dsldML() uses a selected qeML method for
nonparametric group comparisons. Both accept user-supplied comparison points.
Suppose we want to predict wage income while reducing the influence of gender and occupation as a possible proxy. The EDF methods remove sensitive columns from the predictors and deweight selected remaining features.
Use a reproducible train/test split and evaluate both prediction error and association with the sensitive attribute:
set.seed(2026)
model_data <- svcensus[, c("age", "occ", "wageinc", "gender")]
model_data <- model_data[sample.int(nrow(model_data), 1200), ]
train_rows <- sample.int(nrow(model_data), 900)
train_data <- model_data[train_rows, ]
test_data <- model_data[-train_rows, ]
test_x <- test_data[, c("age", "occ", "gender")]
baseline <- dsldQeFairKNN(train_data, "wageinc", "gender", k = 25)
fair_model <- dsldQeFairKNN(
train_data, "wageinc", "gender", deweightPars = list(occ = 0.2), k = 25
)
baseline_predictions <- predict(baseline, test_x)
fair_predictions <- predict(fair_model, test_x)
comparison <- data.frame(
model = c("KNN", "Deweighted KNN"),
test_MAE = c(
mean(abs(baseline_predictions$preds - test_data$wageinc)),
mean(abs(fair_predictions$preds - test_data$wageinc))
)
)
comparison
fair_predictions$correlations
For KNN and random forests, smaller deweights reduce a feature's influence. For the ridge variants, larger penalties increase shrinkage. Evaluate the resulting tradeoff: deweighting does not guarantee that every error or fairness measure improves.
Search a small parameter grid using cross-validation:
set.seed(2026)
tradeoff <- dsldFairUtils(
train_data, "wageinc", "gender", "dsldQeFairKNN",
deweightPars = list(occ = c(1, 0.5, 0.2)),
k_folds = 3, model_args = list(k = 25)
)
tradeoff
trainAcc and testAcc retain their original names but are errors:
mean absolute error (MAE) for regression and misclassification rate for binary
classification. Lower is better. MAE is not a percentage error.trainCorrs and prediction correlations contain signed correlations between
predictions and numeric sensitive attributes or factor-level indicators.
For binary classification, they use the probability of yesYVal consistently.
Compare absolute magnitudes when measuring association; zero correlation is
not a general guarantee of fairness or independence.NA, rather than a misleading zero.| Area | Functions | Purpose |
|---|---|---|
| Group comparisons | dsldLinear, dsldLogit, dsldML |
Estimate adjusted differences between sensitive groups |
| Confounders and proxies | dsldCHunting, dsldOHunting, dsldTakeALookAround |
Explore variable importance, associations, and predictive feature sets |
| Matching and discovery | dsldMatchedATE, dsldIamb |
Matching-based treatment effects and conditional-independence structure learning |
| Group distributions | dsldFrequencyByS, dsldDensityByS, dsldConfounders |
Frequency tables and density comparisons |
| Multivariate graphics | dsldFreqPCoord, dsldScatterPlot3D, dsldConditDisparity |
Parallel coordinates, 3D plots, and conditional disparity plots |
| fairml wrappers | dsldFrrm, dsldFgrrm, dsldNclm, dsldZlm, dsldZlrm |
Models that constrain sensitive-attribute influence |
| EDF wrappers | dsldQeFairKNN, dsldQeFairRF, dsldQeFairRidgeLin, dsldQeFairRidgeLog |
Explicit feature deweighting and penalization |
| Model comparison | dsldFairUtils |
Cross-validated fairness/utility parameter grids |
The help pages describe individual arguments and return values. For example,
run ?dsldLinear, ?dsldQeFairKNN, or ?dsldFairUtils in R.
Norm Matloff, Aditya Mittal, Taha Abdullah, Arjun Ashok, Shubhada Martha, Billy Ouattara, Jonathan Tran, and Brandon Zarate.
Report reproducible problems through GitHub issues,
or contact Aditya Mittal at [email protected]. Include the function call,
sessionInfo(), and a small example that demonstrates the problem.