% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/unsupervised_learning.R
\name{mec_blocking}
\alias{mec_blocking}
\title{Blocked Unsupervised Maximum Entropy Classifier for Record Linkage}
\usage{
mec_blocking(
  A,
  B,
  variables,
  comparators = NULL,
  methods = NULL,
  blocking_x = NULL,
  blocking_y = NULL,
  blocking_variables = variables,
  blocking_sep = " ",
  controls_blocking = list(),
  start_params = NULL,
  alpha = 0,
  delta = 0.5,
  eps = 0.05,
  controls_nleqslv = list(),
  true_matches = NULL,
  keep_blocking_result = FALSE,
  keep_training_data = FALSE,
  verbose = FALSE
)
}
\arguments{
\item{A}{A duplicate-free \code{data.frame} or \code{data.table}.}

\item{B}{A duplicate-free \code{data.frame} or \code{data.table}.}

\item{variables}{A character vector of key variables used to create MEC comparison vectors.}

\item{comparators}{A named list of functions for comparing pairs of records.}

\item{methods}{A named list of methods used for estimation (\code{"binary"} or
\code{"continuous_parametric"}). Other unsupervised MEC methods are not
supported by \code{\link[=mec_blocking]{mec_blocking()}} at this stage.}

\item{blocking_x}{Optional input passed as \code{x} to \link[blocking:blocking]{blocking()}.}

\item{blocking_y}{Optional input passed as \code{y} to \link[blocking:blocking]{blocking()}.}

\item{blocking_variables}{Variables used to create blocking strings when
\code{blocking_x} and \code{blocking_y} are not supplied.}

\item{blocking_sep}{Separator used when concatenating \code{blocking_variables}.}

\item{controls_blocking}{A list of additional arguments passed to
\link[blocking:blocking]{blocking()}, except \code{x} and \code{y}.}

\item{start_params}{Start parameters for the \code{"binary"} and
\code{"continuous_parametric"} methods.}

\item{alpha}{A single numeric value in \verb{[0, 1)} controlling the fraction of
the current nonmatch complement dropped from nonmatch-side parameter fitting
after the first inverted MEC iteration. The first U-side fit uses the full
initial fitting set, and posterior/count formulas continue to use the full
current nonmatch count.}

\item{delta}{A numeric value specifying the tolerance for the change in the
estimated number of nonmatches between MEC iterations.}

\item{eps}{A numeric value specifying the tolerance for the change in model
parameters between MEC iterations.}

\item{controls_nleqslv}{Controls passed to the \link[nleqslv:nleqslv]{nleqslv()} function
(only if the \code{"continuous_parametric"} method has been chosen for at least one
variable).}

\item{true_matches}{A \code{data.frame} or \code{data.table} indicating known matches.}

\item{keep_blocking_result}{Logical indicating whether to store the raw object
returned by \link[blocking:blocking]{blocking()}.}

\item{keep_training_data}{Logical indicating whether to store pooled training
comparison vectors.}

\item{verbose}{Logical indicating whether to print progress messages.}
}
\value{
Returns a list of class \code{"mec_blocking"} containing:
\itemize{
\item{\code{M_est} -- a \code{data.table} with predicted matches and columns \code{a}, \code{b}, \code{block}, and \code{ratio},}
\item{\code{n_M_est} -- estimated total number of matches across all blocks,}
\item{\code{n_U_est} -- estimated total number of candidate nonmatches,}
\item{\code{alpha} -- fraction of the current nonmatch complement dropped from later U-side fitting,}
\item{\code{candidate_pair_count} -- number of candidate pairs in \eqn{\Omega_B},}
\item{\code{block_estimates} -- a \code{data.table} with block-level size and match-count diagnostics,}
\item{\code{block_summary} -- a \code{data.table} describing the final disjoint blocks,}
\item{\code{excluded_records} -- a list with records from \code{A} and \code{B} excluded by blocking,}
\item{\code{b_vars} -- variables used for the \code{"binary"} method, with the prefix \code{"gamma_"},}
\item{\code{cpar_vars} -- variables used for the \code{"continuous_parametric"} method, with the prefix \code{"gamma_"},}
\item{\code{b_params} -- parameters estimated using the \code{"binary"} method,}
\item{\code{cpar_params} -- parameters estimated using the \code{"continuous_parametric"} method,}
\item{\code{variables} -- key variables used for comparison,}
\item{\code{comparators} -- comparison functions used to create comparison vectors,}
\item{\code{methods} -- MEC estimation methods used for the key variables,}
\item{\code{delta} -- tolerance for changes in the estimated number of nonmatches,}
\item{\code{eps} -- tolerance for changes in nonmatch-side model parameters,}
\item{\code{controls_nleqslv} -- controls passed to \link[nleqslv:nleqslv]{nleqslv()},}
\item{\code{blocking_result} -- raw object returned by \link[blocking:blocking]{blocking()} if \code{keep_blocking_result = TRUE}; otherwise \code{NULL},}
\item{\code{training_Omega} -- candidate-space comparison vectors with inverted scores if \code{keep_training_data = TRUE}; otherwise \code{NULL},}
\item{\code{blocking_eval} -- blocking diagnostics if \code{true_matches} is provided; otherwise \code{NULL},}
\item{\code{mec_eval} -- MEC-selection diagnostics among known matches retained in the candidate-pair space if \code{true_matches} is provided; otherwise \code{NULL},}
\item{\code{eval_metrics} -- empirical linkage quality metrics based on \code{true_matches}; otherwise \code{NULL},}
\item{\code{confusion} -- empirical confusion matrix based on \code{true_matches}; otherwise \code{NULL}.}
}
}
\description{
Runs graph-based blocking using \link[blocking:blocking]{blocking()},
defines a blocking candidate-pair space, and fits an inverted unsupervised
maximum entropy classifier (MEC) directly on all candidate pairs.
}
\details{
The function assumes one-to-one linkage. The blocking stage defines disjoint
bipartite blocks, and the candidate-pair space \eqn{\Omega_B} is the union of
within-block Cartesian products. Duplicate candidate pairs are removed
deterministically before MEC fitting.

The blocked MEC fit is inverted relative to \code{\link[=mec]{mec()}}. The initial match set
contains at most \eqn{\nu} feasible pairs, where \eqn{\nu} is the structural
one-to-one upper bound. Initial feasible matches are selected
greedily by an unweighted disagreement norm: binary agreement indicators use
\code{1 - gamma}, while continuous dissimilarities use \code{gamma} unchanged. At each
iteration, match-side parameters are estimated from the current greedy
one-to-one match set, and nonmatch-side parameters are estimated from its
complement.

The \code{alpha} argument applies only to nonmatch-side distribution estimation.
The first U-side fit uses the full initial complement. In later iterations,
the least reliable current nonmatches are dropped from the U-side fitting
sample, with reliability ranked by the previous nonmatch posterior estimate
and then by the inverted density ratio if the posterior is unavailable.
The posterior and count updates still use the full current complement size,
and the final match set remains one-to-one.

The returned \code{ratio} is \eqn{s = u / m}, where \eqn{u} and \eqn{m} denote
the estimated nonmatch and match comparison-vector densities. Smaller values
are therefore more match-like. Updated match sets are selected greedily in
ascending order of this ratio.

If \eqn{N = |\Omega_B|} and \eqn{\nu} is the maximum feasible one-to-one
matching size in the candidate graph, the estimated number of nonmatches is
bounded below by \eqn{N - \nu}. For the disjoint complete blocks reconstructed
by this function, \eqn{\nu = \sum_h \min(n_{Ah}, n_{Bh})}.

If the initialized match set exhausts the candidate-pair space, for example
when \eqn{N = \nu}, there is no candidate complement from which to estimate
nonmatch parameters. In that case the function returns the structurally
feasible initialized match set, sets \code{n_U_est = 0}, and leaves nonmatch-side
parameters unavailable.
}
\examples{
df_1 <- data.frame(
  name = c("Emma", "Liam", "Olivia", "Noah", "Ava"),
  surname = c("Smith", "Jones", "Brown", "Davis", "Miller"),
  city = c("Boston", "Boston", "Austin", "Austin", "Denver")
)
df_2 <- data.frame(
  name = c("Emma", "Liam", "Olivia", "Noah", "Ava"),
  surname = c("Smith", "Jones", "Brown", "Davis", "Miller"),
  city = c("Boston", "Boston", "Austin", "Austin", "Denver")
)

blocking_x <- matrix(
  c(1, 0, 0, 1, 1, 1, 2, 0, 0, 2),
  ncol = 2,
  byrow = TRUE
)
blocking_y <- blocking_x

result <- mec_blocking(
  A = df_1,
  B = df_2,
  variables = c("name", "surname", "city"),
  blocking_x = blocking_x,
  blocking_y = blocking_y,
  controls_blocking = list(
    representation = "custom_matrix",
    ann = "kd",
    distance = "euclidean",
    seed = 1
  ),
  true_matches = data.frame(a = 1:5, b = 1:5)
)
result
}
\author{
Adam Struzik
}
