Discrete variables

Copula models operate on the probability scale, and for a discrete variable that scale has atoms: the model needs both $ F(x) $ and the left limit $ F(x^-) $ . Declaring which variables are discrete is done through var_types, a vector of "c" and "d" strings.

The data layout

For an all-continuous model, every data-taking method uses the usual $ n \times d $ matrix: its d columns contain $ F(x) $ in the model's variable order. In particular, Bicop methods take an $ n \times 2 $ matrix by default.

Extra columns are needed only when variables are discrete. In that case, the expanded layout has $ 2d $ columns: its first d columns hold $ F(x) $ and its next d columns hold $ F(x^-) $ in the same order.

For continuous variables $ F(x^-) = F(x) $ , so their columns in the second block are duplicates. These duplicates may be omitted: if k of the d variables are discrete, the compact $ n \times (d + k) $ layout appends only their k left-limits, ordered by their positions in the first block.

Both discrete-data layouts are accepted throughout the library. The expanded layout has a fixed shape independent of var_types; the compact layout avoids storing duplicate continuous columns.

Bivariate copulas

Bicop's var_types defaults to {"c", "c"}. Pass it to the constructor, or set it later with set_var_types(); the density then dispatches to the mixed-discrete expressions (pdf_c_d, pdf_d_d internally).

  // A discrete model uses an F(x) block followed by an F(x-) block.
  auto continuous = tools_stats::simulate_uniform(500, 2, false, { 1 });
  auto disc = discretize(continuous.col(0), 5);

  Eigen::MatrixXd data(500, 4);
  data.col(0) = disc.first; // F(x) of the discrete variable
  data.col(1) = continuous.col(1);
  data.col(2) = disc.second; // F(x-) of the discrete variable
  data.col(3) = data.col(1); // F(x-) = F(x) for a continuous variable

  // Declare the types, then fit as usual.
  FitControlsBicop controls({ BicopFamily::gaussian, BicopFamily::clayton });
  Bicop model(data, controls, { "d", "c" });

  std::cout << "family: " << model.get_family_name()
            << ", var_types: " << model.get_var_types()[0]
            << model.get_var_types()[1] << std::endl;

  // Evaluation takes data in the same layout.
  auto pdf = model.pdf(data);
  std::cout << "loglik: " << model.loglik(data) << std::endl;

Vine copulas

The same conventions apply to Vinecop, whose var_types has one entry per variable. simulate() returns the d $ F(x) $ columns only.

set_var_types() on an existing model re-types the pair copulas according to their position in the vine: a pair copula is discrete in an argument when the corresponding variable is discrete and that argument has not yet been integrated out by an h-function.

The Rosenblatt transform of a discrete variable is not unique — the value falls in the interval $ [F(x^-), F(x)] $ — so rosenblatt() randomizes within that interval by default. Pass randomize_discrete = false for the deterministic upper endpoint, and seeds to make the randomization reproducible.

  // Same convention for vines: discrete data uses two d-column blocks.
  size_t d = 4;
  auto continuous = tools_stats::simulate_uniform(500, d, false, { 2 });
  auto disc0 = discretize(continuous.col(0), 4);
  auto disc2 = discretize(continuous.col(2), 6);

  Eigen::MatrixXd data(500, 2 * d);
  data.col(0) = disc0.first;
  data.col(1) = continuous.col(1);
  data.col(2) = disc2.first;
  data.col(3) = continuous.col(3);
  data.rightCols(d) = data.leftCols(d);
  data.col(d) = disc0.second;
  data.col(d + 2) = disc2.second;

  Vinecop model(data, RVineStructure(), { "d", "c", "d", "c" });

  std::cout << "loglik: " << model.get_loglik() << std::endl;

  // simulate() returns the d "F(x)" columns only
  auto sim = model.simulate(10, false, 1, { 3 });
  std::cout << "simulated: " << sim.rows() << " x " << sim.cols() << std::endl;

  // set_var_types can also change the types of an existing model; the pair
  // copulas are re-typed to match their position in the vine.
  Vinecop continuous_model(continuous);
  continuous_model.set_var_types({ "d", "c", "d", "c" });