| Title: | Fair Interpolated Transport for Group-Fair Clustering |
|---|---|
| Description: | Implementation of Fair Interpolated Transport (FIT), an algorithm-agnostic preprocessing framework for group-fair clustering. Group-conditional empirical distributions are moved along Wasserstein-2 geodesics toward a shared barycenter at a tunable transport intensity, and the smallest intensity meeting a soft-fairness tolerance is selected. Three soft clustering families are provided, centroid based, graph based, and model based. |
| Authors: | Jesse S. Ghashti [aut, cre], Warren Hare [aut], John R.J. Thompson [aut] |
| Maintainer: | Jesse S. Ghashti <[email protected]> |
| License: | GPL-2 |
| Version: | 1.0.0 |
| Built: | 2026-09-21 09:44:51 UTC |
| Source: | https://github.com/ghashti-j/fitclust |
Precomputes the Wasserstein-2 barycenter and the group transport maps for a data set, and returns a closure that produces the transported data at any transport intensity by McCann interpolation.
buildTransport( dataMat, groupVec, alphaVec, maxBaryIter = 30, tol = 1e-04, baryInit = "pooled", verbose = FALSE )buildTransport( dataMat, groupVec, alphaVec, maxBaryIter = 30, tol = 1e-04, baryInit = "pooled", verbose = FALSE )
dataMat |
A numeric matrix of observations, |
groupVec |
An integer or factor vector of protected-group labels of
length |
alphaVec |
A numeric vector of barycenter weights, one per group, summing to one. |
maxBaryIter |
Maximum number of barycenter fixed-point iterations. |
tol |
Convergence tolerance for the barycenter iteration. |
baryInit |
Initial barycenter support, passed to
|
verbose |
Logical, whether to print barycenter progress. |
At intensity t the transported coordinates of an observation
in group are (1 - t) times the original coordinates plus t
times the transported target. A vector t applies a per-group
intensity.
A list with components fn (a function of a scalar or
length- intensity t returning the transported matrix),
barycenter, targets, idxByGroup, groups,
baryConverged, and baryIter.
dataMat <- rbind(matrix(rnorm(40), ncol = 2), matrix(rnorm(60) + 2, ncol = 2)) groupVec <- rep(1:2, times = c(20, 30)) transport <- buildTransport(dataMat, groupVec, c(0.5, 0.5)) halfway <- transport$fn(0.5) dim(halfway)dataMat <- rbind(matrix(rnorm(40), ncol = 2), matrix(rnorm(60) + 2, ncol = 2)) groupVec <- rep(1:2, times = c(20, 30)) transport <- buildTransport(dataMat, groupVec, c(0.5, 0.5)) halfway <- transport$fn(0.5) dim(halfway)
Recovers the source-to-barycenter transport maps by barycentric projection. When the barycenter plans are supplied they are reused by transposition, since squared-cost optimal transport is symmetric under swapping source and target.
computeTransportMaps(xByGroup, barycenter, plans = NULL)computeTransportMaps(xByGroup, barycenter, plans = NULL)
xByGroup |
A list of numeric matrices, one per protected group. |
barycenter |
The |
plans |
An optional list of transport plans returned by
|
A list of numeric matrices, one per group, giving each group's transported target coordinates on the barycenter support.
xByGroup <- list(matrix(rnorm(40), ncol = 2), matrix(rnorm(60) + 1, ncol = 2)) bary <- wassersteinBarycenter(xByGroup, c(0.5, 0.5), maxIter = 5) maps <- computeTransportMaps(xByGroup, bary$barycenter, bary$plans) length(maps)xByGroup <- list(matrix(rnorm(40), ncol = 2), matrix(rnorm(60) + 1, ncol = 2)) bary <- wassersteinBarycenter(xByGroup, c(0.5, 0.5), maxIter = 5) maps <- computeTransportMaps(xByGroup, bary$barycenter, bary$plans) length(maps)
Computes the matrix of squared Euclidean distances between the rows of two matrices.
distMatrix(xMat, yMat)distMatrix(xMat, yMat)
xMat |
A numeric matrix of size |
yMat |
A numeric matrix of size |
An numeric matrix whose entry in row and column is the
squared Euclidean distance between row of xMat and row of
yMat.
xMat <- matrix(rnorm(20), ncol = 2) yMat <- matrix(rnorm(10), ncol = 2) distMatrix(xMat, yMat)xMat <- matrix(rnorm(20), ncol = 2) yMat <- matrix(rnorm(10), ncol = 2) distMatrix(xMat, yMat)
Fits a full-covariance Gaussian mixture by the expectation maximization algorithm with ridge-regularized covariances and an optional warm start.
emGMM( dataMat, numClusters, initParams = NULL, numStart = 5, maxIter = 200, tol = 1e-06, regLambda = 1e-06 )emGMM( dataMat, numClusters, initParams = NULL, numStart = 5, maxIter = 200, tol = 1e-06, regLambda = 1e-06 )
dataMat |
A numeric matrix of observations, |
numClusters |
The number of mixture components, |
initParams |
An optional list with components |
numStart |
The number of random restarts when |
maxIter |
Maximum number of expectation maximization iterations. |
tol |
Convergence tolerance on the log-likelihood. |
regLambda |
Ridge value added to the diagonal of each component covariance for numerical stability. |
A list with components weights (the mixing proportions),
means (the component means), covariances (a list of
covariance matrices), posterior (the posterior assignment
matrix), logLik (the maximized log-likelihood), and clusters
(the integer vector of hard assignments obtained by maximum posterior).
Dempster, A. P., Laird, N. M. and Rubin, D. B. (1977), Maximum likelihood from incomplete data via the EM algorithm, Journal of the Royal Statistical Society, Series B 39(1), 1–38.
dataMat <- rbind(matrix(rnorm(60), ncol = 2), matrix(rnorm(60) + 4, ncol = 2)) fit <- emGMM(dataMat, numClusters = 2, numStart = 2) table(fit$clusters)dataMat <- rbind(matrix(rnorm(60), ncol = 2), matrix(rnorm(60) + 4, ncol = 2)) fit <- emGMM(dataMat, numClusters = 2, numStart = 2) table(fit$clusters)
Fits fuzzy -means with an optional warm start from supplied centroids. When
initial centroids are given a single run is performed, otherwise the best of
several random restarts is returned.
fcm( dataMat, numClusters, fuzzifier = 2, initCenters = NULL, numStart = 5, maxIter = 100, tol = 1e-06 )fcm( dataMat, numClusters, fuzzifier = 2, initCenters = NULL, numStart = 5, maxIter = 100, tol = 1e-06 )
dataMat |
A numeric matrix of observations, |
numClusters |
The number of clusters, |
fuzzifier |
The fuzziness exponent, greater than one. |
initCenters |
An optional |
numStart |
The number of random restarts when |
maxIter |
Maximum number of iterations per run. |
tol |
Convergence tolerance on the largest centroid change. |
A list with components membership (the soft membership
matrix), centers (the centroid matrix), objective
(the fuzzy objective value), and clusters (the integer vector of
hard assignments obtained by maximum membership).
Bezdek, J. C. (1981), Pattern Recognition with Fuzzy Objective Function Algorithms, Plenum Press, New York.
dataMat <- rbind(matrix(rnorm(40), ncol = 2), matrix(rnorm(40) + 4, ncol = 2)) fit <- fcm(dataMat, numClusters = 2, numStart = 3) table(fit$clusters)dataMat <- rbind(matrix(rnorm(40), ncol = 2), matrix(rnorm(40) + 4, ncol = 2)) fit <- fcm(dataMat, numClusters = 2, numStart = 3) table(fit$clusters)
Applies the Fair Interpolated Transport framework with a fuzzy -means
clustering family. The data are transported toward the Wasserstein-2
barycenter along a grid of intensities, and the smallest intensity at which
the soft-fairness violation falls within the tolerance is selected.
fitSKM( dataMat, groupVec, numClusters, deltaFair = 0.05, alpha = "uniform", tSeq = seq(0, 1, by = 0.02), fuzzifier = 2, numStartBaseline = 5, warmStart = TRUE, verbose = TRUE )fitSKM( dataMat, groupVec, numClusters, deltaFair = 0.05, alpha = "uniform", tSeq = seq(0, 1, by = 0.02), fuzzifier = 2, numStartBaseline = 5, warmStart = TRUE, verbose = TRUE )
dataMat |
A numeric matrix of observations, |
groupVec |
An integer or factor vector of protected-group labels of
length |
numClusters |
The number of clusters, |
deltaFair |
The soft-fairness tolerance in the interval from zero to one. |
alpha |
The barycenter weight specification, passed to
|
tSeq |
A numeric grid of transport intensities in the unit interval. |
fuzzifier |
The fuzzy c-means fuzziness exponent. |
numStartBaseline |
The number of random restarts for the baseline fit at intensity zero. |
warmStart |
Logical, whether to warm-start each intensity from the previous solution along the grid. |
verbose |
Logical, whether to print barycenter progress. |
When no grid intensity satisfies the tolerance, the solution at the intensity minimizing the soft violation is returned with a warning. Warm starting follows the McCann interpolation path so that adjacent intensities give similar clusterings.
A list describing the selected solution, with components including
clusters, centers, membership, the baseline
quantities clustersBaseline, centersBaseline, and
membershipBaseline, the selected intensity tOptimal, the
swept diagnostics in history, and the transported data
dataTransported.
Bezdek, J. C. (1981), Pattern Recognition with Fuzzy Objective Function Algorithms, Plenum Press, New York.
McCann, R. J. (1997), A convexity principle for interacting gases, Advances in Mathematics 128(1), 153–179.
dataMat <- rbind(matrix(rnorm(60), ncol = 2), matrix(rnorm(60) + 4, ncol = 2)) groupVec <- rep(1:2, times = 30) fit <- fitSKM(dataMat, groupVec, numClusters = 2, tSeq = seq(0, 1, by = 0.1), verbose = FALSE) fit$tOptimaldataMat <- rbind(matrix(rnorm(60), ncol = 2), matrix(rnorm(60) + 4, ncol = 2)) groupVec <- rep(1:2, times = 30) fit <- fitSKM(dataMat, groupVec, numClusters = 2, tSeq = seq(0, 1, by = 0.1), verbose = FALSE) fit$tOptimal
Applies the Fair Interpolated Transport framework with a Gaussian mixture clustering family. The mixture posteriors serve as the soft memberships, and the expectation maximization fit is warm-started along the intensity grid.
fitSMM( dataMat, groupVec, numClusters, deltaFair = 0.05, alpha = "uniform", tSeq = seq(0, 1, by = 0.02), numStartBaseline = 5, warmStart = TRUE, regLambda = 1e-06, verbose = TRUE )fitSMM( dataMat, groupVec, numClusters, deltaFair = 0.05, alpha = "uniform", tSeq = seq(0, 1, by = 0.02), numStartBaseline = 5, warmStart = TRUE, regLambda = 1e-06, verbose = TRUE )
dataMat |
A numeric matrix of observations, |
groupVec |
An integer or factor vector of protected-group labels of
length |
numClusters |
The number of mixture components, |
deltaFair |
The soft-fairness tolerance in the interval from zero to one. |
alpha |
The barycenter weight specification, passed to
|
tSeq |
A numeric grid of transport intensities in the unit interval. |
numStartBaseline |
The number of random restarts for the baseline fit. |
warmStart |
Logical, whether to warm-start the mixture from the previous intensity along the grid. |
regLambda |
Ridge value added to component covariances for stability. |
verbose |
Logical, whether to print barycenter progress. |
A list describing the selected solution, with components including
clusters, the posterior membership, the mixture parameters
weights, means, and covariances, the baseline
quantities clustersBaseline and membershipBaseline, the
selected intensity tOptimal, the swept diagnostics in
history, and the transported data dataTransported.
Dempster, A. P., Laird, N. M. and Rubin, D. B. (1977), Maximum likelihood from incomplete data via the EM algorithm, Journal of the Royal Statistical Society, Series B 39(1), 1–38.
McCann, R. J. (1997), A convexity principle for interacting gases, Advances in Mathematics 128(1), 153–179.
dataMat <- rbind(matrix(rnorm(80), ncol = 2), matrix(rnorm(80) + 4, ncol = 2)) groupVec <- rep(1:2, times = 40) fit <- fitSMM(dataMat, groupVec, numClusters = 2, tSeq = seq(0, 1, by = 0.1), verbose = FALSE) fit$tOptimaldataMat <- rbind(matrix(rnorm(80), ncol = 2), matrix(rnorm(80) + 4, ncol = 2)) groupVec <- rep(1:2, times = 40) fit <- fitSMM(dataMat, groupVec, numClusters = 2, tSeq = seq(0, 1, by = 0.1), verbose = FALSE) fit$tOptimal
Applies the Fair Interpolated Transport framework with a soft normalized
spectral clustering family. The spectral embedding is recomputed at each
intensity and aligned to the previous intensity for basis
continuity, and fuzzy -means is run on the embedding.
fitSSC( dataMat, groupVec, numClusters, deltaFair = 0.05, alpha = "uniform", tSeq = seq(0, 1, by = 0.02), numNeighbors = 7, fuzzifier = 2, numStartBaseline = 5, warmStart = TRUE, verbose = TRUE )fitSSC( dataMat, groupVec, numClusters, deltaFair = 0.05, alpha = "uniform", tSeq = seq(0, 1, by = 0.02), numNeighbors = 7, fuzzifier = 2, numStartBaseline = 5, warmStart = TRUE, verbose = TRUE )
dataMat |
A numeric matrix of observations, |
groupVec |
An integer or factor vector of protected-group labels of
length |
numClusters |
The number of clusters, |
deltaFair |
The soft-fairness tolerance in the interval from zero to one. |
alpha |
The barycenter weight specification, passed to
|
tSeq |
A numeric grid of transport intensities in the unit interval. |
numNeighbors |
The neighbor rank for local scaling in the embedding. |
fuzzifier |
The fuzzy |
numStartBaseline |
The number of random restarts for the baseline fit. |
warmStart |
Logical, whether to align embeddings and warm-start centroids across the grid. |
verbose |
Logical, whether to print barycenter progress. |
A list describing the selected solution, with components including
clusters, membership, the embedding embedding and
affinity affinity, the baseline quantities clustersBaseline,
membershipBaseline, embeddingBaseline, and
affinityBaseline, the selected intensity tOptimal, the swept
diagnostics in history, and the transported data
dataTransported.
Bezdek, J. C. (1981), Pattern Recognition with Fuzzy Objective Function Algorithms, Plenum Press, New York.
McCann, R. J. (1997), A convexity principle for interacting gases, Advances in Mathematics 128(1), 153–179.
Zelnik-Manor, L. and Perona, P. (2004), Self-tuning spectral clustering, in Advances in Neural Information Processing Systems, Vol. 17.
dataMat <- rbind(matrix(rnorm(60), ncol = 2), matrix(rnorm(60) + 5, ncol = 2)) groupVec <- rep(1:2, times = 30) fit <- fitSSC(dataMat, groupVec, numClusters = 2, tSeq = seq(0, 1, by = 0.1), verbose = FALSE) fit$tOptimaldataMat <- rbind(matrix(rnorm(60), ncol = 2), matrix(rnorm(60) + 5, ncol = 2)) groupVec <- rep(1:2, times = 30) fit <- fitSSC(dataMat, groupVec, numClusters = 2, tSeq = seq(0, 1, by = 0.1), verbose = FALSE) fit$tOptimal
Computes the worst-case absolute deviation of any cluster's group share from the population group proportion, over all clusters and groups, for a hard partition.
hardViolation(clusters, groupVec)hardViolation(clusters, groupVec)
clusters |
An integer vector of cluster labels, one per observation. |
groupVec |
An integer or factor vector of protected-group labels. |
A single non-negative numeric value. Zero indicates exact proportional representation in every cluster.
clusters <- rep(1:2, each = 50) groupVec <- rep(1:2, times = 50) hardViolation(clusters, groupVec)clusters <- rep(1:2, each = 50) groupVec <- rep(1:2, times = 50) hardViolation(clusters, groupVec)
Rotates one spectral embedding to best match a reference embedding in Frobenius norm, which stabilizes the eigenvector basis across a sequence of related problems.
procrustesAlign(newEmbed, refEmbed)procrustesAlign(newEmbed, refEmbed)
newEmbed |
A numeric matrix, the embedding to rotate. |
refEmbed |
A numeric matrix of the same dimension, the reference embedding. |
The rotated embedding, a numeric matrix of the same dimension as
newEmbed.
refEmbed <- matrix(rnorm(30), ncol = 3) newEmbed <- refEmbed %*% qr.Q(qr(matrix(rnorm(9), 3))) aligned <- procrustesAlign(newEmbed, refEmbed) dim(aligned)refEmbed <- matrix(rnorm(30), ncol = 3) newEmbed <- refEmbed %*% qr.Q(qr(matrix(rnorm(9), 3))) aligned <- procrustesAlign(newEmbed, refEmbed) dim(aligned)
Converts a weight specification into a length- vector of barycenter weights
that sum to one, where is the number of protected groups.
resolveAlpha(alpha, groupVec, groups)resolveAlpha(alpha, groupVec, groups)
alpha |
Either a character string, one of |
groupVec |
An integer or factor vector of protected-group labels, one entry per observation. |
groups |
A vector of the unique group labels, in the order the returned weights should follow. |
Proportional weights are the group frequencies. Inverse
proportional weights are proportional to the reciprocals of the group
frequencies and then renormalized. A custom numeric alpha is checked
for length, non-negativity, and a positive sum, then renormalized.
A named numeric vector of length that sums to one.
groupVec <- rep(1:2, times = c(30, 70)) resolveAlpha("proportional", groupVec, sort(unique(groupVec)))groupVec <- rep(1:2, times = c(30, 70)) resolveAlpha("proportional", groupVec, sort(unique(groupVec)))
Computes the worst-case absolute deviation of any cluster's soft group share from the population group proportion, over all clusters and groups, for a soft membership matrix.
softViolation(membership, groupVec)softViolation(membership, groupVec)
membership |
A non-negative numeric membership matrix of size |
groupVec |
An integer or factor vector of protected-group labels of
length |
A single non-negative numeric value.
membership <- matrix(runif(200), ncol = 2) membership <- membership / rowSums(membership) groupVec <- rep(1:2, times = 50) softViolation(membership, groupVec)membership <- matrix(runif(200), ncol = 2) membership <- membership / rowSums(membership) groupVec <- rep(1:2, times = 50) softViolation(membership, groupVec)
Constructs a self-tuning spectral embedding with local scaling, forms the symmetric normalized Laplacian, and returns the row-normalized top eigenvectors.
spectralEmbed(dataMat, numClusters, numNeighbors = 7)spectralEmbed(dataMat, numClusters, numNeighbors = 7)
dataMat |
A numeric matrix of observations, |
numClusters |
The embedding dimension, |
numNeighbors |
The neighbor rank used for local scaling. |
A list with components embedding (the row-normalized
eigenvector matrix), affinity (the affinity matrix),
scale (the per-observation local scale), and eigenvalues
(the smallest eigenvalues used).
Zelnik-Manor, L. and Perona, P. (2004), Self-tuning spectral clustering, in Advances in Neural Information Processing Systems, Vol. 17.
dataMat <- rbind(matrix(rnorm(40), ncol = 2), matrix(rnorm(40) + 5, ncol = 2)) embed <- spectralEmbed(dataMat, numClusters = 2) dim(embed$embedding)dataMat <- rbind(matrix(rnorm(40), ncol = 2), matrix(rnorm(40) + 5, ncol = 2)) embed <- spectralEmbed(dataMat, numClusters = 2) dim(embed$embedding)
Checks that a transport-intensity grid is numeric and lies in the unit interval, then returns it sorted with duplicates removed.
validateGrid(tSeq)validateGrid(tSeq)
tSeq |
A numeric vector of transport intensities. |
The validated grid, sorted in increasing order with duplicates removed.
validateGrid(c(0, 0.5, 0.5, 1))validateGrid(c(0, 0.5, 0.5, 1))
Computes the Wasserstein-2 barycenter of a collection of group-conditional empirical distributions using the fixed-point iteration of Alvarez-Esteban et al. (2016).
wassersteinBarycenter( xByGroup, alpha, init = c("pooled", "weightedMean"), maxIter = 30, tol = 1e-04, verbose = FALSE )wassersteinBarycenter( xByGroup, alpha, init = c("pooled", "weightedMean"), maxIter = 30, tol = 1e-04, verbose = FALSE )
xByGroup |
A list of numeric matrices, one per protected group, each
with |
alpha |
A numeric vector of barycenter weights of length equal to
|
init |
A character string selecting the initial support, either
|
maxIter |
Maximum number of fixed-point iterations. |
tol |
Convergence tolerance on the largest change in support between iterations. |
verbose |
Logical, whether to print per-iteration progress. |
A list with components barycenter (the support matrix),
plans (the list of optimal transport plans from the final
iteration), iter (the number of iterations run), and
converged (logical).
Alvarez-Esteban, P. C., del Barrio, E., Cuesta-Albertos, J. A. and Matr´an, C. (2016), A fixed-point approach to barycenters in Wasserstein space, Journal of Mathematical Analysis and Applications 441(2), 744–762.
xByGroup <- list(matrix(rnorm(40), ncol = 2), matrix(rnorm(60) + 1, ncol = 2)) fit <- wassersteinBarycenter(xByGroup, c(0.5, 0.5), maxIter = 5) dim(fit$barycenter)xByGroup <- list(matrix(rnorm(40), ncol = 2), matrix(rnorm(60) + 1, ncol = 2)) fit <- wassersteinBarycenter(xByGroup, c(0.5, 0.5), maxIter = 5) dim(fit$barycenter)