#include "libpspp/cast.h"
#include "libpspp/str.h"
#include "libpspp/array.h"
-#include "output/chart-item-provider.h"
+#include "output/chart-provider.h"
#include "gl/xalloc.h"
#include "data/variable.h"
+#include "data/settings.h"
#include "language/stats/freq.h"
}
+static int
+compare_category_by_index_3way (const void *a_, const void *b_,
+ const void *unused UNUSED)
+{
+ const struct category *const*a = a_;
+ const struct category *const*b = b_;
+
+ if ( (*a)->idx < (*b)->idx)
+ return -1;
+
+ return ((*a)->idx > (*b)->idx);
+}
+
static unsigned int
hash_freq_2level_ptr (const void *a_, const void *bc_)
{
return level0;
}
+/* Print out a textual representation of a barchart.
+ This is intended only for testing, and not as a means
+ of visualising the data.
+*/
+static void
+barchart_dump (const struct barchart *bc, FILE *fp)
+{
+ fprintf (fp, "Graphic: Barchart\n");
+ fprintf (fp, "Percentage: %d\n", bc->percent);
+ fprintf (fp, "Total Categories: %d\n", bc->n_nzcats);
+ fprintf (fp, "Primary Categories: %d\n", bc->n_pcats);
+ fprintf (fp, "Largest Category: %g\n", bc->largest);
+ fprintf (fp, "Total Count: %g\n", bc->total_count);
+
+ fprintf (fp, "Y Label: \"%s\"\n", bc->ylabel);
+
+ fprintf (fp, "Categorical Variables:\n");
+ for (int i = 0; i < bc->n_vars; ++i)
+ {
+ fprintf (fp, " Var: \"%s\"\n", var_get_name (bc->var[i]));
+ }
+
+ fprintf (fp, "Categories:\n");
+ struct category *cat;
+ struct category **cats = XCALLOC (hmap_count (&bc->primaries), struct category *);
+ int i = 0;
+ HMAP_FOR_EACH (cat, struct category, node, &bc->primaries)
+ {
+ cats[i++] = cat;
+ }
+ /* HMAP_FOR_EACH is not guaranteed to iterate in any particular order. So
+ we must sort here before we output the results. */
+ sort (cats, i, sizeof (struct category *), compare_category_by_index_3way, bc);
+ for (i = 0; i < hmap_count (&bc->primaries); ++i)
+ {
+ const struct category *c = cats[i];
+ fprintf (fp, " %d \"%s\"\n", c->idx, ds_cstr (&c->label));
+ }
+ free (cats);
+
+ if (bc->ss)
+ {
+ fprintf (fp, "Sub-categories:\n");
+ for (int i = 0; i < bc->n_nzcats / bc->n_pcats; ++i)
+ {
+ const struct category *cat = bc->ss[i];
+ fprintf (fp, " %d \"%s\"\n", cat->idx, ds_cstr(&cat->label));
+ }
+ }
+
+ fprintf (fp, "All Categories:\n");
+ for (int i = 0; i < bc->n_nzcats; ++i)
+ {
+ const struct freq *frq = bc->cats[i];
+ fprintf (fp, "Count: %g; ", frq->count);
+
+ struct string s = DS_EMPTY_INITIALIZER;
+ var_append_value_name (bc->var[0], &frq->values[0], &s);
+
+ fprintf (fp, "Cat: \"%s\"", ds_cstr (&s));
+ ds_clear (&s);
+
+ if (bc->ss)
+ {
+ var_append_value_name (bc->var[1], &frq->values[1], &s);
+ fprintf (fp, ", \"%s\"", ds_cstr (&s));
+ }
+ ds_destroy (&s);
+ fputc ('\n', fp);
+ }
+
+ fputc ('\n', fp);
+}
/* Creates and returns a chart that will render a barchart with
- the given TITLE and the N_BARS described in CATS. */
-struct chart_item *
+ the given TITLE and the N_CATS described in CATS.
+
+ VAR is an array containing the categorical variables, and N_VAR
+ the number of them. N_VAR must be exactly 1 or 2.
+
+ CATS are the counts of the values of those variables. N_CATS is the
+ number of distinct values.
+*/
+struct chart *
barchart_create (const struct variable **var, int n_vars,
- const char *ylabel,
+ const char *ylabel, bool percent,
struct freq *const *cats, int n_cats)
{
- struct barchart *bar;
int i;
const int pidx = 0;
int width = var_get_width (var[pidx]);
- assert (n_vars >= 1);
+ assert (n_vars >= 1 && n_vars <= 2);
- bar = xzalloc (sizeof *bar);
+ struct barchart *bar = XZALLOC (struct barchart);
+ bar->percent = percent;
bar->var = var;
bar->n_vars = n_vars;
bar->n_nzcats = n_cats;
- chart_item_init (&bar->chart_item, &barchart_class, var_to_string (var[pidx]));
+ chart_init (&bar->chart, &barchart_class, var_to_string (var[pidx]));
bar->largest = -1;
bar->ylabel = strdup (ylabel);
int idx = 0;
hmap_init (&bar->primaries);
- /*
+ /*
Iterate the categories and create a hash table of the primary categories.
We need to do this to find out how many there are and to cache the labels.
*/
}
}
- if (!flag)
+ if (!flag)
{
- struct category *s = xzalloc (sizeof *s);
+ struct category *s = XZALLOC (struct category);
s->idx = idx++;
- value_init (&s->val, var_get_width (var[pidx]));
- value_copy (&s->val, &src->values[pidx], var_get_width (var[pidx]));
+ s->width = var_get_width (var[pidx]);
+ value_init (&s->val, s->width);
+ value_copy (&s->val, &src->values[pidx], s->width);
ds_init_empty (&s->label);
var_append_value_name (var[pidx], &s->val, &s->label);
break;
}
}
-
- if (!flag)
+
+ if (!flag)
{
- struct category *s = xzalloc (sizeof *s);
+ struct category *s = XZALLOC (struct category);
s->idx = idx++;
- value_init (&s->val, var_get_width (var[sidx]));
+ s->width = var_get_width (var[sidx]);
+ value_init (&s->val, s->width);
value_copy (&s->val, &src->values[sidx], var_get_width (var[sidx]));
ds_init_empty (&s->label);
var_append_value_name (var[sidx], &s->val, &s->label);
sort (bar->ss, n_category, sizeof *bar->ss,
compare_category_3way, bar);
}
-
+
/* Deep copy. Not necessary for cmd line, but essential for the GUI,
since an expose callback will access these structs which may not
struct hmap level2table;
hmap_init (&level2table);
int x = 0;
-
+
for (i = 0; i < n_cats; i++)
{
struct freq *c = cats[i];
struct freq *foo;
- int flag = 0;
+ bool flag = false;
size_t hash = hash_freq_2level_ptr (&c, bar);
HMAP_FOR_EACH_WITH_HASH (foo, struct freq, node, hash, &level2table)
{
if (0 == compare_freq_2level_ptr_3way (&foo, &c, bar))
{
foo->count += c->count;
-
+ bar->total_count += c->count;
+
if (foo->count > bar->largest)
bar->largest = foo->count;
-
- flag = 1;
+
+ flag = true;
break;
}
}
-
- if (!flag)
+
+ if (!flag)
{
- struct freq *aggregated_freq = freq_clone (c, n_vars, bar->widths);
+ struct freq *aggregated_freq = freq_clone (c, n_vars, bar->widths);
hmap_insert (&level2table, &aggregated_freq->node, hash);
-
+
if (c->count > bar->largest)
bar->largest = aggregated_freq->count;
-
+
+ bar->total_count += c->count;
bar->cats[x++] = aggregated_freq;
}
}
sort (bar->cats, bar->n_nzcats, sizeof *bar->cats,
compare_freq_2level_ptr_3way, bar);
- return &bar->chart_item;
+ if (settings_get_testing_mode ())
+ barchart_dump (bar, stdout);
+
+ return &bar->chart;
}
static void
struct category *next = NULL;
HMAP_FOR_EACH_SAFE (foo, next, struct category, node, m)
{
+ value_destroy (&foo->val, foo->width);
+
ds_destroy (&foo->label);
free (foo);
}
}
static void
-barchart_destroy (struct chart_item *chart_item)
+barchart_destroy (struct chart *chart)
{
- struct barchart *bar = to_barchart (chart_item);
+ struct barchart *bar = to_barchart (chart);
int i;
{
freq_destroy (bar->cats[i], bar->n_vars, bar->widths);
}
-
+
free (bar->cats);
free (bar->ylabel);
free (bar->ss);
free (bar);
}
-const struct chart_item_class barchart_class =
+const struct chart_class barchart_class =
{
barchart_destroy
};