From 4783127cc513fa1bd3aa05f560c4030fa334d669 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sat, 15 Jan 2022 11:03:21 -0800 Subject: [PATCH] Start work on slabels != summary_axis. --- src/language/stats/ctables.c | 557 +++++++++++++++++++++++------------ 1 file changed, 373 insertions(+), 184 deletions(-) diff --git a/src/language/stats/ctables.c b/src/language/stats/ctables.c index 08f7a74180..7247651c7e 100644 --- a/src/language/stats/ctables.c +++ b/src/language/stats/ctables.c @@ -321,6 +321,7 @@ struct ctables_table struct ctables_axis *axes[PIVOT_N_AXES]; struct ctables_stack stacks[PIVOT_N_AXES]; enum pivot_axis_type summary_axis; + struct ctables_summary_spec_set summary_specs; struct hmap cells; struct hmap domains[N_CTDTS]; @@ -2453,6 +2454,366 @@ merge_item_compare_3way (const struct merge_item *a, const struct merge_item *b) return strcmp (as->label, bs->label); } +static void +ctables_table_output_same_axis (struct ctables *ct, struct ctables_table *t) +{ + struct pivot_table *pt = pivot_table_create__ ( + (t->title + ? pivot_value_new_user_text (t->title, SIZE_MAX) + : pivot_value_new_text (N_("Custom Tables"))), + "Custom Tables"); + if (t->caption) + pivot_table_set_caption ( + pt, pivot_value_new_user_text (t->caption, SIZE_MAX)); + if (t->corner) + pivot_table_set_caption ( + pt, pivot_value_new_user_text (t->corner, SIZE_MAX)); + + pivot_table_set_look (pt, ct->look); + struct pivot_dimension *d[PIVOT_N_AXES]; + for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++) + { + static const char *names[] = { + [PIVOT_AXIS_ROW] = N_("Rows"), + [PIVOT_AXIS_COLUMN] = N_("Columns"), + [PIVOT_AXIS_LAYER] = N_("Layers"), + }; + d[a] = (t->axes[a] || a == t->summary_axis + ? pivot_dimension_create (pt, a, names[a]) + : NULL); + if (!d[a]) + continue; + + assert (t->axes[a]); + + struct ctables_cell **sorted = xnmalloc (t->cells.count, sizeof *sorted); + + struct ctables_cell *cell; + size_t n = 0; + HMAP_FOR_EACH (cell, struct ctables_cell, node, &t->cells) + if (!cell->hide) + sorted[n++] = cell; + assert (n <= t->cells.count); + + struct ctables_cell_sort_aux aux = { .t = t, .a = a }; + sort (sorted, n, sizeof *sorted, ctables_cell_compare_3way, &aux); + + size_t max_depth = 0; + for (size_t j = 0; j < t->stacks[a].n; j++) + if (t->stacks[a].nests[j].n > max_depth) + max_depth = t->stacks[a].nests[j].n; + + struct pivot_category **groups = xnmalloc (max_depth, sizeof *groups); + struct pivot_category *top = NULL; + int prev_leaf = 0; + for (size_t j = 0; j < n; j++) + { + struct ctables_cell *cell = sorted[j]; + const struct ctables_nest *nest = &t->stacks[a].nests[cell->axes[a].stack_idx]; + + size_t n_common = 0; + bool new_subtable = false; + if (j > 0) + { + struct ctables_cell *prev = sorted[j - 1]; + if (prev->axes[a].stack_idx == cell->axes[a].stack_idx) + { + for (; n_common < nest->n; n_common++) + if (n_common != nest->scale_idx + && (prev->axes[a].cvs[n_common].category + != cell->axes[a].cvs[n_common].category + || !value_equal (&prev->axes[a].cvs[n_common].value, + &cell->axes[a].cvs[n_common].value, + var_get_type (nest->vars[n_common])))) + break; + } + else + new_subtable = true; + } + else + new_subtable = true; + + if (new_subtable) + { + enum ctables_vlabel vlabel = ct->vlabels[var_get_dict_index (nest->vars[0])]; + top = d[a]->root; + if (vlabel != CTVL_NONE) + top = pivot_category_create_group__ ( + top, pivot_value_new_variable (nest->vars[0])); + } + if (n_common == nest->n) + { + cell->axes[a].leaf = prev_leaf; + continue; + } + + for (size_t k = n_common; k < nest->n; k++) + { + struct pivot_category *parent = k > 0 ? groups[k - 1] : top; + + struct pivot_value *label + = (k == nest->scale_idx ? NULL + : (cell->axes[a].cvs[k].category->type == CCT_TOTAL + || cell->axes[a].cvs[k].category->type == CCT_SUBTOTAL + || cell->axes[a].cvs[k].category->type == CCT_HSUBTOTAL) + ? pivot_value_new_user_text (cell->axes[a].cvs[k].category->total_label, + SIZE_MAX) + : pivot_value_new_var_value (nest->vars[k], + &cell->axes[a].cvs[k].value)); + if (k == nest->n - 1) + { + if (a == t->summary_axis) + { + if (label) + parent = pivot_category_create_group__ (parent, label); + const struct ctables_summary_spec_set *specs = &nest->specs[cell->sv]; + for (size_t m = 0; m < specs->n; m++) + { + int leaf = pivot_category_create_leaf ( + parent, pivot_value_new_text (specs->specs[m].label)); + if (m == 0) + prev_leaf = leaf; + } + } + else + { + /* This assertion is true as long as the summary axis + is the axis where the summaries are displayed. */ + assert (label); + + prev_leaf = pivot_category_create_leaf (parent, label); + } + break; + } + + if (label) + parent = pivot_category_create_group__ (parent, label); + + enum ctables_vlabel vlabel = ct->vlabels[var_get_dict_index (nest->vars[k + 1])]; + if (vlabel != CTVL_NONE) + parent = pivot_category_create_group__ ( + parent, pivot_value_new_variable (nest->vars[k + 1])); + groups[k] = parent; + } + + cell->axes[a].leaf = prev_leaf; + } + free (sorted); + free (groups); + } + struct ctables_cell *cell; + HMAP_FOR_EACH (cell, struct ctables_cell, node, &t->cells) + { + if (cell->hide) + continue; + + const struct ctables_nest *nest = &t->stacks[t->summary_axis].nests[cell->axes[t->summary_axis].stack_idx]; + const struct ctables_summary_spec_set *specs = &nest->specs[cell->sv]; + for (size_t j = 0; j < specs->n; j++) + { + size_t dindexes[3]; + size_t n_dindexes = 0; + + for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++) + if (d[a]) + { + int leaf = cell->axes[a].leaf; + if (a == t->summary_axis) + leaf += j; + dindexes[n_dindexes++] = leaf; + } + + double d = ctables_summary_value (cell, &cell->summaries[j], &specs->specs[j]); + struct pivot_value *value = pivot_value_new_number (d); + value->numeric.format = specs->specs[j].format; + pivot_table_put (pt, dindexes, n_dindexes, value); + } + } + + pivot_table_submit (pt); +} + + +static void +ctables_table_output_different_axis (struct ctables *ct, struct ctables_table *t) +{ + struct pivot_table *pt = pivot_table_create__ ( + (t->title + ? pivot_value_new_user_text (t->title, SIZE_MAX) + : pivot_value_new_text (N_("Custom Tables"))), + "Custom Tables"); + if (t->caption) + pivot_table_set_caption ( + pt, pivot_value_new_user_text (t->caption, SIZE_MAX)); + if (t->corner) + pivot_table_set_caption ( + pt, pivot_value_new_user_text (t->corner, SIZE_MAX)); + + pivot_table_set_look (pt, ct->look); + struct pivot_dimension *d[PIVOT_N_AXES]; + for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++) + { + static const char *names[] = { + [PIVOT_AXIS_ROW] = N_("Rows"), + [PIVOT_AXIS_COLUMN] = N_("Columns"), + [PIVOT_AXIS_LAYER] = N_("Layers"), + }; + d[a] = (t->axes[a] || a == t->summary_axis + ? pivot_dimension_create (pt, a, names[a]) + : NULL); + if (!d[a]) + continue; + + assert (t->axes[a]); + + struct ctables_cell **sorted = xnmalloc (t->cells.count, sizeof *sorted); + + struct ctables_cell *cell; + size_t n = 0; + HMAP_FOR_EACH (cell, struct ctables_cell, node, &t->cells) + if (!cell->hide) + sorted[n++] = cell; + assert (n <= t->cells.count); + + struct ctables_cell_sort_aux aux = { .t = t, .a = a }; + sort (sorted, n, sizeof *sorted, ctables_cell_compare_3way, &aux); + + size_t max_depth = 0; + for (size_t j = 0; j < t->stacks[a].n; j++) + if (t->stacks[a].nests[j].n > max_depth) + max_depth = t->stacks[a].nests[j].n; + + struct pivot_category **groups = xnmalloc (max_depth, sizeof *groups); + struct pivot_category *top = NULL; + int prev_leaf = 0; + for (size_t j = 0; j < n; j++) + { + struct ctables_cell *cell = sorted[j]; + const struct ctables_nest *nest = &t->stacks[a].nests[cell->axes[a].stack_idx]; + + size_t n_common = 0; + bool new_subtable = false; + if (j > 0) + { + struct ctables_cell *prev = sorted[j - 1]; + if (prev->axes[a].stack_idx == cell->axes[a].stack_idx) + { + for (; n_common < nest->n; n_common++) + if (n_common != nest->scale_idx + && (prev->axes[a].cvs[n_common].category + != cell->axes[a].cvs[n_common].category + || !value_equal (&prev->axes[a].cvs[n_common].value, + &cell->axes[a].cvs[n_common].value, + var_get_type (nest->vars[n_common])))) + break; + } + else + new_subtable = true; + } + else + new_subtable = true; + + if (new_subtable) + { + enum ctables_vlabel vlabel = ct->vlabels[var_get_dict_index (nest->vars[0])]; + top = d[a]->root; + if (vlabel != CTVL_NONE) + top = pivot_category_create_group__ ( + top, pivot_value_new_variable (nest->vars[0])); + } + if (n_common == nest->n) + { + cell->axes[a].leaf = prev_leaf; + continue; + } + + for (size_t k = n_common; k < nest->n; k++) + { + struct pivot_category *parent = k > 0 ? groups[k - 1] : top; + + struct pivot_value *label + = (k == nest->scale_idx ? NULL + : (cell->axes[a].cvs[k].category->type == CCT_TOTAL + || cell->axes[a].cvs[k].category->type == CCT_SUBTOTAL + || cell->axes[a].cvs[k].category->type == CCT_HSUBTOTAL) + ? pivot_value_new_user_text (cell->axes[a].cvs[k].category->total_label, + SIZE_MAX) + : pivot_value_new_var_value (nest->vars[k], + &cell->axes[a].cvs[k].value)); + if (k == nest->n - 1) + { + if (a == t->summary_axis) + { + if (label) + parent = pivot_category_create_group__ (parent, label); + const struct ctables_summary_spec_set *specs = &nest->specs[cell->sv]; + for (size_t m = 0; m < specs->n; m++) + { + int leaf = pivot_category_create_leaf ( + parent, pivot_value_new_text (specs->specs[m].label)); + if (m == 0) + prev_leaf = leaf; + } + } + else + { + /* This assertion is true as long as the summary axis + is the axis where the summaries are displayed. */ + assert (label); + + prev_leaf = pivot_category_create_leaf (parent, label); + } + break; + } + + if (label) + parent = pivot_category_create_group__ (parent, label); + + enum ctables_vlabel vlabel = ct->vlabels[var_get_dict_index (nest->vars[k + 1])]; + if (vlabel != CTVL_NONE) + parent = pivot_category_create_group__ ( + parent, pivot_value_new_variable (nest->vars[k + 1])); + groups[k] = parent; + } + + cell->axes[a].leaf = prev_leaf; + } + free (sorted); + free (groups); + } + struct ctables_cell *cell; + HMAP_FOR_EACH (cell, struct ctables_cell, node, &t->cells) + { + if (cell->hide) + continue; + + const struct ctables_nest *nest = &t->stacks[t->summary_axis].nests[cell->axes[t->summary_axis].stack_idx]; + const struct ctables_summary_spec_set *specs = &nest->specs[cell->sv]; + for (size_t j = 0; j < specs->n; j++) + { + size_t dindexes[3]; + size_t n_dindexes = 0; + + for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++) + if (d[a]) + { + int leaf = cell->axes[a].leaf; + if (a == t->summary_axis) + leaf += j; + dindexes[n_dindexes++] = leaf; + } + + double d = ctables_summary_value (cell, &cell->summaries[j], &specs->specs[j]); + struct pivot_value *value = pivot_value_new_number (d); + value->numeric.format = specs->specs[j].format; + pivot_table_put (pt, dindexes, n_dindexes, value); + } + } + + pivot_table_submit (pt); +} + + static bool ctables_execute (struct dataset *ds, struct ctables *ct) { @@ -2554,7 +2915,7 @@ ctables_execute (struct dataset *ds, struct ctables *ct) &nest->specs[CSV_CELL]); } - struct ctables_summary_spec_set merged = { .n = 0 }; + struct ctables_summary_spec_set *merged = &t->summary_specs; struct merge_item *items = xnmalloc (2 * stack->n, sizeof *items); size_t n_left = 0; for (size_t j = 0; j < stack->n; j++) @@ -2581,17 +2942,17 @@ ctables_execute (struct dataset *ds, struct ctables *ct) min = items[j]; /* XXX Add to 'merged' */ - if (merged.n >= merged.allocated) - merged.specs = x2nrealloc (merged.specs, &merged.allocated, - sizeof *merged.specs); - merged.specs[merged.n++] = min.set->specs[min.ofs]; + if (merged->n >= merged->allocated) + merged->specs = x2nrealloc (merged->specs, &merged->allocated, + sizeof *merged->specs); + merged->specs[merged->n++] = min.set->specs[min.ofs]; for (size_t j = 0; j < n_left; ) { if (merge_item_compare_3way (&items[j], &min) == 0) { struct merge_item *item = &items[j]; - item->set->specs[item->ofs].axis_idx = merged.n - 1; + item->set->specs[item->ofs].axis_idx = merged->n - 1; if (++item->ofs >= item->set->n) { items[j] = items[--n_left]; @@ -2602,8 +2963,8 @@ ctables_execute (struct dataset *ds, struct ctables *ct) } } - for (size_t j = 0; j < merged.n; j++) - printf ("%s\n", ctables_summary_function_name (merged.specs[j].function)); + for (size_t j = 0; j < merged->n; j++) + printf ("%s\n", ctables_summary_function_name (merged->specs[j].function)); for (size_t j = 0; j < stack->n; j++) { @@ -2646,183 +3007,11 @@ ctables_execute (struct dataset *ds, struct ctables *ct) for (size_t i = 0; i < ct->n_tables; i++) { struct ctables_table *t = ct->tables[i]; - - struct pivot_table *pt = pivot_table_create__ ( - (t->title - ? pivot_value_new_user_text (t->title, SIZE_MAX) - : pivot_value_new_text (N_("Custom Tables"))), - "Custom Tables"); - if (t->caption) - pivot_table_set_caption ( - pt, pivot_value_new_user_text (t->caption, SIZE_MAX)); - if (t->corner) - pivot_table_set_caption ( - pt, pivot_value_new_user_text (t->corner, SIZE_MAX)); - - pivot_table_set_look (pt, ct->look); - struct pivot_dimension *d[PIVOT_N_AXES]; - for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++) - { - static const char *names[] = { - [PIVOT_AXIS_ROW] = N_("Rows"), - [PIVOT_AXIS_COLUMN] = N_("Columns"), - [PIVOT_AXIS_LAYER] = N_("Layers"), - }; - d[a] = (t->axes[a] || a == t->summary_axis - ? pivot_dimension_create (pt, a, names[a]) - : NULL); - if (!d[a]) - continue; - - assert (t->axes[a]); - - struct ctables_cell **sorted = xnmalloc (t->cells.count, sizeof *sorted); - - struct ctables_cell *cell; - size_t n = 0; - HMAP_FOR_EACH (cell, struct ctables_cell, node, &t->cells) - if (!cell->hide) - sorted[n++] = cell; - assert (n <= t->cells.count); - - struct ctables_cell_sort_aux aux = { .t = t, .a = a }; - sort (sorted, n, sizeof *sorted, ctables_cell_compare_3way, &aux); - - size_t max_depth = 0; - for (size_t j = 0; j < t->stacks[a].n; j++) - if (t->stacks[a].nests[j].n > max_depth) - max_depth = t->stacks[a].nests[j].n; - - struct pivot_category **groups = xnmalloc (max_depth, sizeof *groups); - struct pivot_category *top = NULL; - int prev_leaf = 0; - for (size_t j = 0; j < n; j++) - { - struct ctables_cell *cell = sorted[j]; - const struct ctables_nest *nest = &t->stacks[a].nests[cell->axes[a].stack_idx]; - - size_t n_common = 0; - bool new_subtable = false; - if (j > 0) - { - struct ctables_cell *prev = sorted[j - 1]; - if (prev->axes[a].stack_idx == cell->axes[a].stack_idx) - { - for (; n_common < nest->n; n_common++) - if (n_common != nest->scale_idx - && (prev->axes[a].cvs[n_common].category - != cell->axes[a].cvs[n_common].category - || !value_equal (&prev->axes[a].cvs[n_common].value, - &cell->axes[a].cvs[n_common].value, - var_get_type (nest->vars[n_common])))) - break; - } - else - new_subtable = true; - } - else - new_subtable = true; - - if (new_subtable) - { - enum ctables_vlabel vlabel = ct->vlabels[var_get_dict_index (nest->vars[0])]; - top = d[a]->root; - if (vlabel != CTVL_NONE) - top = pivot_category_create_group__ ( - top, pivot_value_new_variable (nest->vars[0])); - } - if (n_common == nest->n) - { - cell->axes[a].leaf = prev_leaf; - continue; - } - - for (size_t k = n_common; k < nest->n; k++) - { - struct pivot_category *parent = k > 0 ? groups[k - 1] : top; - - struct pivot_value *label - = (k == nest->scale_idx ? NULL - : (cell->axes[a].cvs[k].category->type == CCT_TOTAL - || cell->axes[a].cvs[k].category->type == CCT_SUBTOTAL - || cell->axes[a].cvs[k].category->type == CCT_HSUBTOTAL) - ? pivot_value_new_user_text (cell->axes[a].cvs[k].category->total_label, - SIZE_MAX) - : pivot_value_new_var_value (nest->vars[k], - &cell->axes[a].cvs[k].value)); - if (k == nest->n - 1) - { - if (a == t->summary_axis) - { - if (label) - parent = pivot_category_create_group__ (parent, label); - const struct ctables_summary_spec_set *specs = &nest->specs[cell->sv]; - for (size_t m = 0; m < specs->n; m++) - { - int leaf = pivot_category_create_leaf ( - parent, pivot_value_new_text (specs->specs[m].label)); - if (m == 0) - prev_leaf = leaf; - } - } - else - { - /* This assertion is true as long as the summary axis - is the axis where the summaries are displayed. */ - assert (label); - - prev_leaf = pivot_category_create_leaf (parent, label); - } - break; - } - - if (label) - parent = pivot_category_create_group__ (parent, label); - - enum ctables_vlabel vlabel = ct->vlabels[var_get_dict_index (nest->vars[k + 1])]; - if (vlabel != CTVL_NONE) - parent = pivot_category_create_group__ ( - parent, pivot_value_new_variable (nest->vars[k + 1])); - groups[k] = parent; - } - - cell->axes[a].leaf = prev_leaf; - } - free (sorted); - free (groups); - } - struct ctables_cell *cell; - HMAP_FOR_EACH (cell, struct ctables_cell, node, &t->cells) - { - if (cell->hide) - continue; - - const struct ctables_nest *nest = &t->stacks[t->summary_axis].nests[cell->axes[t->summary_axis].stack_idx]; - const struct ctables_summary_spec_set *specs = &nest->specs[cell->sv]; - for (size_t j = 0; j < specs->n; j++) - { - size_t dindexes[3]; - size_t n_dindexes = 0; - - for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++) - if (d[a]) - { - int leaf = cell->axes[a].leaf; - if (a == t->summary_axis) - leaf += j; - dindexes[n_dindexes++] = leaf; - } - - double d = ctables_summary_value (cell, &cell->summaries[j], &specs->specs[j]); - struct pivot_value *value = pivot_value_new_number (d); - value->numeric.format = specs->specs[j].format; - pivot_table_put (pt, dindexes, n_dindexes, value); - } - } - - pivot_table_submit (pt); + if (t->summary_axis == t->slabels_position) + ctables_table_output_same_axis (ct, ct->tables[i]); + else + ctables_table_output_different_axis (ct, ct->tables[i]); } - return proc_commit (ds); } -- 2.30.2