Kernel Conditional Density Estimation with Mixed Data Types
np.condensity.Rdnpcdens computes kernel conditional density estimates on
\(p+q\)-variate evaluation data, given a set of training data (both
explanatory and dependent) and a bandwidth specification (a
conbandwidth object or a bandwidth vector, bandwidth type, and
kernel type) using the method of Hall, Racine, and Li (2004).
The data may be continuous, discrete (unordered and ordered
factors), or some combination thereof.
Usage
npcdens(bws, ...)
# S3 method for class 'formula'
npcdens(bws, data = NULL, newdata = NULL, ...)
# S3 method for class 'conbandwidth'
npcdens(bws,
txdat = stop("invoked without training data 'txdat'"),
tydat = stop("invoked without training data 'tydat'"),
exdat,
eydat,
gradients = FALSE,
gradient.order = 1L,
proper = FALSE,
proper.method = c("project"),
proper.control = list(),
...)
# Default S3 method
npcdens(bws, txdat, tydat, nomad = FALSE, ...)Arguments
Data, Bandwidth Inputs And Formula Interface
These arguments identify the bandwidth specification, formula/data interface, and training data.
- bws
a bandwidth specification. This can be set as a
conbandwidthobject returned from a previous invocation ofnpcdensbw, or as a \(p+q\)-vector of bandwidths, with each element \(i\) up to \(i=q\) corresponding to the bandwidth for column \(i\) intydat, and each element \(i\) from \(i=q+1\) to \(i=p+q\) corresponding to the bandwidth for column \(i-q\) intxdat. If specified as a vector, then additional arguments will need to be supplied as necessary to specify the bandwidth type, kernel types, training data, and so on.- data
an optional data frame, list or environment (or object coercible to a data frame by
as.data.frame) containing the variables in the model. If not found in data, the variables are taken fromenvironment(bws), typically the environment from whichnpcdensbwwas called.- txdat
a \(p\)-variate data frame of sample realizations of explanatory data (training data). Defaults to the training data used to compute the bandwidth object.
- tydat
a \(q\)-variate data frame of sample realizations of dependent data (training data). Defaults to the training data used to compute the bandwidth object.
Local-Polynomial Degree And Bandwidth Search
This argument controls the recommended automatic local-polynomial NOMAD route, which jointly selects continuous polynomial degree and bandwidths when these are computed inside npcdens.
- nomad
logical shortcut passed through to
npcdensbwwhen bandwidths are computed insidenpcdens. WhenTRUE, the bandwidth route fills any missing values amongregtype,search.engine,degree.select,bernstein.basis,degree.min,degree.max,degree.verify, andbwtypewith the recommended automatic local-polynomial degree-and-bandwidth NOMAD preset documented innpcdensbw. Additional NOMAD tuning arguments such asnomad.nmultimay also be supplied through...;nmultiremains the outer restart count whilenomad.nmulticontrols inner native crs NOMAD API multistarts within each outer restart. After fitting, inspectfit$bws$nomad.shortcuton the returned objectfitto see the normalized shortcut metadata.
Evaluation Data And Returned Quantities
These arguments control where the fitted conditional density is evaluated and which estimates are returned.
- exdat
a \(p\)-variate data frame of explanatory data on which conditional densities will be evaluated. By default, evaluation takes place on the data provided by
txdat.- eydat
a \(q\)-variate data frame of dependent data on which conditional densities will be evaluated. By default, evaluation takes place on the data provided by
tydat.- gradients
a logical value specifying whether to return estimates of the gradients at the evaluation points. Defaults to
FALSE.- gradient.order
derivative order for continuous explanatory-variable gradients when
gradients = TRUEandregtype = "lp". A scalar is recycled across continuous explanatory variables, or one value may be supplied per continuous explanatory variable. The default1Lreturns first derivatives. Higher orders are available for continuous explanatory variables only and must not exceed the corresponding local-polynomial degree; unordered and ordered predictors retain their usual first-order discrete effects.- newdata
An optional data frame in which to look for evaluation data. If omitted, the training data are used.
Fit Properization Controls
These arguments control optional post-estimation properization of the fitted conditional density.
- proper
a logical value specifying whether to apply post-estimation properization to the conditional density estimate. Defaults to
FALSE.- proper.control
a list of controls for properization. Supported entries are
tol,grid.check,store.raw, andfail.on.unsupported.- proper.method
a character string specifying the properization method. Currently
"project"is supported.
Additional Arguments
Further arguments are passed to npcdensbw when bandwidths are computed internally, or used to interpret a numeric bws vector.
- ...
additional arguments supplied to
npcdensbwwhennpcdenscomputes bandwidths internally, or arguments needed to interpret a numericbwsvector. This is where bandwidth selection controls such asbwmethod,bwtype, kernel/support controls such ascxkertype,cykertype,cxkerbound, andcykerbound, search controls such asnmulti,scale.factor.search.lower, andnomad.nmulti, and local-polynomial controls such asregtype,degree,basis, andbernstein.basisare supplied. Seenpcdensbwfor the complete bandwidth-selection argument surface.
Details
Documentation guide: see npcdensbw for bandwidth selection and search controls, np.kernels for kernels, np.options for global options, and plot, plot.np for plotting options.
When bws is omitted, the formula and default methods call
npcdensbw first and pass bandwidth-selection arguments
from ... to that call. When bws is already a
conbandwidth object, npcdens estimates with the stored
bandwidth metadata in that object.
Argument groups for bandwidth selection are documented on
npcdensbw. The most common workflow is to choose data
and bandwidth inputs first, then bandwidth criterion and
representation, then kernel/support controls, numerical search
controls, bounded cv.ls quadrature controls if relevant, and
finally local-polynomial/NOMAD controls for polynomial-adaptive fits.
For S3 plotting help, see plot.np. You can list
available plot methods with methods("plot").
npcdens implements a variety of methods for estimating
multivariate conditional distributions (\(p+q\)-variate) defined
over a set of possibly continuous and/or discrete (unordered, ordered)
data. The approach is based on Li and Racine (2004) who employ
‘generalized product kernels’ that admit a mix of continuous
and discrete data types.
Three classes of kernel estimators for the continuous data types are available: fixed, adaptive nearest-neighbor, and generalized nearest-neighbor. Adaptive nearest-neighbor bandwidths change with each sample realization in the set, \(x_i\), when estimating the density at the point \(x\). Generalized nearest-neighbor bandwidths change with the point at which the density is estimated, \(x\). Fixed bandwidths are constant over the support of \(x\).
Training and evaluation input data may be a
mix of continuous (default), unordered discrete (to be specified in
the data frames using factor), and ordered discrete (to be
specified in the data frames using ordered). Data can be
entered in an arbitrary order and data types will be detected
automatically by the routine (see np for details).
A variety of kernels may be specified by the user. Kernels implemented for continuous data types include the second, fourth, sixth, and eighth order Gaussian and Epanechnikov kernels, and the uniform kernel. Unordered discrete data types use a variation on Aitchison and Aitken's (1976) kernel, while ordered data types use a variation of the Wang and van Ryzin (1981) kernel.
For practitioners who want the recommended automatic local-polynomial degree-and-bandwidth NOMAD route
without spelling out all LP tuning arguments,
npcdens(..., nomad=TRUE) and npcdensbw(..., nomad=TRUE)
expand missing settings to the same documented preset. Explicit
incompatible settings fail fast rather than being silently rewritten.
With regtype = "lp", gradients = TRUE and
gradient.order greater than one expose higher-order derivative
estimates with respect to continuous explanatory variables. These
derivatives use the same local-polynomial basis, degree, Bernstein
option, bandwidths, and kernels as the fitted conditional density.
Asymptotic standard errors for these higher-order derivative columns
are not currently reported; the corresponding entries of
congerr are NA. Use bootstrap intervals when inference
on higher-order derivatives is required.
Value
npcdens returns a condensity object. The generic
accessor functions fitted, se, and
gradients, extract estimated values, asymptotic standard
errors on estimates, and gradients, respectively, from the returned
object. Furthermore, the functions predict,
summary and plot support objects of both
classes. The returned objects have the following components:
- xbw
bandwidth(s), scale factor(s) or nearest neighbours for the explanatory data,
txdat- ybw
bandwidth(s), scale factor(s) or nearest neighbours for the dependent data,
tydat- xeval
the evaluation points of the explanatory data
- yeval
the evaluation points of the dependent data
- condens
estimates of the conditional density at the evaluation points
- conderr
standard errors of the conditional density estimates
- congrad
if invoked with
gradients = TRUE, estimates of the gradients at the evaluation points. For local-polynomial fits, continuous-coordinate derivative orders are controlled bygradient.order.- congerr
if invoked with
gradients = TRUE, standard errors of the gradients at the evaluation points. Higher-order continuous-coordinate derivative standard errors are currently returned asNA; bootstrap inference is preferred for those targets.- log_likelihood
log likelihood of the conditional density estimate
Book And Method Pointers
The conditional density target is \(f(y\mid x)\). In the product-kernel formulation this can be viewed as estimating the joint mixed-data density of \((Y,X)\) and normalizing by the marginal density of \(X\), with local-polynomial routes using the selected continuous-coordinate polynomial degree in the conditional fit. The returned standard errors are asymptotic standard errors for the fitted conditional density values, and gradient standard errors are returned when gradients are requested and defined.
For book-length derivations, see Li and Racine (2007), Chapter 5 Conditional Density Estimation, especially Sections 5.1-5.4, and Chapter 4 Kernel Estimation with Mixed Data. The later workflow treatment is Racine (2019), Chapter 4 Conditional Probability Density and Cumulative Distribution Functions.
References
Aitchison, J. and C.G.G. Aitken (1976), “Multivariate binary discrimination by the kernel method,” Biometrika, 63, 413-420.
Hall, P. and J.S. Racine and Q. Li (2004), “Cross-validation and the estimation of conditional probability densities,” Journal of the American Statistical Association, 99, 1015-1026.
Li, Q. and J.S. Racine (2007), Nonparametric Econometrics: Theory and Practice, Princeton University Press.
Pagan, A. and A. Ullah (1999), Nonparametric Econometrics, Cambridge University Press.
Scott, D.W. (1992), Multivariate Density Estimation. Theory, Practice and Visualization, New York: Wiley.
Silverman, B.W. (1986), Density Estimation, London: Chapman and Hall.
Wang, M.C. and J. van Ryzin (1981), “A class of smooth estimators for discrete distributions,” Biometrika, 68, 301-309.
Author
Tristen Hayfield tristen.hayfield@gmail.com, Jeffrey S. Racine racinej@mcmaster.ca
Usage Issues
If you are using data of mixed types, then it is advisable to use the
data.frame function to construct your input data and not
cbind, since cbind will typically not work as
intended on mixed data types and will coerce the data to the same
type.
Examples
if (FALSE) { # \dontrun{
# EXAMPLE 1 (INTERFACE=FORMULA): For this example, we load Giovanni
# Baiocchi's Italian GDP panel (see Italy for details), and compute the
# likelihood cross-validated bandwidths (default) using a second-order
# Gaussian kernel (default). Note - this may take a minute or two
# depending on the speed of your computer.
data("Italy")
Italy <- Italy[seq_len(min(300, nrow(Italy))), ]
with(Italy, {
# First, compute the bandwidths... note that this may take a minute or
# two depending on the speed of your computer.
bw <- npcdensbw(formula=gdp~ordered(year), nmulti=1)
# Next, compute the condensity object...
fhat <- npcdens(bws=bw)
# The object fhat now contains results such as the estimated conditional
# density function (fhat$condens) and so on...
summary(fhat)
# Call the plot() function to visualize the results (<ctrl>-C will
# interrupt on *NIX systems, <esc> will interrupt on MS Windows
# systems).
if (interactive()) plot(bw)
})
# EXAMPLE 1 (INTERFACE=DATA FRAME): For this example, we load Giovanni
# Baiocchi's Italian GDP panel (see Italy for details), and compute the
# likelihood cross-validated bandwidths (default) using a second-order
# Gaussian kernel (default). Note - this may take a minute or two
# depending on the speed of your computer.
data("Italy")
Italy <- Italy[seq_len(min(300, nrow(Italy))), ]
with(Italy, {
# First, compute the bandwidths... note that this may take a minute or
# two depending on the speed of your computer.
# Note - we cast `X' and `y' as data frames so that plot() can
# automatically grab names (this looks like overkill, but in
# multivariate settings you would do this anyway, so may as well get in
# the habit).
X <- data.frame(year=ordered(year))
y <- data.frame(gdp)
bw <- npcdensbw(xdat=X, ydat=y, nmulti=1)
# Next, compute the condensity object...
fhat <- npcdens(bws=bw)
# The object fhat now contains results such as the estimated conditional
# density function (fhat$condens) and so on...
summary(fhat)
# Call the plot() function to visualize the results (<ctrl>-C will
# interrupt on *NIX systems, <esc> will interrupt on MS Windows systems).
if (interactive()) plot(bw)
})
# EXAMPLE 2 (INTERFACE=FORMULA): For this example, we load the old
# faithful geyser data from the R `datasets' library and compute the
# conditional density function.
library("datasets")
data("faithful")
with(faithful, {
# Note - this may take a few minutes depending on the speed of your
# computer...
bw <- npcdensbw(formula=eruptions~waiting, nmulti=1)
summary(bw)
# Plot the density function (<ctrl>-C will interrupt on *NIX systems,
# <esc> will interrupt on MS Windows systems).
if (interactive()) plot(bw)
})
# EXAMPLE 2 (INTERFACE=DATA FRAME): For this example, we load the old
# faithful geyser data from the R `datasets' library and compute the
# conditional density function.
library("datasets")
data("faithful")
with(faithful, {
# Note - this may take a few minutes depending on the speed of your
# computer...
# Note - we cast `X' and `y' as data frames so that plot() can
# automatically grab names (this looks like overkill, but in
# multivariate settings you would do this anyway, so may as well get in
# the habit).
X <- data.frame(waiting)
y <- data.frame(eruptions)
bw <- npcdensbw(xdat=X, ydat=y, nmulti=1)
summary(bw)
# Plot the density function (<ctrl>-C will interrupt on *NIX systems,
# <esc> will interrupt on MS Windows systems)
if (interactive()) plot(bw)
})
# EXAMPLE 3 (INTERFACE=FORMULA): Replicate the DGP of Klein & Spady
# (1993) (see their description on page 405, pay careful attention to
# footnote 6 on page 405).
set.seed(123)
n <- 300
# x1 is chi-squared having 3 df truncated at 6 standardized by
# subtracting 2.348 and dividing by 1.511
x <- rchisq(n, df=3)
x1 <- (ifelse(x < 6, x, 6) - 2.348)/1.511
# x2 is normal (0, 1) truncated at +- 2 divided by 0.8796
x <- rnorm(n)
x2 <- ifelse(abs(x) < 2 , x, 2) / 0.8796
# y is 1 if y* > 0, 0 otherwise.
y <- ifelse(x1 + x2 + rnorm(n) > 0, 1, 0)
# Generate data-driven bandwidths (likelihood cross-validation). Note -
# this may take a few minutes depending on the speed of your computer...
bw <- npcdensbw(formula=factor(y)~x1+x2, nmulti=1)
# Next, create the evaluation data in order to generate a perspective
# plot
x1.seq <- seq(min(x1), max(x1), length=30)
x2.seq <- seq(min(x2), max(x2), length=30)
X.eval <- expand.grid(x1=x1.seq,x2=x2.seq)
data.eval <- data.frame(y=factor(rep(1, nrow(X.eval))),x1=X.eval[,1],x2=X.eval[,2])
# Now evaluate the conditional probability for y=1 and for the
# evaluation Xs
fit <- fitted(npcdens(bws=bw,newdata=data.eval))
# Finally, coerce the data into a matrix for plotting with persp()
fit.mat <- matrix(fit, 30, 30)
# Generate a perspective plot similar to Figure 2 b of Klein and Spady
# (1993)
persp(x1.seq,
x2.seq,
fit.mat,
col="white",
ticktype="detailed",
expand=0.5,
axes=FALSE,
box=FALSE,
main="Estimated Nonparametric Probability Perspective",
theta=310,
phi=25)
# EXAMPLE 3 (INTERFACE=DATA FRAME): Replicate the DGP of Klein & Spady
# (1993) (see their description on page 405, pay careful attention to
# footnote 6 on page 405).
set.seed(123)
n <- 300
# x1 is chi-squared having 3 df truncated at 6 standardized by
# subtracting 2.348 and dividing by 1.511
x <- rchisq(n, df=3)
x1 <- (ifelse(x < 6, x, 6) - 2.348)/1.511
# x2 is normal (0, 1) truncated at +- 2 divided by 0.8796
x <- rnorm(n)
x2 <- ifelse(abs(x) < 2 , x, 2) / 0.8796
# y is 1 if y* > 0, 0 otherwise.
y <- ifelse(x1 + x2 + rnorm(n) > 0, 1, 0)
# Create the X matrix
X <- cbind(x1, x2)
# Generate data-driven bandwidths (likelihood cross-validation). Note -
# this may take a few minutes depending on the speed of your computer...
bw <- npcdensbw(xdat=X, ydat=factor(y), nmulti=1)
# Next, create the evaluation data in order to generate a perspective
# plot
x1.seq <- seq(min(x1), max(x1), length=30)
x2.seq <- seq(min(x2), max(x2), length=30)
X.eval <- expand.grid(x1=x1.seq,x2=x2.seq)
# Now evaluate the conditional probability for y=1 and for the
# evaluation Xs
fit <- fitted(npcdens(exdat=X.eval,
eydat=factor(rep(1, nrow(X.eval))),
bws=bw))
# Finally, coerce the data into a matrix for plotting with persp()
fit.mat <- matrix(fit, 30, 30)
# Generate a perspective plot similar to Figure 2 b of Klein and Spady
# (1993)
persp(x1.seq,
x2.seq,
fit.mat,
col="white",
ticktype="detailed",
expand=0.5,
axes=FALSE,
box=FALSE,
main="Estimated Nonparametric Probability Perspective",
theta=310,
phi=25)
# EXAMPLE 4: Variations on local polynomial conditional density
# estimation with proper = TRUE.
data("Italy")
Italy2 <- within(Italy, {
year <- as.numeric(as.character(year))
})
# Plot only: make the plotted surface proper on the plot evaluation grid.
fhat <- npcdens(gdp ~ year, data = Italy2,
regtype = "lp", degree = 3, nmulti = 1)
plot(fhat, proper = TRUE)
# Fit an object whose fitted values are themselves proper.
ctrl_fit <- list(
mode = "slice",
apply = "fitted",
slice.grid.size = 101L,
slice.extend.factor = 0.1
)
fhat_fit <- npcdens(
gdp ~ year,
data = Italy2,
regtype = "lp",
degree = 3,
nmulti = 1,
proper = TRUE,
proper.control = ctrl_fit
)
fit_proper <- fitted(fhat_fit)
fit_raw <- fhat_fit$condens.raw
# Display the repaired and raw fitted values for cases where the raw
# fitted density is negative.
head(cbind(fit_proper, fit_raw)[which(fit_raw < 0), ])
# Predict on a common explicit y-grid for several years, and render
# those predictions proper.
g.grid <- seq(min(Italy2$gdp), max(Italy2$gdp), length.out = 200)
nd_grid <- expand.grid(
gdp = g.grid,
year = c(1955, 1975, 1995)
)
pred_grid <- predict(fhat, newdata = nd_grid, proper = TRUE)
# Predict on paired rows with different gdp grids by year, and still
# make the predictions proper via slice mode.
g1 <- seq(quantile(Italy2$gdp, 0.10),
quantile(Italy2$gdp, 0.60), length.out = 60)
g2 <- seq(quantile(Italy2$gdp, 0.30),
quantile(Italy2$gdp, 0.90), length.out = 35)
nd_slice <- rbind(
data.frame(gdp = g1, year = rep(1960, length(g1))),
data.frame(gdp = g2, year = rep(1985, length(g2)))
)
pred_slice <- predict(
fhat,
newdata = nd_slice,
proper = TRUE,
proper.control = list(mode = "slice")
)
# One object that carries properization for fitted values and for later
# predict() calls.
ctrl_both <- list(
mode = "slice",
apply = "both",
slice.grid.size = 101L,
slice.extend.factor = 0.1
)
fhat_both <- npcdens(
gdp ~ year,
data = Italy2,
regtype = "lp",
degree = 3,
nmulti = 1,
proper = TRUE,
proper.control = ctrl_both
)
fit_both <- fitted(fhat_both)
pred_both <- predict(
fhat_both,
newdata = nd_slice,
proper.control = ctrl_both
)
} # }