---
title: "Quick Start"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Quick Start}
  %\VignetteEngine{knitr::rmarkdown}
  \usepackage[utf8]{inputenc}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 5)
library(dsld)
set.seed(2026)
```

# Data Science Looks at Discrimination

DSLD provides statistical, graphical, and tabular methods for exploring possible
discrimination and bias. It supports two related questions:

- **Adjusted group comparisons:** how does an outcome differ between groups
  after accounting for selected confounders? For example, how do expected wages
  differ by gender among people with similar age, education, and occupation?
- **Fair prediction:** how can a model predict an outcome while limiting the
  influence of sensitive attributes and their proxies?

We use **Y** for the outcome, **S** for a sensitive attribute, and **X** for other
predictors. Many functions wrap base R, qeML, or fairml methods and add group
comparisons or fairness diagnostics. Their first arguments usually identify the
data frame, outcome column (`yName`), and sensitive column (`sName` or `sNames`).
Use `summary()` and `predict()` for the supported fitted-model classes.

# Analytical operations

## Group comparisons with confounder adjustment

The `lsa` dataset, supplied by qeML, contains law-school admissions and outcome
variables. We use LSAT score as Y, race as S, and the remaining columns as
covariates. Some covariates are
measured after the LSAT, so this is an illustration of the software rather than
a causal model of test-score differences.

```{r law-school-model}
data(lsa, package = "qeML")
names(lsa)
lsat_model <- dsldLinear(lsa, "lsat", "race1")
summary(lsat_model)
```

The summary reports model coefficients and pairwise sensitive-group differences,
including their standard errors. Factor columns are represented using the
fitted model's contrasts. For two groups, the comparison is the second factor
level minus the first; for more groups, the labels state each difference's
direction.

For example, the family-income coefficient describes the adjusted change in
expected LSAT score per income-category step. The race coefficients and group
comparisons describe adjusted associations under this model. Their practical
size and uncertainty matter alongside their p-values; these associations alone
do not establish discrimination or its cause.

Setting `interactions = TRUE` fits a separate model for each sensitive group.
Supply `sComparisonPts` to compare these fitted models at specific predictor
values. The repository README demonstrates this with wage income and age.
`useSandwich = TRUE` requests a sandwich covariance estimate for linear models.

## Fair prediction

A predictor can carry information about S even when S is omitted from the
model. Fair-prediction methods limit this influence through constraints or
feature deweighting. Evaluate prediction error and fairness diagnostics together:
a stronger constraint does not guarantee improvement in every measure.

The bundled `compas1` data include recidivism outcomes and demographic variables.
We illustrate `dsldFgrrm()`, a wrapper for fairml's generalized ridge method,
using a reproducible subset of two racial groups. `unfairness` lies in (0, 1];
smaller values impose a stronger fairness constraint under the selected
definition. Always identify the positive outcome using `yesYVal`.

```{r fair-models}
data(compas1, package = "dsld")
records <- droplevels(subset(compas1, race %in% c("African-American", "Caucasian")))
records <- records[, c("age", "priors_count", "sex", "two_year_recid", "race")]
records <- records[sample.int(nrow(records), 800), ]
train_rows <- sample.int(nrow(records), 600)
training <- records[train_rows, ]
testing <- records[-train_rows, ]
test_x <- testing[, setdiff(names(testing), "two_year_recid")]

baseline <- dsldFgrrm(training, "two_year_recid", "race", unfairness = 1,
                      yesYVal = "Yes")
constrained <- dsldFgrrm(training, "two_year_recid", "race", unfairness = 0.1,
                         yesYVal = "Yes")
baseline_result <- predict(baseline, test_x)
constrained_result <- predict(constrained, test_x)
data.frame(
  model = c("Baseline", "Constrained"),
  test_error = c(
    mean((baseline_result$preds > 0.5) != (testing$two_year_recid == "Yes")),
    mean((constrained_result$preds > 0.5) != (testing$two_year_recid == "Yes"))
  )
)
constrained_result$correlations
```

These probabilities describe the specified positive outcome. Correlations
measure association with sensitive attributes; their absolute magnitudes are
useful for comparison, but zero correlation is not a general fairness guarantee.
The historical fields `trainAcc` and `testAcc` contain **errors**, not accuracy:
mean absolute error for regression and misclassification rate for classification.

# Graphical and tabular operations

## Conditional disparity

`dsldConditDisparity()` plots an outcome against a numeric predictor, separately
by sensitive group. It can restrict rows through `condits` and omit groups with
fewer than `minS` rows. The binary plotting interface uses the first outcome
factor level as its positive class.

```{r conditional-disparity}
law_sample <- lsa[sample.int(nrow(lsa), 1500), ]
levels(law_sample$bar)
dsldConditDisparity(law_sample, "bar", "race1", "lsat", minS = 50)
```

Inspect the outcome coding, group sample sizes, and uncertainty before drawing
substantive conclusions from the curves.

## Group frequencies and possible confounders

The bundled `svcensus` data describe workers in engineering occupations in
Silicon Valley. Compare education and occupation distributions by gender:

```{r group-frequencies}
data(svcensus, package = "dsld")
names(svcensus)
dsldFrequencyByS(svcensus, cName = "educ", sName = "gender")
dsldFrequencyByS(svcensus, cName = "occ", sName = "gender")
tapply(svcensus$wageinc, svcensus$occ, mean)
```

Columns follow the factor levels shown in the table labels. Education codes 14
and 16 represent master's and doctoral degrees. Differences in occupation
frequencies and mean wages can motivate examining occupation as a possible
confounder. Selecting adjustment variables still requires substantive reasoning;
similar group frequencies alone do not rule a variable in or out.

`dsldDensityByS()` compares numeric distributions. `dsldConfounders()` applies
frequency or density comparisons to the other columns in a data frame.

## Frequency-based parallel coordinates

Parallel coordinates place each variable on a separate vertical axis and join
the values belonging to one observation. `dsldFreqPCoord()` can display the
most typical patterns within each sensitive group, using nearest-neighbor
density estimates. The axes are centered and scaled.

```{r parallel-coordinates}
plot_data <- law_sample[, c("fam_inc", "ugpa", "gender", "lsat", "race1")]
dsldFreqPCoord(plot_data, m = 25, sName = "race1", k = 15)
```

This view can reveal multivariate patterns that separate plots miss. Its density
and sampling settings affect which observations appear; it does not summarize
every member of a group.

## Comparing feature sets

`dsldTakeALookAround()` compares predictor subsets C. It reports:

- **a:** prediction error for Y using C.
- **b:** prediction error for Y using C and S.
- **c:** prediction error for S using C.

The same held-out rows are used for all feature sets. Lower (c) means S is more
predictable from C, which can indicate stronger proxy information. Interpret
these errors relative to suitable outcome baselines.

```{r feature-sets}
wages <- svcensus[sample.int(nrow(svcensus), 800),
                   c("age", "educ", "wkswrkd", "gender", "wageinc")]
dsldTakeALookAround(wages, "wageinc", "gender", maxFeatureSetSize = 2, holdout = 100)
```

# Python interface

Install the R package first, then run `python -m pip install ./inst` from the
repository root. The [Python guide](https://github.com/matloff/dsld/blob/master/inst/README.md)
includes installation instructions and three notebooks covering data conversion,
analysis, fair prediction, and graphics.

```python
import rpy2.robjects as ro
from rpy2.robjects.packages import importr
from dsldPy import dsldPyLinear, dsldPyLinearSummary

bundled = ro.Environment()
importr("utils").data("svcensus", package="dsld", envir=bundled)
model = dsldPyLinear(bundled["svcensus"], "wageinc", "gender")
dsldPyLinearSummary(model)
```

# Associated textbook and function reference

The [free companion textbook](https://matloff.github.io/dsldBook/)
explains the statistical concepts using DSLD examples. For the categorized
function reference, run `vignette("Function_List", package = "dsld")`.
