X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Foutput%2Fcharts%2Fbox-whisker.c;h=5fd9d295f749189aa20358712ba782bac0034b5a;hb=507ef4ef1ad945accdbc86e97deefc1b001d266e;hp=173906644508c727e4315d184ecbcfd9e2e64e39;hpb=000e3e8c5818476c3afbc75fad9347aefb6e902a;p=pspp-builds.git diff --git a/src/output/charts/box-whisker.c b/src/output/charts/box-whisker.c index 17390664..5fd9d295 100644 --- a/src/output/charts/box-whisker.c +++ b/src/output/charts/box-whisker.c @@ -1,243 +1,263 @@ -/* PSPP - computes sample statistics. - Copyright (C) 2004 Free Software Foundation, Inc. - Written by John Darrington +/* PSPP - a program for statistical analysis. + Copyright (C) 2004, 2008, 2009 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 2 of the - License, or (at your option) any later version. + 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. + 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with this program. If not, see . */ -#include "chart.h" -#include "chart-geometry.h" +#include + +#include + #include #include -#include "misc.h" - -#include "factor-stats.h" -#include "box-whisker.h" -#include "plot-chart.h" +#include +#include +#include +#include +#include +#include +#include +#include /* Draw a box-and-whiskers plot */ -/* Draw an outlier on the plot CH - * at CENTRELINE - * The outlier is in (*wvp)[idx] - * If EXTREME is non zero, then consider it to be an extreme - * value - */ -void -draw_outlier(struct chart *ch, double centreline, - struct weighted_value **wvp, - int idx, - short extreme); - - -void -draw_outlier(struct chart *ch, double centreline, - struct weighted_value **wvp, - int idx, - short extreme - ) -{ - char label[10]; +struct box + { + struct box_whisker *bw; + char *label; + }; -#define MARKER_CIRCLE 4 -#define MARKER_STAR 3 +struct boxplot + { + struct chart chart; + double y_min; + double y_max; + char *title; + struct box *boxes; + size_t n_boxes, boxes_allocated; + }; + +static const struct chart_class boxplot_chart_class; + +struct boxplot * +boxplot_create (double y_min, double y_max, const char *title) +{ + struct boxplot *boxplot = xmalloc (sizeof *boxplot); + chart_init (&boxplot->chart, &boxplot_chart_class); + boxplot->y_min = y_min; + boxplot->y_max = y_max; + boxplot->title = xstrdup (title); + boxplot->boxes = NULL; + boxplot->n_boxes = boxplot->boxes_allocated = 0; + return boxplot; +} - pl_fmarker_r(ch->lp, - centreline, - ch->data_bottom + - (wvp[idx]->v.f - ch->y_min ) * ch->ordinate_scale, - extreme?MARKER_STAR:MARKER_CIRCLE, - 20); +void +boxplot_add_box (struct boxplot *boxplot, + struct box_whisker *bw, const char *label) +{ + struct box *box; + if (boxplot->n_boxes >= boxplot->boxes_allocated) + boxplot->boxes = x2nrealloc (boxplot->boxes, &boxplot->boxes_allocated, + sizeof *boxplot->boxes); + box = &boxplot->boxes[boxplot->n_boxes++]; + box->bw = bw; + box->label = xstrdup (label); +} - pl_moverel_r(ch->lp, 10,0); +struct chart * +boxplot_get_chart (struct boxplot *boxplot) +{ + return &boxplot->chart; +} - snprintf(label, 10, "%d", wvp[idx]->case_nos->num); - - pl_alabel_r(ch->lp, 'l', 'c', label); +/* Draw an OUTLIER on the plot CH + * at CENTRELINE + */ +static void +draw_case (cairo_t *cr, const struct chart_geometry *geom, double centreline, + const struct outlier *outlier) +{ + double y = geom->data_bottom + (outlier->value - geom->y_min) * geom->ordinate_scale; + chart_draw_marker (cr, centreline, y, + outlier->extreme ? MARKER_ASTERISK : MARKER_CIRCLE, + 20); + cairo_move_to (cr, centreline + 10, y); + chart_label (cr, 'l', 'c', geom->font_size, ds_cstr (&outlier->label)); } - -void -boxplot_draw_boxplot(struct chart *ch, - double box_centre, - double box_width, - struct metrics *m, - const char *name) +static void +boxplot_draw_box (cairo_t *cr, const struct chart_geometry *geom, + double box_centre, + double box_width, + const struct box_whisker *bw, + const char *name) { double whisker[2]; - int i; - - const double *hinge = m->hinge; - struct weighted_value **wvp = m->wvp; - const int n_data = m->n_data; - - const double step = (hinge[2] - hinge[0]) * 1.5; + double hinge[3]; + struct ll *ll; + const struct ll_list *outliers; const double box_left = box_centre - box_width / 2.0; const double box_right = box_centre + box_width / 2.0; + double box_bottom ; + double box_top ; + double bottom_whisker ; + double top_whisker ; - const double box_bottom = - ch->data_bottom + ( hinge[0] - ch->y_min ) * ch->ordinate_scale; - - - const double box_top = - ch->data_bottom + ( hinge[2] - ch->y_min ) * ch->ordinate_scale; + box_whisker_whiskers (bw, whisker); + box_whisker_hinges (bw, hinge); - assert(m); + box_bottom = geom->data_bottom + (hinge[0] - geom->y_min ) * geom->ordinate_scale; - /* Can't really draw a boxplot if there's no data */ - if ( n_data == 0 ) - return ; + box_top = geom->data_bottom + (hinge[2] - geom->y_min ) * geom->ordinate_scale; - whisker[1] = hinge[2]; - whisker[0] = wvp[0]->v.f; - - for ( i = 0 ; i < n_data ; ++i ) - { - if ( hinge[2] + step > wvp[i]->v.f) - whisker[1] = wvp[i]->v.f; - - if ( hinge[0] - step > wvp[i]->v.f) - whisker[0] = wvp[i]->v.f; - - } - - { - const double bottom_whisker = - ch->data_bottom + ( whisker[0] - ch->y_min ) * ch->ordinate_scale; - - const double top_whisker = - ch->data_bottom + ( whisker[1] - ch->y_min ) * ch->ordinate_scale; - - - pl_savestate_r(ch->lp); + bottom_whisker = geom->data_bottom + (whisker[0] - geom->y_min) * + geom->ordinate_scale; + top_whisker = geom->data_bottom + (whisker[1] - geom->y_min) * geom->ordinate_scale; /* Draw the box */ - pl_savestate_r(ch->lp); - pl_fillcolorname_r(ch->lp,ch->fill_colour); - pl_filltype_r(ch->lp,1); - pl_fbox_r(ch->lp, - box_left, - box_bottom, - box_right, - box_top); + cairo_rectangle (cr, + box_left, + box_bottom, + box_right - box_left, + box_top - box_bottom); + 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 (cr); + cairo_restore (cr); + cairo_stroke (cr); - pl_restorestate_r(ch->lp); - - - /* Draw the median */ - pl_savestate_r(ch->lp); - pl_linewidth_r(ch->lp,5); - pl_fline_r(ch->lp, - box_left, - ch->data_bottom + ( hinge[1] - ch->y_min ) * ch->ordinate_scale, - box_right, - ch->data_bottom + ( hinge[1] - ch->y_min ) * ch->ordinate_scale); - pl_restorestate_r(ch->lp); - + cairo_save (cr); + cairo_set_line_width (cr, cairo_get_line_width (cr) * 5); + cairo_move_to (cr, + box_left, + geom->data_bottom + (hinge[1] - geom->y_min) * geom->ordinate_scale); + cairo_line_to (cr, + box_right, + geom->data_bottom + (hinge[1] - geom->y_min) * geom->ordinate_scale); + cairo_stroke (cr); + cairo_restore (cr); /* Draw the bottom whisker */ - pl_fline_r(ch->lp, - box_left, - bottom_whisker, - box_right, - bottom_whisker); + cairo_move_to (cr, box_left, bottom_whisker); + cairo_line_to (cr, box_right, bottom_whisker); + cairo_stroke (cr); /* Draw top whisker */ - pl_fline_r(ch->lp, - box_left, - top_whisker, - box_right, - top_whisker); - - + cairo_move_to (cr, box_left, top_whisker); + cairo_line_to (cr, box_right, top_whisker); + cairo_stroke (cr); /* Draw centre line. (bottom half) */ - pl_fline_r(ch->lp, - box_centre, bottom_whisker, - box_centre, box_bottom); + cairo_move_to (cr, box_centre, bottom_whisker); + cairo_line_to (cr, box_centre, box_bottom); + cairo_stroke (cr); /* (top half) */ - pl_fline_r(ch->lp, - box_centre, top_whisker, - box_centre, box_top); - } + cairo_move_to (cr, box_centre, top_whisker); + cairo_line_to (cr, box_centre, box_top); + cairo_stroke (cr); - /* Draw outliers */ - for ( i = 0 ; i < n_data ; ++i ) + outliers = box_whisker_outliers (bw); + for (ll = ll_head (outliers); + ll != ll_null (outliers); ll = ll_next (ll)) { - if ( wvp[i]->v.f >= hinge[2] + step ) - draw_outlier(ch, box_centre, wvp, i, - ( wvp[i]->v.f > hinge[2] + 2 * step ) - ); - - if ( wvp[i]->v.f <= hinge[0] - step ) - draw_outlier(ch, box_centre, wvp, i, - ( wvp[i]->v.f < hinge[0] - 2 * step ) - ); + const struct outlier *outlier = ll_data (ll, struct outlier, ll); + draw_case (cr, geom, box_centre, outlier); } - /* Draw tick mark on x axis */ - draw_tick(ch, TICK_ABSCISSA, box_centre - ch->data_left, name); - - pl_restorestate_r(ch->lp); - + draw_tick(cr, geom, TICK_ABSCISSA, box_centre - geom->data_left, "%s", name); } - - -void -boxplot_draw_yscale(struct chart *ch , double y_max, double y_min) +static void +boxplot_draw_yscale (cairo_t *cr, struct chart_geometry *geom, + double y_max, double y_min) { double y_tick; double d; - if ( !ch ) - return ; + geom->y_max = y_max; + geom->y_min = y_min; + + y_tick = chart_rounded_tick (fabs (geom->y_max - geom->y_min) / 5.0); - ch->y_max = y_max; - ch->y_min = y_min; + geom->y_min = (ceil (geom->y_min / y_tick) - 1.0) * y_tick; - y_tick = chart_rounded_tick(fabs(ch->y_max - ch->y_min) / 5.0); + geom->y_max = (floor (geom->y_max / y_tick) + 1.0) * y_tick; - ch->y_min = (ceil( ch->y_min / y_tick ) - 1.0 ) * y_tick; - - ch->y_max = ( floor( ch->y_max / y_tick ) + 1.0 ) * y_tick; + geom->ordinate_scale = (fabs (geom->data_top - geom->data_bottom) + / fabs (geom->y_max - geom->y_min)); - ch->ordinate_scale = fabs(ch->data_top - ch->data_bottom) - / fabs(ch->y_max - ch->y_min) ; + for (d = geom->y_min; d <= geom->y_max; d += y_tick) + draw_tick (cr, geom, TICK_ORDINATE, + (d - geom->y_min) * geom->ordinate_scale, "%g", d); +} +static void +boxplot_chart_draw (const struct chart *chart, cairo_t *cr, + struct chart_geometry *geom) +{ + const struct boxplot *boxplot = UP_CAST (chart, struct boxplot, chart); + double box_width; + size_t i; - /* Move to data bottom-left */ - pl_move_r(ch->lp, - ch->data_left, ch->data_bottom); + boxplot_draw_yscale (cr, geom, boxplot->y_max, boxplot->y_min); + chart_write_title (cr, geom, "%s", boxplot->title); - for ( d = ch->y_min; d <= ch->y_max ; d += y_tick ) + box_width = (geom->data_right - geom->data_left) / boxplot->n_boxes / 2.0; + for (i = 0; i < boxplot->n_boxes; i++) { - draw_tick (ch, TICK_ORDINATE, (d - ch->y_min ) * ch->ordinate_scale, "%g", d); + const struct box *box = &boxplot->boxes[i]; + const double box_centre = (i * 2 + 1) * box_width + geom->data_left; + boxplot_draw_box (cr, geom, box_centre, box_width, box->bw, box->label); } +} +static void +boxplot_chart_destroy (struct chart *chart) +{ + struct boxplot *boxplot = UP_CAST (chart, struct boxplot, chart); + size_t i; + + free (boxplot->title); + for (i = 0; i < boxplot->n_boxes; i++) + { + struct box *box = &boxplot->boxes[i]; + struct statistic *statistic = &box->bw->parent.parent; + statistic->destroy (statistic); + free (box->label); + } + free (boxplot->boxes); + free (boxplot); } + +static const struct chart_class boxplot_chart_class = + { + boxplot_chart_draw, + boxplot_chart_destroy + };