#include "data/casereader.h"
#include "data/casewriter.h"
+#include "data/data-in.h"
+#include "data/data-out.h"
#include "data/dataset.h"
#include "data/dictionary.h"
#include "data/mrset.h"
#include "language/command.h"
#include "language/lexer/format-parser.h"
#include "language/lexer/lexer.h"
+#include "language/lexer/token.h"
#include "language/lexer/variable-parser.h"
#include "libpspp/array.h"
#include "libpspp/assertion.h"
S(CTSF_LAYERROWPCT_TOTALN, "LAYERROWPCT.TOTALN", N_("Layer Row Total N %"), CTF_PERCENT, CTFA_ALL) \
S(CTSF_LAYERCOLPCT_TOTALN, "LAYERCOLPCT.TOTALN", N_("Layer Column Total N %"), CTF_PERCENT, CTFA_ALL) \
\
+ /* All variables (unweighted.) */ \
+ S(CTSF_UCOUNT, "UCOUNT", N_("Unweighted Count"), CTF_COUNT, CTFA_ALL) \
+ S(CTSF_UROWPCT_COUNT, "UROWPCT.COUNT", N_("Unweighted Row %"), CTF_PERCENT, CTFA_ALL) \
+ S(CTSF_UCOLPCT_COUNT, "UCOLPCT.COUNT", N_("Unweighted Column %"), CTF_PERCENT, CTFA_ALL) \
+ S(CTSF_UTABLEPCT_COUNT, "UTABLEPCT.COUNT", N_("Unweighted Table %"), CTF_PERCENT, CTFA_ALL) \
+ S(CTSF_USUBTABLEPCT_COUNT, "USUBTABLEPCT.COUNT", N_("Unweighted Subtable %"), CTF_PERCENT, CTFA_ALL) \
+ S(CTSF_ULAYERPCT_COUNT, "ULAYERPCT.COUNT", N_("Unweighted Layer %"), CTF_PERCENT, CTFA_ALL) \
+ S(CTSF_ULAYERROWPCT_COUNT, "ULAYERROWPCT.COUNT", N_("Unweighted Layer Row %"), CTF_PERCENT, CTFA_ALL) \
+ S(CTSF_ULAYERCOLPCT_COUNT, "ULAYERCOLPCT.COUNT", N_("Unweighted Layer Column %"), CTF_PERCENT, CTFA_ALL) \
+ S(CTSF_UROWPCT_VALIDN, "UROWPCT.VALIDN", N_("Unweighted Row Valid N %"), CTF_PERCENT, CTFA_ALL) \
+ S(CTSF_UCOLPCT_VALIDN, "UCOLPCT.VALIDN", N_("Unweighted Column Valid N %"), CTF_PERCENT, CTFA_ALL) \
+ S(CTSF_UTABLEPCT_VALIDN, "UTABLEPCT.VALIDN", N_("Unweighted Table Valid N %"), CTF_PERCENT, CTFA_ALL) \
+ S(CTSF_USUBTABLEPCT_VALIDN, "USUBTABLEPCT.VALIDN", N_("Unweighted Subtable Valid N %"), CTF_PERCENT, CTFA_ALL) \
+ S(CTSF_ULAYERPCT_VALIDN, "ULAYERPCT.VALIDN", N_("Unweighted Layer Valid N %"), CTF_PERCENT, CTFA_ALL) \
+ S(CTSF_ULAYERROWPCT_VALIDN, "ULAYERROWPCT.VALIDN", N_("Unweighted Layer Row Valid N %"), CTF_PERCENT, CTFA_ALL) \
+ S(CTSF_ULAYERCOLPCT_VALIDN, "ULAYERCOLPCT.VALIDN", N_("Unweighted Layer Column Valid N %"), CTF_PERCENT, CTFA_ALL) \
+ S(CTSF_UROWPCT_TOTALN, "UROWPCT.TOTALN", N_("Unweighted Row Total N %"), CTF_PERCENT, CTFA_ALL) \
+ S(CTSF_UCOLPCT_TOTALN, "UCOLPCT.TOTALN", N_("Unweighted Column Total N %"), CTF_PERCENT, CTFA_ALL) \
+ S(CTSF_UTABLEPCT_TOTALN, "UTABLEPCT.TOTALN", N_("Unweighted Table Total N %"), CTF_PERCENT, CTFA_ALL) \
+ S(CTSF_USUBTABLEPCT_TOTALN, "USUBTABLEPCT.TOTALN", N_("Unweighted Subtable Total N %"), CTF_PERCENT, CTFA_ALL) \
+ S(CTSF_ULAYERPCT_TOTALN, "ULAYERPCT.TOTALN", N_("Unweighted Layer Total N %"), CTF_PERCENT, CTFA_ALL) \
+ S(CTSF_ULAYERROWPCT_TOTALN, "ULAYERROWPCT.TOTALN", N_("Unweighted Layer Row Total N %"), CTF_PERCENT, CTFA_ALL) \
+ S(CTSF_ULAYERCOLPCT_TOTALN, "ULAYERCOLPCT.TOTALN", N_("Unweighted Layer Column Total N %"), CTF_PERCENT, CTFA_ALL) \
+ \
/* Scale variables, totals, and subtotals. */ \
S(CTSF_MAXIMUM, "MAXIMUM", N_("Maximum"), CTF_GENERAL, CTFA_SCALE) \
S(CTSF_MEAN, "MEAN", N_("Mean"), CTF_GENERAL, CTFA_SCALE) \
S(CTSF_LAYERROWPCT_SUM, "LAYERROWPCT.SUM", N_("Layer Row Sum %"), CTF_PERCENT, CTFA_SCALE) \
S(CTSF_LAYERCOLPCT_SUM, "LAYERCOLPCT.SUM", N_("Layer Column Sum %"), CTF_PERCENT, CTFA_SCALE) \
\
- /* Multiple response sets. */ \
+ /* Scale variables, totals, and subtotals (unweighted). */ \
+ S(CTSF_UMEAN, "UMEAN", N_("Unweighted Mean"), CTF_GENERAL, CTFA_SCALE) \
+ S(CTSF_UMEDIAN, "UMEDIAN", N_("Unweighted Median"), CTF_GENERAL, CTFA_SCALE) \
+ S(CTSF_UMISSING, "UMISSING", N_("Unweighted Missing"), CTF_GENERAL, CTFA_SCALE) \
+ S(CTSF_UMODE, "UMODE", N_("Unweighted Mode"), CTF_GENERAL, CTFA_SCALE) \
+ S(CTSF_UPTILE, "UPTILE", N_("Unweighted Percentile"), CTF_GENERAL, CTFA_SCALE) \
+ S(CTSF_USEMEAN, "USEMEAN", N_("Unweighted Std Error of Mean"), CTF_GENERAL, CTFA_SCALE) \
+ S(CTSF_USTDDEV, "USTDDEV", N_("Unweighted Std Deviation"), CTF_GENERAL, CTFA_SCALE) \
+ S(CTSF_USUM, "USUM", N_("Unweighted Sum"), CTF_GENERAL, CTFA_SCALE) \
+ S(CSTF_UTOTALN, "UTOTALN", N_("Unweighted Total N"), CTF_COUNT, CTFA_SCALE) \
+ S(CTSF_UVALIDN, "UVALIDN", N_("Unweighted Valid N"), CTF_COUNT, CTFA_SCALE) \
+ S(CTSF_UVARIANCE, "UVARIANCE", N_("Unweighted Variance"), CTF_GENERAL, CTFA_SCALE) \
+ S(CTSF_UROWPCT_SUM, "UROWPCT.SUM", N_("Unweighted Row Sum %"), CTF_PERCENT, CTFA_SCALE) \
+ S(CTSF_UCOLPCT_SUM, "UCOLPCT.SUM", N_("Unweighted Column Sum %"), CTF_PERCENT, CTFA_SCALE) \
+ S(CTSF_UTABLEPCT_SUM, "UTABLEPCT.SUM", N_("Unweighted Table Sum %"), CTF_PERCENT, CTFA_SCALE) \
+ S(CTSF_USUBTABLEPCT_SUM, "USUBTABLEPCT.SUM", N_("Unweighted Subtable Sum %"), CTF_PERCENT, CTFA_SCALE) \
+ S(CTSF_ULAYERPCT_SUM, "ULAYERPCT.SUM", N_("Unweighted Layer Sum %"), CTF_PERCENT, CTFA_SCALE) \
+ S(CTSF_ULAYERROWPCT_SUM, "ULAYERROWPCT.SUM", N_("Unweighted Layer Row Sum %"), CTF_PERCENT, CTFA_SCALE) \
+ S(CTSF_ULAYERCOLPCT_SUM, "ULAYERCOLPCT.SUM", N_("Unweighted Layer Column Sum %"), CTF_PERCENT, CTFA_SCALE) \
+
+#if 0 /* Multiple response sets not yet implemented. */
S(CTSF_RESPONSES, "RESPONSES", N_("Responses"), CTF_COUNT, CTFA_MRSETS) \
S(CTSF_ROWPCT_RESPONSES, "ROWPCT.RESPONSES", N_("Row Responses %"), CTF_PERCENT, CTFA_MRSETS) \
S(CTSF_COLPCT_RESPONSES, "COLPCT.RESPONSES", N_("Column Responses %"), CTF_PERCENT, CTFA_MRSETS) \
S(CTSF_LAYERPCT_COUNT_RESPONSES, "LAYERPCT.COUNT.RESPONSES", N_("Layer Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
S(CTSF_LAYERROWPCT_COUNT_RESPONSES, "LAYERROWPCT.COUNT.RESPONSES", N_("Layer Row Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
S(CTSF_LAYERCOLPCT_COUNT_RESPONSES, "LAYERCOLPCT.RESPONSES.COUNT", N_("Layer Column Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS)
+#endif
enum ctables_summary_function
{
const struct ctables_cell *example;
double d_valid; /* Dictionary weight. */
- double d_missing;
+ double d_count;
+ double d_total;
double e_valid; /* Effective weight */
- double e_missing;
+ double e_count;
+ double e_total;
+ 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
struct hmap_node node;
/* The domains that contain this cell. */
- bool contributes_to_domains;
+ uint32_t omit_domains;
struct ctables_domain *domains[N_CTDTS];
bool hide;
+
bool postcompute;
enum ctables_summary_variant sv;
axes[PIVOT_N_AXES];
union ctables_summary *summaries;
+
+ //char *name;
};
struct ctables
const struct dictionary *dict;
struct pivot_table_look *look;
+ /* CTABLES has a number of extra formats that we implement via custom
+ currency specifications on an alternate fmt_settings. */
+#define CTEF_NEGPAREN FMT_CCA
+#define CTEF_NEQUAL FMT_CCB
+#define CTEF_PAREN FMT_CCC
+#define CTEF_PCTPAREN FMT_CCD
+ struct fmt_settings ctables_formats;
+
/* If this is NULL, zeros are displayed using the normal print format.
Otherwise, this string is displayed. */
char *zero;
CTPO_CONSTANT, /* 5 */
CTPO_CAT_NUMBER, /* [5] */
CTPO_CAT_STRING, /* ["STRING"] */
- CTPO_CAT_RANGE, /* [LO THRU 5] */
+ CTPO_CAT_NRANGE, /* [LO THRU 5] */
+ CTPO_CAT_SRANGE, /* ["A" THRU "B"] */
CTPO_CAT_MISSING, /* MISSING */
CTPO_CAT_OTHERNM, /* OTHERNM */
CTPO_CAT_SUBTOTAL, /* SUBTOTAL */
/* CTPO_CAT_NUMBER. */
double number;
- /* CTPO_CAT_STRING. */
- char *string;
+ /* CTPO_CAT_STRING, in dictionary encoding. */
+ struct substring string;
+
+ /* CTPO_CAT_NRANGE. */
+ double nrange[2];
- /* CTPO_CAT_RANGE. */
- double range[2];
+ /* CTPO_CAT_SRANGE. */
+ struct substring srange[2];
/* CTPO_CAT_SUBTOTAL. */
size_t subtotal_index;
size_t n;
size_t allocated;
+ /* The variable to which the summary specs are applied. */
struct variable *var;
+
+ /* Whether the variable to which the summary specs are applied is a scale
+ variable for the purpose of summarization.
+
+ (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];
};
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;
struct ctables_pairwise *pairwise;
};
-struct ctables_var
- {
- bool is_mrset;
- union
- {
- struct variable *var;
- const struct mrset *mrset;
- };
- };
-
-static const struct fmt_spec *
-ctables_var_get_print_format (const struct ctables_var *var)
-{
- return (var->is_mrset
- ? var_get_print_format (var->mrset->vars[0])
- : var_get_print_format (var->var));
-}
-
-static const char *
-ctables_var_name (const struct ctables_var *var)
-{
- return var->is_mrset ? var->mrset->name : var_get_name (var->var);
-}
-
struct ctables_categories
{
size_t n_refs;
/* Explicit category lists. */
CCT_NUMBER,
CCT_STRING,
- CCT_RANGE,
+ CCT_NRANGE, /* Numerical range. */
+ CCT_SRANGE, /* String range. */
CCT_MISSING,
CCT_OTHERNM,
CCT_POSTCOMPUTE,
CCT_VALUE,
CCT_LABEL,
CCT_FUNCTION,
+
+ /* For contributing to TOTALN. */
+ CCT_EXCLUDED_MISSING,
}
type;
union
{
- double number; /* CCT_NUMBER. */
- char *string; /* CCT_STRING. */
- double range[2]; /* CCT_RANGE. */
+ double number; /* CCT_NUMBER. */
+ struct substring string; /* CCT_STRING, in dictionary encoding. */
+ double nrange[2]; /* CCT_NRANGE. */
+ struct substring srange[2]; /* CCT_SRANGE. */
struct
{
};
/* Source location. This is null for CCT_TOTAL, CCT_VALUE, CCT_LABEL,
- CCT_FUNCTION. */
+ CCT_FUNCTION, CCT_EXCLUDED_MISSING. */
struct msg_location *location;
};
switch (cat->type)
{
case CCT_NUMBER:
- case CCT_RANGE:
+ case CCT_NRANGE:
case CCT_MISSING:
case CCT_OTHERNM:
case CCT_POSTCOMPUTE:
break;
case CCT_STRING:
- free (cat->string);
+ ss_dealloc (&cat->string);
+ break;
+
+ case CCT_SRANGE:
+ ss_dealloc (&cat->srange[0]);
+ ss_dealloc (&cat->srange[1]);
break;
case CCT_SUBTOTAL:
case CCT_LABEL:
case CCT_FUNCTION:
break;
+
+ case CCT_EXCLUDED_MISSING:
+ break;
}
}
+static bool
+nullable_substring_equal (const struct substring *a,
+ const struct substring *b)
+{
+ return !a->string ? !b->string : b->string && ss_equals (*a, *b);
+}
+
static bool
ctables_category_equal (const struct ctables_category *a,
const struct ctables_category *b)
return a->number == b->number;
case CCT_STRING:
- return strcmp (a->string, b->string);
+ return ss_equals (a->string, b->string);
- case CCT_RANGE:
- return a->range[0] == b->range[0] && a->range[1] == b->range[1];
+ case CCT_NRANGE:
+ return a->nrange[0] == b->nrange[0] && a->nrange[1] == b->nrange[1];
+
+ case CCT_SRANGE:
+ return (nullable_substring_equal (&a->srange[0], &b->srange[0])
+ && nullable_substring_equal (&a->srange[1], &b->srange[1]));
case CCT_MISSING:
case CCT_OTHERNM:
&& a->sort_function == b->sort_function
&& a->sort_var == b->sort_var
&& a->percentile == b->percentile);
+
+ case CCT_EXCLUDED_MISSING:
+ return true;
}
NOT_REACHED ();
/* Terminals. */
struct
{
- struct ctables_var var;
+ struct variable *var;
bool scale;
struct ctables_summary_spec_set specs[N_CSVS];
};
enum ctables_summary_function function;
double percentile; /* CTSF_PTILE only. */
char *label;
- struct fmt_spec format; /* XXX extra CTABLES formats */
+
+ struct fmt_spec format;
+ bool is_ctables_format; /* Is 'format' one of CTEF_*? */
+
size_t axis_idx;
+ size_t sum_var_idx;
};
static void
const struct ctables_summary_spec *src)
{
*dst = *src;
- dst->label = xstrdup (src->label);
+ dst->label = xstrdup_if_nonnull (src->label);
}
static void
.specs = specs,
.n = src->n,
.allocated = src->n,
- .var = src->var
+ .var = src->var,
+ .is_scale = src->is_scale,
};
}
static bool
ctables_summary_function_is_count (enum ctables_summary_function f)
{
- static const bool is_count[N_CTSF_FUNCTIONS] = {
- [CTSF_COUNT] = true,
- [CTSF_ECOUNT] = true,
- [CTSF_ROWPCT_COUNT] = true,
- [CTSF_COLPCT_COUNT] = true,
- [CTSF_TABLEPCT_COUNT] = true,
- [CTSF_SUBTABLEPCT_COUNT] = true,
- [CTSF_LAYERPCT_COUNT] = true,
- [CTSF_LAYERROWPCT_COUNT] = true,
- [CTSF_LAYERCOLPCT_COUNT] = true,
- [CTSF_ROWPCT_RESPONSES_COUNT] = true,
- [CTSF_COLPCT_RESPONSES_COUNT] = true,
- [CTSF_TABLEPCT_RESPONSES_COUNT] = true,
- [CTSF_SUBTABLEPCT_RESPONSES_COUNT] = true,
- [CTSF_LAYERPCT_RESPONSES_COUNT] = true,
- [CTSF_LAYERROWPCT_RESPONSES_COUNT] = true,
- [CTSF_LAYERCOLPCT_RESPONSES_COUNT] = true,
- [CTSF_ROWPCT_COUNT_RESPONSES] = true,
- [CTSF_COLPCT_COUNT_RESPONSES] = true,
- [CTSF_TABLEPCT_COUNT_RESPONSES] = true,
- [CTSF_SUBTABLEPCT_COUNT_RESPONSES] = true,
- [CTSF_LAYERPCT_COUNT_RESPONSES] = true,
- [CTSF_LAYERROWPCT_COUNT_RESPONSES] = true,
- [CTSF_LAYERCOLPCT_COUNT_RESPONSES] = true,
- };
- return is_count[f];
+ switch (f)
+ {
+ case CTSF_COUNT:
+ case CTSF_ECOUNT:
+ case CTSF_ROWPCT_COUNT:
+ case CTSF_COLPCT_COUNT:
+ case CTSF_TABLEPCT_COUNT:
+ case CTSF_SUBTABLEPCT_COUNT:
+ case CTSF_LAYERPCT_COUNT:
+ case CTSF_LAYERROWPCT_COUNT:
+ case CTSF_LAYERCOLPCT_COUNT:
+ case CTSF_UCOUNT:
+ case CTSF_UROWPCT_COUNT:
+ case CTSF_UCOLPCT_COUNT:
+ case CTSF_UTABLEPCT_COUNT:
+ case CTSF_USUBTABLEPCT_COUNT:
+ case CTSF_ULAYERPCT_COUNT:
+ case CTSF_ULAYERROWPCT_COUNT:
+ case CTSF_ULAYERCOLPCT_COUNT:
+ return true;
+
+ case CTSF_ROWPCT_VALIDN:
+ case CTSF_COLPCT_VALIDN:
+ case CTSF_TABLEPCT_VALIDN:
+ case CTSF_SUBTABLEPCT_VALIDN:
+ case CTSF_LAYERPCT_VALIDN:
+ case CTSF_LAYERROWPCT_VALIDN:
+ case CTSF_LAYERCOLPCT_VALIDN:
+ case CTSF_ROWPCT_TOTALN:
+ case CTSF_COLPCT_TOTALN:
+ case CTSF_TABLEPCT_TOTALN:
+ case CTSF_SUBTABLEPCT_TOTALN:
+ case CTSF_LAYERPCT_TOTALN:
+ case CTSF_LAYERROWPCT_TOTALN:
+ case CTSF_LAYERCOLPCT_TOTALN:
+ case CTSF_MAXIMUM:
+ case CTSF_MEAN:
+ case CTSF_MEDIAN:
+ case CTSF_MINIMUM:
+ case CTSF_MISSING:
+ case CTSF_MODE:
+ case CTSF_PTILE:
+ case CTSF_RANGE:
+ case CTSF_SEMEAN:
+ case CTSF_STDDEV:
+ case CTSF_SUM:
+ case CSTF_TOTALN:
+ case CTSF_ETOTALN:
+ case CTSF_VALIDN:
+ case CTSF_EVALIDN:
+ case CTSF_VARIANCE:
+ case CTSF_ROWPCT_SUM:
+ case CTSF_COLPCT_SUM:
+ case CTSF_TABLEPCT_SUM:
+ case CTSF_SUBTABLEPCT_SUM:
+ case CTSF_LAYERPCT_SUM:
+ case CTSF_LAYERROWPCT_SUM:
+ case CTSF_LAYERCOLPCT_SUM:
+ case CTSF_UROWPCT_VALIDN:
+ case CTSF_UCOLPCT_VALIDN:
+ case CTSF_UTABLEPCT_VALIDN:
+ case CTSF_USUBTABLEPCT_VALIDN:
+ case CTSF_ULAYERPCT_VALIDN:
+ case CTSF_ULAYERROWPCT_VALIDN:
+ case CTSF_ULAYERCOLPCT_VALIDN:
+ case CTSF_UROWPCT_TOTALN:
+ case CTSF_UCOLPCT_TOTALN:
+ case CTSF_UTABLEPCT_TOTALN:
+ case CTSF_USUBTABLEPCT_TOTALN:
+ case CTSF_ULAYERPCT_TOTALN:
+ case CTSF_ULAYERROWPCT_TOTALN:
+ case CTSF_ULAYERCOLPCT_TOTALN:
+ case CTSF_UMEAN:
+ case CTSF_UMEDIAN:
+ case CTSF_UMISSING:
+ case CTSF_UMODE:
+ case CTSF_UPTILE:
+ case CTSF_USEMEAN:
+ case CTSF_USTDDEV:
+ case CTSF_USUM:
+ case CSTF_UTOTALN:
+ case CTSF_UVALIDN:
+ case CTSF_UVARIANCE:
+ case CTSF_UROWPCT_SUM:
+ case CTSF_UCOLPCT_SUM:
+ case CTSF_UTABLEPCT_SUM:
+ case CTSF_USUBTABLEPCT_SUM:
+ case CTSF_ULAYERPCT_SUM:
+ case CTSF_ULAYERROWPCT_SUM:
+ case CTSF_ULAYERCOLPCT_SUM:
+ return false;
+ }
+ NOT_REACHED ();
}
static struct fmt_spec
ctables_summary_default_format (enum ctables_summary_function function,
- const struct ctables_var *var)
+ const struct variable *var)
{
static const enum ctables_format default_formats[] = {
#define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = FORMAT,
return (struct fmt_spec) { .type = FMT_PCT, .w = 40, .d = 1 };
case CTF_GENERAL:
- return *ctables_var_get_print_format (var);
+ return *var_get_print_format (var);
default:
NOT_REACHED ();
}
}
-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 *
add_summary_spec (struct ctables_axis *axis,
enum ctables_summary_function function, double percentile,
const char *label, const struct fmt_spec *format,
- const struct msg_location *loc, enum ctables_summary_variant sv)
+ bool is_ctables_format, const struct msg_location *loc,
+ enum ctables_summary_variant sv)
{
if (axis->op == CTAO_VAR)
{
const char *function_name = ctables_summary_function_name (function);
- const char *var_name = ctables_var_name (&axis->var);
+ const char *var_name = var_get_name (axis->var);
switch (ctables_function_availability (function))
{
case CTFA_MRSETS:
- if (!axis->var.is_mrset)
- {
- msg_at (SE, loc, _("Summary function %s applies only to multiple "
- "response sets."), function_name);
- msg_at (SN, axis->loc, _("'%s' is not a multiple response set."),
- var_name);
- return false;
- }
- break;
+ msg_at (SE, loc, _("Summary function %s applies only to multiple "
+ "response sets."), function_name);
+ msg_at (SN, axis->loc, _("'%s' is not a multiple response set."),
+ var_name);
+ return false;
case CTFA_SCALE:
+#if 0
if (!axis->scale)
{
msg_at (SE, loc,
var_name);
return false;
}
+#endif
break;
case CTFA_ALL:
*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)),
+ : ctables_summary_default_format (function, axis->var)),
+ .is_ctables_format = is_ctables_format,
};
return true;
}
{
for (size_t i = 0; i < 2; i++)
if (!add_summary_spec (axis->subs[i], function, percentile, label,
- format, loc, sv))
+ format, is_ctables_format, loc, sv))
return false;
return true;
}
static struct ctables_axis *ctables_axis_parse_stack (
struct ctables_axis_parse_ctx *);
-static bool
-ctables_var_parse (struct lexer *lexer, struct dictionary *dict,
- struct ctables_var *var)
-{
- if (ss_starts_with (lex_tokss (lexer), ss_cstr ("$")))
- {
- *var = (struct ctables_var) {
- .is_mrset = true,
- .mrset = dict_lookup_mrset (dict, lex_tokcstr (lexer))
- };
- if (!var->mrset)
- {
- lex_error (lexer, _("'%s' does not name a multiple-response set "
- "in the active file dictionary."),
- lex_tokcstr (lexer));
- return false;
- }
- lex_get (lexer);
- return true;
- }
- else
- {
- *var = (struct ctables_var) {
- .is_mrset = false,
- .var = parse_variable (lexer, dict),
- };
- return var->var != NULL;
- }
-}
static struct ctables_axis *
ctables_axis_parse_primary (struct ctables_axis_parse_ctx *ctx)
return NULL;
int start_ofs = lex_ofs (ctx->lexer);
- struct ctables_var var;
- if (!ctables_var_parse (ctx->lexer, ctx->dict, &var))
+ struct variable *var = parse_variable (ctx->lexer, ctx->dict);
+ if (!var)
return NULL;
struct ctables_axis *axis = xmalloc (sizeof *axis);
*axis = (struct ctables_axis) { .op = CTAO_VAR, .var = var };
/* XXX should figure out default measures by reading data */
- axis->scale = (var.is_mrset ? false
- : lex_match_phrase (ctx->lexer, "[S]") ? true
+ axis->scale = (lex_match_phrase (ctx->lexer, "[S]") ? true
: lex_match_phrase (ctx->lexer, "[C]") ? false
- : var_get_measure (var.var) == MEASURE_SCALE);
+ : var_get_measure (var) == MEASURE_SCALE);
axis->loc = lex_ofs_location (ctx->lexer, start_ofs,
lex_ofs (ctx->lexer) - 1);
+ if (axis->scale && var_is_alpha (var))
+ {
+ msg_at (SE, axis->loc, _("Cannot use string variable %s as a scale "
+ "variable."),
+ var_get_name (var));
+ ctables_axis_destroy (axis);
+ return NULL;
+ }
+
return axis;
}
return s[strcspn (s, "0123456789")] != '\0';
}
+static bool
+parse_ctables_format_specifier (struct lexer *lexer, struct fmt_spec *format,
+ bool *is_ctables_format)
+{
+ char type[FMT_TYPE_LEN_MAX + 1];
+ if (!parse_abstract_format_specifier__ (lexer, type, &format->w, &format->d))
+ return false;
+
+ if (!strcasecmp (type, "NEGPAREN"))
+ format->type = CTEF_NEGPAREN;
+ else if (!strcasecmp (type, "NEQUAL"))
+ format->type = CTEF_NEQUAL;
+ else if (!strcasecmp (type, "PAREN"))
+ format->type = CTEF_PAREN;
+ else if (!strcasecmp (type, "PCTPAREN"))
+ format->type = CTEF_PCTPAREN;
+ else
+ {
+ *is_ctables_format = false;
+ return (parse_format_specifier (lexer, format)
+ && fmt_check_output (format)
+ && fmt_check_type_compat (format, VAL_NUMERIC));
+ }
+
+ if (format->w < 2)
+ {
+ msg (SE, _("Output format %s requires width 2 or greater."), type);
+ return false;
+ }
+ else if (format->d > format->w - 1)
+ {
+ msg (SE, _("Output format %s requires width greater than decimals."),
+ type);
+ return false;
+ }
+ else
+ {
+ *is_ctables_format = true;
+ return true;
+ }
+}
+
static struct ctables_axis *
ctables_axis_parse_postfix (struct ctables_axis_parse_ctx *ctx)
{
}
/* 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;
const struct fmt_spec *formatp;
+ bool is_ctables_format = false;
if (lex_token (ctx->lexer) == T_ID
&& has_digit (lex_tokcstr (ctx->lexer)))
{
- if (!parse_format_specifier (ctx->lexer, &format)
- || !fmt_check_output (&format)
- || !fmt_check_type_compat (&format, VAL_NUMERIC))
+ if (!parse_ctables_format_specifier (ctx->lexer, &format,
+ &is_ctables_format))
{
free (label);
goto error;
struct msg_location *loc = lex_ofs_location (ctx->lexer, start_ofs,
lex_ofs (ctx->lexer) - 1);
- add_summary_spec (sub, function, percentile, label, formatp, loc, sv);
+ add_summary_spec (sub, function, percentile, label, formatp,
+ is_ctables_format, loc, sv);
free (label);
msg_location_destroy (loc);
if (!axis)
return NULL;
else if (axis->op == CTAO_VAR)
- {
- if (axis->scale)
- {
- assert (!axis->var.is_mrset);
- return axis;
- }
- else
- return NULL;
- }
+ return axis->scale ? axis : NULL;
else
{
for (size_t i = 0; i < 2; i++)
}
static struct ctables_category
-cct_range (double low, double high)
+cct_nrange (double low, double high)
+{
+ return (struct ctables_category) {
+ .type = CCT_NRANGE,
+ .nrange = { low, high }
+ };
+}
+
+static struct ctables_category
+cct_srange (struct substring low, struct substring high)
{
return (struct ctables_category) {
- .type = CCT_RANGE,
- .range = { low, high }
+ .type = CCT_SRANGE,
+ .srange = { low, high }
};
}
return true;
}
+static struct substring
+parse_substring (struct lexer *lexer, struct dictionary *dict)
+{
+ struct substring s = recode_substring_pool (
+ dict_get_encoding (dict), "UTF-8", lex_tokss (lexer), NULL);
+ ss_rtrim (&s, ss_cstr (" "));
+ lex_get (lexer);
+ return s;
+}
+
static bool
-ctables_table_parse_explicit_category (struct lexer *lexer, struct ctables *ct,
+ctables_table_parse_explicit_category (struct lexer *lexer,
+ struct dictionary *dict,
+ struct ctables *ct,
struct ctables_category *cat)
{
if (lex_match_id (lexer, "OTHERNM"))
return ctables_table_parse_subtotal (lexer, true, cat);
else if (lex_match_id (lexer, "LO"))
{
- if (!lex_force_match_id (lexer, "THRU") || lex_force_num (lexer))
+ if (!lex_force_match_id (lexer, "THRU"))
+ return false;
+ if (lex_is_string (lexer))
+ {
+ struct substring sr0 = { .string = NULL };
+ struct substring sr1 = parse_substring (lexer, dict);
+ *cat = cct_srange (sr0, sr1);
+ }
+ else if (lex_force_num (lexer))
+ {
+ *cat = cct_nrange (-DBL_MAX, lex_number (lexer));
+ lex_get (lexer);
+ }
+ else
return false;
- *cat = cct_range (-DBL_MAX, lex_number (lexer));
- lex_get (lexer);
}
else if (lex_is_number (lexer))
{
if (lex_match_id (lexer, "THRU"))
{
if (lex_match_id (lexer, "HI"))
- *cat = cct_range (number, DBL_MAX);
+ *cat = cct_nrange (number, DBL_MAX);
else
{
if (!lex_force_num (lexer))
return false;
- *cat = cct_range (number, lex_number (lexer));
+ *cat = cct_nrange (number, lex_number (lexer));
lex_get (lexer);
}
}
}
else if (lex_is_string (lexer))
{
- *cat = (struct ctables_category) {
- .type = CCT_STRING,
- .string = ss_xstrdup (lex_tokss (lexer)),
- };
- lex_get (lexer);
+ struct substring s = parse_substring (lexer, dict);
+ if (lex_match_id (lexer, "THRU"))
+ {
+ if (lex_match_id (lexer, "HI"))
+ {
+ struct substring sr1 = { .string = NULL };
+ *cat = cct_srange (s, sr1);
+ }
+ else
+ {
+ if (!lex_force_string (lexer))
+ return false;
+ struct substring sr1 = parse_substring (lexer, dict);
+ *cat = cct_srange (s, sr1);
+ }
+ }
+ else
+ *cat = (struct ctables_category) { .type = CCT_STRING, .string = s };
}
else if (lex_match (lexer, T_AND))
{
break;
case CTPO_CAT_STRING:
- if (cat->type == CCT_STRING && !strcmp (cat->string, e->string))
+ if (cat->type == CCT_STRING && ss_equals (cat->string, e->string))
+ best = cat;
+ break;
+
+ case CTPO_CAT_NRANGE:
+ if (cat->type == CCT_NRANGE
+ && cat->nrange[0] == e->nrange[0]
+ && cat->nrange[1] == e->nrange[1])
best = cat;
break;
- case CTPO_CAT_RANGE:
- if (cat->type == CCT_RANGE
- && cat->range[0] == e->range[0]
- && cat->range[1] == e->range[1])
+ case CTPO_CAT_SRANGE:
+ if (cat->type == CCT_SRANGE
+ && nullable_substring_equal (&cat->srange[0], &e->srange[0])
+ && nullable_substring_equal (&cat->srange[1], &e->srange[1]))
best = cat;
break;
{
case CTPO_CAT_NUMBER:
case CTPO_CAT_STRING:
- case CTPO_CAT_RANGE:
+ case CTPO_CAT_NRANGE:
case CTPO_CAT_MISSING:
case CTPO_CAT_OTHERNM:
case CTPO_CAT_SUBTOTAL:
}
}
+static bool
+parse_category_string (const struct ctables_category *cat,
+ struct substring s, struct dictionary *dict,
+ enum fmt_type format, double *n)
+{
+ union value v;
+ char *error = data_in (s, dict_get_encoding (dict), format,
+ settings_get_fmt_settings (), &v, 0, NULL);
+ if (error)
+ {
+ msg_at (SE, cat->location,
+ _("Failed to parse category specification as format %s: %s."),
+ fmt_name (format), error);
+ free (error);
+ return false;
+ }
+
+ *n = v.f;
+ return true;
+}
+
+static bool
+all_strings (struct variable **vars, size_t n_vars,
+ const struct ctables_category *cat)
+{
+ for (size_t j = 0; j < n_vars; j++)
+ if (var_is_numeric (vars[j]))
+ {
+ msg_at (SE, cat->location,
+ _("This category specification may be applied only to string "
+ "variables, but this subcommand tries to apply it to "
+ "numeric variable %s."),
+ var_get_name (vars[j]));
+ return false;
+ }
+ return true;
+}
+
static bool
ctables_table_parse_categories (struct lexer *lexer, struct dictionary *dict,
struct ctables *ct, struct ctables_table *t)
if (!parse_variables (lexer, dict, &vars, &n_vars, PV_NO_SCRATCH))
return false;
+ const struct fmt_spec *common_format = var_get_print_format (vars[0]);
+ for (size_t i = 1; i < n_vars; i++)
+ {
+ const struct fmt_spec *f = var_get_print_format (vars[i]);
+ if (f->type != common_format->type)
+ {
+ common_format = NULL;
+ break;
+ }
+ }
+ bool parse_strings
+ = (common_format
+ && (fmt_get_category (common_format->type)
+ & (FMT_CAT_DATE | FMT_CAT_TIME | FMT_CAT_DATE_COMPONENT)));
+
struct ctables_categories *c = xmalloc (sizeof *c);
*c = (struct ctables_categories) { .n_refs = n_vars, .show_empty = true };
for (size_t i = 0; i < n_vars; i++)
ctables_categories_unref (*cp);
*cp = c;
}
- free (vars);
size_t allocated_cats = 0;
if (lex_match (lexer, T_LBRACK))
int start_ofs = lex_ofs (lexer);
struct ctables_category *cat = &c->cats[c->n_cats];
- if (!ctables_table_parse_explicit_category (lexer, ct, cat))
+ if (!ctables_table_parse_explicit_category (lexer, dict, ct, cat))
return false;
cat->location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1);
c->n_cats++;
for (size_t i = 0; i < c->n_cats; i++)
{
struct ctables_category *cat = &c->cats[i];
- if (cat->type == CCT_POSTCOMPUTE
- && !ctables_recursive_check_postcompute (cat->pc->expr, cat,
- c, cats_location))
- return false;
+ switch (cat->type)
+ {
+ case CCT_POSTCOMPUTE:
+ if (!ctables_recursive_check_postcompute (cat->pc->expr, cat,
+ c, cats_location))
+ return false;
+ break;
+
+ case CCT_NUMBER:
+ case CCT_NRANGE:
+ for (size_t j = 0; j < n_vars; j++)
+ if (var_is_alpha (vars[j]))
+ {
+ msg_at (SE, cat->location,
+ _("This category specification may be applied "
+ "only to numeric variables, but this "
+ "subcommand tries to apply it to string "
+ "variable %s."),
+ var_get_name (vars[j]));
+ return false;
+ }
+ break;
+
+ case CCT_STRING:
+ if (parse_strings)
+ {
+ double n;
+ if (!parse_category_string (cat, cat->string, dict,
+ common_format->type, &n))
+ return false;
+
+ ss_dealloc (&cat->string);
+
+ cat->type = CCT_NUMBER;
+ cat->number = n;
+ }
+ else if (!all_strings (vars, n_vars, cat))
+ return false;
+ break;
+
+ case CCT_SRANGE:
+ if (parse_strings)
+ {
+ double n[2];
+
+ if (!cat->srange[0].string)
+ n[0] = -DBL_MAX;
+ else if (!parse_category_string (cat, cat->srange[0], dict,
+ common_format->type, &n[0]))
+ return false;
+
+ if (!cat->srange[1].string)
+ n[1] = DBL_MAX;
+ else if (!parse_category_string (cat, cat->srange[1], dict,
+ common_format->type, &n[1]))
+ return false;
+
+ ss_dealloc (&cat->srange[0]);
+ ss_dealloc (&cat->srange[1]);
+
+ cat->type = CCT_NRANGE;
+ cat->nrange[0] = n[0];
+ cat->nrange[1] = n[1];
+ }
+ else if (!all_strings (vars, n_vars, cat))
+ return false;
+ break;
+
+ case CCT_MISSING:
+ case CCT_OTHERNM:
+ case CCT_SUBTOTAL:
+ case CCT_TOTAL:
+ case CCT_VALUE:
+ case CCT_LABEL:
+ case CCT_FUNCTION:
+ case CCT_EXCLUDED_MISSING:
+ break;
+ }
}
}
{
case CCT_NUMBER:
case CCT_STRING:
- case CCT_RANGE:
+ case CCT_NRANGE:
+ case CCT_SRANGE:
case CCT_MISSING:
case CCT_OTHERNM:
cat->subtotal = subtotal;
case CCT_VALUE:
case CCT_LABEL:
case CCT_FUNCTION:
+ case CCT_EXCLUDED_MISSING:
break;
}
}
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)
+{
+ struct variable **vars = xmalloc (sizeof *vars);
+ *vars = a->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;
+ 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;
- }
- 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]));
}
union ctables_summary
{
/* COUNT, VALIDN, TOTALN. */
- struct
- {
- double valid;
- double missing;
- };
+ double count;
/* MINIMUM, MAXIMUM, RANGE. */
struct
case CTSF_ETOTALN:
case CTSF_VALIDN:
case CTSF_EVALIDN:
- s->missing = s->valid = 0;
+ case CTSF_UCOUNT:
+ case CTSF_UROWPCT_COUNT:
+ case CTSF_UCOLPCT_COUNT:
+ case CTSF_UTABLEPCT_COUNT:
+ case CTSF_USUBTABLEPCT_COUNT:
+ case CTSF_ULAYERPCT_COUNT:
+ case CTSF_ULAYERROWPCT_COUNT:
+ case CTSF_ULAYERCOLPCT_COUNT:
+ case CTSF_UROWPCT_VALIDN:
+ case CTSF_UCOLPCT_VALIDN:
+ case CTSF_UTABLEPCT_VALIDN:
+ case CTSF_USUBTABLEPCT_VALIDN:
+ case CTSF_ULAYERPCT_VALIDN:
+ case CTSF_ULAYERROWPCT_VALIDN:
+ case CTSF_ULAYERCOLPCT_VALIDN:
+ case CTSF_UROWPCT_TOTALN:
+ case CTSF_UCOLPCT_TOTALN:
+ case CTSF_UTABLEPCT_TOTALN:
+ case CTSF_USUBTABLEPCT_TOTALN:
+ case CTSF_ULAYERPCT_TOTALN:
+ case CTSF_ULAYERROWPCT_TOTALN:
+ case CTSF_ULAYERCOLPCT_TOTALN:
+ case CTSF_UMISSING:
+ case CSTF_UTOTALN:
+ case CTSF_UVALIDN:
+ s->count = 0;
break;
case CTSF_MAXIMUM:
case CTSF_LAYERPCT_SUM:
case CTSF_LAYERROWPCT_SUM:
case CTSF_LAYERCOLPCT_SUM:
+ case CTSF_UMEAN:
+ case CTSF_USEMEAN:
+ case CTSF_USTDDEV:
+ case CTSF_USUM:
+ case CTSF_UVARIANCE:
+ case CTSF_UROWPCT_SUM:
+ case CTSF_UCOLPCT_SUM:
+ case CTSF_UTABLEPCT_SUM:
+ case CTSF_USUBTABLEPCT_SUM:
+ case CTSF_ULAYERPCT_SUM:
+ case CTSF_ULAYERROWPCT_SUM:
+ case CTSF_ULAYERCOLPCT_SUM:
s->moments = moments1_create (MOMENT_VARIANCE);
break;
case CTSF_MEDIAN:
case CTSF_MODE:
case CTSF_PTILE:
+ case CTSF_UMEDIAN:
+ case CTSF_UMODE:
+ case CTSF_UPTILE:
{
struct caseproto *proto = caseproto_create ();
proto = caseproto_add_width (proto, 0);
s->ovalue = SYSMIS;
}
break;
-
- case CTSF_RESPONSES:
- case CTSF_ROWPCT_RESPONSES:
- case CTSF_COLPCT_RESPONSES:
- case CTSF_TABLEPCT_RESPONSES:
- case CTSF_SUBTABLEPCT_RESPONSES:
- case CTSF_LAYERPCT_RESPONSES:
- case CTSF_LAYERROWPCT_RESPONSES:
- case CTSF_LAYERCOLPCT_RESPONSES:
- case CTSF_ROWPCT_RESPONSES_COUNT:
- case CTSF_COLPCT_RESPONSES_COUNT:
- case CTSF_TABLEPCT_RESPONSES_COUNT:
- case CTSF_SUBTABLEPCT_RESPONSES_COUNT:
- case CTSF_LAYERPCT_RESPONSES_COUNT:
- case CTSF_LAYERROWPCT_RESPONSES_COUNT:
- case CTSF_LAYERCOLPCT_RESPONSES_COUNT:
- case CTSF_ROWPCT_COUNT_RESPONSES:
- case CTSF_COLPCT_COUNT_RESPONSES:
- case CTSF_TABLEPCT_COUNT_RESPONSES:
- case CTSF_SUBTABLEPCT_COUNT_RESPONSES:
- case CTSF_LAYERPCT_COUNT_RESPONSES:
- case CTSF_LAYERROWPCT_COUNT_RESPONSES:
- case CTSF_LAYERCOLPCT_COUNT_RESPONSES:
- NOT_REACHED ();
}
}
case CTSF_ETOTALN:
case CTSF_VALIDN:
case CTSF_EVALIDN:
+ case CTSF_UCOUNT:
+ case CTSF_UROWPCT_COUNT:
+ case CTSF_UCOLPCT_COUNT:
+ case CTSF_UTABLEPCT_COUNT:
+ case CTSF_USUBTABLEPCT_COUNT:
+ case CTSF_ULAYERPCT_COUNT:
+ case CTSF_ULAYERROWPCT_COUNT:
+ case CTSF_ULAYERCOLPCT_COUNT:
+ case CTSF_UROWPCT_VALIDN:
+ case CTSF_UCOLPCT_VALIDN:
+ case CTSF_UTABLEPCT_VALIDN:
+ case CTSF_USUBTABLEPCT_VALIDN:
+ case CTSF_ULAYERPCT_VALIDN:
+ case CTSF_ULAYERROWPCT_VALIDN:
+ case CTSF_ULAYERCOLPCT_VALIDN:
+ case CTSF_UROWPCT_TOTALN:
+ case CTSF_UCOLPCT_TOTALN:
+ case CTSF_UTABLEPCT_TOTALN:
+ case CTSF_USUBTABLEPCT_TOTALN:
+ case CTSF_ULAYERPCT_TOTALN:
+ case CTSF_ULAYERROWPCT_TOTALN:
+ case CTSF_ULAYERCOLPCT_TOTALN:
+ case CTSF_UMISSING:
+ case CSTF_UTOTALN:
+ case CTSF_UVALIDN:
break;
case CTSF_MAXIMUM:
case CTSF_LAYERPCT_SUM:
case CTSF_LAYERROWPCT_SUM:
case CTSF_LAYERCOLPCT_SUM:
+ case CTSF_UMEAN:
+ case CTSF_USEMEAN:
+ case CTSF_USTDDEV:
+ case CTSF_USUM:
+ case CTSF_UVARIANCE:
+ case CTSF_UROWPCT_SUM:
+ case CTSF_UCOLPCT_SUM:
+ case CTSF_UTABLEPCT_SUM:
+ case CTSF_USUBTABLEPCT_SUM:
+ case CTSF_ULAYERPCT_SUM:
+ case CTSF_ULAYERROWPCT_SUM:
+ case CTSF_ULAYERCOLPCT_SUM:
moments1_destroy (s->moments);
break;
case CTSF_MEDIAN:
case CTSF_MODE:
case CTSF_PTILE:
+ case CTSF_UMEDIAN:
+ case CTSF_UMODE:
+ case CTSF_UPTILE:
casewriter_destroy (s->writer);
break;
-
- case CTSF_RESPONSES:
- case CTSF_ROWPCT_RESPONSES:
- case CTSF_COLPCT_RESPONSES:
- case CTSF_TABLEPCT_RESPONSES:
- case CTSF_SUBTABLEPCT_RESPONSES:
- case CTSF_LAYERPCT_RESPONSES:
- case CTSF_LAYERROWPCT_RESPONSES:
- case CTSF_LAYERCOLPCT_RESPONSES:
- case CTSF_ROWPCT_RESPONSES_COUNT:
- case CTSF_COLPCT_RESPONSES_COUNT:
- case CTSF_TABLEPCT_RESPONSES_COUNT:
- case CTSF_SUBTABLEPCT_RESPONSES_COUNT:
- case CTSF_LAYERPCT_RESPONSES_COUNT:
- case CTSF_LAYERROWPCT_RESPONSES_COUNT:
- case CTSF_LAYERCOLPCT_RESPONSES_COUNT:
- case CTSF_ROWPCT_COUNT_RESPONSES:
- case CTSF_COLPCT_COUNT_RESPONSES:
- case CTSF_TABLEPCT_COUNT_RESPONSES:
- case CTSF_SUBTABLEPCT_COUNT_RESPONSES:
- case CTSF_LAYERPCT_COUNT_RESPONSES:
- case CTSF_LAYERROWPCT_COUNT_RESPONSES:
- case CTSF_LAYERCOLPCT_COUNT_RESPONSES:
- NOT_REACHED ();
}
}
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_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
+ kind of summary, consider the following charts for each variable in the
+ table. Only if "yes" appears for every variable for the summary is the
+ case counted.
+
+ Categorical variables: VALIDN COUNT TOTALN
+ Valid values in included categories yes yes yes
+ Missing values in included categories --- yes yes
+ Missing values in excluded categories --- --- yes
+ Valid values in excluded categories --- --- ---
+
+ Scale variables: VALIDN COUNT TOTALN
+ Valid value yes yes yes
+ Missing value --- yes yes
+
+ Missing values include both user- and system-missing. (The system-missing
+ value is always in an excluded category.)
+ */
switch (ss->function)
{
- case CTSF_COUNT:
case CSTF_TOTALN:
- case CTSF_VALIDN:
- if (var_is_value_missing (var, value))
- s->missing += d_weight;
- else
- s->valid += d_weight;
+ case CTSF_ROWPCT_TOTALN:
+ case CTSF_COLPCT_TOTALN:
+ case CTSF_TABLEPCT_TOTALN:
+ case CTSF_SUBTABLEPCT_TOTALN:
+ case CTSF_LAYERPCT_TOTALN:
+ case CTSF_LAYERROWPCT_TOTALN:
+ case CTSF_LAYERCOLPCT_TOTALN:
+ s->count += d_weight;
break;
- case CTSF_ECOUNT:
+ case CSTF_UTOTALN:
+ case CTSF_UROWPCT_TOTALN:
+ case CTSF_UCOLPCT_TOTALN:
+ case CTSF_UTABLEPCT_TOTALN:
+ case CTSF_USUBTABLEPCT_TOTALN:
+ case CTSF_ULAYERPCT_TOTALN:
+ case CTSF_ULAYERROWPCT_TOTALN:
+ case CTSF_ULAYERCOLPCT_TOTALN:
+ s->count += 1.0;
+ break;
+
+ case CTSF_COUNT:
case CTSF_ROWPCT_COUNT:
case CTSF_COLPCT_COUNT:
case CTSF_TABLEPCT_COUNT:
case CTSF_LAYERPCT_COUNT:
case CTSF_LAYERROWPCT_COUNT:
case CTSF_LAYERCOLPCT_COUNT:
+ if (is_scale || !excluded_missing)
+ s->count += d_weight;
+ break;
+
+ case CTSF_UCOUNT:
+ case CTSF_UROWPCT_COUNT:
+ case CTSF_UCOLPCT_COUNT:
+ case CTSF_UTABLEPCT_COUNT:
+ case CTSF_USUBTABLEPCT_COUNT:
+ case CTSF_ULAYERPCT_COUNT:
+ case CTSF_ULAYERROWPCT_COUNT:
+ case CTSF_ULAYERCOLPCT_COUNT:
+ if (is_scale || !excluded_missing)
+ s->count += 1.0;
+ break;
+
+ case CTSF_VALIDN:
case CTSF_ROWPCT_VALIDN:
case CTSF_COLPCT_VALIDN:
case CTSF_TABLEPCT_VALIDN:
case CTSF_LAYERPCT_VALIDN:
case CTSF_LAYERROWPCT_VALIDN:
case CTSF_LAYERCOLPCT_VALIDN:
- case CTSF_ROWPCT_TOTALN:
- case CTSF_COLPCT_TOTALN:
- case CTSF_TABLEPCT_TOTALN:
- case CTSF_SUBTABLEPCT_TOTALN:
- case CTSF_LAYERPCT_TOTALN:
- case CTSF_LAYERROWPCT_TOTALN:
- case CTSF_LAYERCOLPCT_TOTALN:
+ if (is_scale
+ ? !is_scale_missing
+ : !is_missing)
+ s->count += d_weight;
+ break;
+
+ case CTSF_UVALIDN:
+ case CTSF_UROWPCT_VALIDN:
+ case CTSF_UCOLPCT_VALIDN:
+ case CTSF_UTABLEPCT_VALIDN:
+ case CTSF_USUBTABLEPCT_VALIDN:
+ case CTSF_ULAYERPCT_VALIDN:
+ case CTSF_ULAYERROWPCT_VALIDN:
+ case CTSF_ULAYERCOLPCT_VALIDN:
+ if (is_scale
+ ? !is_scale_missing
+ : !is_missing)
+ s->count += 1.0;
+ break;
+
case CTSF_MISSING:
- case CTSF_ETOTALN:
+ if (is_missing)
+ s->count += d_weight;
+ break;
+
+ case CTSF_UMISSING:
+ if (is_missing)
+ s->count += 1.0;
+ break;
+
+ case CTSF_ECOUNT:
+ if (is_scale || !excluded_missing)
+ s->count += e_weight;
+ break;
+
case CTSF_EVALIDN:
- if (var_is_value_missing (var, value))
- s->missing += e_weight;
- else
- s->valid += e_weight;
+ if (is_scale
+ ? !is_scale_missing
+ : !is_missing)
+ s->count += e_weight;
+ break;
+
+ case CTSF_ETOTALN:
+ 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_UMEAN:
+ case CTSF_USEMEAN:
+ case CTSF_USTDDEV:
+ case CTSF_USUM:
+ case CTSF_UVARIANCE:
+ case CTSF_UROWPCT_SUM:
+ case CTSF_UCOLPCT_SUM:
+ case CTSF_UTABLEPCT_SUM:
+ case CTSF_USUBTABLEPCT_SUM:
+ case CTSF_ULAYERPCT_SUM:
+ case CTSF_ULAYERROWPCT_SUM:
+ case CTSF_ULAYERCOLPCT_SUM:
+ if (!is_scale_missing)
+ moments1_add (s->moments, value->f, 1.0);
+ break;
+
+ case CTSF_UMEDIAN:
+ case CTSF_UMODE:
+ case CTSF_UPTILE:
+ d_weight = e_weight = 1.0;
+ /* Fall through. */
case CTSF_MEDIAN:
case CTSF_MODE:
case CTSF_PTILE:
- if (var_is_value_missing (var, value))
+ if (!is_scale_missing)
{
s->ovalid += e_weight;
casewriter_write (s->writer, c);
}
break;
-
- case CTSF_RESPONSES:
- case CTSF_ROWPCT_RESPONSES:
- case CTSF_COLPCT_RESPONSES:
- case CTSF_TABLEPCT_RESPONSES:
- case CTSF_SUBTABLEPCT_RESPONSES:
- case CTSF_LAYERPCT_RESPONSES:
- case CTSF_LAYERROWPCT_RESPONSES:
- case CTSF_LAYERCOLPCT_RESPONSES:
- case CTSF_ROWPCT_RESPONSES_COUNT:
- case CTSF_COLPCT_RESPONSES_COUNT:
- case CTSF_TABLEPCT_RESPONSES_COUNT:
- case CTSF_SUBTABLEPCT_RESPONSES_COUNT:
- case CTSF_LAYERPCT_RESPONSES_COUNT:
- case CTSF_LAYERROWPCT_RESPONSES_COUNT:
- case CTSF_LAYERCOLPCT_RESPONSES_COUNT:
- case CTSF_ROWPCT_COUNT_RESPONSES:
- case CTSF_COLPCT_COUNT_RESPONSES:
- case CTSF_TABLEPCT_COUNT_RESPONSES:
- case CTSF_SUBTABLEPCT_COUNT_RESPONSES:
- case CTSF_LAYERPCT_COUNT_RESPONSES:
- case CTSF_LAYERROWPCT_COUNT_RESPONSES:
- case CTSF_LAYERCOLPCT_COUNT_RESPONSES:
- NOT_REACHED ();
}
}
case CTSF_MEDIAN:
case CTSF_PTILE:
case CTSF_MODE:
- case CTSF_RESPONSES:
+ 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:
NOT_REACHED ();
case CTSF_COLPCT_COUNT:
- case CTSF_COLPCT_COUNT_RESPONSES:
- case CTSF_COLPCT_RESPONSES:
- case CTSF_COLPCT_RESPONSES_COUNT:
case CTSF_COLPCT_SUM:
case CTSF_COLPCT_TOTALN:
case CTSF_COLPCT_VALIDN:
+ case CTSF_UCOLPCT_COUNT:
+ case CTSF_UCOLPCT_SUM:
+ case CTSF_UCOLPCT_TOTALN:
+ case CTSF_UCOLPCT_VALIDN:
return CTDT_COL;
case CTSF_LAYERCOLPCT_COUNT:
- case CTSF_LAYERCOLPCT_COUNT_RESPONSES:
- case CTSF_LAYERCOLPCT_RESPONSES:
- case CTSF_LAYERCOLPCT_RESPONSES_COUNT:
case CTSF_LAYERCOLPCT_SUM:
case CTSF_LAYERCOLPCT_TOTALN:
case CTSF_LAYERCOLPCT_VALIDN:
+ case CTSF_ULAYERCOLPCT_COUNT:
+ case CTSF_ULAYERCOLPCT_SUM:
+ case CTSF_ULAYERCOLPCT_TOTALN:
+ case CTSF_ULAYERCOLPCT_VALIDN:
return CTDT_LAYERCOL;
case CTSF_LAYERPCT_COUNT:
- case CTSF_LAYERPCT_COUNT_RESPONSES:
- case CTSF_LAYERPCT_RESPONSES:
- case CTSF_LAYERPCT_RESPONSES_COUNT:
case CTSF_LAYERPCT_SUM:
case CTSF_LAYERPCT_TOTALN:
case CTSF_LAYERPCT_VALIDN:
+ case CTSF_ULAYERPCT_COUNT:
+ case CTSF_ULAYERPCT_SUM:
+ case CTSF_ULAYERPCT_TOTALN:
+ case CTSF_ULAYERPCT_VALIDN:
return CTDT_LAYER;
case CTSF_LAYERROWPCT_COUNT:
- case CTSF_LAYERROWPCT_COUNT_RESPONSES:
- case CTSF_LAYERROWPCT_RESPONSES:
- case CTSF_LAYERROWPCT_RESPONSES_COUNT:
case CTSF_LAYERROWPCT_SUM:
case CTSF_LAYERROWPCT_TOTALN:
case CTSF_LAYERROWPCT_VALIDN:
+ case CTSF_ULAYERROWPCT_COUNT:
+ case CTSF_ULAYERROWPCT_SUM:
+ case CTSF_ULAYERROWPCT_TOTALN:
+ case CTSF_ULAYERROWPCT_VALIDN:
return CTDT_LAYERROW;
case CTSF_ROWPCT_COUNT:
- case CTSF_ROWPCT_COUNT_RESPONSES:
- case CTSF_ROWPCT_RESPONSES:
- case CTSF_ROWPCT_RESPONSES_COUNT:
case CTSF_ROWPCT_SUM:
case CTSF_ROWPCT_TOTALN:
case CTSF_ROWPCT_VALIDN:
+ case CTSF_UROWPCT_COUNT:
+ case CTSF_UROWPCT_SUM:
+ case CTSF_UROWPCT_TOTALN:
+ case CTSF_UROWPCT_VALIDN:
return CTDT_ROW;
case CTSF_SUBTABLEPCT_COUNT:
- case CTSF_SUBTABLEPCT_COUNT_RESPONSES:
- case CTSF_SUBTABLEPCT_RESPONSES:
- case CTSF_SUBTABLEPCT_RESPONSES_COUNT:
case CTSF_SUBTABLEPCT_SUM:
case CTSF_SUBTABLEPCT_TOTALN:
case CTSF_SUBTABLEPCT_VALIDN:
+ case CTSF_USUBTABLEPCT_COUNT:
+ case CTSF_USUBTABLEPCT_SUM:
+ case CTSF_USUBTABLEPCT_TOTALN:
+ case CTSF_USUBTABLEPCT_VALIDN:
return CTDT_SUBTABLE;
case CTSF_TABLEPCT_COUNT:
- case CTSF_TABLEPCT_COUNT_RESPONSES:
- case CTSF_TABLEPCT_RESPONSES:
- case CTSF_TABLEPCT_RESPONSES_COUNT:
case CTSF_TABLEPCT_SUM:
case CTSF_TABLEPCT_TOTALN:
case CTSF_TABLEPCT_VALIDN:
+ case CTSF_UTABLEPCT_COUNT:
+ case CTSF_UTABLEPCT_SUM:
+ case CTSF_UTABLEPCT_TOTALN:
+ case CTSF_UTABLEPCT_VALIDN:
return CTDT_TABLE;
}
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_COUNT:
case CTSF_ECOUNT:
- return s->valid;
+ case CTSF_UCOUNT:
+ return s->count;
case CTSF_ROWPCT_COUNT:
case CTSF_COLPCT_COUNT:
case CTSF_LAYERCOLPCT_COUNT:
{
enum ctables_domain_type d = ctables_function_domain (ss->function);
- return (cell->domains[d]->e_valid
- ? s->valid / cell->domains[d]->e_valid * 100
+ return (cell->domains[d]->e_count
+ ? s->count / cell->domains[d]->e_count * 100
+ : SYSMIS);
+ }
+
+ case CTSF_UROWPCT_COUNT:
+ case CTSF_UCOLPCT_COUNT:
+ case CTSF_UTABLEPCT_COUNT:
+ case CTSF_USUBTABLEPCT_COUNT:
+ case CTSF_ULAYERPCT_COUNT:
+ case CTSF_ULAYERROWPCT_COUNT:
+ case CTSF_ULAYERCOLPCT_COUNT:
+ {
+ enum ctables_domain_type d = ctables_function_domain (ss->function);
+ return (cell->domains[d]->u_count
+ ? s->count / cell->domains[d]->u_count * 100
: SYSMIS);
}
case CTSF_LAYERPCT_VALIDN:
case CTSF_LAYERROWPCT_VALIDN:
case CTSF_LAYERCOLPCT_VALIDN:
+ {
+ enum ctables_domain_type d = ctables_function_domain (ss->function);
+ return (cell->domains[d]->e_valid
+ ? s->count / cell->domains[d]->e_valid * 100
+ : SYSMIS);
+ }
+
+ case CTSF_UROWPCT_VALIDN:
+ case CTSF_UCOLPCT_VALIDN:
+ case CTSF_UTABLEPCT_VALIDN:
+ case CTSF_USUBTABLEPCT_VALIDN:
+ case CTSF_ULAYERPCT_VALIDN:
+ case CTSF_ULAYERROWPCT_VALIDN:
+ case CTSF_ULAYERCOLPCT_VALIDN:
+ {
+ enum ctables_domain_type d = ctables_function_domain (ss->function);
+ return (cell->domains[d]->u_valid
+ ? s->count / cell->domains[d]->u_valid * 100
+ : SYSMIS);
+ }
+
case CTSF_ROWPCT_TOTALN:
case CTSF_COLPCT_TOTALN:
case CTSF_TABLEPCT_TOTALN:
case CTSF_LAYERPCT_TOTALN:
case CTSF_LAYERROWPCT_TOTALN:
case CTSF_LAYERCOLPCT_TOTALN:
- NOT_REACHED ();
+ {
+ enum ctables_domain_type d = ctables_function_domain (ss->function);
+ return (cell->domains[d]->e_total
+ ? s->count / cell->domains[d]->e_total * 100
+ : SYSMIS);
+ }
- case CTSF_MISSING:
- return s->missing;
+ case CTSF_UROWPCT_TOTALN:
+ case CTSF_UCOLPCT_TOTALN:
+ case CTSF_UTABLEPCT_TOTALN:
+ case CTSF_USUBTABLEPCT_TOTALN:
+ case CTSF_ULAYERPCT_TOTALN:
+ case CTSF_ULAYERROWPCT_TOTALN:
+ case CTSF_ULAYERCOLPCT_TOTALN:
+ {
+ enum ctables_domain_type d = ctables_function_domain (ss->function);
+ return (cell->domains[d]->u_total
+ ? s->count / cell->domains[d]->u_total * 100
+ : SYSMIS);
+ }
+ case CTSF_MISSING:
+ case CTSF_UMISSING:
case CSTF_TOTALN:
case CTSF_ETOTALN:
- return s->valid + s->missing;
-
+ case CSTF_UTOTALN:
case CTSF_VALIDN:
+ case CTSF_UVALIDN:
case CTSF_EVALIDN:
- return s->valid;
+ return s->count;
case CTSF_MAXIMUM:
return s->max;
return s->max != SYSMIS && s->min != SYSMIS ? s->max - s->min : SYSMIS;
case CTSF_MEAN:
+ case CTSF_UMEAN:
{
double mean;
moments1_calculate (s->moments, NULL, &mean, NULL, NULL, NULL);
}
case CTSF_SEMEAN:
+ case CTSF_USEMEAN:
{
double weight, variance;
moments1_calculate (s->moments, &weight, NULL, &variance, NULL, NULL);
}
case CTSF_STDDEV:
+ case CTSF_USTDDEV:
{
double variance;
moments1_calculate (s->moments, NULL, NULL, &variance, NULL, NULL);
}
case CTSF_SUM:
+ case CTSF_USUM:
{
double weight, mean;
moments1_calculate (s->moments, &weight, &mean, NULL, NULL, NULL);
}
case CTSF_VARIANCE:
+ case CTSF_UVARIANCE:
{
double variance;
moments1_calculate (s->moments, NULL, NULL, &variance, NULL, NULL);
case CTSF_LAYERPCT_SUM:
case CTSF_LAYERROWPCT_SUM:
case CTSF_LAYERCOLPCT_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].e_sum;
+ return denom != 0 ? num / denom * 100 : SYSMIS;
+ }
+ case CTSF_UROWPCT_SUM:
+ case CTSF_UCOLPCT_SUM:
+ case CTSF_UTABLEPCT_SUM:
+ case CTSF_USUBTABLEPCT_SUM:
+ case CTSF_ULAYERPCT_SUM:
+ case CTSF_ULAYERROWPCT_SUM:
+ case CTSF_ULAYERCOLPCT_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].u_sum;
+ return denom != 0 ? num / denom * 100 : SYSMIS;
+ }
case CTSF_MEDIAN:
case CTSF_PTILE:
+ case CTSF_UMEDIAN:
+ case CTSF_UPTILE:
if (s->writer)
{
struct casereader *reader = casewriter_make_reader (s->writer);
return s->ovalue;
case CTSF_MODE:
+ case CTSF_UMODE:
if (s->writer)
{
struct casereader *reader = casewriter_make_reader (s->writer);
statistic_destroy (&mode->parent.parent);
}
return s->ovalue;
-
- case CTSF_RESPONSES:
- case CTSF_ROWPCT_RESPONSES:
- case CTSF_COLPCT_RESPONSES:
- case CTSF_TABLEPCT_RESPONSES:
- case CTSF_SUBTABLEPCT_RESPONSES:
- case CTSF_LAYERPCT_RESPONSES:
- case CTSF_LAYERROWPCT_RESPONSES:
- case CTSF_LAYERCOLPCT_RESPONSES:
- case CTSF_ROWPCT_RESPONSES_COUNT:
- case CTSF_COLPCT_RESPONSES_COUNT:
- case CTSF_TABLEPCT_RESPONSES_COUNT:
- case CTSF_SUBTABLEPCT_RESPONSES_COUNT:
- case CTSF_LAYERPCT_RESPONSES_COUNT:
- case CTSF_LAYERROWPCT_RESPONSES_COUNT:
- case CTSF_LAYERCOLPCT_RESPONSES_COUNT:
- case CTSF_ROWPCT_COUNT_RESPONSES:
- case CTSF_COLPCT_COUNT_RESPONSES:
- case CTSF_TABLEPCT_COUNT_RESPONSES:
- case CTSF_SUBTABLEPCT_COUNT_RESPONSES:
- case CTSF_LAYERPCT_COUNT_RESPONSES:
- case CTSF_LAYERROWPCT_COUNT_RESPONSES:
- case CTSF_LAYERCOLPCT_COUNT_RESPONSES:
- NOT_REACHED ();
}
NOT_REACHED ();
case CCT_SUBTOTAL:
case CCT_TOTAL:
case CCT_POSTCOMPUTE:
+ case CCT_EXCLUDED_MISSING:
/* Must be equal. */
continue;
- case CCT_RANGE:
+ case CCT_NRANGE:
+ case CCT_SRANGE:
case CCT_MISSING:
case CCT_OTHERNM:
{
for (size_t i = 0; i < nest->n_domains[domain]; i++)
{
size_t v_idx = nest->domains[domain][i];
- hash = value_hash (&cell->axes[a].cvs[v_idx].value,
- var_get_width (nest->vars[v_idx]), hash);
+ struct ctables_cell_value *cv = &cell->axes[a].cvs[v_idx];
+ hash = hash_pointer (cv->category, hash);
+ if (cv->category->type != CCT_TOTAL
+ && cv->category->type != CCT_SUBTOTAL
+ && cv->category->type != CCT_POSTCOMPUTE)
+ hash = value_hash (&cv->value,
+ var_get_width (nest->vars[v_idx]), hash);
}
}
for (size_t i = 0; i < nest->n_domains[domain]; i++)
{
size_t v_idx = nest->domains[domain][i];
- if (!value_equal (&df->axes[a].cvs[v_idx].value,
- &cell->axes[a].cvs[v_idx].value,
- var_get_width (nest->vars[v_idx])))
+ struct ctables_cell_value *cv1 = &df->axes[a].cvs[v_idx];
+ struct ctables_cell_value *cv2 = &cell->axes[a].cvs[v_idx];
+ if (cv1->category != cv2->category
+ || (cv1->category->type != CCT_TOTAL
+ && cv1->category->type != CCT_SUBTOTAL
+ && cv1->category->type != CCT_POSTCOMPUTE
+ && !value_equal (&cv1->value, &cv2->value,
+ var_get_width (nest->vars[v_idx]))))
goto not_equal;
}
}
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;
}
+static struct substring
+rtrim_value (const union value *v, const struct variable *var)
+{
+ struct substring s = ss_buffer (CHAR_CAST (char *, v->s),
+ var_get_width (var));
+ ss_rtrim (&s, ss_cstr (" "));
+ return s;
+}
+
+static bool
+in_string_range (const union value *v, const struct variable *var,
+ const struct substring *srange)
+{
+ struct substring s = rtrim_value (v, var);
+ return ((!srange[0].string || ss_compare (s, srange[0]) >= 0)
+ && (!srange[1].string || ss_compare (s, srange[1]) <= 0));
+}
+
static const struct ctables_category *
ctables_categories_match (const struct ctables_categories *c,
const union value *v, const struct variable *var)
{
+ if (var_is_numeric (var) && v->f == SYSMIS)
+ return NULL;
+
const struct ctables_category *othernm = NULL;
for (size_t i = c->n_cats; i-- > 0; )
{
break;
case CCT_STRING:
- NOT_REACHED ();
+ if (ss_equals (cat->string, rtrim_value (v, var)))
+ return cat;
+ break;
+
+ case CCT_NRANGE:
+ if ((cat->nrange[0] == -DBL_MAX || v->f >= cat->nrange[0])
+ && (cat->nrange[1] == DBL_MAX || v->f <= cat->nrange[1]))
+ return cat;
+ break;
- case CCT_RANGE:
- if ((cat->range[0] == -DBL_MAX || v->f >= cat->range[0])
- && (cat->range[1] == DBL_MAX || v->f <= cat->range[1]))
+ case CCT_SRANGE:
+ if (in_string_range (v, var, cat->srange))
return cat;
break;
case CCT_FUNCTION:
return (cat->include_missing || !var_is_value_missing (var, v) ? cat
: NULL);
+
+ case CCT_EXCLUDED_MISSING:
+ break;
}
}
cell = xmalloc (sizeof *cell);
cell->hide = false;
cell->sv = sv;
- cell->contributes_to_domains = true;
+ cell->omit_domains = 0;
cell->postcompute = false;
+ //struct string name = DS_EMPTY_INITIALIZER;
for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
{
const struct ctables_nest *nest = s->nests[a];
cell->axes[a].cvs = (nest->n
- ? xnmalloc (nest->n, sizeof *cell->axes[a].cvs)
- : NULL);
+ ? xnmalloc (nest->n, sizeof *cell->axes[a].cvs)
+ : NULL);
for (size_t i = 0; i < nest->n; i++)
{
const struct ctables_category *cat = cats[a][i];
+ const struct variable *var = nest->vars[i];
+ const union value *value = case_data (c, var);
if (i != nest->scale_idx)
{
const struct ctables_category *subtotal = cat->subtotal;
if (cat->type == CCT_TOTAL
|| cat->type == CCT_SUBTOTAL
|| cat->type == CCT_POSTCOMPUTE)
- cell->contributes_to_domains = false;
+ {
+ /* XXX these should be more encompassing I think.*/
+
+ switch (a)
+ {
+ case PIVOT_AXIS_COLUMN:
+ cell->omit_domains |= ((1u << CTDT_TABLE) |
+ (1u << CTDT_LAYER) |
+ (1u << CTDT_LAYERCOL) |
+ (1u << CTDT_SUBTABLE) |
+ (1u << CTDT_COL));
+ break;
+ case PIVOT_AXIS_ROW:
+ cell->omit_domains |= ((1u << CTDT_TABLE) |
+ (1u << CTDT_LAYER) |
+ (1u << CTDT_LAYERROW) |
+ (1u << CTDT_SUBTABLE) |
+ (1u << CTDT_ROW));
+ break;
+ case PIVOT_AXIS_LAYER:
+ cell->omit_domains |= ((1u << CTDT_TABLE) |
+ (1u << CTDT_LAYER));
+ break;
+ }
+ }
if (cat->type == CCT_POSTCOMPUTE)
cell->postcompute = true;
}
cell->axes[a].cvs[i].category = cat;
- value_clone (&cell->axes[a].cvs[i].value, case_data (c, nest->vars[i]),
- var_get_width (nest->vars[i]));
+ value_clone (&cell->axes[a].cvs[i].value, value, var_get_width (var));
+
+#if 0
+ if (i != nest->scale_idx)
+ {
+ if (!ds_is_empty (&name))
+ ds_put_cstr (&name, ", ");
+ char *value_s = data_out (value, var_get_encoding (var),
+ var_get_print_format (var),
+ settings_get_fmt_settings ());
+ if (cat->type == CCT_TOTAL
+ || cat->type == CCT_SUBTOTAL
+ || cat->type == CCT_POSTCOMPUTE)
+ ds_put_format (&name, "%s=total", var_get_name (var));
+ else
+ ds_put_format (&name, "%s=%s", var_get_name (var),
+ value_s + strspn (value_s, " "));
+ free (value_s);
+ }
+#endif
}
}
+ //cell->name = ds_steal_cstr (&name);
const struct ctables_nest *ss = s->nests[s->table->summary_axis];
const struct ctables_summary_spec_set *specs = &ss->specs[cell->sv];
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],
+ bool is_missing, bool excluded_missing,
double d_weight, double e_weight)
{
struct ctables_cell *cell = ctables_cell_insert__ (s, c, cats);
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), d_weight, e_weight);
- if (cell->contributes_to_domains)
- {
- for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
- {
- cell->domains[dt]->d_valid += d_weight;
- cell->domains[dt]->e_valid += e_weight;
- }
- }
+ ctables_summary_add (&cell->summaries[i], &specs->specs[i],
+ specs->var, case_data (c, specs->var), specs->is_scale,
+ 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)))
+ {
+ struct ctables_domain *d = cell->domains[dt];
+ d->d_total += d_weight;
+ d->e_total += e_weight;
+ d->u_total += 1.0;
+ if (!excluded_missing)
+ {
+ d->d_count += d_weight;
+ d->e_count += e_weight;
+ d->u_count += 1.0;
+ }
+ if (!is_missing)
+ {
+ 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;
+ }
+ }
+ }
+ }
}
static void
recurse_totals (struct ctables_section *s, const struct ccase *c,
const struct ctables_category *cats[PIVOT_N_AXES][10],
+ bool is_missing, bool excluded_missing,
double d_weight, double e_weight,
enum pivot_axis_type start_axis, size_t start_nest)
{
{
const struct ctables_category *save = cats[a][i];
cats[a][i] = total;
- ctables_cell_add__ (s, c, cats, d_weight, e_weight);
- recurse_totals (s, c, cats, d_weight, e_weight, a, i + 1);
+ ctables_cell_add__ (s, c, cats, is_missing, excluded_missing,
+ d_weight, e_weight);
+ recurse_totals (s, c, cats, is_missing, excluded_missing,
+ d_weight, e_weight, a, i + 1);
cats[a][i] = save;
}
}
static void
recurse_subtotals (struct ctables_section *s, const struct ccase *c,
const struct ctables_category *cats[PIVOT_N_AXES][10],
+ bool is_missing, bool excluded_missing,
double d_weight, double e_weight,
enum pivot_axis_type start_axis, size_t start_nest)
{
if (save->subtotal)
{
cats[a][i] = save->subtotal;
- ctables_cell_add__ (s, c, cats, d_weight, e_weight);
- recurse_subtotals (s, c, cats, d_weight, e_weight, a, i + 1);
+ ctables_cell_add__ (s, c, cats, is_missing, excluded_missing,
+ d_weight, e_weight);
+ recurse_subtotals (s, c, cats, is_missing, excluded_missing,
+ d_weight, e_weight, a, i + 1);
cats[a][i] = save;
}
}
double d_weight, double e_weight)
{
const struct ctables_category *cats[PIVOT_N_AXES][10]; /* XXX */
+
+ /* Does at least one categorical variable have a missing value in an included
+ or excluded category? */
+ bool is_missing = false;
+
+ /* Does at least one categorical variable have a missing value in an excluded
+ category? */
+ bool excluded_missing = false;
+
for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
{
const struct ctables_nest *nest = s->nests[a];
const struct variable *var = nest->vars[i];
const union value *value = case_data (c, var);
- if (var_is_numeric (var) && value->f == SYSMIS)
- return;
+ bool var_missing = var_is_value_missing (var, value) != 0;
+ if (var_missing)
+ is_missing = true;
cats[a][i] = ctables_categories_match (
s->table->categories[var_get_dict_index (var)], value, var);
if (!cats[a][i])
- return;
+ {
+ if (!var_missing)
+ return;
+
+ static const struct ctables_category cct_excluded_missing = {
+ .type = CCT_EXCLUDED_MISSING,
+ .hide = true,
+ };
+ cats[a][i] = &cct_excluded_missing;
+ excluded_missing = true;
+ }
}
}
- for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
- {
- const struct ctables_nest *nest = s->nests[a];
- for (size_t i = 0; i < nest->n; i++)
- if (i != nest->scale_idx)
- {
- const struct variable *var = nest->vars[i];
- const union value *value = case_data (c, var);
- ctables_add_occurrence (var, value, &s->occurrences[a][i]);
- }
- }
+ if (!excluded_missing)
+ for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
+ {
+ const struct ctables_nest *nest = s->nests[a];
+ for (size_t i = 0; i < nest->n; i++)
+ if (i != nest->scale_idx)
+ {
+ const struct variable *var = nest->vars[i];
+ const union value *value = case_data (c, var);
+ ctables_add_occurrence (var, value, &s->occurrences[a][i]);
+ }
+ }
- ctables_cell_add__ (s, c, cats, d_weight, e_weight);
+ ctables_cell_add__ (s, c, cats, is_missing, excluded_missing,
+ d_weight, e_weight);
- recurse_totals (s, c, cats, d_weight, e_weight, 0, 0);
- recurse_subtotals (s, c, cats, d_weight, e_weight, 0, 0);
+ //if (!excluded_missing)
+ {
+ recurse_totals (s, c, cats, is_missing, excluded_missing,
+ d_weight, e_weight, 0, 0);
+ recurse_subtotals (s, c, cats, is_missing, excluded_missing,
+ d_weight, e_weight, 0, 0);
+ }
}
struct merge_item
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 *
-ctables_category_create_label (const struct ctables_category *cat,
- const struct variable *var,
- const union value *value)
+ctables_category_create_label__ (const struct ctables_category *cat,
+ const struct variable *var,
+ const union value *value)
{
return (cat->type == CCT_TOTAL || cat->type == CCT_SUBTOTAL
? pivot_value_new_user_text (cat->total_label, SIZE_MAX)
- : cat->type == CCT_POSTCOMPUTE && cat->pc->label
- ? pivot_value_new_user_text (cat->pc->label, SIZE_MAX)
: pivot_value_new_var_value (var, value));
}
+static struct pivot_value *
+ctables_postcompute_label (const struct ctables_categories *cats,
+ const struct ctables_category *cat,
+ const struct variable *var,
+ const union value *value)
+{
+ struct substring in = ss_cstr (cat->pc->label);
+ struct substring target = ss_cstr (")LABEL[");
+
+ struct string out = DS_EMPTY_INITIALIZER;
+ for (;;)
+ {
+ size_t chunk = ss_find_substring (in, target);
+ if (chunk == SIZE_MAX)
+ {
+ if (ds_is_empty (&out))
+ return pivot_value_new_user_text (in.string, in.length);
+ else
+ {
+ ds_put_substring (&out, in);
+ return pivot_value_new_user_text_nocopy (ds_steal_cstr (&out));
+ }
+ }
+
+ ds_put_substring (&out, ss_head (in, chunk));
+ ss_advance (&in, chunk + target.length);
+
+ struct substring idx_s;
+ if (!ss_get_until (&in, ']', &idx_s))
+ goto error;
+ char *tail;
+ long int idx = strtol (idx_s.string, &tail, 10);
+ if (idx < 1 || idx > cats->n_cats || tail != ss_end (idx_s))
+ goto error;
+
+ struct ctables_category *cat2 = &cats->cats[idx - 1];
+ struct pivot_value *label2
+ = ctables_category_create_label__ (cat2, var, value);
+ char *label2_s = pivot_value_to_string_defaults (label2);
+ ds_put_cstr (&out, label2_s);
+ free (label2_s);
+ pivot_value_destroy (label2);
+ }
+
+error:
+ ds_destroy (&out);
+ return pivot_value_new_user_text (cat->pc->label, SIZE_MAX);
+}
+
+static struct pivot_value *
+ctables_category_create_label (const struct ctables_categories *cats,
+ const struct ctables_category *cat,
+ const struct variable *var,
+ const union value *value)
+{
+ return (cat->type == CCT_POSTCOMPUTE && cat->pc->label
+ ? ctables_postcompute_label (cats, cat, var, value)
+ : ctables_category_create_label__ (cat, var, value));
+}
+
static struct ctables_value *
ctables_value_find__ (struct ctables_table *t, const union value *value,
int width, unsigned int hash)
const struct ctables_categories *cats;
enum pivot_axis_type pc_a;
size_t pc_a_idx;
+ size_t summary_idx;
};
static double ctables_pcexpr_evaluate (
const struct ctables_table *t = s->table;
const struct ctables_nest *specs_nest = s->nests[t->summary_axis];
const struct ctables_summary_spec_set *specs = &specs_nest->specs[tc->sv];
- size_t j = 0 /* XXX */;
- return ctables_summary_value (tc, &tc->summaries[j], &specs->specs[j]);
+ return ctables_summary_value (tc, &tc->summaries[ctx->summary_idx],
+ &specs->specs[ctx->summary_idx]);
}
static double
case CTPO_CONSTANT:
return e->number;
- case CTPO_CAT_RANGE:
+ case CTPO_CAT_NRANGE:
+ case CTPO_CAT_SRANGE:
{
struct ctables_cell_value cv = {
.category = ctables_find_category_for_postcompute (ctx->cats, e)
NOT_REACHED ();
}
+/* XXX what if there is a postcompute in more than one dimension?? */
+static const struct ctables_postcompute *
+ctables_cell_postcompute (const struct ctables_section *s,
+ const struct ctables_cell *cell,
+ enum pivot_axis_type *pc_a_p,
+ size_t *pc_a_idx_p)
+{
+ assert (cell->postcompute);
+ 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;
+ }
+
+ 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
ctables_cell_calculate_postcompute (const struct ctables_section *s,
- const struct ctables_cell *cell)
+ const struct ctables_cell *cell,
+ const struct ctables_summary_spec *ss,
+ struct fmt_spec *format,
+ bool *is_ctables_format,
+ size_t summary_idx)
{
enum pivot_axis_type pc_a;
size_t pc_a_idx;
- const struct ctables_postcompute *pc;
- for (pc_a = 0; ; pc_a++)
+ const struct ctables_postcompute *pc = ctables_cell_postcompute (
+ s, cell, &pc_a, &pc_a_idx);
+ if (!pc)
+ return SYSMIS;
+
+ if (pc->specs)
{
- assert (pc_a < PIVOT_N_AXES);
- for (pc_a_idx = 0; pc_a_idx < s->nests[pc_a]->n; pc_a_idx++)
+ for (size_t i = 0; i < pc->specs->n; i++)
{
- const struct ctables_cell_value *cv = &cell->axes[pc_a].cvs[pc_a_idx];
- if (cv->category->type == CCT_POSTCOMPUTE)
+ const struct ctables_summary_spec *ss2 = &pc->specs->specs[i];
+ if (ss->function == ss2->function
+ && ss->percentile == ss2->percentile)
{
- pc = cv->category->pc;
- goto found;
+ *format = ss2->format;
+ *is_ctables_format = ss2->is_ctables_format;
+ break;
}
}
}
-found: ;
const struct variable *var = s->nests[pc_a]->vars[pc_a_idx];
const struct ctables_categories *cats = s->table->categories[
.cats = cats,
.pc_a = pc_a,
.pc_a_idx = pc_a_idx,
+ .summary_idx = summary_idx,
};
return ctables_pcexpr_evaluate (&ctx, pc->expr);
}
pivot_table_set_caption (
pt, pivot_value_new_user_text (t->caption, SIZE_MAX));
if (t->corner)
- pivot_table_set_caption (
+ pivot_table_set_corner_text (
pt, pivot_value_new_user_text (t->corner, SIZE_MAX));
bool summary_dimension = (t->summary_axis != t->slabels_axis
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;
const struct ctables_category *cat = ctables_categories_match (c, &value->value, var);
assert (cat != NULL);
pivot_category_create_leaf (d->root, ctables_category_create_label (
- cat, t->clabels_example, &value->value));
+ c, cat, t->clabels_example,
+ &value->value));
}
}
struct ctables_cell_sort_aux aux = { .nest = nest, .a = a };
sort (sorted, n_sorted, sizeof *sorted, ctables_cell_compare_3way, &aux);
+#if 0
+ for (size_t j = 0; j < n_sorted; j++)
+ {
+ printf ("%s (%s): %f/%f = %.1f%%\n", sorted[j]->name, sorted[j]->contributes_to_domains ? "y" : "n", sorted[j]->summaries[0].count, sorted[j]->domains[CTDT_COL]->e_count, sorted[j]->summaries[0].count / sorted[j]->domains[CTDT_COL]->e_count * 100.0);
+ }
+ printf ("\n");
+#endif
+
struct ctables_level
{
enum ctables_level_type
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;
}
else if (level->type == CTL_CATEGORY)
{
const struct ctables_cell_value *cv = &cell->axes[a].cvs[level->var_idx];
- label = ctables_category_create_label (cv->category,
- var, &cv->value);
+ label = ctables_category_create_label (
+ t->categories[var_get_dict_index (var)],
+ cv->category, var, &cv->value);
}
else
NOT_REACHED ();
const struct ctables_summary_spec *ss = &specs->specs[j];
+ struct fmt_spec format = specs->specs[j].format;
+ bool is_ctables_format = ss->is_ctables_format;
double d = (cell->postcompute
- ? ctables_cell_calculate_postcompute (s, cell)
- : ctables_summary_value (cell, &cell->summaries[j], ss));
+ ? ctables_cell_calculate_postcompute (
+ s, cell, ss, &format, &is_ctables_format, j)
+ : ctables_summary_value (cell, &cell->summaries[j],
+ ss));
+
struct pivot_value *value;
if (ct->hide_threshold != 0
&& d < ct->hide_threshold
- && (cell->postcompute
- ? false /* XXX */
- : ctables_summary_function_is_count (ss->function)))
+ && ctables_summary_function_is_count (ss->function))
{
value = pivot_value_new_user_text_nocopy (
xasprintf ("<%d", ct->hide_threshold));
value = pivot_value_new_user_text (ct->zero, SIZE_MAX);
else if (d == SYSMIS && ct->missing)
value = pivot_value_new_user_text (ct->missing, SIZE_MAX);
+ else if (is_ctables_format)
+ {
+ char *s = data_out_stretchy (&(union value) { .f = d },
+ "UTF-8", &format,
+ &ct->ctables_formats, NULL);
+ value = pivot_value_new_user_text_nocopy (s);
+ }
else
{
value = pivot_value_new_number (d);
- value->numeric.format = specs->specs[j].format;
+ value->numeric.format = format;
}
pivot_table_put (pt, dindexes, n_dindexes, value);
}
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->n = 1;
enum ctables_summary_function function
- = specs->var ? CTSF_MEAN : CTSF_COUNT;
- struct ctables_var var = { .is_mrset = false, .var = specs->var };
+ = specs->is_scale ? CTSF_MEAN : CTSF_COUNT;
*specs->specs = (struct ctables_summary_spec) {
.function = function,
- .format = ctables_summary_default_format (function, &var),
- .label = ctables_summary_default_label (function, 0),
+ .format = ctables_summary_default_format (function, specs->var),
};
if (!specs->var)
specs->var = nest->vars[0];
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;
- 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));
}
break;
case CCT_STRING:
- abort (); /* XXX */
+ {
+ int width = var_get_width (var);
+ union value value;
+ value_init (&value, width);
+ value_copy_buf_rpad (&value, width,
+ CHAR_CAST (uint8_t *, c->string.string),
+ c->string.length, ' ');
+ ctables_add_occurrence (var, &value, occurrences);
+ value_destroy (&value, width);
+ }
+ break;
- case CCT_RANGE:
+ case CCT_NRANGE:
assert (var_is_numeric (var));
for (const struct val_lab *vl = val_labs_first (val_labs); vl;
vl = val_labs_next (val_labs, vl))
- if (vl->value.f >= c->range[0] && vl->value.f <= c->range[1])
+ if (vl->value.f >= c->nrange[0] && vl->value.f <= c->nrange[1])
+ ctables_add_occurrence (var, &vl->value, occurrences);
+ break;
+
+ case CCT_SRANGE:
+ assert (var_is_alpha (var));
+ for (const struct val_lab *vl = val_labs_first (val_labs); vl;
+ vl = val_labs_next (val_labs, vl))
+ if (in_string_range (&vl->value, var, c->srange))
ctables_add_occurrence (var, &vl->value, occurrences);
break;
if (c->include_missing || !var_is_value_missing (var, &vl->value))
ctables_add_occurrence (var, &vl->value, occurrences);
break;
+
+ case CCT_EXCLUDED_MISSING:
+ break;
}
}
}
\f
/* Postcomputes. */
-typedef struct ctables_pcexpr *parse_recursively_func (struct lexer *);
+typedef struct ctables_pcexpr *parse_recursively_func (struct lexer *,
+ struct dictionary *);
static void
ctables_pcexpr_destroy (struct ctables_pcexpr *e)
switch (e->op)
{
case CTPO_CAT_STRING:
- free (e->string);
+ ss_dealloc (&e->string);
+ break;
+
+ case CTPO_CAT_SRANGE:
+ for (size_t i = 0; i < 2; i++)
+ ss_dealloc (&e->srange[i]);
break;
case CTPO_ADD:
case CTPO_CONSTANT:
case CTPO_CAT_NUMBER:
- case CTPO_CAT_RANGE:
+ case CTPO_CAT_NRANGE:
case CTPO_CAT_MISSING:
case CTPO_CAT_OTHERNM:
case CTPO_CAT_SUBTOTAL:
};
static const struct operator *
-match_operator (struct lexer *lexer, const struct operator ops[], size_t n_ops)
+ctable_pcexpr_match_operator (struct lexer *lexer,
+ const struct operator ops[], size_t n_ops)
{
for (const struct operator *op = ops; op < ops + n_ops; op++)
if (lex_token (lexer) == op->token)
}
static struct ctables_pcexpr *
-parse_binary_operators__ (struct lexer *lexer,
- const struct operator ops[], size_t n_ops,
- parse_recursively_func *parse_next_level,
- const char *chain_warning,
- struct ctables_pcexpr *lhs)
+ctable_pcexpr_parse_binary_operators__ (
+ struct lexer *lexer, struct dictionary *dict,
+ const struct operator ops[], size_t n_ops,
+ parse_recursively_func *parse_next_level,
+ const char *chain_warning, struct ctables_pcexpr *lhs)
{
for (int op_count = 0; ; op_count++)
{
- const struct operator *op = match_operator (lexer, ops, n_ops);
+ const struct operator *op
+ = ctable_pcexpr_match_operator (lexer, ops, n_ops);
if (!op)
{
if (op_count > 1 && chain_warning)
return lhs;
}
- struct ctables_pcexpr *rhs = parse_next_level (lexer);
+ struct ctables_pcexpr *rhs = parse_next_level (lexer, dict);
if (!rhs)
{
ctables_pcexpr_destroy (lhs);
}
static struct ctables_pcexpr *
-parse_binary_operators (struct lexer *lexer,
- const struct operator ops[], size_t n_ops,
- parse_recursively_func *parse_next_level,
- const char *chain_warning)
+ctable_pcexpr_parse_binary_operators (struct lexer *lexer,
+ struct dictionary *dict,
+ const struct operator ops[], size_t n_ops,
+ parse_recursively_func *parse_next_level,
+ const char *chain_warning)
{
- struct ctables_pcexpr *lhs = parse_next_level (lexer);
+ struct ctables_pcexpr *lhs = parse_next_level (lexer, dict);
if (!lhs)
return NULL;
- return parse_binary_operators__ (lexer, ops, n_ops, parse_next_level,
- chain_warning, lhs);
+ return ctable_pcexpr_parse_binary_operators__ (lexer, dict, ops, n_ops,
+ parse_next_level,
+ chain_warning, lhs);
}
-static struct ctables_pcexpr *parse_add (struct lexer *);
+static struct ctables_pcexpr *ctable_pcexpr_parse_add (struct lexer *,
+ struct dictionary *);
static struct ctables_pcexpr
-ctpo_cat_range (double low, double high)
+ctpo_cat_nrange (double low, double high)
{
return (struct ctables_pcexpr) {
- .op = CTPO_CAT_RANGE,
- .range = { low, high },
+ .op = CTPO_CAT_NRANGE,
+ .nrange = { low, high },
};
}
static struct ctables_pcexpr *
-parse_primary (struct lexer *lexer)
+ctable_pcexpr_parse_primary (struct lexer *lexer, struct dictionary *dict)
{
int start_ofs = lex_ofs (lexer);
struct ctables_pcexpr e;
{
if (!lex_force_match_id (lexer, "THRU") || lex_force_num (lexer))
return false;
- e = ctpo_cat_range (-DBL_MAX, lex_number (lexer));
+ e = ctpo_cat_nrange (-DBL_MAX, lex_number (lexer));
lex_get (lexer);
}
else if (lex_is_number (lexer))
if (lex_match_id (lexer, "THRU"))
{
if (lex_match_id (lexer, "HI"))
- e = ctpo_cat_range (number, DBL_MAX);
+ e = ctpo_cat_nrange (number, DBL_MAX);
else
{
if (!lex_force_num (lexer))
return false;
- e = ctpo_cat_range (number, lex_number (lexer));
+ e = ctpo_cat_nrange (number, lex_number (lexer));
lex_get (lexer);
}
}
}
else if (lex_is_string (lexer))
{
- e = (struct ctables_pcexpr) {
- .op = CTPO_CAT_STRING,
- .string = ss_xstrdup (lex_tokss (lexer)),
- };
+ struct substring s = recode_substring_pool (
+ dict_get_encoding (dict), "UTF-8", lex_tokss (lexer), NULL);
+ ss_rtrim (&s, ss_cstr (" "));
+
+ e = (struct ctables_pcexpr) { .op = CTPO_CAT_STRING, .string = s };
lex_get (lexer);
}
else
if (!lex_force_match (lexer, T_RBRACK))
{
if (e.op == CTPO_CAT_STRING)
- free (e.string);
+ ss_dealloc (&e.string);
return NULL;
}
}
else if (lex_match (lexer, T_LPAREN))
{
- struct ctables_pcexpr *ep = parse_add (lexer);
+ struct ctables_pcexpr *ep = ctable_pcexpr_parse_add (lexer, dict);
if (!ep)
return NULL;
if (!lex_force_match (lexer, T_RPAREN))
}
static struct ctables_pcexpr *
-parse_exp (struct lexer *lexer)
+ctable_pcexpr_parse_exp (struct lexer *lexer, struct dictionary *dict)
{
static const struct operator op = { T_EXP, CTPO_POW };
"To disable this warning, insert parentheses.");
if (lex_token (lexer) != T_NEG_NUM || lex_next_token (lexer, 1) != T_EXP)
- return parse_binary_operators (lexer, &op, 1,
- parse_primary, chain_warning);
+ return ctable_pcexpr_parse_binary_operators (lexer, dict, &op, 1,
+ ctable_pcexpr_parse_primary,
+ chain_warning);
/* Special case for situations like "-5**6", which must be parsed as
-(5**6). */
};
lex_get (lexer);
- struct ctables_pcexpr *node = parse_binary_operators__ (
- lexer, &op, 1, parse_primary, chain_warning, lhs);
+ struct ctables_pcexpr *node = ctable_pcexpr_parse_binary_operators__ (
+ lexer, dict, &op, 1,
+ ctable_pcexpr_parse_primary, chain_warning, lhs);
if (!node)
return NULL;
/* Parses the unary minus level. */
static struct ctables_pcexpr *
-parse_neg (struct lexer *lexer)
+ctable_pcexpr_parse_neg (struct lexer *lexer, struct dictionary *dict)
{
int start_ofs = lex_ofs (lexer);
if (!lex_match (lexer, T_DASH))
- return parse_exp (lexer);
+ return ctable_pcexpr_parse_exp (lexer, dict);
- struct ctables_pcexpr *inner = parse_neg (lexer);
+ struct ctables_pcexpr *inner = ctable_pcexpr_parse_neg (lexer, dict);
if (!inner)
return NULL;
/* Parses the multiplication and division level. */
static struct ctables_pcexpr *
-parse_mul (struct lexer *lexer)
+ctable_pcexpr_parse_mul (struct lexer *lexer, struct dictionary *dict)
{
static const struct operator ops[] =
{
{ T_SLASH, CTPO_DIV },
};
- return parse_binary_operators (lexer, ops, sizeof ops / sizeof *ops,
- parse_neg, NULL);
+ return ctable_pcexpr_parse_binary_operators (lexer, dict, ops,
+ sizeof ops / sizeof *ops,
+ ctable_pcexpr_parse_neg, NULL);
}
/* Parses the addition and subtraction level. */
static struct ctables_pcexpr *
-parse_add (struct lexer *lexer)
+ctable_pcexpr_parse_add (struct lexer *lexer, struct dictionary *dict)
{
static const struct operator ops[] =
{
{ T_NEG_NUM, CTPO_ADD },
};
- return parse_binary_operators (lexer, ops, sizeof ops / sizeof *ops,
- parse_mul, NULL);
+ return ctable_pcexpr_parse_binary_operators (lexer, dict,
+ ops, sizeof ops / sizeof *ops,
+ ctable_pcexpr_parse_mul, NULL);
}
static struct ctables_postcompute *
}
static bool
-ctables_parse_pcompute (struct lexer *lexer, struct ctables *ct)
+ctables_parse_pcompute (struct lexer *lexer, struct dictionary *dict,
+ struct ctables *ct)
{
int pcompute_start = lex_ofs (lexer) - 1;
}
int expr_start = lex_ofs (lexer);
- struct ctables_pcexpr *expr = parse_add (lexer);
+ struct ctables_pcexpr *expr = ctable_pcexpr_parse_add (lexer, dict);
int expr_end = lex_ofs (lexer) - 1;
if (!expr || !lex_force_match (lexer, T_RPAREN))
{
/* Parse format. */
struct fmt_spec format;
- if (!parse_format_specifier (lexer, &format)
- || !fmt_check_output (&format)
- || !fmt_check_type_compat (&format, VAL_NUMERIC))
+ bool is_ctables_format;
+ if (!parse_ctables_format_specifier (lexer, &format, &is_ctables_format))
goto error;
if (sss->n >= sss->allocated)
.function = function,
.percentile = percentile,
.format = format,
+ .is_ctables_format = is_ctables_format,
};
}
return true;
return false;
}
+static void
+put_strftime (struct string *out, time_t now, const char *format)
+{
+ const struct tm *tm = localtime (&now);
+ char value[128];
+ strftime (value, sizeof value, format, tm);
+ ds_put_cstr (out, value);
+}
+
+static bool
+skip_prefix (struct substring *s, struct substring prefix)
+{
+ if (ss_starts_with (*s, prefix))
+ {
+ ss_advance (s, prefix.length);
+ return true;
+ }
+ else
+ return false;
+}
+
+static void
+put_table_expression (struct string *out, struct lexer *lexer,
+ struct dictionary *dict, int expr_start, int expr_end)
+{
+ size_t nest = 0;
+ for (int ofs = expr_start; ofs < expr_end; ofs++)
+ {
+ const struct token *t = lex_ofs_token (lexer, ofs);
+ if (t->type == T_LBRACK)
+ nest++;
+ else if (t->type == T_RBRACK && nest > 0)
+ nest--;
+ else if (nest > 0)
+ {
+ /* Nothing. */
+ }
+ else if (t->type == T_ID)
+ {
+ const struct variable *var
+ = dict_lookup_var (dict, t->string.string);
+ const char *label = var ? var_get_label (var) : NULL;
+ ds_put_cstr (out, label ? label : t->string.string);
+ }
+ else
+ {
+ if (ofs != expr_start && t->type != T_RPAREN && ds_last (out) != ' ')
+ ds_put_byte (out, ' ');
+
+ char *repr = lex_ofs_representation (lexer, ofs, ofs);
+ ds_put_cstr (out, repr);
+ free (repr);
+
+ if (ofs + 1 != expr_end && t->type != T_LPAREN)
+ ds_put_byte (out, ' ');
+ }
+ }
+}
+
+static void
+put_title_text (struct string *out, struct substring in, time_t now,
+ struct lexer *lexer, struct dictionary *dict,
+ int expr_start, int expr_end)
+{
+ for (;;)
+ {
+ size_t chunk = ss_find_byte (in, ')');
+ ds_put_substring (out, ss_head (in, chunk));
+ ss_advance (&in, chunk);
+ if (ss_is_empty (in))
+ return;
+
+ if (skip_prefix (&in, ss_cstr (")DATE")))
+ put_strftime (out, now, "%x");
+ else if (skip_prefix (&in, ss_cstr (")TIME")))
+ put_strftime (out, now, "%X");
+ else if (skip_prefix (&in, ss_cstr (")TABLE")))
+ put_table_expression (out, lexer, dict, expr_start, expr_end);
+ else
+ {
+ ds_put_byte (out, ')');
+ ss_advance (&in, 1);
+ }
+ }
+}
+
int
cmd_ctables (struct lexer *lexer, struct dataset *ds)
{
for (size_t i = 0; i < n_vars; i++)
vlabels[i] = (enum ctables_vlabel) tvars;
+ struct pivot_table_look *look = pivot_table_look_unshare (
+ pivot_table_look_ref (pivot_table_look_get_default ()));
+ look->omit_empty = false;
+
struct ctables *ct = xmalloc (sizeof *ct);
*ct = (struct ctables) {
.dict = dataset_dict (ds),
- .look = pivot_table_look_unshare (pivot_table_look_ref (
- pivot_table_look_get_default ())),
+ .look = look,
+ .ctables_formats = FMT_SETTINGS_INIT,
.vlabels = vlabels,
.postcomputes = HMAP_INITIALIZER (ct->postcomputes),
};
- ct->look->omit_empty = false;
+
+ time_t now = time (NULL);
+
+ struct ctf
+ {
+ enum fmt_type type;
+ const char *dot_string;
+ const char *comma_string;
+ };
+ static const struct ctf ctfs[4] = {
+ { CTEF_NEGPAREN, "(,,,)", "(...)" },
+ { CTEF_NEQUAL, "-,N=,,", "-.N=.." },
+ { CTEF_PAREN, "-,(,),", "-.(.)." },
+ { CTEF_PCTPAREN, "-,(,%),", "-.(.%)." },
+ };
+ bool is_dot = settings_get_fmt_settings ()->decimal == '.';
+ for (size_t i = 0; i < 4; i++)
+ {
+ const char *s = is_dot ? ctfs[i].dot_string : ctfs[i].comma_string;
+ fmt_settings_set_cc (&ct->ctables_formats, ctfs[i].type,
+ fmt_number_style_from_string (s));
+ }
if (!lex_force_match (lexer, T_SLASH))
goto error;
}
else if (lex_match_id (lexer, "PCOMPUTE"))
{
- if (!ctables_parse_pcompute (lexer, ct))
+ if (!ctables_parse_pcompute (lexer, dataset_dict (ds), ct))
goto error;
}
else if (lex_match_id (lexer, "PPROPERTIES"))
ct->tables[ct->n_tables++] = t;
lex_match (lexer, T_EQUALS);
+ int expr_start = lex_ofs (lexer);
if (!ctables_axis_parse (lexer, dataset_dict (ds), ct, t, PIVOT_AXIS_ROW))
goto error;
if (lex_match (lexer, T_BY))
goto error;
}
}
+ int expr_end = lex_ofs (lexer);
if (!t->axes[PIVOT_AXIS_ROW] && !t->axes[PIVOT_AXIS_COLUMN]
&& !t->axes[PIVOT_AXIS_LAYER])
{
if (!ds_is_empty (&s))
ds_put_byte (&s, ' ');
- ds_put_substring (&s, lex_tokss (lexer));
+ put_title_text (&s, lex_tokss (lexer), now,
+ lexer, dataset_dict (ds),
+ expr_start, expr_end);
lex_get (lexer);
}
free (*textp);