X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flanguage%2Fstats%2Fctables.c;h=aeae6223fc45db75643f608772fc39b3a4392db0;hb=2ad9fc0f13133346ba6b153bf38a2fc06bf0be51;hp=1abbf8a667cb31134125a1d87b4f0c6b26dc7e49;hpb=d8b3292a8c12564dbc67e59f24d626dcfbf2e274;p=pspp diff --git a/src/language/stats/ctables.c b/src/language/stats/ctables.c index 1abbf8a667..aeae6223fc 100644 --- a/src/language/stats/ctables.c +++ b/src/language/stats/ctables.c @@ -455,6 +455,7 @@ struct ctables_table */ enum pivot_axis_type label_axis[PIVOT_N_AXES]; enum pivot_axis_type clabels_from_axis; + enum pivot_axis_type clabels_to_axis; const struct variable *clabels_example; struct hmap clabels_values_map; struct ctables_value **clabels_values; @@ -735,12 +736,32 @@ static void ctables_axis_destroy (struct ctables_axis *); struct ctables_summary_spec { + /* The calculation to be performed. + + 'function' is the function to calculate. 'weighted' specifies whether + to use weighted or unweighted data (for functions that do not support a + choice, it must be true). 'calc_area' is the area over which the + calculation takes place (for functions that target only an individual + cell, it must be 0). For CTSF_PTILE only, 'percentile' is the + percentile between 0 and 100 (for other functions it must be 0). */ enum ctables_summary_function function; bool weighted; - enum ctables_area_type area; + enum ctables_area_type calc_area; double percentile; /* CTSF_PTILE only. */ - char *label; + /* How to display the result of the calculation. + + 'label' is a user-specified label, NULL if the user didn't specify + one. + + 'user_area' is usually the same as 'calc_area', but when category labels + are rotated from one axis to another it swaps rows and columns. + + 'format' is the format for displaying the output. If + 'is_ctables_format' is true, then 'format.type' is one of the special + CTEF_* formats instead of the standard ones. */ + char *label; + enum ctables_area_type user_area; struct fmt_spec format; bool is_ctables_format; /* Is 'format' one of CTEF_*? */ @@ -965,7 +986,7 @@ static const char * ctables_summary_label__ (const struct ctables_summary_spec *spec) { bool w = spec->weighted; - enum ctables_area_type a = spec->area; + enum ctables_area_type a = spec->user_area; switch (spec->function) { case CTSF_COUNT: @@ -1160,7 +1181,8 @@ add_summary_spec (struct ctables_axis *axis, *dst = (struct ctables_summary_spec) { .function = function, .weighted = weighted, - .area = area, + .calc_area = area, + .user_area = area, .percentile = percentile, .label = xstrdup_if_nonnull (label), .format = (format ? *format @@ -2726,25 +2748,25 @@ ctables_summary_value (const struct ctables_cell *cell, return s->count; case CTSF_areaID: - return cell->areas[ss->area]->sequence; + return cell->areas[ss->calc_area]->sequence; case CTSF_areaPCT_COUNT: { - const struct ctables_area *a = cell->areas[ss->area]; + const struct ctables_area *a = cell->areas[ss->calc_area]; double a_count = ss->weighted ? a->e_count : a->u_count; return a_count ? s->count / a_count * 100 : SYSMIS; } case CTSF_areaPCT_VALIDN: { - const struct ctables_area *a = cell->areas[ss->area]; + const struct ctables_area *a = cell->areas[ss->calc_area]; double a_valid = ss->weighted ? a->e_valid : a->u_valid; return a_valid ? s->count / a_valid * 100 : SYSMIS; } case CTSF_areaPCT_TOTALN: { - const struct ctables_area *a = cell->areas[ss->area]; + const struct ctables_area *a = cell->areas[ss->calc_area]; double a_total = ss->weighted ? a->e_total : a->u_total; return a_total ? s->count / a_total * 100 : SYSMIS; } @@ -2807,7 +2829,7 @@ ctables_summary_value (const struct ctables_cell *cell, if (weight == SYSMIS || mean == SYSMIS) return SYSMIS; - const struct ctables_area *a = cell->areas[ss->area]; + const struct ctables_area *a = cell->areas[ss->calc_area]; const struct ctables_sum *sum = &a->sums[ss->sum_var_idx]; double denom = ss->weighted ? sum->e_sum : sum->u_sum; return denom != 0 ? weight * mean / denom * 100 : SYSMIS; @@ -3499,8 +3521,8 @@ merge_item_compare_3way (const struct merge_item *a, const struct merge_item *b) return as->function > bs->function ? 1 : -1; else if (as->weighted != bs->weighted) return as->weighted > bs->weighted ? 1 : -1; - else if (as->area != bs->area) - return as->area > bs->area ? 1 : -1; + else if (as->calc_area != bs->calc_area) + return as->calc_area > bs->calc_area ? 1 : -1; else if (as->percentile != bs->percentile) return as->percentile < bs->percentile ? 1 : -1; @@ -3996,7 +4018,7 @@ ctables_cell_calculate_postcompute (const struct ctables_section *s, const struct ctables_summary_spec *ss2 = &pc->specs->specs[i]; if (ss->function == ss2->function && ss->weighted == ss2->weighted - && ss->area == ss2->area + && ss->calc_area == ss2->calc_area && ss->percentile == ss2->percentile) { *format = ss2->format; @@ -4476,8 +4498,6 @@ ctables_check_label_position (struct ctables_table *t, enum pivot_axis_type a) if (label_pos == a) return true; - t->clabels_from_axis = a; - const char *subcommand_name = a == PIVOT_AXIS_ROW ? "ROWLABELS" : "COLLABELS"; const char *pos_name = label_pos == PIVOT_AXIS_LAYER ? "LAYER" : "OPPOSITE"; @@ -4575,6 +4595,33 @@ add_sum_var (struct variable *var, return (*n)++; } +static enum ctables_area_type +rotate_area (enum ctables_area_type area) +{ + return area; + switch (area) + { + case CTAT_TABLE: + case CTAT_LAYER: + case CTAT_SUBTABLE: + return area; + + case CTAT_LAYERROW: + return CTAT_LAYERCOL; + + case CTAT_LAYERCOL: + return CTAT_LAYERROW; + + case CTAT_ROW: + return CTAT_COL; + + case CTAT_COL: + return CTAT_ROW; + } + + NOT_REACHED (); +} + static void enumerate_sum_vars (const struct ctables_axis *a, struct variable ***sum_vars, size_t *n, size_t *allocated) @@ -4618,48 +4665,100 @@ ctables_prepare_table (struct ctables_table *t) nest->areas[at] = xmalloc (nest->n * sizeof *nest->areas[at]); nest->n_areas[at] = 0; - for (size_t k = 0; k < nest->n; k++) + bool add_vars = (at == CTAT_LAYER ? a == PIVOT_AXIS_LAYER + : at == CTAT_LAYERROW ? a != PIVOT_AXIS_COLUMN + : at == CTAT_LAYERCOL ? a != PIVOT_AXIS_ROW + : at == CTAT_TABLE ? false + : true); + if (add_vars) + for (size_t k = 0; k < nest->n; k++) + { + if (k == nest->scale_idx) + continue; + nest->areas[at][nest->n_areas[at]++] = k; + } + else if ((at == CTAT_LAYERCOL && a == PIVOT_AXIS_ROW && t->label_axis[a] != a) + || (at == CTAT_LAYERROW && a == PIVOT_AXIS_COLUMN && t->label_axis[a] != a) + || (at == CTAT_LAYER && t->label_axis[a] == PIVOT_AXIS_LAYER)) { - if (k == nest->scale_idx) - continue; - - switch (at) + for (size_t k = nest->n - 1; k < nest->n; k--) { - case CTAT_TABLE: - continue; - - case CTAT_LAYER: - if (a != PIVOT_AXIS_LAYER) + if (k == nest->scale_idx) continue; + nest->areas[at][nest->n_areas[at]++] = k; break; + } + continue; + } - case CTAT_SUBTABLE: - case CTAT_ROW: - case CTAT_COL: - if (at == CTAT_SUBTABLE ? a != PIVOT_AXIS_LAYER - : at == CTAT_ROW ? a == PIVOT_AXIS_COLUMN - : a == PIVOT_AXIS_ROW) - { - if (k == nest->n - 1 - || (nest->scale_idx == nest->n - 1 - && k == nest->n - 2)) - continue; - } - break; + enum pivot_axis_type ata, atb; + if (at == CTAT_ROW || at == CTAT_LAYERROW) + { + ata = PIVOT_AXIS_ROW; + atb = PIVOT_AXIS_COLUMN; + } + else if (at == CTAT_COL || at == CTAT_LAYERCOL) + { + ata = PIVOT_AXIS_COLUMN; + atb = PIVOT_AXIS_ROW; + } - case CTAT_LAYERROW: - if (a == PIVOT_AXIS_COLUMN) - continue; - break; + size_t n_drop = 0; + bool drop_inner = false; + switch (at) + { + case CTAT_SUBTABLE: + if (t->clabels_from_axis == PIVOT_AXIS_LAYER) + n_drop = a != PIVOT_AXIS_LAYER; + else if (t->clabels_to_axis != PIVOT_AXIS_LAYER) + n_drop = a == t->clabels_from_axis ? 2 : 0; + else + { + n_drop = a != PIVOT_AXIS_LAYER && a != t->clabels_from_axis; + drop_inner = a == t->clabels_from_axis; + } + break; - case CTAT_LAYERCOL: - if (a == PIVOT_AXIS_ROW) - continue; - break; + case CTAT_LAYERROW: + case CTAT_LAYERCOL: + n_drop = a == ata && t->label_axis[ata] == atb; + break; + + case CTAT_ROW: + case CTAT_COL: + if (a == atb) + { + if (t->label_axis[ata] == atb) + ; + else if (t->label_axis[atb] == ata) + drop_inner = true; + else if (t->label_axis[atb] == PIVOT_AXIS_LAYER) + drop_inner = true; + else + n_drop = 1; } + if (a == ata && t->label_axis[ata] == atb) + n_drop++; + break; + + case CTAT_LAYER: + case CTAT_TABLE: + n_drop = 0; + break; + } - nest->areas[at][nest->n_areas[at]++] = k; + if (drop_inner) + { + size_t n = nest->n_areas[at]; + if (n > 1) + { + nest->areas[at][n - 2] = nest->areas[at][n - 1]; + nest->n_areas[at]--; + } } + for (size_t i = 0; i < n_drop; i++) + if (nest->n_areas[at] > 0) + nest->n_areas[at]--; } } } @@ -4702,6 +4801,20 @@ ctables_prepare_table (struct ctables_table *t) ctables_summary_spec_set_clone (&nest->specs[CSV_TOTAL], &nest->specs[CSV_CELL]); + if (t->label_axis[PIVOT_AXIS_ROW] == PIVOT_AXIS_COLUMN + || t->label_axis[PIVOT_AXIS_COLUMN] == PIVOT_AXIS_ROW) + { + for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++) + for (size_t i = 0; i < nest->specs[sv].n; i++) + { + struct ctables_summary_spec *ss = &nest->specs[sv].specs[i]; + const struct ctables_function_info *cfi = + &ctables_function_info[ss->function]; + if (cfi->is_area) + ss->calc_area = rotate_area (ss->calc_area); + } + } + if (t->ctables->smissing_listwise) { struct variable **listwise_vars = NULL; @@ -5691,7 +5804,8 @@ ctables_parse_pproperties_format (struct lexer *lexer, sss->specs[sss->n++] = (struct ctables_summary_spec) { .function = function, .weighted = weighted, - .area = area, + .calc_area = area, + .user_area = area, .percentile = percentile, .format = format, .is_ctables_format = is_ctables_format, @@ -6166,6 +6280,7 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds) [PIVOT_AXIS_LAYER] = PIVOT_AXIS_LAYER, }, .clabels_from_axis = PIVOT_AXIS_LAYER, + .clabels_to_axis = PIVOT_AXIS_LAYER, .categories = categories, .n_categories = n_vars, .cilevel = 95, @@ -6604,12 +6719,18 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds) break; } - if (t->label_axis[PIVOT_AXIS_ROW] != PIVOT_AXIS_ROW - && t->label_axis[PIVOT_AXIS_COLUMN] != PIVOT_AXIS_COLUMN) + if (t->label_axis[PIVOT_AXIS_ROW] != PIVOT_AXIS_ROW) { - msg (SE, _("ROWLABELS and COLLABELS may not both be specified.")); - goto error; + t->clabels_from_axis = PIVOT_AXIS_ROW; + if (t->label_axis[PIVOT_AXIS_COLUMN] != PIVOT_AXIS_COLUMN) + { + msg (SE, _("ROWLABELS and COLLABELS may not both be specified.")); + goto error; + } } + else if (t->label_axis[PIVOT_AXIS_COLUMN] != PIVOT_AXIS_COLUMN) + t->clabels_from_axis = PIVOT_AXIS_COLUMN; + t->clabels_to_axis = t->label_axis[t->clabels_from_axis]; if (!ctables_prepare_table (t)) goto error;