From: John Darrington Date: Sun, 18 Jan 2015 10:43:06 +0000 (+0100) Subject: Frequencies: Added the /BARCHART subcommand. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?p=pspp;a=commitdiff_plain;h=5443c41f89a2b29cf8cf55f21eecb839fae08177 Frequencies: Added the /BARCHART subcommand. --- diff --git a/NEWS b/NEWS index 5ffa65200e..a52e25f7b4 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,8 @@ Please send PSPP bug reports to bug-gnu-pspp@gnu.org. Changes since 0.8.4: + * The FREQUENCIES command can now generate barcharts. + * The FACTOR command can now perform PROMAX rotations. * SPSS/PC+ system files are now supported on GET and other commands diff --git a/doc/statistics.texi b/doc/statistics.texi index 789e37bc95..f0160f40a8 100644 --- a/doc/statistics.texi +++ b/doc/statistics.texi @@ -136,9 +136,11 @@ FREQUENCIES [@{FREQ[(@var{y_max})],PERCENT[(@var{y_max})]@}] [@{NONORMAL,NORMAL@}] /PIECHART=[MINIMUM(@var{x_min})] [MAXIMUM(@var{x_max})] [@{FREQ,PERCENT@}] [@{NOMISSING,MISSING@}] + /BARCHART=[MINIMUM(@var{x_min})] [MAXIMUM(@var{x_max})] + [@{FREQ,PERCENT@}] + (These options are not currently implemented.) - /BARCHART=@dots{} /HBAR=@dots{} /GROUPED=@dots{} @end display diff --git a/src/language/stats/frequencies.c b/src/language/stats/frequencies.c index 17e0313092..254bda3ef7 100644 --- a/src/language/stats/frequencies.c +++ b/src/language/stats/frequencies.c @@ -1,6 +1,6 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000, 2007, 2009, 2010, 2011, 2014 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2007, 2009, 2010, 2011, 2014, 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 @@ -53,6 +53,7 @@ #include "output/chart-item.h" +#include "output/charts/barchart.h" #include "output/charts/piechart.h" #include "output/charts/plot-hist.h" #include "output/tab.h" @@ -222,7 +223,7 @@ struct frq_proc int n_stats; /* Histogram and pie chart settings. */ - struct frq_chart *hist, *pie; + struct frq_chart *hist, *pie, *bar; }; @@ -241,6 +242,10 @@ static void do_piechart(const struct frq_chart *pie, const struct variable *var, const struct freq_tab *frq_tab); +static void do_barchart(const struct frq_chart *bar, + const struct variable *var, + const struct freq_tab *frq_tab); + static void dump_statistics (const struct frq_proc *frq, const struct var_freqs *vf, const struct variable *wv); @@ -563,6 +568,9 @@ postcalc (struct frq_proc *frq, const struct dataset *ds) if (frq->pie) do_piechart(frq->pie, vf->var, &vf->tab); + if (frq->bar) + do_barchart(frq->bar, vf->var, &vf->tab); + cleanup_freq_tab (vf); } } @@ -582,6 +590,10 @@ cmd_frequencies (struct lexer *lexer, struct dataset *ds) double pie_max = DBL_MAX; bool pie_missing = false; + double bar_min = -DBL_MAX; + double bar_max = DBL_MAX; + bool bar_freq = true; + double hi_min = -DBL_MAX; double hi_max = DBL_MAX; int hi_scale = FRQ_FREQ; @@ -610,6 +622,7 @@ cmd_frequencies (struct lexer *lexer, struct dataset *ds) frq.hist = NULL; frq.pie = NULL; + frq.bar = NULL; /* Accept an optional, completely pointless "/VARIABLES=" */ @@ -980,6 +993,66 @@ cmd_frequencies (struct lexer *lexer, struct dataset *ds) } sbc_piechart = true; } + else if (lex_match_id (lexer, "BARCHART")) + { + lex_match (lexer, T_EQUALS); + while (lex_token (lexer) != T_ENDCMD + && lex_token (lexer) != T_SLASH) + { + if (lex_match_id (lexer, "MINIMUM")) + { + lex_force_match (lexer, T_LPAREN); + if (lex_force_num (lexer)) + { + bar_min = lex_number (lexer); + lex_get (lexer); + } + lex_force_match (lexer, T_RPAREN); + } + else if (lex_match_id (lexer, "MAXIMUM")) + { + lex_force_match (lexer, T_LPAREN); + if (lex_force_num (lexer)) + { + bar_max = lex_number (lexer); + lex_get (lexer); + } + lex_force_match (lexer, T_RPAREN); + } + else if (lex_match_id (lexer, "FREQ")) + { + if ( lex_match (lexer, T_LPAREN)) + { + if (lex_force_num (lexer)) + { + lex_number (lexer); + lex_get (lexer); + } + lex_force_match (lexer, T_RPAREN); + } + bar_freq = true; + } + else if (lex_match_id (lexer, "PERCENT")) + { + if ( lex_match (lexer, T_LPAREN)) + { + if (lex_force_num (lexer)) + { + lex_number (lexer); + lex_get (lexer); + } + lex_force_match (lexer, T_RPAREN); + } + bar_freq = false; + } + else + { + lex_error (lexer, NULL); + goto error; + } + } + sbc_barchart = true; + } else if (lex_match_id (lexer, "MISSING")) { lex_match (lexer, T_EQUALS); @@ -1024,9 +1097,6 @@ cmd_frequencies (struct lexer *lexer, struct dataset *ds) /* Figure out which charts the user requested. */ { - if (sbc_barchart) - msg (SW, _("Bar charts are not implemented.")); - if (sbc_histogram) { struct frq_chart *hist; @@ -1066,6 +1136,15 @@ cmd_frequencies (struct lexer *lexer, struct dataset *ds) frq.n_percentiles+=2; } + if (sbc_barchart) + { + frq.bar = xmalloc (sizeof *frq.bar); + frq.bar->x_min = bar_min; + frq.bar->x_max = bar_max; + frq.bar->include_missing = true; + frq.bar->y_scale = bar_freq ? FRQ_FREQ : FRQ_PERCENT; + } + if (sbc_piechart) { struct frq_chart *pie; @@ -1256,7 +1335,7 @@ add_slice (const struct frq_chart *pie, const struct freq *freq, The caller is responsible for freeing slices */ static struct slice * -freq_tab_to_slice_array(const struct frq_chart *pie, +freq_tab_to_slice_array(const struct frq_chart *catchart, const struct freq_tab *frq_tab, const struct variable *var, int *n_slicesp) @@ -1264,14 +1343,34 @@ freq_tab_to_slice_array(const struct frq_chart *pie, struct slice *slices; int n_slices; int i; + double total = 0; slices = xnmalloc (frq_tab->n_valid + frq_tab->n_missing, sizeof *slices); n_slices = 0; + for (i = 0; i < frq_tab->n_valid; i++) - n_slices += add_slice (pie, &frq_tab->valid[i], var, &slices[n_slices]); + { + const struct freq *f = &frq_tab->valid[i]; + total += f->count; + if (f->count > catchart->x_max) + continue; + + if (f->count < catchart->x_min) + continue; + + n_slices += add_slice (catchart, f, var, &slices[n_slices]); + } + + if (catchart->y_scale == FRQ_PERCENT) + for (i = 0; i < frq_tab->n_valid; i++) + { + slices[i].magnitude /= total; + slices[i].magnitude *= 100.00; + } + for (i = 0; i < frq_tab->n_missing; i++) - n_slices += add_slice (pie, &frq_tab->missing[i], var, &slices[n_slices]); + n_slices += add_slice (catchart, &frq_tab->missing[i], var, &slices[n_slices]); *n_slicesp = n_slices; return slices; @@ -1301,6 +1400,26 @@ do_piechart(const struct frq_chart *pie, const struct variable *var, free (slices); } + +static void +do_barchart(const struct frq_chart *bar, const struct variable *var, + const struct freq_tab *frq_tab) +{ + struct slice *slices; + int n_slices, i; + + slices = freq_tab_to_slice_array (bar, frq_tab, var, &n_slices); + + chart_item_submit (barchart_create (var_to_string (var), + (bar->y_scale == FRQ_FREQ) ? _("Count") : _("Percent"), + slices, n_slices)); + + for (i = 0; i < n_slices; i++) + ds_destroy (&slices[i].label); + free (slices); +} + + /* Calculates all the pertinent statistics for VF, putting them in array D[]. */ static void diff --git a/src/output/automake.mk b/src/output/automake.mk index 39c52d7496..04fddfd942 100644 --- a/src/output/automake.mk +++ b/src/output/automake.mk @@ -14,6 +14,8 @@ src_output_liboutput_la_SOURCES = \ src/output/charts/boxplot.h \ src/output/charts/np-plot.c \ src/output/charts/np-plot.h \ + src/output/charts/barchart.c \ + src/output/charts/barchart.h \ src/output/charts/piechart.c \ src/output/charts/piechart.h \ src/output/charts/plot-hist.c \ @@ -68,6 +70,7 @@ src_output_liboutput_la_SOURCES += \ src/output/cairo.h \ src/output/charts/boxplot-cairo.c \ src/output/charts/np-plot-cairo.c \ + src/output/charts/barchart-cairo.c \ src/output/charts/piechart-cairo.c \ src/output/charts/plot-hist-cairo.c \ src/output/charts/roc-chart-cairo.c \ diff --git a/src/output/cairo.c b/src/output/cairo.c index a962deb7ac..4af75caae4 100644 --- a/src/output/cairo.c +++ b/src/output/cairo.c @@ -30,6 +30,7 @@ #include "output/charts/boxplot.h" #include "output/charts/np-plot.h" #include "output/charts/piechart.h" +#include "output/charts/barchart.h" #include "output/charts/plot-hist.h" #include "output/charts/roc-chart.h" #include "output/charts/spreadlevel-plot.h" @@ -1410,6 +1411,8 @@ xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr, xrchart_draw_np_plot (chart_item, cr, &geom); else if (is_piechart (chart_item)) xrchart_draw_piechart (chart_item, cr, &geom); + else if (is_barchart (chart_item)) + xrchart_draw_barchart (chart_item, cr, &geom); else if (is_roc_chart (chart_item)) xrchart_draw_roc (chart_item, cr, &geom); else if (is_scree (chart_item)) diff --git a/src/output/charts/barchart-cairo.c b/src/output/charts/barchart-cairo.c new file mode 100644 index 0000000000..3ba3f001c5 --- /dev/null +++ b/src/output/charts/barchart-cairo.c @@ -0,0 +1,87 @@ +/* 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 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include + +#include "output/charts/barchart.h" +#include "output/charts/piechart.h" + +#include + +#include "output/cairo-chart.h" + +#include "gl/minmax.h" + +#include "gettext.h" +#define _(msgid) gettext (msgid) + + +static void +draw_bar (cairo_t *cr, const struct xrchart_geometry *geom, + const struct barchart *bc, int bar) +{ + const double width = + (geom->axis[SCALE_ABSCISSA].data_max - geom->axis[SCALE_ABSCISSA].data_min) + / + (double) bc->n_bars ; + + const double x_pos = + (geom->axis[SCALE_ABSCISSA].data_max - geom->axis[SCALE_ABSCISSA].data_min) * + bar + / + (double) bc->n_bars ; + + + double height = geom->axis[SCALE_ORDINATE].scale * bc->bars[bar].magnitude; + + cairo_rectangle (cr, + geom->axis[SCALE_ABSCISSA].data_min + x_pos + width * 0.1, + geom->axis[SCALE_ORDINATE].data_min, + width * 0.8, height); + cairo_save (cr); + cairo_set_source_rgb (cr, + geom->fill_colour.red / 255.0, + geom->fill_colour.green / 255.0, + geom->fill_colour.blue / 255.0); + cairo_fill_preserve (cr); + cairo_restore (cr); + cairo_stroke (cr); + + draw_tick (cr, geom, SCALE_ABSCISSA, true, + x_pos + width / 2.0, "%s", ds_cstr (&bc->bars[bar].label)); +} + + +void +xrchart_draw_barchart (const struct chart_item *chart_item, cairo_t *cr, + struct xrchart_geometry *geom) +{ + struct barchart *bc = to_barchart (chart_item); + int i; + + xrchart_write_title (cr, geom, _("BARCHART")); + + xrchart_write_ylabel (cr, geom, bc->ylabel); + xrchart_write_xlabel (cr, geom, chart_item_get_title (chart_item)); + + xrchart_write_yscale (cr, geom, 0, bc->largest); + + for (i = 0; i < bc->n_bars; i++) + { + draw_bar (cr, geom, bc, i); + } +} + diff --git a/src/output/charts/barchart.c b/src/output/charts/barchart.c new file mode 100644 index 0000000000..f37998747a --- /dev/null +++ b/src/output/charts/barchart.c @@ -0,0 +1,76 @@ +/* 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 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include + +#include "output/charts/barchart.h" +#include "output/charts/piechart.h" + +#include + +#include "libpspp/cast.h" +#include "libpspp/str.h" +#include "output/chart-item-provider.h" + +#include "gl/xalloc.h" + +/* Creates and returns a chart that will render a barchart with + the given TITLE and the N_BARS described in BARS. */ +struct chart_item * +barchart_create (const char *title, const char *ylabel, const struct slice *bars, int n_bars) +{ + struct barchart *bar; + int i; + + bar = xmalloc (sizeof *bar); + chart_item_init (&bar->chart_item, &barchart_class, title); + bar->bars = xnmalloc (n_bars, sizeof *bar->bars); + bar->largest = 0; + bar->ylabel = strdup (ylabel); + for (i = 0; i < n_bars; i++) + { + const struct slice *src = &bars[i]; + struct slice *dst = &bar->bars[i]; + + ds_init_string (&dst->label, &src->label); + dst->magnitude = src->magnitude; + if (dst->magnitude > bar->largest) + bar->largest = dst->magnitude; + } + bar->n_bars = n_bars; + return &bar->chart_item; +} + +static void +barchart_destroy (struct chart_item *chart_item) +{ + struct barchart *bar = to_barchart (chart_item); + int i; + + for (i = 0; i < bar->n_bars; i++) + { + struct slice *slice = &bar->bars[i]; + ds_destroy (&slice->label); + } + free (bar->ylabel); + free (bar->bars); + free (bar); +} + +const struct chart_item_class barchart_class = + { + barchart_destroy + }; diff --git a/src/output/charts/barchart.h b/src/output/charts/barchart.h new file mode 100644 index 0000000000..ea6c27e079 --- /dev/null +++ b/src/output/charts/barchart.h @@ -0,0 +1,95 @@ +/* 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 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef BARCHART_H +#define BARCHART_H + +#include "libpspp/str.h" +#include "output/chart-item.h" + +struct barchart + { + struct chart_item chart_item; + struct slice *bars; + int n_bars; + double largest; + char *ylabel; + }; + +struct chart_item *barchart_create (const char *title, const char *ylabel, + const struct slice *, int n_bars); + +/* This boilerplate for barchart, a subclass of chart_item, was + autogenerated by mk-class-boilerplate. */ + +#include +#include "libpspp/cast.h" + +extern const struct chart_item_class barchart_class; + +/* Returns true if SUPER is a barchart, otherwise false. */ +static inline bool +is_barchart (const struct chart_item *super) +{ + return super->class == &barchart_class; +} + +/* Returns SUPER converted to barchart. SUPER must be a barchart, as + reported by is_barchart. */ +static inline struct barchart * +to_barchart (const struct chart_item *super) +{ + assert (is_barchart (super)); + return UP_CAST (super, struct barchart, chart_item); +} + +/* Returns INSTANCE converted to chart_item. */ +static inline struct chart_item * +barchart_super (const struct barchart *instance) +{ + return CONST_CAST (struct chart_item *, &instance->chart_item); +} + +/* Increments INSTANCE's reference count and returns INSTANCE. */ +static inline struct barchart * +barchart_ref (const struct barchart *instance) +{ + return to_barchart (chart_item_ref (&instance->chart_item)); +} + +/* Decrements INSTANCE's reference count, then destroys INSTANCE if + the reference count is now zero. */ +static inline void +barchart_unref (struct barchart *instance) +{ + chart_item_unref (&instance->chart_item); +} + +/* Returns true if INSTANCE's reference count is greater than 1, + false otherwise. */ +static inline bool +barchart_is_shared (const struct barchart *instance) +{ + return chart_item_is_shared (&instance->chart_item); +} + +static inline void +barchart_submit (struct barchart *instance) +{ + chart_item_submit (&instance->chart_item); +} + +#endif /* output/charts/barchart.h */ diff --git a/tests/output/charts.at b/tests/output/charts.at index 049fc28192..0b951845d8 100644 --- a/tests/output/charts.at +++ b/tests/output/charts.at @@ -155,3 +155,40 @@ dnl The --testing-mode flag is important!! AT_CHECK([pspp --testing-mode -O format=csv histogram.sps], [0], [ignore]) AT_CLEANUP + + +AT_SETUP([FREQUENCIES charts]) +AT_DATA([xxx.sps],[ +DATA LIST LIST /nationality (A10) religion (A20) gender (A8). +BEGIN DATA. +Australian Sikh Male +Australian Sikh Male +Australian Sikh Male +Australian Sikh Male +British Zoroastrian Female +British Buddist Female +British Buddist Female +British Zoroastrian Female +German Muslim Male +German Christian Male +German Christian Female +German Christian Male +German Zoroastrian Female +German Sikh Female +German Muslim Female +German Pastafarian Female +German "Jedi Knight" Female +Belgian Sikh Male +French Muslim Male +French Muslim Male +French Christian Male +END DATA. + + +FREQUENCIES /VARIABLES=religion nationality /BARCHART /PIECHART. +]) + + +AT_CHECK([pspp -O format=csv xxx.sps], [0], [ignore]) + +AT_CLEANUP