1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2004, 2008, 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/>. */
20 #include <output/charts/boxplot.h>
24 #include <cairo/cairo.h>
26 #include <libpspp/cast.h>
27 #include <libpspp/misc.h>
28 #include <math/chart-geometry.h>
29 #include <math/box-whisker.h>
30 #include <output/chart.h>
31 #include <output/chart-provider.h>
32 #include <output/charts/plot-chart.h>
34 /* Draw a box-and-whiskers plot
39 struct box_whisker *bw;
50 size_t n_boxes, boxes_allocated;
53 static const struct chart_class boxplot_chart_class;
56 boxplot_create (double y_min, double y_max, const char *title)
58 struct boxplot *boxplot = xmalloc (sizeof *boxplot);
59 chart_init (&boxplot->chart, &boxplot_chart_class);
60 boxplot->y_min = y_min;
61 boxplot->y_max = y_max;
62 boxplot->title = xstrdup (title);
63 boxplot->boxes = NULL;
64 boxplot->n_boxes = boxplot->boxes_allocated = 0;
69 boxplot_add_box (struct boxplot *boxplot,
70 struct box_whisker *bw, const char *label)
73 if (boxplot->n_boxes >= boxplot->boxes_allocated)
74 boxplot->boxes = x2nrealloc (boxplot->boxes, &boxplot->boxes_allocated,
75 sizeof *boxplot->boxes);
76 box = &boxplot->boxes[boxplot->n_boxes++];
78 box->label = xstrdup (label);
82 boxplot_get_chart (struct boxplot *boxplot)
84 return &boxplot->chart;
87 /* Draw an OUTLIER on the plot CH
91 draw_case (cairo_t *cr, const struct chart_geometry *geom, double centreline,
92 const struct outlier *outlier)
94 double y = geom->data_bottom + (outlier->value - geom->y_min) * geom->ordinate_scale;
95 chart_draw_marker (cr, centreline, y,
96 outlier->extreme ? MARKER_ASTERISK : MARKER_CIRCLE,
99 cairo_move_to (cr, centreline + 10, y);
100 chart_label (cr, 'l', 'c', geom->font_size, ds_cstr (&outlier->label));
104 boxplot_draw_box (cairo_t *cr, const struct chart_geometry *geom,
107 const struct box_whisker *bw,
114 const struct ll_list *outliers;
116 const double box_left = box_centre - box_width / 2.0;
118 const double box_right = box_centre + box_width / 2.0;
122 double bottom_whisker ;
125 box_whisker_whiskers (bw, whisker);
126 box_whisker_hinges (bw, hinge);
128 box_bottom = geom->data_bottom + (hinge[0] - geom->y_min ) * geom->ordinate_scale;
130 box_top = geom->data_bottom + (hinge[2] - geom->y_min ) * geom->ordinate_scale;
132 bottom_whisker = geom->data_bottom + (whisker[0] - geom->y_min) *
133 geom->ordinate_scale;
135 top_whisker = geom->data_bottom + (whisker[1] - geom->y_min) * geom->ordinate_scale;
141 box_right - box_left,
142 box_top - box_bottom);
144 cairo_set_source_rgb (cr,
145 geom->fill_colour.red / 255.0,
146 geom->fill_colour.green / 255.0,
147 geom->fill_colour.blue / 255.0);
152 /* Draw the median */
154 cairo_set_line_width (cr, cairo_get_line_width (cr) * 5);
157 geom->data_bottom + (hinge[1] - geom->y_min) * geom->ordinate_scale);
160 geom->data_bottom + (hinge[1] - geom->y_min) * geom->ordinate_scale);
164 /* Draw the bottom whisker */
165 cairo_move_to (cr, box_left, bottom_whisker);
166 cairo_line_to (cr, box_right, bottom_whisker);
169 /* Draw top whisker */
170 cairo_move_to (cr, box_left, top_whisker);
171 cairo_line_to (cr, box_right, top_whisker);
176 cairo_move_to (cr, box_centre, bottom_whisker);
177 cairo_line_to (cr, box_centre, box_bottom);
181 cairo_move_to (cr, box_centre, top_whisker);
182 cairo_line_to (cr, box_centre, box_top);
185 outliers = box_whisker_outliers (bw);
186 for (ll = ll_head (outliers);
187 ll != ll_null (outliers); ll = ll_next (ll))
189 const struct outlier *outlier = ll_data (ll, struct outlier, ll);
190 draw_case (cr, geom, box_centre, outlier);
193 /* Draw tick mark on x axis */
194 draw_tick(cr, geom, TICK_ABSCISSA, box_centre - geom->data_left, "%s", name);
198 boxplot_draw_yscale (cairo_t *cr, struct chart_geometry *geom,
199 double y_max, double y_min)
207 y_tick = chart_rounded_tick (fabs (geom->y_max - geom->y_min) / 5.0);
209 geom->y_min = (ceil (geom->y_min / y_tick) - 1.0) * y_tick;
211 geom->y_max = (floor (geom->y_max / y_tick) + 1.0) * y_tick;
213 geom->ordinate_scale = (fabs (geom->data_top - geom->data_bottom)
214 / fabs (geom->y_max - geom->y_min));
216 for (d = geom->y_min; d <= geom->y_max; d += y_tick)
217 draw_tick (cr, geom, TICK_ORDINATE,
218 (d - geom->y_min) * geom->ordinate_scale, "%g", d);
222 boxplot_chart_draw (const struct chart *chart, cairo_t *cr,
223 struct chart_geometry *geom)
225 const struct boxplot *boxplot = UP_CAST (chart, struct boxplot, chart);
229 boxplot_draw_yscale (cr, geom, boxplot->y_max, boxplot->y_min);
230 chart_write_title (cr, geom, "%s", boxplot->title);
232 box_width = (geom->data_right - geom->data_left) / boxplot->n_boxes / 2.0;
233 for (i = 0; i < boxplot->n_boxes; i++)
235 const struct box *box = &boxplot->boxes[i];
236 const double box_centre = (i * 2 + 1) * box_width + geom->data_left;
237 boxplot_draw_box (cr, geom, box_centre, box_width, box->bw, box->label);
242 boxplot_chart_destroy (struct chart *chart)
244 struct boxplot *boxplot = UP_CAST (chart, struct boxplot, chart);
247 free (boxplot->title);
248 for (i = 0; i < boxplot->n_boxes; i++)
250 struct box *box = &boxplot->boxes[i];
251 struct statistic *statistic = &box->bw->parent.parent;
252 statistic->destroy (statistic);
255 free (boxplot->boxes);
259 static const struct chart_class boxplot_chart_class =
262 boxplot_chart_destroy