X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Foutput%2Fcharts%2Fbarchart.c;h=07a39e818f65193b6ddc6ed4816eab19c3a65dd5;hb=89cbb0a7f4f31bc826621ffff9138fc4743f2b11;hp=88a1d741ddf9199f08404d49096de62dada0a373;hpb=7fbfc32fc3c636959b0a25b3e76609f86519e84a;p=pspp diff --git a/src/output/charts/barchart.c b/src/output/charts/barchart.c index 88a1d741dd..07a39e818f 100644 --- a/src/output/charts/barchart.c +++ b/src/output/charts/barchart.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 2004 Free Software Foundation, Inc. + 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 @@ -14,239 +14,280 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ - #include -#include -#include -#include -#include -#include -#include -#include +#include "output/charts/barchart.h" +#include "output/charts/piechart.h" -#define CATAGORIES 6 -#define SUB_CATAGORIES 3 +#include -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, + 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 ); + const int pidx = 0; + const int sidx = 1; - double bar_width = interval_size / 1.1 ; - double ordinate_scale = fabs(ch->data_top - ch->data_bottom) / - fabs(y_max - y_min) ; + int width = var_get_width (var[pidx]); - if ( opt != BAR_STACKED ) - bar_width /= SUB_CATAGORIES; + assert (n_vars >= 1); - /* Move to data bottom-left */ - pl_move_r(ch->lp, ch->data_left, ch->data_bottom); + bar = xzalloc (sizeof *bar); + 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])); - pl_savestate_r(ch->lp); - pl_filltype_r(ch->lp,1); + bar->largest = -1; + bar->ylabel = strdup (ylabel); - /* Draw the data */ - for (i = 0 ; i < CATAGORIES ; ++i ) { - int sc; - double ystart=0.0; - double x = i * interval_size; - - pl_savestate_r(ch->lp); - - draw_tick (ch, TICK_ABSCISSA, x + (interval_size/2 ), - cat_labels[i]); - - 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++) { + const struct freq *src = cats[i]; + size_t hash = value_hash (&src->values[pidx], width, 0); - pl_savestate_r(ch->lp); - pl_fillcolorname_r(ch->lp,data_colour[sc % N_CHART_COLOURS]); - - switch ( opt ) + struct category *foo; + int flag = 0; + HMAP_FOR_EACH_WITH_HASH (foo, struct category, node, hash, &bar->primaries) { - 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: - - 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; + 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++; + value_init (&s->val, var_get_width (var[pidx])); + value_copy (&s->val, &src->values[pidx], var_get_width (var[pidx])); + 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); - - } - - /* Write the abscissa label */ - pl_move_r(ch->lp,ch->data_left, ch->abscissa_top); - pl_alabel_r(ch->lp,0,'t',xlabel); - - - /* 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); + 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++; + value_init (&s->val, var_get_width (var[sidx])); + 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; + } + } + int n_category = hmap_count (&bar->secondaries); - chart_write_title(ch, title); + sort (bar->ss, n_category, sizeof *bar->ss, + compare_category_3way, bar); + } + - write_legend(ch); + /* 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); + bar->widths[0] = var_get_width (bar->var[0]); + if (n_vars > 1) + bar->widths[1] = var_get_width (bar->var[1]); + { + 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; + 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; + + if (foo->count > bar->largest) + bar->largest = foo->count; + + flag = 1; + 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->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; - - pl_savestate_r(chart->lp); + struct category *foo = NULL; + struct category *next = NULL; + HMAP_FOR_EACH_SAFE (foo, next, struct category, node, m) + { + 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 + };