(VALIDN and TOTALN act differently for summarizing scale and categorical
variables.) */
bool is_scale;
+
+ /* If any of these optional additional scale variables are missing, then
+ treat 'var' as if it's missing too. This is for implementing
+ SMISSING=LISTWISE. */
+ struct variable **listwise_vars;
+ size_t n_listwise_vars;
};
static void ctables_summary_spec_set_clone (struct ctables_summary_spec_set *,
size_t scale_idx;
size_t *domains[N_CTDTS];
size_t n_domains[N_CTDTS];
+ size_t group_head;
struct ctables_summary_spec_set specs[N_CSVS];
};
for (size_t i = 0; i < s0.n; i++)
stack.nests[stack.n++] = s0.nests[i];
for (size_t i = 0; i < s1.n; i++)
- stack.nests[stack.n++] = s1.nests[i];
+ {
+ stack.nests[stack.n] = s1.nests[i];
+ stack.nests[stack.n].group_head += s0.n;
+ stack.n++;
+ }
assert (stack.n == s0.n + s1.n);
free (s0.nests);
free (s1.nests);
return stack;
}
+static struct ctables_stack
+var_fts (const struct ctables_axis *a)
+{
+ assert (!a->var.is_mrset);
+
+ struct variable **vars = xmalloc (sizeof *vars);
+ *vars = a->var.var;
+
+ struct ctables_nest *nest = xmalloc (sizeof *nest);
+ *nest = (struct ctables_nest) {
+ .vars = vars,
+ .n = 1,
+ .scale_idx = a->scale ? 0 : SIZE_MAX,
+ };
+ if (a->specs[CSV_CELL].n || a->scale)
+ for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
+ {
+ ctables_summary_spec_set_clone (&nest->specs[sv], &a->specs[sv]);
+ nest->specs[sv].var = a->var.var;
+ nest->specs[sv].is_scale = a->scale;
+ }
+ return (struct ctables_stack) { .nests = nest, .n = 1 };
+}
+
static struct ctables_stack
enumerate_fts (enum pivot_axis_type axis_type, const struct ctables_axis *a)
{
switch (a->op)
{
case CTAO_VAR:
- assert (!a->var.is_mrset);
-
- struct variable **vars = xmalloc (sizeof *vars);
- *vars = a->var.var;
-
- struct ctables_nest *nest = xmalloc (sizeof *nest);
- *nest = (struct ctables_nest) {
- .vars = vars,
- .n = 1,
- .scale_idx = a->scale ? 0 : SIZE_MAX,
- };
- if (a->specs[CSV_CELL].n || a->scale)
- for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
- {
- ctables_summary_spec_set_clone (&nest->specs[sv], &a->specs[sv]);
- nest->specs[sv].var = a->var.var;
- nest->specs[sv].is_scale = a->scale;
- }
- return (struct ctables_stack) { .nests = nest, .n = 1 };
+ return var_fts (a);
case CTAO_STACK:
return stack_fts (enumerate_fts (axis_type, a->subs[0]),
enumerate_fts (axis_type, a->subs[1]));
case CTAO_NEST:
+ /* This should consider any of the scale variables found in the result to
+ be linked to each other listwise for SMISSING=LISTWISE. */
return nest_fts (enumerate_fts (axis_type, a->subs[0]),
enumerate_fts (axis_type, a->subs[1]));
}
ctables_summary_add (union ctables_summary *s,
const struct ctables_summary_spec *ss,
const struct variable *var, const union value *value,
- bool is_scale, bool is_missing, bool excluded_missing,
+ bool is_scale, bool is_scale_missing,
+ bool is_missing, bool excluded_missing,
double d_weight, double e_weight)
{
/* To determine whether a case is included in a given table for a particular
case CTSF_LAYERROWPCT_VALIDN:
case CTSF_LAYERCOLPCT_VALIDN:
if (is_scale
- ? !var_is_value_missing (var, value)
+ ? !is_scale_missing
: !is_missing)
s->count += d_weight;
break;
case CTSF_EVALIDN:
if (is_scale
- ? !var_is_value_missing (var, value)
+ ? !is_scale_missing
: !is_missing)
s->count += e_weight;
break;
case CTSF_MAXIMUM:
case CTSF_MINIMUM:
case CTSF_RANGE:
- if (!var_is_value_missing (var, value))
+ if (!is_scale_missing)
{
assert (!var_is_alpha (var)); /* XXX? */
if (s->min == SYSMIS || value->f < s->min)
case CTSF_LAYERPCT_SUM:
case CTSF_LAYERROWPCT_SUM:
case CTSF_LAYERCOLPCT_SUM:
- if (!var_is_value_missing (var, value))
+ if (!is_scale_missing)
moments1_add (s->moments, value->f, e_weight);
break;
case CTSF_MEDIAN:
case CTSF_MODE:
case CTSF_PTILE:
- if (var_is_value_missing (var, value))
+ if (!is_scale_missing)
{
s->ovalid += e_weight;
return cell;
}
+static bool
+is_scale_missing (const struct ctables_summary_spec_set *specs,
+ const struct ccase *c)
+{
+ if (!specs->is_scale)
+ return false;
+
+ if (var_is_num_missing (specs->var, case_num (c, specs->var)))
+ return true;
+
+ for (size_t i = 0; i < specs->n_listwise_vars; i++)
+ {
+ const struct variable *var = specs->listwise_vars[i];
+ if (var_is_num_missing (var, case_num (c, var)))
+ return true;
+ }
+
+ return false;
+}
+
static void
ctables_cell_add__ (struct ctables_section *s, const struct ccase *c,
const struct ctables_category *cats[PIVOT_N_AXES][10],
const struct ctables_nest *ss = s->nests[s->table->summary_axis];
const struct ctables_summary_spec_set *specs = &ss->specs[cell->sv];
+
+ bool scale_missing = is_scale_missing (specs, c);
for (size_t i = 0; i < specs->n; i++)
ctables_summary_add (&cell->summaries[i], &specs->specs[i],
specs->var, case_data (c, specs->var), specs->is_scale,
- is_missing, excluded_missing, d_weight, e_weight);
+ scale_missing, is_missing, excluded_missing,
+ d_weight, e_weight);
for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
if (!(cell->omit_domains && (1u << dt)))
{
else if (!nest->specs[CSV_TOTAL].n)
ctables_summary_spec_set_clone (&nest->specs[CSV_TOTAL],
&nest->specs[CSV_CELL]);
+
+ if (t->ctables->smissing_listwise)
+ {
+ struct variable **listwise_vars = NULL;
+ size_t n = 0;
+ size_t allocated = 0;
+
+ for (size_t j = nest->group_head; j < stack->n; j++)
+ {
+ const struct ctables_nest *other_nest = &stack->nests[j];
+ if (other_nest->group_head != nest->group_head)
+ break;
+
+ if (nest != other_nest && other_nest->scale_idx < other_nest->n)
+ {
+ if (n >= allocated)
+ listwise_vars = x2nrealloc (listwise_vars, &allocated,
+ sizeof *listwise_vars);
+ listwise_vars[n++] = other_nest->vars[other_nest->scale_idx];
+ }
+ }
+ for (size_t j = 0; j < N_CSVS; j++)
+ {
+ nest->specs[j].listwise_vars = listwise_vars;
+ nest->specs[j].n_listwise_vars = n;
+ }
+ }
}
struct ctables_summary_spec_set *merged = &t->summary_specs;