1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006, 2008, 2009, 2010, 2011, 2012, 2014 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "language/stats/aggregate.h"
23 #include "data/any-writer.h"
24 #include "data/case.h"
25 #include "data/casegrouper.h"
26 #include "data/casereader.h"
27 #include "data/casewriter.h"
28 #include "data/dataset.h"
29 #include "data/dictionary.h"
30 #include "data/file-handle-def.h"
31 #include "data/format.h"
32 #include "data/settings.h"
33 #include "data/subcase.h"
34 #include "data/sys-file-writer.h"
35 #include "data/variable.h"
36 #include "language/command.h"
37 #include "language/data-io/file-handle.h"
38 #include "language/lexer/lexer.h"
39 #include "language/lexer/variable-parser.h"
40 #include "language/stats/sort-criteria.h"
41 #include "libpspp/assertion.h"
42 #include "libpspp/i18n.h"
43 #include "libpspp/message.h"
44 #include "libpspp/misc.h"
45 #include "libpspp/pool.h"
46 #include "libpspp/str.h"
47 #include "math/moments.h"
48 #include "math/percentiles.h"
49 #include "math/sort.h"
50 #include "math/statistic.h"
52 #include "gl/c-strcase.h"
53 #include "gl/minmax.h"
54 #include "gl/xalloc.h"
57 #define _(msgid) gettext (msgid)
58 #define N_(msgid) msgid
60 /* Argument for AGGREGATE function. */
63 double f; /* Numeric. */
64 char *c; /* Short or long string. */
67 /* Specifies how to make an aggregate variable. */
70 /* Collected during parsing. */
71 const struct variable *src; /* Source variable. */
72 struct variable *dest; /* Target variable. */
73 int function; /* Function. */
74 enum mv_class exclude; /* Classes of missing values to exclude. */
75 union agr_argument arg[2]; /* Arguments. */
77 /* Accumulated during AGGREGATE execution. */
82 struct moments1 *moments;
85 struct variable *subject;
86 struct variable *weight;
87 struct casewriter *writer;
91 /* Attributes of aggregation functions. */
92 const struct agr_func agr_func_tab[] =
94 {"SUM", N_("Sum of values"), AGR_SV_YES, 0, -1, { .type = FMT_F, .w = 8, .d = 2 }},
95 {"MEAN", N_("Mean average"), AGR_SV_YES, 0, -1, { .type = FMT_F, .w = 8, .d = 2 }},
96 {"MEDIAN", N_("Median average"), AGR_SV_YES, 0, -1, { .type = FMT_F, .w = 8, .d = 2 }},
97 {"SD", N_("Standard deviation"), AGR_SV_YES, 0, -1, { .type = FMT_F, .w = 8, .d = 2 }},
98 {"MAX", N_("Maximum value"), AGR_SV_YES, 0, VAL_STRING, {-1, -1, -1}},
99 {"MIN", N_("Minimum value"), AGR_SV_YES, 0, VAL_STRING, {-1, -1, -1}},
100 {"PGT", N_("Percentage greater than"), AGR_SV_YES, 1, VAL_NUMERIC, { .type = FMT_F, .w = 5, .d = 1 }},
101 {"PLT", N_("Percentage less than"), AGR_SV_YES, 1, VAL_NUMERIC, { .type = FMT_F, .w = 5, .d = 1 }},
102 {"PIN", N_("Percentage included in range"), AGR_SV_YES, 2, VAL_NUMERIC, { .type = FMT_F, .w = 5, .d = 1 }},
103 {"POUT", N_("Percentage excluded from range"), AGR_SV_YES, 2, VAL_NUMERIC, { .type = FMT_F, .w = 5, .d = 1 }},
104 {"FGT", N_("Fraction greater than"), AGR_SV_YES, 1, VAL_NUMERIC, { .type = FMT_F, .w = 5, .d = 3 }},
105 {"FLT", N_("Fraction less than"), AGR_SV_YES, 1, VAL_NUMERIC, { .type = FMT_F, .w = 5, .d = 3 }},
106 {"FIN", N_("Fraction included in range"), AGR_SV_YES, 2, VAL_NUMERIC, { .type = FMT_F, .w = 5, .d = 3 }},
107 {"FOUT", N_("Fraction excluded from range"), AGR_SV_YES, 2, VAL_NUMERIC, { .type = FMT_F, .w = 5, .d = 3 }},
108 {"N", N_("Number of cases"), AGR_SV_NO, 0, VAL_NUMERIC, { .type = FMT_F, .w = 7, .d = 0 }},
109 {"NU", N_("Number of cases (unweighted)"), AGR_SV_OPT, 0, VAL_NUMERIC, { .type = FMT_F, .w = 7, .d = 0 }},
110 {"NMISS", N_("Number of missing values"), AGR_SV_YES, 0, VAL_NUMERIC, { .type = FMT_F, .w = 7, .d = 0 }},
111 {"NUMISS", N_("Number of missing values (unweighted)"), AGR_SV_YES, 0, VAL_NUMERIC, { .type = FMT_F, .w = 7, .d = 0 }},
112 {"FIRST", N_("First non-missing value"), AGR_SV_YES, 0, VAL_STRING, {-1, -1, -1}},
113 {"LAST", N_("Last non-missing value"), AGR_SV_YES, 0, VAL_STRING, {-1, -1, -1}},
114 {NULL, NULL, AGR_SV_NO, 0, -1, {-1, -1, -1}},
117 /* Missing value types. */
118 enum missing_treatment
120 ITEMWISE, /* Missing values item by item. */
121 COLUMNWISE /* Missing values column by column. */
124 /* An entire AGGREGATE procedure. */
127 /* Break variables. */
128 struct subcase sort; /* Sort criteria (break variables). */
129 const struct variable **break_vars; /* Break variables. */
130 size_t break_n_vars; /* Number of break variables. */
132 enum missing_treatment missing; /* How to treat missing values. */
133 struct agr_var *agr_vars; /* Aggregate variables. */
135 struct dictionary *dict; /* Aggregate dictionary. */
136 const struct dictionary *src_dict; /* Dict of the source */
137 int n_cases; /* Counts aggregated cases. */
139 bool add_variables; /* True iff the aggregated variables should
140 be appended to the existing dictionary */
143 static void initialize_aggregate_info (struct agr_proc *);
145 static void accumulate_aggregate_info (struct agr_proc *,
146 const struct ccase *);
148 static bool parse_aggregate_functions (struct lexer *, const struct dictionary *,
150 static void agr_destroy (struct agr_proc *);
151 static void dump_aggregate_info (const struct agr_proc *agr,
152 struct casewriter *output,
153 const struct ccase *break_case);
157 /* Parses and executes the AGGREGATE procedure. */
159 cmd_aggregate (struct lexer *lexer, struct dataset *ds)
161 struct dictionary *dict = dataset_dict (ds);
163 struct file_handle *out_file = NULL;
164 struct casereader *input = NULL, *group;
165 struct casegrouper *grouper;
166 struct casewriter *output = NULL;
168 bool copy_documents = false;
169 bool presorted = false;
173 memset(&agr, 0 , sizeof (agr));
174 agr.missing = ITEMWISE;
176 subcase_init_empty (&agr.sort);
178 /* OUTFILE subcommand must be first. */
179 lex_match (lexer, T_SLASH);
180 if (!lex_force_match_id (lexer, "OUTFILE"))
182 lex_match (lexer, T_EQUALS);
183 if (!lex_match (lexer, T_ASTERISK))
185 out_file = fh_parse (lexer, FH_REF_FILE, dataset_session (ds));
186 if (out_file == NULL)
190 if (out_file == NULL && lex_match_id (lexer, "MODE"))
192 lex_match (lexer, T_EQUALS);
193 if (lex_match_id (lexer, "ADDVARIABLES"))
195 agr.add_variables = true;
197 /* presorted is assumed in ADDVARIABLES mode */
200 else if (lex_match_id (lexer, "REPLACE"))
202 agr.add_variables = false;
208 if (agr.add_variables)
209 agr.dict = dict_clone (dict);
211 agr.dict = dict_create (dict_get_encoding (dict));
213 dict_set_label (agr.dict, dict_get_label (dict));
214 dict_set_documents (agr.dict, dict_get_documents (dict));
216 /* Read most of the subcommands. */
219 lex_match (lexer, T_SLASH);
221 if (lex_match_id (lexer, "MISSING"))
223 lex_match (lexer, T_EQUALS);
224 if (!lex_match_id (lexer, "COLUMNWISE"))
226 lex_error_expecting (lexer, "COLUMNWISE");
229 agr.missing = COLUMNWISE;
231 else if (lex_match_id (lexer, "DOCUMENT"))
232 copy_documents = true;
233 else if (lex_match_id (lexer, "PRESORTED"))
235 else if (lex_force_match_id (lexer, "BREAK"))
239 lex_match (lexer, T_EQUALS);
240 if (!parse_sort_criteria (lexer, dict, &agr.sort, &agr.break_vars,
243 agr.break_n_vars = subcase_get_n_fields (&agr.sort);
245 if (! agr.add_variables)
246 for (i = 0; i < agr.break_n_vars; i++)
247 dict_clone_var_assert (agr.dict, agr.break_vars[i]);
249 /* BREAK must follow the options. */
256 if (presorted && saw_direction)
257 msg (SW, _("When PRESORTED is specified, specifying sorting directions "
258 "with (A) or (D) has no effect. Output data will be sorted "
259 "the same way as the input data."));
261 /* Read in the aggregate functions. */
262 lex_match (lexer, T_SLASH);
263 if (!parse_aggregate_functions (lexer, dict, &agr))
266 /* Delete documents. */
268 dict_clear_documents (agr.dict);
270 /* Cancel SPLIT FILE. */
271 dict_clear_split_vars (agr.dict);
276 if (out_file == NULL)
278 /* The active dataset will be replaced by the aggregated data,
279 so TEMPORARY is moot. */
280 proc_cancel_temporary_transformations (ds);
281 proc_discard_output (ds);
282 output = autopaging_writer_create (dict_get_proto (agr.dict));
286 output = any_writer_open (out_file, agr.dict);
291 input = proc_open (ds);
292 if (!subcase_is_empty (&agr.sort) && !presorted)
294 input = sort_execute (input, &agr.sort);
295 subcase_clear (&agr.sort);
298 for (grouper = casegrouper_create_vars (input, agr.break_vars,
300 casegrouper_get_next_group (grouper, &group);
301 casereader_destroy (group))
303 struct casereader *placeholder = NULL;
304 struct ccase *c = casereader_peek (group, 0);
308 casereader_destroy (group);
312 initialize_aggregate_info (&agr);
314 if (agr.add_variables)
315 placeholder = casereader_clone (group);
319 for (; (cg = casereader_read (group)) != NULL; case_unref (cg))
320 accumulate_aggregate_info (&agr, cg);
324 if (agr.add_variables)
327 for (; (cg = casereader_read (placeholder)) != NULL; case_unref (cg))
328 dump_aggregate_info (&agr, output, cg);
330 casereader_destroy (placeholder);
334 dump_aggregate_info (&agr, output, c);
338 if (!casegrouper_destroy (grouper))
341 if (!proc_commit (ds))
348 if (out_file == NULL)
350 struct casereader *next_input = casewriter_make_reader (output);
351 if (next_input == NULL)
354 dataset_set_dict (ds, agr.dict);
355 dataset_set_source (ds, next_input);
360 ok = casewriter_destroy (output);
373 casewriter_destroy (output);
376 return CMD_CASCADING_FAILURE;
379 /* Parse all the aggregate functions. */
381 parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
382 struct agr_proc *agr)
384 size_t allocated_agr_vars = 0;
386 /* Parse everything. */
392 struct string function_name;
394 enum mv_class exclude;
395 const struct agr_func *function;
398 union agr_argument arg[2];
400 const struct variable **src;
413 ds_init_empty (&function_name);
415 /* Parse the list of target variables. */
416 while (!lex_match (lexer, T_EQUALS))
418 size_t n_dest_prev = n_dest;
420 if (!parse_DATA_LIST_vars (lexer, dict, &dest, &n_dest,
421 (PV_APPEND | PV_SINGLE | PV_NO_SCRATCH
425 /* Assign empty labels. */
429 dest_label = xnrealloc (dest_label, n_dest, sizeof *dest_label);
430 for (j = n_dest_prev; j < n_dest; j++)
431 dest_label[j] = NULL;
436 if (lex_is_string (lexer))
438 dest_label[n_dest - 1] = xstrdup (lex_tokcstr (lexer));
443 /* Get the name of the aggregation function. */
444 if (lex_token (lexer) != T_ID)
446 lex_error (lexer, _("Syntax error expecting aggregation function."));
450 ds_assign_substring (&function_name, lex_tokss (lexer));
451 exclude = ds_chomp_byte (&function_name, '.') ? MV_SYSTEM : MV_ANY;
453 for (function = agr_func_tab; function->name; function++)
454 if (!c_strcasecmp (function->name, ds_cstr (&function_name)))
456 if (NULL == function->name)
458 lex_error (lexer, _("Unknown aggregation function %s."),
459 ds_cstr (&function_name));
462 ds_destroy (&function_name);
463 func_index = function - agr_func_tab;
466 /* Check for leading lparen. */
467 if (!lex_match (lexer, T_LPAREN))
469 if (function->src_vars == AGR_SV_YES)
476 /* Parse list of source variables. */
477 int pv_opts = PV_NO_SCRATCH;
478 if (func_index == SUM || func_index == MEAN || func_index == SD)
479 pv_opts |= PV_NUMERIC;
480 else if (function->n_args)
481 pv_opts |= PV_SAME_TYPE;
483 int vars_start_ofs = lex_ofs (lexer);
484 if (!parse_variables_const (lexer, dict, &src, &n_src, pv_opts))
486 int vars_end_ofs = lex_ofs (lexer) - 1;
488 /* Parse function arguments, for those functions that
489 require arguments. */
490 int args_start_ofs = 0;
491 if (function->n_args != 0)
492 for (i = 0; i < function->n_args; i++)
496 lex_match (lexer, T_COMMA);
498 args_start_ofs = lex_ofs (lexer);
499 if (lex_is_string (lexer))
501 arg[i].c = recode_string (dict_get_encoding (agr->dict),
502 "UTF-8", lex_tokcstr (lexer),
506 else if (lex_is_number (lexer))
508 arg[i].f = lex_tokval (lexer);
513 lex_error (lexer, _("Missing argument %zu to %s."),
514 i + 1, function->name);
517 if (type != var_get_type (src[0]))
519 msg (SE, _("Arguments to %s must be of same type as "
520 "source variables."),
522 if (type == VAL_NUMERIC)
524 lex_next_msg (lexer, SN, 0, 0,
525 _("The argument is numeric."));
526 lex_ofs_msg (lexer, SN, vars_start_ofs, vars_end_ofs,
527 _("The variables have string type."));
531 lex_next_msg (lexer, SN, 0, 0,
532 _("The argument is a string."));
533 lex_ofs_msg (lexer, SN, vars_start_ofs, vars_end_ofs,
534 _("The variables are numeric."));
541 int args_end_ofs = lex_ofs (lexer) - 1;
543 /* Trailing rparen. */
544 if (!lex_force_match (lexer, T_RPAREN))
547 /* Now check that the number of source variables match
548 the number of target variables. If we check earlier
549 than this, the user can get very misleading error
550 message, i.e. `AGGREGATE x=SUM(y t).' will get this
551 error message when a proper message would be more
552 like `unknown variable t'. */
555 msg (SE, _("Number of source variables (%zu) does not match "
556 "number of target variables (%zu)."),
561 if ((func_index == PIN || func_index == POUT
562 || func_index == FIN || func_index == FOUT)
563 && (var_is_numeric (src[0])
564 ? arg[0].f > arg[1].f
565 : str_compare_rpad (arg[0].c, arg[1].c) > 0))
567 union agr_argument t = arg[0];
571 lex_ofs_msg (lexer, SW, args_start_ofs, args_end_ofs,
572 _("The value arguments passed to the %s function "
573 "are out of order. They will be treated as if "
574 "they had been specified in the correct order."),
579 /* Finally add these to the linked list of aggregation
581 for (i = 0; i < n_dest; i++)
583 if (agr->n_agr_vars >= allocated_agr_vars)
584 agr->agr_vars = x2nrealloc (agr->agr_vars, &allocated_agr_vars,
585 sizeof *agr->agr_vars);
586 struct agr_var *v = &agr->agr_vars[agr->n_agr_vars++];
587 *v = (struct agr_var) {
589 .src = src ? src[i] : NULL,
593 /* Create the target variable in the aggregate
596 struct variable *destvar;
598 v->function = func_index;
602 if (var_is_alpha (src[i]))
604 v->function |= FSTRING;
605 v->string = xmalloc (var_get_width (src[i]));
608 if (function->alpha_type == VAL_STRING)
609 destvar = dict_clone_var_as (agr->dict, v->src, dest[i]);
612 assert (var_is_numeric (v->src)
613 || function->alpha_type == VAL_NUMERIC);
614 destvar = dict_create_var (agr->dict, dest[i], 0);
618 if ((func_index == N || func_index == NMISS)
619 && dict_get_weight (dict) != NULL)
620 f = fmt_for_output (FMT_F, 8, 2);
622 f = function->format;
623 var_set_both_formats (destvar, &f);
628 destvar = dict_create_var (agr->dict, dest[i], 0);
631 if ((func_index == N || func_index == NMISS)
632 && dict_get_weight (dict) != NULL)
633 f = fmt_for_output (FMT_F, 8, 2);
635 f = function->format;
636 var_set_both_formats (destvar, &f);
642 msg (SE, _("Variable name %s is not unique within the "
643 "aggregate file dictionary, which contains "
644 "the aggregate variables and the break "
652 var_set_label (destvar, dest_label[i]);
661 if (var_is_numeric (v->src))
662 for (j = 0; j < function->n_args; j++)
663 v->arg[j].f = arg[j].f;
665 for (j = 0; j < function->n_args; j++)
666 v->arg[j].c = xstrdup (arg[j].c);
670 if (src != NULL && var_is_alpha (src[0]))
671 for (i = 0; i < function->n_args; i++)
681 if (!lex_match (lexer, T_SLASH))
683 if (lex_token (lexer) == T_ENDCMD)
686 lex_error (lexer, "Syntax error expecting end of command.");
692 ds_destroy (&function_name);
693 for (i = 0; i < n_dest; i++)
696 free (dest_label[i]);
702 if (src && n_src && var_is_alpha (src[0]))
703 for (i = 0; i < function->n_args; i++)
716 agr_destroy (struct agr_proc *agr)
718 subcase_uninit (&agr->sort);
719 free (agr->break_vars);
720 for (size_t i = 0; i < agr->n_agr_vars; i++)
722 struct agr_var *av = &agr->agr_vars[i];
724 if (av->function & FSTRING)
726 size_t n_args = agr_func_tab[av->function & FUNC].n_args;
727 for (size_t i = 0; i < n_args; i++)
731 else if (av->function == SD)
732 moments1_destroy (av->moments);
734 dict_destroy_internal_var (av->subject);
735 dict_destroy_internal_var (av->weight);
737 free (agr->agr_vars);
738 if (agr->dict != NULL)
739 dict_unref (agr->dict);
744 /* Accumulates aggregation data from the case INPUT. */
746 accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
749 bool bad_warn = true;
751 weight = dict_get_case_weight (agr->src_dict, input, &bad_warn);
753 for (size_t i = 0; i < agr->n_agr_vars; i++)
755 struct agr_var *av = &agr->agr_vars[i];
758 const union value *v = case_data (input, av->src);
759 int src_width = var_get_width (av->src);
761 if (var_is_value_missing (av->src, v) & av->exclude)
763 switch (av->function)
766 case NMISS | FSTRING:
767 av->dbl[0] += weight;
770 case NUMISS | FSTRING:
774 av->saw_missing = true;
778 /* This is horrible. There are too many possibilities. */
779 switch (av->function)
782 av->dbl[0] += v->f * weight;
786 av->dbl[0] += v->f * weight;
787 av->dbl[1] += weight;
794 cout = case_create (casewriter_get_proto (av->writer));
796 *case_num_rw (cout, av->subject) = case_num (input, av->src);
798 wv = dict_get_case_weight (agr->src_dict, input, NULL);
800 *case_num_rw (cout, av->weight) = wv;
804 casewriter_write (av->writer, cout);
808 moments1_add (av->moments, v->f, weight);
811 av->dbl[0] = MAX (av->dbl[0], v->f);
815 /* Need to do some kind of Unicode collation thingy here */
816 if (memcmp (av->string, v->s, src_width) < 0)
817 memcpy (av->string, v->s, src_width);
821 av->dbl[0] = MIN (av->dbl[0], v->f);
825 if (memcmp (av->string, v->s, src_width) > 0)
826 memcpy (av->string, v->s, src_width);
831 if (v->f > av->arg[0].f)
832 av->dbl[0] += weight;
833 av->dbl[1] += weight;
837 if (memcmp (av->arg[0].c, v->s, src_width) < 0)
838 av->dbl[0] += weight;
839 av->dbl[1] += weight;
843 if (v->f < av->arg[0].f)
844 av->dbl[0] += weight;
845 av->dbl[1] += weight;
849 if (memcmp (av->arg[0].c, v->s, src_width) > 0)
850 av->dbl[0] += weight;
851 av->dbl[1] += weight;
855 if (av->arg[0].f <= v->f && v->f <= av->arg[1].f)
856 av->dbl[0] += weight;
857 av->dbl[1] += weight;
861 if (memcmp (av->arg[0].c, v->s, src_width) <= 0
862 && memcmp (av->arg[1].c, v->s, src_width) >= 0)
863 av->dbl[0] += weight;
864 av->dbl[1] += weight;
868 if (av->arg[0].f > v->f || v->f > av->arg[1].f)
869 av->dbl[0] += weight;
870 av->dbl[1] += weight;
874 if (memcmp (av->arg[0].c, v->s, src_width) > 0
875 || memcmp (av->arg[1].c, v->s, src_width) < 0)
876 av->dbl[0] += weight;
877 av->dbl[1] += weight;
881 av->dbl[0] += weight;
894 case FIRST | FSTRING:
897 memcpy (av->string, v->s, src_width);
906 memcpy (av->string, v->s, src_width);
910 case NMISS | FSTRING:
912 case NUMISS | FSTRING:
913 /* Our value is not missing or it would have been
914 caught earlier. Nothing to do. */
922 switch (av->function)
925 av->dbl[0] += weight;
937 /* Writes an aggregated record to OUTPUT. */
939 dump_aggregate_info (const struct agr_proc *agr, struct casewriter *output, const struct ccase *break_case)
941 struct ccase *c = case_create (dict_get_proto (agr->dict));
943 if (agr->add_variables)
945 case_copy (c, 0, break_case, 0, dict_get_n_vars (agr->src_dict));
952 for (i = 0; i < agr->break_n_vars; i++)
954 const struct variable *v = agr->break_vars[i];
955 value_copy (case_data_rw_idx (c, value_idx),
956 case_data (break_case, v),
962 for (size_t i = 0; i < agr->n_agr_vars; i++)
964 struct agr_var *av = &agr->agr_vars[i];
965 union value *v = case_data_rw (c, av->dest);
966 int width = var_get_width (av->dest);
968 if (agr->missing == COLUMNWISE && av->saw_missing
969 && (av->function & FUNC) != N && (av->function & FUNC) != NU
970 && (av->function & FUNC) != NMISS && (av->function & FUNC) != NUMISS)
972 value_set_missing (v, width);
973 casewriter_destroy (av->writer);
977 switch (av->function)
980 v->f = av->int1 ? av->dbl[0] : SYSMIS;
983 v->f = av->dbl[1] != 0.0 ? av->dbl[0] / av->dbl[1] : SYSMIS;
989 struct percentile *median = percentile_create (0.5, av->cc);
990 struct order_stats *os = &median->parent;
991 struct casereader *sorted_reader = casewriter_make_reader (av->writer);
994 order_stats_accumulate (&os, 1,
999 av->dbl[0] = percentile_calculate (median, PC_HAVERAGE);
1000 statistic_destroy (&median->parent.parent);
1009 /* FIXME: we should use two passes. */
1010 moments1_calculate (av->moments, NULL, NULL, &variance,
1012 if (variance != SYSMIS)
1013 v->f = sqrt (variance);
1020 v->f = av->int1 ? av->dbl[0] : SYSMIS;
1025 memcpy (v->s, av->string, width);
1027 value_set_missing (v, width);
1036 case FOUT | FSTRING:
1037 v->f = av->dbl[1] ? av->dbl[0] / av->dbl[1] : SYSMIS;
1046 case POUT | FSTRING:
1047 v->f = av->dbl[1] ? av->dbl[0] / av->dbl[1] * 100.0 : SYSMIS;
1059 v->f = av->int1 ? av->dbl[0] : SYSMIS;
1061 case FIRST | FSTRING:
1062 case LAST | FSTRING:
1064 memcpy (v->s, av->string, width);
1066 value_set_missing (v, width);
1069 case NMISS | FSTRING:
1073 case NUMISS | FSTRING:
1081 casewriter_write (output, c);
1084 /* Resets the state for all the aggregate functions. */
1086 initialize_aggregate_info (struct agr_proc *agr)
1088 for (size_t i = 0; i < agr->n_agr_vars; i++)
1090 struct agr_var *av = &agr->agr_vars[i];
1091 av->saw_missing = false;
1092 av->dbl[0] = av->dbl[1] = av->dbl[2] = 0.0;
1094 switch (av->function)
1097 av->dbl[0] = DBL_MAX;
1100 memset (av->string, 255, var_get_width (av->src));
1103 av->dbl[0] = -DBL_MAX;
1106 memset (av->string, 0, var_get_width (av->src));
1110 struct caseproto *proto;
1111 struct subcase ordering;
1113 proto = caseproto_create ();
1114 proto = caseproto_add_width (proto, 0);
1115 proto = caseproto_add_width (proto, 0);
1118 av->subject = dict_create_internal_var (0, 0);
1121 av->weight = dict_create_internal_var (1, 0);
1123 subcase_init_var (&ordering, av->subject, SC_ASCEND);
1124 av->writer = sort_create_writer (&ordering, proto);
1125 subcase_uninit (&ordering);
1126 caseproto_unref (proto);
1132 if (av->moments == NULL)
1133 av->moments = moments1_create (MOMENT_VARIANCE);
1135 moments1_clear (av->moments);