output: Make box-whisker plots work again.
authorBen Pfaff <blp@gnu.org>
Sat, 4 Jul 2009 05:33:01 +0000 (22:33 -0700)
committerBen Pfaff <blp@gnu.org>
Sun, 5 Jul 2009 17:51:53 +0000 (10:51 -0700)
src/language/stats/examine.q
src/output/charts/automake.mk
src/output/charts/box-whisker.c
src/output/charts/box-whisker.h

index f0d831ba01201b726c858f303146469caa21ef26..e39dc8d3f74d200046d3c4e287ced638703e88c5 100644 (file)
@@ -549,30 +549,22 @@ show_boxplot_groups (const struct variable **dependent_var,
                     int n_dep_var,
                     const struct xfactor *fctr)
 {
-#if 0
   int v;
 
   for (v = 0; v < n_dep_var; ++v)
     {
-      struct ll *ll;
-      int f = 0;
-      struct chart *ch = chart_create ();
+      const struct factor_result *result;
+      struct boxplot *boxplot;
       double y_min = DBL_MAX;
       double y_max = -DBL_MAX;
+      char *title;
 
-      for (ll = ll_head (&fctr->result_list);
-          ll != ll_null (&fctr->result_list);
-          ll = ll_next (ll))
+      ll_for_each (result, struct factor_result, ll, &fctr->result_list)
        {
+          struct factor_metrics *metrics = &result->metrics[v];
+         const struct ll_list *max_list = extrema_list (metrics->maxima);
+         const struct ll_list *min_list = extrema_list (metrics->minima);
          const struct extremum  *max, *min;
-         const struct factor_result *result =
-           ll_data (ll, struct factor_result, ll);
-
-         const struct ll_list *max_list =
-           extrema_list (result->metrics[v].maxima);
-
-         const struct ll_list *min_list =
-           extrema_list (result->metrics[v].minima);
 
          if ( ll_is_empty (max_list))
            {
@@ -580,54 +572,37 @@ show_boxplot_groups (const struct variable **dependent_var,
              continue;
            }
 
-         max = (const struct extremum *)
-           ll_data (ll_head(max_list), struct extremum, ll);
-
-          min = (const struct extremum *)
-           ll_data (ll_head (min_list), struct extremum, ll);
+         max = ll_data (ll_head(max_list), struct extremum, ll);
+          min = ll_data (ll_head (min_list), struct extremum, ll);
 
          y_max = MAX (y_max, max->value);
          y_min = MIN (y_min, min->value);
        }
 
-      boxplot_draw_yscale (ch, y_max, y_min);
-
-      if ( fctr->indep_var[0])
-       chart_write_title (ch, _("Boxplot of %s vs. %s"),
+      if (fctr->indep_var[0])
+       title = xasprintf (_("Boxplot of %s vs. %s"),
                           var_to_string (dependent_var[v]),
-                          var_to_string (fctr->indep_var[0]) );
+                          var_to_string (fctr->indep_var[0]));
       else
-       chart_write_title (ch, _("Boxplot of %s"),
-                          var_to_string (dependent_var[v]));
+       title = xasprintf (_("Boxplot of %s"),
+                           var_to_string (dependent_var[v]));
+      boxplot = boxplot_create (y_min, y_max, title);
+      free (title);
 
-      for (ll = ll_head (&fctr->result_list);
-          ll != ll_null (&fctr->result_list);
-          ll = ll_next (ll))
+      ll_for_each (result, struct factor_result, ll, &fctr->result_list)
        {
-         const struct factor_result *result =
-           ll_data (ll, struct factor_result, ll);
-
-         struct string str;
-         const double box_width = (ch->data_right - ch->data_left)
-           / (ll_count (&fctr->result_list) * 2.0 ) ;
-
-         const double box_centre = (f++ * 2 + 1) * box_width + ch->data_left;
-
-         ds_init_empty (&str);
+          struct factor_metrics *metrics = &result->metrics[v];
+         struct string str = DS_EMPTY_INITIALIZER;
          factor_to_string_concise (fctr, result, &str);
-
-         boxplot_draw_boxplot (ch,
-                               box_centre, box_width,
-                               (const struct box_whisker *)
-                                result->metrics[v].box_whisker,
-                               ds_cstr (&str));
-
+          boxplot_add_box (boxplot,
+                           (struct box_whisker *) metrics->box_whisker,
+                           ds_cstr (&str));
+          metrics->box_whisker = NULL;
          ds_destroy (&str);
        }
 
-      chart_submit (ch);
+      chart_submit (boxplot_get_chart (boxplot));
     }
-#endif
 }
 
 
@@ -639,77 +614,44 @@ show_boxplot_variables (const struct variable **dependent_var,
                        )
 
 {
-#if 0
+  const struct factor_result *result;
   int v;
-  struct ll *ll;
-  const struct ll_list *result_list = &fctr->result_list;
-
-  for (ll = ll_head (result_list);
-       ll != ll_null (result_list);
-       ll = ll_next (ll))
 
+  ll_for_each (result, struct factor_result, ll, &fctr->result_list)
     {
       struct string title;
-      struct chart *ch = chart_create ();
       double y_min = DBL_MAX;
       double y_max = -DBL_MAX;
-
-      const struct factor_result *result =
-       ll_data (ll, struct factor_result, ll);
-
-      const double box_width = (ch->data_right - ch->data_left)
-       / (n_dep_var * 2.0 ) ;
+      struct boxplot *boxplot;
 
       for (v = 0; v < n_dep_var; ++v)
        {
-         const struct ll *max_ll =
-           ll_head (extrema_list (result->metrics[v].maxima));
-         const struct ll *min_ll =
-           ll_head (extrema_list (result->metrics[v].minima));
-
-         const struct extremum  *max =
-           (const struct extremum *) ll_data (max_ll, struct extremum, ll);
-
-          const struct extremum  *min =
-           (const struct extremum *) ll_data (min_ll, struct extremum, ll);
+          const struct factor_metrics *metrics = &result->metrics[v];
+         const struct ll *max_ll = ll_head (extrema_list (metrics->maxima));
+         const struct ll *min_ll = ll_head (extrema_list (metrics->minima));
+         const struct extremum *max = ll_data (max_ll, struct extremum, ll);
+          const struct extremum *min = ll_data (min_ll, struct extremum, ll);
 
          y_max = MAX (y_max, max->value);
          y_min = MIN (y_min, min->value);
        }
 
-
-      boxplot_draw_yscale (ch, y_max, y_min);
-
       ds_init_empty (&title);
       factor_to_string (fctr, result, &title);
-
-#if 0
-      ds_put_format (&title, "%s = ", var_get_name (fctr->indep_var[0]));
-      var_append_value_name (fctr->indep_var[0], &result->value[0], &title);
-#endif
-
-      chart_write_title (ch, "%s", ds_cstr (&title));
+      boxplot = boxplot_create (y_min, y_max, ds_cstr (&title));
       ds_destroy (&title);
 
       for (v = 0; v < n_dep_var; ++v)
        {
-         struct string str;
-         const double box_centre = (v * 2 + 1) * box_width + ch->data_left;
-
-         ds_init_empty (&str);
-         ds_init_cstr (&str, var_get_name (dependent_var[v]));
-
-         boxplot_draw_boxplot (ch,
-                               box_centre, box_width,
-                               (const struct box_whisker *) result->metrics[v].box_whisker,
-                               ds_cstr (&str));
-
-         ds_destroy (&str);
+          struct factor_metrics *metrics = &result->metrics[v];
+          boxplot_add_box (boxplot,
+                           (struct box_whisker *) metrics->box_whisker,
+                           var_get_name (dependent_var[v]));
+          metrics->box_whisker = NULL;
        }
 
-      chart_submit (ch);
+      chart_submit (boxplot_get_chart (boxplot));
     }
-#endif
 }
 
 
@@ -757,16 +699,14 @@ output_examine (const struct dictionary *dict)
       if ( cmd.sbc_percentiles)
        show_percentiles (dependent_vars, n_dependent_vars, factor);
 
-      if (cmd.a_plot[XMN_PLT_BOXPLOT] &&
-         cmd.cmp == XMN_GROUPS)
-       show_boxplot_groups (dependent_vars, n_dependent_vars, factor);
-
-
-      if (cmd.a_plot[XMN_PLT_BOXPLOT] &&
-         cmd.cmp == XMN_VARIABLES)
-       show_boxplot_variables (dependent_vars, n_dependent_vars,
-                               factor);
-
+      if (cmd.a_plot[XMN_PLT_BOXPLOT])
+        {
+          if (cmd.cmp == XMN_GROUPS)
+            show_boxplot_groups (dependent_vars, n_dependent_vars, factor);
+          else if (cmd.cmp == XMN_VARIABLES)
+            show_boxplot_variables (dependent_vars, n_dependent_vars, factor);
+        }
+      
       if (cmd.a_plot[XMN_PLT_HISTOGRAM])
        show_histogram (dependent_vars, n_dependent_vars, factor);
 
index e260f3283a9facf7208957ae82924b76aa831107..b2a65636b384352218b514b2f5d7a38714332b53 100644 (file)
@@ -3,6 +3,8 @@
 noinst_LTLIBRARIES += src/output/charts/libcharts.la
 
 chart_sources = \
+       src/output/charts/box-whisker.c \
+       src/output/charts/box-whisker.h \
        src/output/charts/cartesian.c \
        src/output/charts/cartesian.h \
        src/output/charts/piechart.c \
index 33c445b09a61b0ef36714272bd221bfd2cd9f741..301d762b8b8fca1039a6ed197c30457175097f93 100644 (file)
 
 #include <config.h>
 
+#include <output/charts/box-whisker.h>
+
 #include <math.h>
 #include <assert.h>
-#include <libpspp/misc.h>
-
-#include <output/charts/box-whisker.h>
-#include <output/charts/plot-chart.h>
 
-#include <output/chart.h>
+#include <libpspp/misc.h>
 #include <math/chart-geometry.h>
 #include <math/box-whisker.h>
+#include <output/chart.h>
+#include <output/chart-provider.h>
+#include <output/charts/plot-chart.h>
 
 /* Draw a box-and-whiskers plot
 */
 
+struct box
+  {
+    struct box_whisker *bw;
+    char *label;
+  };
+
+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;
+}
+
+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);
+}
+
+struct chart *
+boxplot_get_chart (struct boxplot *boxplot)
+{
+  return &boxplot->chart;
+}
+
 /* Draw an OUTLIER on the plot CH
  * at CENTRELINE
  */
 static void
-draw_case (struct chart *ch, double centreline,
+draw_case (plPlotter *lp, const struct chart_geometry *geom, double centreline,
           const struct outlier *outlier)
 {
 
 #define MARKER_CIRCLE 4
 #define MARKER_STAR 3
 
-  pl_fmarker_r(ch->lp,
+  pl_fmarker_r(lp,
               centreline,
-              ch->data_bottom + (outlier->value - ch->y_min) * ch->ordinate_scale,
+              geom->data_bottom + (outlier->value - geom->y_min) * geom->ordinate_scale,
               outlier->extreme ? MARKER_STAR : MARKER_CIRCLE,
               20);
 
-  pl_moverel_r(ch->lp, 10,0);
+  pl_moverel_r(lp, 10,0);
 
-  pl_alabel_r(ch->lp, 'l', 'c', ds_cstr (&outlier->label));
+  pl_alabel_r(lp, 'l', 'c', ds_cstr (&outlier->label));
 }
 
-
-void
-boxplot_draw_boxplot (struct chart *ch,
-                     double box_centre,
-                     double box_width,
-                     const struct box_whisker *bw,
-                     const char *name)
+static void
+boxplot_draw_box (plPlotter *lp, const struct chart_geometry *geom,
+                  double box_centre,
+                  double box_width,
+                  const struct box_whisker *bw,
+                  const char *name)
 {
   double whisker[2];
   double hinge[3];
@@ -79,48 +129,48 @@ boxplot_draw_boxplot (struct chart *ch,
   box_whisker_whiskers (bw, whisker);
   box_whisker_hinges (bw, hinge);
 
-  box_bottom = ch->data_bottom + (hinge[0] - ch->y_min ) * ch->ordinate_scale;
+  box_bottom = geom->data_bottom + (hinge[0] - geom->y_min ) * geom->ordinate_scale;
 
-  box_top = ch->data_bottom + (hinge[2] - ch->y_min ) * ch->ordinate_scale;
+  box_top = geom->data_bottom + (hinge[2] - geom->y_min ) * geom->ordinate_scale;
 
-  bottom_whisker = ch->data_bottom + (whisker[0] - ch->y_min) *
-    ch->ordinate_scale;
+  bottom_whisker = geom->data_bottom + (whisker[0] - geom->y_min) *
+    geom->ordinate_scale;
 
-  top_whisker = ch->data_bottom + (whisker[1] - ch->y_min) * ch->ordinate_scale;
+  top_whisker = geom->data_bottom + (whisker[1] - geom->y_min) * geom->ordinate_scale;
 
-  pl_savestate_r(ch->lp);
+  pl_savestate_r(lp);
 
   /* 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,
+  pl_savestate_r (lp);
+  pl_fillcolorname_r (lp, geom->fill_colour);
+  pl_filltype_r (lp,1);
+  pl_fbox_r (lp,
            box_left,
            box_bottom,
            box_right,
            box_top);
 
-  pl_restorestate_r (ch->lp);
+  pl_restorestate_r (lp);
 
   /* Draw the median */
-  pl_savestate_r (ch->lp);
-  pl_linewidth_r (ch->lp, 5);
-  pl_fline_r (ch->lp,
+  pl_savestate_r (lp);
+  pl_linewidth_r (lp, 5);
+  pl_fline_r (lp,
             box_left,
-            ch->data_bottom + (hinge[1] - ch->y_min) * ch->ordinate_scale,
+            geom->data_bottom + (hinge[1] - geom->y_min) * geom->ordinate_scale,
             box_right,
-            ch->data_bottom + (hinge[1] - ch->y_min) * ch->ordinate_scale);
-  pl_restorestate_r (ch->lp);
+            geom->data_bottom + (hinge[1] - geom->y_min) * geom->ordinate_scale);
+  pl_restorestate_r (lp);
 
   /* Draw the bottom whisker */
-  pl_fline_r (ch->lp,
+  pl_fline_r (lp,
             box_left,
             bottom_whisker,
             box_right,
             bottom_whisker);
 
   /* Draw top whisker */
-  pl_fline_r (ch->lp,
+  pl_fline_r (lp,
             box_left,
             top_whisker,
             box_right,
@@ -129,12 +179,12 @@ boxplot_draw_boxplot (struct chart *ch,
 
   /* Draw centre line.
      (bottom half) */
-  pl_fline_r (ch->lp,
+  pl_fline_r (lp,
             box_centre, bottom_whisker,
             box_centre, box_bottom);
 
   /* (top half) */
-  pl_fline_r (ch->lp,
+  pl_fline_r (lp,
             box_centre, top_whisker,
             box_centre, box_top);
 
@@ -143,42 +193,85 @@ boxplot_draw_boxplot (struct chart *ch,
        ll != ll_null (outliers); ll = ll_next (ll))
     {
       const struct outlier *outlier = ll_data (ll, struct outlier, ll);
-      draw_case (ch, box_centre, outlier);
+      draw_case (lp, geom, box_centre, outlier);
     }
 
   /* Draw  tick  mark on x axis */
-  draw_tick(ch, TICK_ABSCISSA, box_centre - ch->data_left, "%s", name);
+  draw_tick(lp, geom, TICK_ABSCISSA, box_centre - geom->data_left, "%s", name);
 
-  pl_restorestate_r(ch->lp);
+  pl_restorestate_r(lp);
 }
 
-void
-boxplot_draw_yscale (struct chart *ch, double y_max, double y_min)
+static void
+boxplot_draw_yscale (plPlotter *lp, struct chart_geometry *geom,
+                     double y_max, double y_min)
 {
   double y_tick;
   double d;
 
-  if ( !ch )
-     return ;
-
-  ch->y_max  = y_max;
-  ch->y_min  = y_min;
+  geom->y_max = y_max;
+  geom->y_min = y_min;
 
-  y_tick = chart_rounded_tick (fabs(ch->y_max - ch->y_min) / 5.0);
+  y_tick = chart_rounded_tick (fabs (geom->y_max - geom->y_min) / 5.0);
 
-  ch->y_min = (ceil( ch->y_min  / y_tick ) - 1.0  ) * y_tick;
+  geom->y_min = (ceil (geom->y_min / y_tick) - 1.0) * y_tick;
 
-  ch->y_max = ( floor( ch->y_max  / y_tick ) + 1.0  ) * y_tick;
+  geom->y_max = (floor (geom->y_max / y_tick) + 1.0) * y_tick;
 
-  ch->ordinate_scale = fabs(ch->data_top - ch->data_bottom)
-    / fabs(ch->y_max - ch->y_min) ;
+  geom->ordinate_scale = (fabs (geom->data_top - geom->data_bottom)
+                          / fabs (geom->y_max - geom->y_min));
 
   /* Move to data bottom-left */
-  pl_move_r(ch->lp,
-           ch->data_left, ch->data_bottom);
+  pl_move_r (lp, geom->data_left, geom->data_bottom);
+
+  for (d = geom->y_min; d <= geom->y_max; d += y_tick)
+    draw_tick (lp, geom, TICK_ORDINATE,
+               (d - geom->y_min) * geom->ordinate_scale, "%g", d);
+}
+
+static void
+boxplot_chart_draw (const struct chart *chart, plPlotter *lp)
+{
+  const struct boxplot *boxplot = (struct boxplot *) chart;
+  struct chart_geometry geom;
+  double box_width;
+  size_t i;
 
-  for ( d = ch->y_min; d <= ch->y_max ; d += y_tick )
+  chart_geometry_init (lp, &geom);
+  boxplot_draw_yscale (lp, &geom, boxplot->y_max, boxplot->y_min);
+  chart_write_title (lp, &geom, "%s", boxplot->title);
+
+  box_width = (geom.data_right - geom.data_left) / boxplot->n_boxes / 2.0;
+  for (i = 0; i < boxplot->n_boxes; i++)
+    {
+      const struct box *box = &boxplot->boxes[i];
+      const double box_centre = (i * 2 + 1) * box_width + geom.data_left;
+      boxplot_draw_box (lp, &geom, box_centre, box_width, box->bw, box->label);
+    }
+
+  chart_geometry_free (lp);
+}
+
+static void
+boxplot_chart_destroy (struct chart *chart)
+{
+  struct boxplot *boxplot = (struct boxplot *) chart;
+  size_t i;
+
+  free (boxplot->title);
+  for (i = 0; i < boxplot->n_boxes; i++)
     {
-      draw_tick (ch, TICK_ORDINATE, (d - ch->y_min ) * ch->ordinate_scale, "%g", d);
+      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
+  };
index 7b2c4b8fdc1c2407c86cfe308a1c5ed4b72a31ba..67c1e1281192d640ae6a96bc0b20e3ed7130c272 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 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
 #ifndef BOX_WHISKER_H
 #define BOX_WHISKER_H
 
-struct chart ;
 struct box_whisker;
 
-void boxplot_draw_boxplot (struct chart *ch,
-                          double box_centre,
-                          double box_width,
-                          const struct box_whisker *w,
-                          const char *name);
-
-
-void boxplot_draw_yscale (struct chart *ch , double y_max, double y_min);
+struct boxplot *boxplot_create (double y_min, double y_max, const char *title);
+void boxplot_add_box (struct boxplot *,
+                      struct box_whisker *, const char *label);
+struct chart *boxplot_get_chart (struct boxplot *);
 
 #endif