1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2015 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 "output/charts/barchart.h"
20 #include "output/charts/piechart.h"
24 #include "libpspp/cast.h"
25 #include "libpspp/str.h"
26 #include "libpspp/array.h"
27 #include "output/chart-item-provider.h"
29 #include "gl/xalloc.h"
30 #include "data/variable.h"
31 #include "language/stats/freq.h"
35 compare_category_3way (const void *a_, const void *b_, const void *bc_)
37 const struct category *const*a = a_;
38 const struct category *const*b = b_;
39 const struct barchart *bc = bc_;
41 return value_compare_3way (&(*a)->val, &(*b)->val, var_get_width (bc->var[1]));
46 hash_freq_2level_ptr (const void *a_, const void *bc_)
48 const struct freq *const *ap = a_;
49 const struct barchart *bc = bc_;
51 size_t hash = value_hash (&(*ap)->values[0], bc->widths[0], 0);
54 hash = value_hash (&(*ap)->values[1], bc->widths[1], hash);
61 compare_freq_2level_ptr_3way (const void *a_, const void *b_, const void *bc_)
63 const struct freq *const *ap = a_;
64 const struct freq *const *bp = b_;
65 const struct barchart *bc = bc_;
67 const int level0 = value_compare_3way (&(*ap)->values[0], &(*bp)->values[0], bc->widths[0]);
69 if (level0 == 0 && bc->n_vars > 1)
70 return value_compare_3way (&(*ap)->values[1], &(*bp)->values[1], bc->widths[1]);
77 /* Creates and returns a chart that will render a barchart with
78 the given TITLE and the N_BARS described in CATS. */
80 barchart_create (const struct variable **var, int n_vars,
82 struct freq *const *cats, int n_cats)
91 int width = var_get_width (var[pidx]);
95 bar = xzalloc (sizeof *bar);
98 bar->n_nzcats = n_cats;
99 chart_item_init (&bar->chart_item, &barchart_class, var_to_string (var[pidx]));
102 bar->ylabel = strdup (ylabel);
106 hmap_init (&bar->primaries);
109 Iterate the categories and create a hash table of the primary categories.
110 We need to do this to find out how many there are and to cache the labels.
112 for (i = 0; i < n_cats; i++)
114 const struct freq *src = cats[i];
115 size_t hash = value_hash (&src->values[pidx], width, 0);
117 struct category *foo;
119 HMAP_FOR_EACH_WITH_HASH (foo, struct category, node, hash, &bar->primaries)
121 if (value_equal (&foo->val, &src->values[pidx], width))
130 struct category *s = xzalloc (sizeof *s);
132 value_init (&s->val, var_get_width (var[pidx]));
133 value_copy (&s->val, &src->values[pidx], var_get_width (var[pidx]));
134 ds_init_empty (&s->label);
135 var_append_value_name (var[pidx], &s->val, &s->label);
137 hmap_insert (&bar->primaries, &s->node, hash);
141 bar->n_pcats = hmap_count (&bar->primaries);
146 hmap_init (&bar->secondaries);
148 /* Iterate the categories, and create a hash table of secondary categories */
149 for (i = 0; i < n_cats; i++)
151 struct freq *src = cats[i];
153 struct category *foo;
155 size_t hash = value_hash (&src->values[sidx], var_get_width (var[sidx]), 0);
156 HMAP_FOR_EACH_WITH_HASH (foo, struct category, node, hash, &bar->secondaries)
158 if (value_equal (&foo->val, &src->values[sidx], var_get_width (var[sidx])))
167 struct category *s = xzalloc (sizeof *s);
169 value_init (&s->val, var_get_width (var[sidx]));
170 value_copy (&s->val, &src->values[sidx], var_get_width (var[sidx]));
171 ds_init_empty (&s->label);
172 var_append_value_name (var[sidx], &s->val, &s->label);
174 hmap_insert (&bar->secondaries, &s->node, hash);
175 bar->ss = xrealloc (bar->ss, idx * sizeof *bar->ss);
176 bar->ss[idx - 1] = s;
180 int n_category = hmap_count (&bar->secondaries);
182 sort (bar->ss, n_category, sizeof *bar->ss,
183 compare_category_3way, bar);
187 /* Deep copy. Not necessary for cmd line, but essential for the GUI,
188 since an expose callback will access these structs which may not
191 bar->cats = xcalloc (n_cats, sizeof *bar->cats);
193 bar->widths[0] = var_get_width (bar->var[0]);
195 bar->widths[1] = var_get_width (bar->var[1]);
198 struct hmap level2table;
199 hmap_init (&level2table);
202 for (i = 0; i < n_cats; i++)
204 struct freq *c = cats[i];
208 size_t hash = hash_freq_2level_ptr (&c, bar);
209 HMAP_FOR_EACH_WITH_HASH (foo, struct freq, node, hash, &level2table)
211 if (0 == compare_freq_2level_ptr_3way (&foo, &c, bar))
213 foo->count += c->count;
215 if (foo->count > bar->largest)
216 bar->largest = foo->count;
225 struct freq *aggregated_freq = freq_clone (c, n_vars, bar->widths);
226 hmap_insert (&level2table, &aggregated_freq->node, hash);
228 if (c->count > bar->largest)
229 bar->largest = aggregated_freq->count;
231 bar->cats[x++] = aggregated_freq;
235 bar->n_nzcats = hmap_count (&level2table);
236 hmap_destroy (&level2table);
239 sort (bar->cats, bar->n_nzcats, sizeof *bar->cats,
240 compare_freq_2level_ptr_3way, bar);
242 return &bar->chart_item;
246 destroy_cat_map (struct hmap *m)
248 struct category *foo = NULL;
249 struct category *next = NULL;
250 HMAP_FOR_EACH_SAFE (foo, next, struct category, node, m)
252 ds_destroy (&foo->label);
260 barchart_destroy (struct chart_item *chart_item)
262 struct barchart *bar = to_barchart (chart_item);
266 destroy_cat_map (&bar->primaries);
269 destroy_cat_map (&bar->secondaries);
272 for (i = 0; i < bar->n_nzcats; i++)
274 freq_destroy (bar->cats[i], bar->n_vars, bar->widths);
283 const struct chart_item_class barchart_class =