1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2004, 2009 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/>. */
23 #include <gsl/gsl_histogram.h>
24 #include <gsl/gsl_randist.h>
27 #include <output/charts/plot-hist.h>
28 #include <output/charts/plot-chart.h>
29 #include <output/chart-provider.h>
31 #include <data/variable.h>
32 #include <libpspp/hash.h>
33 #include <output/chart.h>
34 #include <math/histogram.h>
35 #include <math/moments.h>
38 #define _(msgid) gettext (msgid)
40 static const struct chart_class histogram_chart_class;
42 /* Write the legend of the chart */
44 histogram_write_legend (plPlotter *lp, const struct chart_geometry *geom,
45 double n, double mean, double stddev)
47 double y = geom->data_bottom;
52 char *buf = xasprintf ("N = %.2f", n);
53 pl_move_r (lp, geom->legend_left, y);
54 pl_alabel_r (lp, 0, 'b', buf);
55 y += geom->font_size * 1.5;
61 char *buf = xasprintf ("Mean = %.1f", mean);
62 pl_fmove_r (lp,geom->legend_left, y);
63 pl_alabel_r (lp, 0, 'b', buf);
64 y += geom->font_size * 1.5;
70 char *buf = xasprintf ("Std. Dev = %.2f", stddev);
71 pl_fmove_r (lp, geom->legend_left, y);
72 pl_alabel_r (lp, 0, 'b', buf);
76 pl_restorestate_r (lp);
80 hist_draw_bar (plPlotter *lp, const struct chart_geometry *geom,
81 const gsl_histogram *h, int bar)
87 const size_t bins = gsl_histogram_bins (h);
88 const double x_pos = (geom->data_right - geom->data_left) * bar / (double) bins ;
89 const double width = (geom->data_right - geom->data_left) / (double) bins ;
91 assert ( 0 == gsl_histogram_get_range (h, bar, &lower, &upper));
93 assert ( upper >= lower);
95 height = gsl_histogram_get (h, bar) *
96 (geom->data_top - geom->data_bottom) / gsl_histogram_max_val (h);
99 pl_move_r (lp,geom->data_left, geom->data_bottom);
100 pl_fillcolorname_r (lp, geom->fill_colour);
101 pl_filltype_r (lp,1);
106 x_pos + width, height);
108 pl_restorestate_r (lp);
110 draw_tick (lp, geom, TICK_ABSCISSA,
111 x_pos + width / 2.0, "%g", (upper + lower) / 2.0);
114 struct histogram_chart
117 gsl_histogram *gsl_hist;
125 /* Plots a histogram of the data in HIST with the given LABEL.
126 Labels the histogram with each of N, MEAN, and STDDEV that is
127 not SYSMIS. If all three are not SYSMIS and SHOW_NORMAL is
128 true, also draws a normal curve on the histogram. */
130 histogram_chart_create (const struct histogram *hist, const char *label,
131 double n, double mean, double stddev,
134 struct histogram_chart *h;
136 h = xmalloc (sizeof *h);
137 chart_init (&h->chart, &histogram_chart_class);
138 h->gsl_hist = hist->gsl_hist ? gsl_histogram_clone (hist->gsl_hist) : NULL;
139 h->label = xstrdup (label);
143 h->show_normal = show_normal;
148 histogram_chart_draw (const struct chart *chart, plPlotter *lp)
150 struct histogram_chart *h = (struct histogram_chart *) chart;
151 struct chart_geometry geom;
155 chart_geometry_init (lp, &geom);
157 chart_write_title (lp, &geom, _("HISTOGRAM"));
159 chart_write_ylabel (lp, &geom, _("Frequency"));
160 chart_write_xlabel (lp, &geom, h->label);
162 if (h->gsl_hist == NULL)
164 /* Probably all values are SYSMIS. */
168 bins = gsl_histogram_bins (h->gsl_hist);
170 chart_write_yscale (lp, &geom, 0, gsl_histogram_max_val (h->gsl_hist), 5);
172 for (i = 0; i < bins; i++)
173 hist_draw_bar (lp, &geom, h->gsl_hist, i);
175 histogram_write_legend (lp, &geom, h->n, h->mean, h->stddev);
178 && h->n != SYSMIS && h->mean != SYSMIS && h->stddev != SYSMIS)
180 /* Draw the normal curve */
182 double x_min, x_max, not_used;
183 double abscissa_scale;
184 double ordinate_scale;
187 gsl_histogram_get_range (h->gsl_hist, 0, &x_min, ¬_used);
188 range = not_used - x_min;
189 gsl_histogram_get_range (h->gsl_hist, bins - 1, ¬_used, &x_max);
191 abscissa_scale = (geom.data_right - geom.data_left) / (x_max - x_min);
192 ordinate_scale = (geom.data_top - geom.data_bottom) /
193 gsl_histogram_max_val (h->gsl_hist);
195 pl_move_r (lp, geom.data_left, geom.data_bottom);
196 for (d = geom.data_left;
197 d <= geom.data_right;
198 d += (geom.data_right - geom.data_left) / 100.0)
200 const double x = (d - geom.data_left) / abscissa_scale + x_min;
201 const double y = h->n * range *
202 gsl_ran_gaussian_pdf (x - h->mean, h->stddev);
204 pl_fcont_r (lp, d, geom.data_bottom + y * ordinate_scale);
210 chart_geometry_free (lp);
215 histogram_chart_destroy (struct chart *chart)
217 struct histogram_chart *h = (struct histogram_chart *) chart;
218 if (h->gsl_hist != NULL)
219 gsl_histogram_free (h->gsl_hist);
224 static const struct chart_class histogram_chart_class =
226 histogram_chart_draw,
227 histogram_chart_destroy