-/* PSPP - computes sample statistics.
- Copyright (C) 2004 Free Software Foundation, Inc.
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2015 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+#include <config.h>
-#include <stdio.h>
-#include <plot.h>
-#include <stdarg.h>
-#include <math.h>
-#include <output/charts/barchart.h>
-#include <output/chart.h>
-#include <output/charts/plot-chart.h>
+#include "output/charts/barchart.h"
+#include "output/charts/piechart.h"
-#define CATAGORIES 6
-#define SUB_CATAGORIES 3
+#include <stdlib.h>
-static const double x_min = 0;
-static const double x_max = 15.0;
+#include "libpspp/cast.h"
+#include "libpspp/str.h"
+#include "libpspp/array.h"
+#include "output/chart-item-provider.h"
-static const char *cat_labels[] =
- {
- "Age",
- "Intelligence",
- "Wealth",
- "Emotional",
- "cat 5",
- "cat 6",
- "cat 7",
- "cat 8",
- "cat 9",
- "cat 10",
- "cat 11"
- };
+#include "gl/xalloc.h"
+#include "data/variable.h"
+#include "language/stats/freq.h"
+static int
+compare_category_3way (const void *a_, const void *b_, const void *bc_)
+{
+ const struct category *const*a = a_;
+ const struct category *const*b = b_;
+ const struct barchart *bc = bc_;
+ return value_compare_3way (&(*a)->val, &(*b)->val, var_get_width (bc->var[1]));
+}
-/* Subcatagories */
-static const double data1[] =
-{
- 28,83,
- 34,
- 29,13,
- 9,4,
- 3,3,
- 2,0,
- 1,0,
- 0,
- 1,1
-};
-
-
-static const double data2[] =
+
+static unsigned int
+hash_freq_2level_ptr (const void *a_, const void *bc_)
{
- 45,13,
- 9,4,
- 3,43,
- 2,0,
- 1,20,
- 0,0,
- 1,1,
- 0,0
-};
-
-static const double data3[] =
- {
- 23,18,
- 0, 45,23, 9, 40, 24,4, 8
- };
+ const struct freq *const *ap = a_;
+ const struct barchart *bc = bc_;
+ size_t hash = value_hash (&(*ap)->values[0], bc->widths[0], 0);
-static const char subcat_name[]="Gender";
+ if (bc->n_vars > 1)
+ hash = value_hash (&(*ap)->values[1], bc->widths[1], hash);
+ return hash;
+}
-struct subcat {
- const double *data;
- const char *label;
-};
-static const struct subcat sub_catagory[SUB_CATAGORIES] =
- {
- {data1, "male"},
- {data2, "female"},
- {data3, "47xxy"}
- };
+static int
+compare_freq_2level_ptr_3way (const void *a_, const void *b_, const void *bc_)
+{
+ const struct freq *const *ap = a_;
+ const struct freq *const *bp = b_;
+ const struct barchart *bc = bc_;
+ const int level0 = value_compare_3way (&(*ap)->values[0], &(*bp)->values[0], bc->widths[0]);
+ if (level0 == 0 && bc->n_vars > 1)
+ return value_compare_3way (&(*ap)->values[1], &(*bp)->values[1], bc->widths[1]);
-static const double y_min = 0;
-static const double y_max = 120.0;
-static const double y_tick = 20.0;
+ return level0;
+}
-static void write_legend(struct chart *chart) ;
+/* Creates and returns a chart that will render a barchart with
+ 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.
-void
-draw_barchart(struct chart *ch, const char *title,
- const char *xlabel, const char *ylabel, enum bar_opts opt)
+ CATS are the counts of the values of those variables. N_CATS is the
+ number of distinct values.
+*/
+struct chart_item *
+barchart_create (const struct variable **var, int n_vars,
+ const char *ylabel, bool percent,
+ struct freq *const *cats, int n_cats)
{
- double d;
+ struct barchart *bar;
int i;
- double interval_size = fabs(ch->data_right - ch->data_left) / ( CATAGORIES );
-
- double bar_width = interval_size / 1.1 ;
-
- double ordinate_scale = fabs(ch->data_top - ch->data_bottom) /
- fabs(y_max - y_min) ;
+ const int pidx = 0;
+ const int sidx = 1;
- if ( opt != BAR_STACKED )
- bar_width /= SUB_CATAGORIES;
- /* Move to data bottom-left */
- pl_move_r(ch->lp, ch->data_left, ch->data_bottom);
+ int width = var_get_width (var[pidx]);
- pl_savestate_r(ch->lp);
- pl_filltype_r(ch->lp,1);
-
- /* Draw the data */
- for (i = 0 ; i < CATAGORIES ; ++i )
- {
- int sc;
- double ystart=0.0;
- double x = i * interval_size;
+ assert (n_vars >= 1);
- pl_savestate_r(ch->lp);
+ bar = xzalloc (sizeof *bar);
+ 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]));
- draw_tick (ch, TICK_ABSCISSA, x + (interval_size/2 ),
- cat_labels[i]);
+ bar->largest = -1;
+ bar->ylabel = strdup (ylabel);
- for(sc = 0 ; sc < SUB_CATAGORIES ; ++sc )
+ {
+ 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.
+ */
+ for (i = 0; i < n_cats; i++)
{
-
- pl_savestate_r(ch->lp);
- pl_fillcolorname_r(ch->lp,data_colour[sc]);
-
- switch ( opt )
- {
- case BAR_GROUPED:
- pl_fboxrel_r(ch->lp,
- x + (sc * bar_width ), 0,
- x + (sc + 1) * bar_width,
- sub_catagory[sc].data[i] * ordinate_scale );
- break;
-
-
- case BAR_STACKED:
+ const struct freq *src = cats[i];
+ size_t hash = value_hash (&src->values[pidx], width, 0);
- pl_fboxrel_r(ch->lp,
- x, ystart,
- x + bar_width,
- ystart + sub_catagory[sc].data[i] * ordinate_scale );
-
- ystart += sub_catagory[sc].data[i] * ordinate_scale ;
-
- break;
+ struct category *foo;
+ int flag = 0;
+ HMAP_FOR_EACH_WITH_HASH (foo, struct category, node, hash, &bar->primaries)
+ {
+ if (value_equal (&foo->val, &src->values[pidx], width))
+ {
+ flag = 1;
+ break;
+ }
+ }
- default:
- break;
+ if (!flag)
+ {
+ struct category *s = xzalloc (sizeof *s);
+ s->idx = idx++;
+ 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);
+
+ hmap_insert (&bar->primaries, &s->node, hash);
}
- pl_restorestate_r(ch->lp);
}
- pl_restorestate_r(ch->lp);
+ bar->n_pcats = hmap_count (&bar->primaries);
}
- pl_restorestate_r(ch->lp);
- for ( d = y_min; d <= y_max ; d += y_tick )
+ if (n_vars > 1)
{
+ hmap_init (&bar->secondaries);
+ int idx = 0;
+ /* Iterate the categories, and create a hash table of secondary categories */
+ for (i = 0; i < n_cats; i++)
+ {
+ struct freq *src = cats[i];
- draw_tick (ch, TICK_ORDINATE,
- (d - y_min ) * ordinate_scale, "%g", d);
+ struct category *foo;
+ int flag = 0;
+ size_t hash = value_hash (&src->values[sidx], var_get_width (var[sidx]), 0);
+ HMAP_FOR_EACH_WITH_HASH (foo, struct category, node, hash, &bar->secondaries)
+ {
+ if (value_equal (&foo->val, &src->values[sidx], var_get_width (var[sidx])))
+ {
+ flag = 1;
+ break;
+ }
+ }
- }
+ if (!flag)
+ {
+ struct category *s = xzalloc (sizeof *s);
+ s->idx = idx++;
+ 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);
+
+ hmap_insert (&bar->secondaries, &s->node, hash);
+ bar->ss = xrealloc (bar->ss, idx * sizeof *bar->ss);
+ bar->ss[idx - 1] = s;
+ }
+ }
- /* Write the abscissa label */
- pl_move_r(ch->lp,ch->data_left, ch->abscissa_top);
- pl_alabel_r(ch->lp,0,'t',xlabel);
+ int n_category = hmap_count (&bar->secondaries);
-
- /* Write the ordinate label */
- pl_savestate_r(ch->lp);
- pl_move_r(ch->lp,ch->data_bottom, ch->ordinate_right);
- pl_textangle_r(ch->lp,90);
- pl_alabel_r(ch->lp,0,0,ylabel);
- pl_restorestate_r(ch->lp);
+ 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
+ exist.
+ */
+ bar->cats = xcalloc (n_cats, sizeof *bar->cats);
- chart_write_title(ch, title);
+ bar->widths[0] = var_get_width (bar->var[0]);
+ if (n_vars > 1)
+ bar->widths[1] = var_get_width (bar->var[1]);
- write_legend(ch);
+ {
+ struct hmap level2table;
+ hmap_init (&level2table);
+ int x = 0;
-
+ for (i = 0; i < n_cats; i++)
+ {
+ struct freq *c = cats[i];
+
+ struct freq *foo;
+ 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 = true;
+ break;
+ }
+ }
+
+ if (!flag)
+ {
+ 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;
+ }
+ }
+
+ bar->n_nzcats = hmap_count (&level2table);
+ hmap_destroy (&level2table);
+ }
+
+ sort (bar->cats, bar->n_nzcats, sizeof *bar->cats,
+ compare_freq_2level_ptr_3way, bar);
+
+ return &bar->chart_item;
}
-
-
-
-
static void
-write_legend(struct chart *chart)
+destroy_cat_map (struct hmap *m)
{
- int sc;
+ struct category *foo = NULL;
+ struct category *next = NULL;
+ HMAP_FOR_EACH_SAFE (foo, next, struct category, node, m)
+ {
+ value_destroy (&foo->val, foo->width);
- pl_savestate_r(chart->lp);
+ ds_destroy (&foo->label);
+ free (foo);
+ }
- pl_filltype_r(chart->lp,1);
+ hmap_destroy (m);
+}
- pl_move_r(chart->lp, chart->legend_left,
- chart->data_bottom + chart->font_size * SUB_CATAGORIES * 1.5);
+static void
+barchart_destroy (struct chart_item *chart_item)
+{
+ struct barchart *bar = to_barchart (chart_item);
- pl_alabel_r(chart->lp,0,'b',subcat_name);
+ int i;
- for (sc = 0 ; sc < SUB_CATAGORIES ; ++sc )
+ destroy_cat_map (&bar->primaries);
+ if (bar->ss)
{
- pl_fmove_r(chart->lp,
- chart->legend_left,
- chart->data_bottom + chart->font_size * sc * 1.5);
-
- pl_savestate_r(chart->lp);
- pl_fillcolorname_r(chart->lp,data_colour[sc]);
- pl_fboxrel_r (chart->lp,
- 0,0,
- chart->font_size, chart->font_size);
- pl_restorestate_r(chart->lp);
-
- pl_fmove_r(chart->lp,
- chart->legend_left + chart->font_size * 1.5,
- chart->data_bottom + chart->font_size * sc * 1.5);
-
- pl_alabel_r(chart->lp,'l','b',sub_catagory[sc].label);
+ destroy_cat_map (&bar->secondaries);
}
-
- pl_restorestate_r(chart->lp);
+ for (i = 0; i < bar->n_nzcats; 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 =
+ {
+ barchart_destroy
+ };