#include <math.h>
#include "data/casereader.h"
+#include "data/casewriter.h"
#include "data/dataset.h"
#include "data/dictionary.h"
#include "data/mrset.h"
+#include "data/subcase.h"
#include "data/value-labels.h"
#include "language/command.h"
#include "language/lexer/format-parser.h"
#include "libpspp/message.h"
#include "libpspp/string-array.h"
#include "math/moments.h"
+#include "math/percentiles.h"
+#include "math/sort.h"
#include "output/pivot-table.h"
#include "gl/minmax.h"
struct hmap_node node;
/* The domains that contain this cell. */
+ bool contributes_to_domains;
struct ctables_domain *domains[N_CTDTS];
bool hide;
/* MEAN, SEMEAN, STDDEV, SUM, VARIANCE, *.SUM. */
struct moments1 *moments;
- /* XXX percentiles, median, mode, multiple response */
+ struct
+ {
+ struct casewriter *writer;
+ double mvalid;
+ double median;
+ };
+
+ /* XXX percentiles, mode, multiple response */
};
static void
case CTSF_LAYERPCT_TOTALN:
case CTSF_LAYERROWPCT_TOTALN:
case CTSF_LAYERCOLPCT_TOTALN:
+ case CTSF_MISSING:
case CSTF_TOTALN:
case CTSF_ETOTALN:
case CTSF_VALIDN:
break;
case CTSF_MEDIAN:
- case CTSF_MISSING:
+ {
+ struct caseproto *proto = caseproto_create ();
+ proto = caseproto_add_width (proto, 0);
+ proto = caseproto_add_width (proto, 0);
+
+ struct subcase ordering;
+ subcase_init (&ordering, 0, 0, SC_ASCEND);
+ s->writer = sort_create_writer (&ordering, proto);
+ subcase_uninit (&ordering);
+ caseproto_unref (proto);
+
+ s->mvalid = 0;
+ s->median = SYSMIS;
+ }
+ break;
+
case CTSF_MODE:
case CTSF_PTILE:
NOT_REACHED ();
case CTSF_LAYERPCT_TOTALN:
case CTSF_LAYERROWPCT_TOTALN:
case CTSF_LAYERCOLPCT_TOTALN:
+ case CTSF_MISSING:
case CSTF_TOTALN:
case CTSF_ETOTALN:
case CTSF_VALIDN:
break;
case CTSF_MEDIAN:
- case CTSF_MISSING:
+ casewriter_destroy (s->writer);
+ break;
+
case CTSF_MODE:
case CTSF_PTILE:
NOT_REACHED ();
case CTSF_LAYERPCT_TOTALN:
case CTSF_LAYERROWPCT_TOTALN:
case CTSF_LAYERCOLPCT_TOTALN:
+ case CTSF_MISSING:
case CSTF_TOTALN:
case CTSF_ETOTALN:
case CTSF_VALIDN:
break;
case CTSF_MEDIAN:
- case CTSF_MISSING:
+ if (var_is_value_missing (var, value))
+ {
+ s->mvalid += weight;
+
+ struct ccase *c = case_create (casewriter_get_proto (s->writer));
+ *case_num_rw_idx (c, 0) = value->f;
+ *case_num_rw_idx (c, 1) = weight;
+ casewriter_write (s->writer, c);
+ }
+ break;
+
case CTSF_MODE:
case CTSF_PTILE:
NOT_REACHED ();
case CTSF_LAYERCOLPCT_TOTALN:
NOT_REACHED ();
+ case CTSF_MISSING:
+ return s->missing;
+
case CSTF_TOTALN:
case CTSF_ETOTALN:
return s->valid + s->missing;
NOT_REACHED ();
case CTSF_MEDIAN:
- case CTSF_MISSING:
+ if (s->writer)
+ {
+ struct casereader *reader = casewriter_make_reader (s->writer);
+ s->writer = NULL;
+
+ struct percentile *median = percentile_create (0.5, s->mvalid);
+ struct order_stats *os = &median->parent;
+ order_stats_accumulate_idx (&os, 1, reader, 1, 0);
+ s->median = percentile_calculate (median, PC_HAVERAGE);
+ statistic_destroy (&median->parent.parent);
+ }
+ return s->median;
+
case CTSF_MODE:
case CTSF_PTILE:
NOT_REACHED ();
cell = xmalloc (sizeof *cell);
cell->hide = false;
cell->sv = sv;
+ cell->contributes_to_domains = true;
for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
{
const struct ctables_nest *nest = &t->stacks[a].nests[ix[a]];
: NULL);
for (size_t i = 0; i < nest->n; i++)
{
+ const struct ctables_category *cat = cats[a][i];
+
if (i != nest->scale_idx)
{
- const struct ctables_category *subtotal = cats[a][i]->subtotal;
+ const struct ctables_category *subtotal = cat->subtotal;
if (subtotal && subtotal->type == CCT_HSUBTOTAL)
cell->hide = true;
+
+ if (cat->type == CCT_TOTAL || cat->type == CCT_SUBTOTAL || cat->type == CCT_HSUBTOTAL)
+ cell->contributes_to_domains = false;
}
- cell->axes[a].cvs[i].category = cats[a][i];
+ 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]));
}
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), weight);
- for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
- cell->domains[dt]->valid += weight;
+ if (cell->contributes_to_domains)
+ for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
+ cell->domains[dt]->valid += weight;
}
static void
}
}
+static void
+recurse_subtotals (struct ctables_table *t, const struct ccase *c,
+ size_t ix[PIVOT_N_AXES],
+ const struct ctables_category *cats[PIVOT_N_AXES][10],
+ double weight,
+ enum pivot_axis_type start_axis, size_t start_nest)
+{
+ for (enum pivot_axis_type a = start_axis; a < PIVOT_N_AXES; a++)
+ {
+ const struct ctables_nest *nest = &t->stacks[a].nests[ix[a]];
+ for (size_t i = start_nest; i < nest->n; i++)
+ {
+ if (i == nest->scale_idx)
+ continue;
+
+ const struct ctables_category *save = cats[a][i];
+ if (save->subtotal)
+ {
+ cats[a][i] = save->subtotal;
+ ctables_cell_add__ (t, c, ix, cats, weight);
+ recurse_subtotals (t, c, ix, cats, weight, a, i + 1);
+ cats[a][i] = save;
+ }
+ }
+ start_nest = 0;
+ }
+}
+
static void
ctables_cell_insert (struct ctables_table *t,
const struct ccase *c,
ctables_cell_add__ (t, c, ix, cats, weight);
recurse_totals (t, c, ix, cats, weight, 0, 0);
-
- for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
- {
- const struct ctables_nest *nest = &t->stacks[a].nests[ix[a]];
- for (size_t i = 0; i < nest->n; i++)
- {
- if (i == nest->scale_idx)
- continue;
-
- const struct ctables_category *save = cats[a][i];
- if (save->subtotal)
- {
- cats[a][i] = save->subtotal;
- ctables_cell_add__ (t, c, ix, cats, weight);
- cats[a][i] = save;
- }
- }
- }
+ recurse_subtotals (t, c, ix, cats, weight, 0, 0);
}
struct merge_item