double u_valid; /* Unweighted. */
double u_count;
double u_total;
+ struct ctables_sum *sums;
+ };
+
+struct ctables_sum
+ {
+ double e_sum;
+ double u_sum;
};
enum ctables_summary_variant
size_t n_sections;
enum pivot_axis_type summary_axis;
struct ctables_summary_spec_set summary_specs;
+ struct variable **sum_vars;
+ size_t n_sum_vars;
const struct variable *clabels_example;
struct hmap clabels_values_map;
bool is_ctables_format; /* Is 'format' one of CTEF_*? */
size_t axis_idx;
+ size_t sum_var_idx;
};
static void
}
}
-static char *
-ctables_summary_default_label (enum ctables_summary_function function,
- double percentile)
+static struct pivot_value *
+ctables_summary_label (const struct ctables_summary_spec *spec, double cilevel)
{
- static const char *default_labels[] = {
+ if (!spec->label)
+ {
+ static const char *default_labels[] = {
#define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = LABEL,
- SUMMARIES
+ SUMMARIES
#undef S
- };
+ };
+
+ return (spec->function == CTSF_PTILE
+ ? pivot_value_new_text_format (N_("Percentile %.2f"),
+ spec->percentile)
+ : pivot_value_new_text (default_labels[spec->function]));
+ }
+ else
+ {
+ struct substring in = ss_cstr (spec->label);
+ struct substring target = ss_cstr (")CILEVEL");
- return (function == CTSF_PTILE
- ? xasprintf (_("Percentile %.2f"), percentile)
- : xstrdup (gettext (default_labels[function])));
+ struct string out = DS_EMPTY_INITIALIZER;
+ for (;;)
+ {
+ size_t chunk = ss_find_substring (in, target);
+ ds_put_substring (&out, ss_head (in, chunk));
+ ss_advance (&in, chunk);
+ if (!in.length)
+ return pivot_value_new_user_text_nocopy (ds_steal_cstr (&out));
+
+ ss_advance (&in, target.length);
+ ds_put_format (&out, "%g", cilevel);
+ }
+ }
}
static const char *
*dst = (struct ctables_summary_spec) {
.function = function,
.percentile = percentile,
- .label = xstrdup (label),
+ .label = xstrdup_if_nonnull (label),
.format = (format ? *format
: ctables_summary_default_format (function, axis->var)),
.is_ctables_format = is_ctables_format,
}
/* Parse label. */
- char *label;
+ char *label = NULL;
if (lex_is_string (ctx->lexer))
{
label = ss_xstrdup (lex_tokss (ctx->lexer));
lex_get (ctx->lexer);
}
- else
- label = ctables_summary_default_label (function, percentile);
/* Parse format. */
struct fmt_spec format;
NOT_REACHED ();
}
+static enum ctables_domain_type
+ctables_function_is_pctsum (enum ctables_summary_function function)
+{
+ switch (function)
+ {
+ case CTSF_COUNT:
+ case CTSF_ECOUNT:
+ case CTSF_MISSING:
+ case CSTF_TOTALN:
+ case CTSF_ETOTALN:
+ case CTSF_VALIDN:
+ case CTSF_EVALIDN:
+ case CTSF_MAXIMUM:
+ case CTSF_MINIMUM:
+ case CTSF_RANGE:
+ case CTSF_MEAN:
+ case CTSF_SEMEAN:
+ case CTSF_STDDEV:
+ case CTSF_SUM:
+ case CTSF_VARIANCE:
+ case CTSF_MEDIAN:
+ case CTSF_PTILE:
+ case CTSF_MODE:
+ case CTSF_UCOUNT:
+ case CTSF_UMISSING:
+ case CSTF_UTOTALN:
+ case CTSF_UVALIDN:
+ case CTSF_UMEAN:
+ case CTSF_USEMEAN:
+ case CTSF_USTDDEV:
+ case CTSF_USUM:
+ case CTSF_UVARIANCE:
+ case CTSF_UMEDIAN:
+ case CTSF_UPTILE:
+ case CTSF_UMODE:
+ case CTSF_COLPCT_COUNT:
+ case CTSF_COLPCT_TOTALN:
+ case CTSF_COLPCT_VALIDN:
+ case CTSF_UCOLPCT_COUNT:
+ case CTSF_UCOLPCT_TOTALN:
+ case CTSF_UCOLPCT_VALIDN:
+ case CTSF_LAYERCOLPCT_COUNT:
+ case CTSF_LAYERCOLPCT_TOTALN:
+ case CTSF_LAYERCOLPCT_VALIDN:
+ case CTSF_ULAYERCOLPCT_COUNT:
+ case CTSF_ULAYERCOLPCT_TOTALN:
+ case CTSF_ULAYERCOLPCT_VALIDN:
+ case CTSF_LAYERPCT_COUNT:
+ case CTSF_LAYERPCT_TOTALN:
+ case CTSF_LAYERPCT_VALIDN:
+ case CTSF_ULAYERPCT_COUNT:
+ case CTSF_ULAYERPCT_TOTALN:
+ case CTSF_ULAYERPCT_VALIDN:
+ case CTSF_LAYERROWPCT_COUNT:
+ case CTSF_LAYERROWPCT_TOTALN:
+ case CTSF_LAYERROWPCT_VALIDN:
+ case CTSF_ULAYERROWPCT_COUNT:
+ case CTSF_ULAYERROWPCT_TOTALN:
+ case CTSF_ULAYERROWPCT_VALIDN:
+ case CTSF_ROWPCT_COUNT:
+ case CTSF_ROWPCT_TOTALN:
+ case CTSF_ROWPCT_VALIDN:
+ case CTSF_UROWPCT_COUNT:
+ case CTSF_UROWPCT_TOTALN:
+ case CTSF_UROWPCT_VALIDN:
+ case CTSF_SUBTABLEPCT_COUNT:
+ case CTSF_SUBTABLEPCT_TOTALN:
+ case CTSF_SUBTABLEPCT_VALIDN:
+ case CTSF_USUBTABLEPCT_COUNT:
+ case CTSF_USUBTABLEPCT_TOTALN:
+ case CTSF_USUBTABLEPCT_VALIDN:
+ case CTSF_TABLEPCT_COUNT:
+ case CTSF_TABLEPCT_TOTALN:
+ case CTSF_TABLEPCT_VALIDN:
+ case CTSF_UTABLEPCT_COUNT:
+ case CTSF_UTABLEPCT_TOTALN:
+ case CTSF_UTABLEPCT_VALIDN:
+ return false;
+
+ case CTSF_COLPCT_SUM:
+ case CTSF_UCOLPCT_SUM:
+ case CTSF_LAYERCOLPCT_SUM:
+ case CTSF_ULAYERCOLPCT_SUM:
+ case CTSF_LAYERPCT_SUM:
+ case CTSF_ULAYERPCT_SUM:
+ case CTSF_LAYERROWPCT_SUM:
+ case CTSF_ULAYERROWPCT_SUM:
+ case CTSF_ROWPCT_SUM:
+ case CTSF_UROWPCT_SUM:
+ case CTSF_SUBTABLEPCT_SUM:
+ case CTSF_USUBTABLEPCT_SUM:
+ case CTSF_TABLEPCT_SUM:
+ case CTSF_UTABLEPCT_SUM:
+ return true;
+ }
+
+ NOT_REACHED ();
+}
+
static double
ctables_summary_value (const struct ctables_cell *cell,
union ctables_summary *s,
case CTSF_LAYERPCT_SUM:
case CTSF_LAYERROWPCT_SUM:
case CTSF_LAYERCOLPCT_SUM:
+ {
+ double weight, mean;
+ moments1_calculate (s->moments, &weight, &mean, NULL, NULL, NULL);
+ if (weight == SYSMIS || mean == SYSMIS)
+ return SYSMIS;
+ enum ctables_domain_type d = ctables_function_domain (ss->function);
+ double num = weight * mean;
+ double denom = cell->domains[d]->sums[ss->sum_var_idx].e_sum;
+ return denom != 0 ? num / denom * 100 : SYSMIS;
+ }
case CTSF_UROWPCT_SUM:
case CTSF_UCOLPCT_SUM:
case CTSF_UTABLEPCT_SUM:
case CTSF_ULAYERPCT_SUM:
case CTSF_ULAYERROWPCT_SUM:
case CTSF_ULAYERCOLPCT_SUM:
- NOT_REACHED ();
+ {
+ double weight, mean;
+ moments1_calculate (s->moments, &weight, &mean, NULL, NULL, NULL);
+ if (weight == SYSMIS || mean == SYSMIS)
+ return SYSMIS;
+ enum ctables_domain_type d = ctables_function_domain (ss->function);
+ double num = weight * mean;
+ double denom = cell->domains[d]->sums[ss->sum_var_idx].u_sum;
+ return denom != 0 ? num / denom * 100 : SYSMIS;
+ }
case CTSF_MEDIAN:
case CTSF_PTILE:
not_equal: ;
}
+ struct ctables_sum *sums = (s->table->n_sum_vars
+ ? xzalloc (s->table->n_sum_vars * sizeof *sums)
+ : NULL);
+
d = xmalloc (sizeof *d);
- *d = (struct ctables_domain) { .example = cell };
+ *d = (struct ctables_domain) { .example = cell, .sums = sums };
hmap_insert (&s->domains[domain], &d->node, hash);
return d;
}
d->d_valid += d_weight;
d->e_valid += e_weight;
d->u_count += 1.0;
+
+ for (size_t i = 0; i < s->table->n_sum_vars; i++)
+ {
+ /* XXX listwise_missing??? */
+ const struct variable *var = s->table->sum_vars[i];
+ double addend = case_num (c, var);
+ if (!var_is_num_missing (var, addend))
+ {
+ struct ctables_sum *sum = &d->sums[i];
+ sum->e_sum += addend * e_weight;
+ sum->u_sum += addend;
+ }
+ }
}
}
}
return as->function > bs->function ? 1 : -1;
else if (as->percentile != bs->percentile)
return as->percentile < bs->percentile ? 1 : -1;
- return strcmp (as->label, bs->label);
+
+ const char *as_label = as->label ? as->label : "";
+ const char *bs_label = bs->label ? bs->label : "";
+ return strcmp (as_label, bs_label);
}
static struct pivot_value *
size_t *pc_a_idx_p)
{
assert (cell->postcompute);
- for (enum pivot_axis_type pc_a = 0; ; pc_a++)
- {
- assert (pc_a < PIVOT_N_AXES);
- for (size_t pc_a_idx = 0; pc_a_idx < s->nests[pc_a]->n; pc_a_idx++)
- {
- const struct ctables_cell_value *cv = &cell->axes[pc_a].cvs[pc_a_idx];
- if (cv->category->type == CCT_POSTCOMPUTE)
- {
- if (pc_a_p)
- *pc_a_p = pc_a;
- if (pc_a_idx_p)
- *pc_a_idx_p = pc_a_idx;
- return cv->category->pc;
- }
- }
- }
+ const struct ctables_postcompute *pc = NULL;
+ for (enum pivot_axis_type pc_a = 0; pc_a < PIVOT_N_AXES; pc_a++)
+ for (size_t pc_a_idx = 0; pc_a_idx < s->nests[pc_a]->n; pc_a_idx++)
+ {
+ const struct ctables_cell_value *cv = &cell->axes[pc_a].cvs[pc_a_idx];
+ if (cv->category->type == CCT_POSTCOMPUTE)
+ {
+ if (pc)
+ {
+ /* Multiple postcomputes cross each other. The value is
+ undefined. */
+ return NULL;
+ }
- NOT_REACHED ();
+ pc = cv->category->pc;
+ if (pc_a_p)
+ *pc_a_p = pc_a;
+ if (pc_a_idx_p)
+ *pc_a_idx_p = pc_a_idx;
+ }
+ }
+
+ assert (pc != NULL);
+ return pc;
}
static double
size_t pc_a_idx;
const struct ctables_postcompute *pc = ctables_cell_postcompute (
s, cell, &pc_a, &pc_a_idx);
+ if (!pc)
+ return SYSMIS;
if (pc->specs)
{
d->hide_all_labels = true;
for (size_t i = 0; i < specs->n; i++)
pivot_category_create_leaf (
- d->root, pivot_value_new_text (specs->specs[i].label));
+ d->root, ctables_summary_label (&specs->specs[i], t->cilevel));
}
bool categories_dimension = t->clabels_example != NULL;
for (size_t m = 0; m < specs->n; m++)
{
int leaf = pivot_category_create_leaf (
- parent, pivot_value_new_text (specs->specs[m].label));
+ parent, ctables_summary_label (&specs->specs[m],
+ t->cilevel));
if (!m)
prev_leaf = leaf;
}
return true;
}
+static size_t
+add_sum_var (struct variable *var,
+ struct variable ***sum_vars, size_t *n, size_t *allocated)
+{
+ for (size_t i = 0; i < *n; i++)
+ if (var == (*sum_vars)[i])
+ return i;
+
+ if (*n >= *allocated)
+ *sum_vars = x2nrealloc (*sum_vars, allocated, sizeof **sum_vars);
+ (*sum_vars)[*n] = var;
+ return (*n)++;
+}
+
+static void
+enumerate_sum_vars (const struct ctables_axis *a,
+ struct variable ***sum_vars, size_t *n, size_t *allocated)
+{
+ if (!a)
+ return;
+
+ switch (a->op)
+ {
+ case CTAO_VAR:
+ for (size_t i = 0; i < N_CSVS; i++)
+ for (size_t j = 0; j < a->specs[i].n; j++)
+ {
+ struct ctables_summary_spec *spec = &a->specs[i].specs[j];
+ if (ctables_function_is_pctsum (spec->function))
+ spec->sum_var_idx = add_sum_var (a->var, sum_vars, n, allocated);
+ }
+ break;
+
+ case CTAO_STACK:
+ case CTAO_NEST:
+ for (size_t i = 0; i < 2; i++)
+ enumerate_sum_vars (a->subs[i], sum_vars, n, allocated);
+ break;
+ }
+}
+
static bool
ctables_prepare_table (struct ctables_table *t)
{
*specs->specs = (struct ctables_summary_spec) {
.function = function,
.format = ctables_summary_default_format (function, specs->var),
- .label = ctables_summary_default_label (function, 0),
};
if (!specs->var)
specs->var = nest->vars[0];
}
struct ctables_summary_spec_set *merged = &t->summary_specs;
- struct merge_item *items = xnmalloc (2 * stack->n, sizeof *items);
+ struct merge_item *items = xnmalloc (N_CSVS * stack->n, sizeof *items);
size_t n_left = 0;
for (size_t j = 0; j < stack->n; j++)
{
}
#endif
+ size_t allocated_sum_vars = 0;
+ enumerate_sum_vars (t->axes[t->summary_axis],
+ &t->sum_vars, &t->n_sum_vars, &allocated_sum_vars);
+
return (ctables_check_label_position (t, PIVOT_AXIS_ROW)
&& ctables_check_label_position (t, PIVOT_AXIS_COLUMN));
}