Move implementation of NP plots out of EXAMINE into the charts engine.
authorBen Pfaff <blp@gnu.org>
Mon, 20 Jul 2009 00:11:21 +0000 (17:11 -0700)
committerBen Pfaff <blp@gnu.org>
Mon, 20 Jul 2009 00:11:53 +0000 (17:11 -0700)
This seems like a helpful cleanup.  It should also ease making it possible
to again build PSPP without libplot, which was broken a number of commits
ago.

src/language/stats/examine.q
src/output/charts/automake.mk
src/output/charts/np-plot.c [new file with mode: 0644]
src/output/charts/np-plot.h [new file with mode: 0644]

index e39dc8d3f74d200046d3c4e287ced638703e88c5..d17aebf97efa8b839c27731aa4d2d3a73d469371 100644 (file)
@@ -48,9 +48,8 @@
 #include <libpspp/misc.h>
 #include <libpspp/str.h>
 #include <math/moments.h>
-#include <output/chart-provider.h>
 #include <output/charts/box-whisker.h>
-#include <output/charts/cartesian.h>
+#include <output/charts/np-plot.h>
 #include <output/manager.h>
 #include <output/table.h>
 
@@ -321,150 +320,6 @@ cmd_examine (struct lexer *lexer, struct dataset *ds)
 };
 
 
-struct np_plot_chart
-  {
-    struct chart chart;
-    char *label;
-    struct casereader *data;
-
-    /* Copied directly from struct np. */
-    double y_min, y_max;
-    double dns_min, dns_max;
-
-    /* Calculated. */
-    double slope, intercept;
-    double y_first, y_last;
-    double x_lower, x_upper;
-    double slack;
-  };
-
-static const struct chart_class np_plot_chart_class;
-static const struct chart_class dnp_plot_chart_class;
-
-/* Plot the normal and detrended normal plots for RESULT.
-   Label the plots with LABEL */
-static void
-np_plot (struct np *np, const char *label)
-{
-  struct np_plot_chart *np_plot, *dnp_plot;
-
-  if ( np->n < 1.0 )
-    {
-      msg (MW, _("Not creating plot because data set is empty."));
-      return ;
-    }
-
-  np_plot = xmalloc (sizeof *np_plot);
-  chart_init (&np_plot->chart, &np_plot_chart_class);
-  np_plot->label = xstrdup (label);
-  np_plot->data = casewriter_make_reader (np->writer);
-  np_plot->y_min = np->y_min;
-  np_plot->y_max = np->y_max;
-  np_plot->dns_min = np->dns_min;
-  np_plot->dns_max = np->dns_max;
-
-  /* Slope and intercept of the ideal normal probability line. */
-  np_plot->slope = 1.0 / np->stddev;
-  np_plot->intercept = -np->mean / np->stddev;
-
-  np_plot->y_first = gsl_cdf_ugaussian_Pinv (1 / (np->n + 1));
-  np_plot->y_last = gsl_cdf_ugaussian_Pinv (np->n / (np->n + 1));
-
-  /* Need to make sure that both the scatter plot and the ideal fit into the
-     plot */
-  np_plot->x_lower = MIN (
-    np->y_min, (np_plot->y_first - np_plot->intercept) / np_plot->slope);
-  np_plot->x_upper = MAX (
-    np->y_max, (np_plot->y_last  - np_plot->intercept) / np_plot->slope) ;
-  np_plot->slack = (np_plot->x_upper - np_plot->x_lower) * 0.05 ;
-
-  dnp_plot = xmemdup (np_plot, sizeof *np_plot);
-  chart_init (&dnp_plot->chart, &dnp_plot_chart_class);
-  dnp_plot->label = xstrdup (dnp_plot->label);
-  dnp_plot->data = casereader_clone (dnp_plot->data);
-
-  chart_submit (&np_plot->chart);
-  chart_submit (&dnp_plot->chart);
-}
-
-static void
-np_plot_chart_draw (const struct chart *chart, plPlotter *lp)
-{
-  const struct np_plot_chart *plot = (struct np_plot_chart *) chart;
-  struct chart_geometry geom;
-  struct casereader *data;
-  struct ccase *c;
-
-  chart_geometry_init (lp, &geom);
-  chart_write_title (lp, &geom, _("Normal Q-Q Plot of %s"), plot->label);
-  chart_write_xlabel (lp, &geom, _("Observed Value"));
-  chart_write_ylabel (lp, &geom, _("Expected Normal"));
-  chart_write_xscale (lp, &geom,
-                      plot->x_lower - plot->slack,
-                      plot->x_upper + plot->slack, 5);
-  chart_write_yscale (lp, &geom, plot->y_first, plot->y_last, 5);
-
-  data = casereader_clone (plot->data);
-  for (; (c = casereader_read (data)) != NULL; case_unref (c))
-    chart_datum (lp, &geom, 0,
-                 case_data_idx (c, NP_IDX_Y)->f,
-                 case_data_idx (c, NP_IDX_NS)->f);
-  casereader_destroy (data);
-
-  chart_line (lp, &geom, plot->slope, plot->intercept,
-              plot->y_first, plot->y_last, CHART_DIM_Y);
-
-  chart_geometry_free (lp);
-}
-
-static void
-dnp_plot_chart_draw (const struct chart *chart, plPlotter *lp)
-{
-  const struct np_plot_chart *plot = (struct np_plot_chart *) chart;
-  struct chart_geometry geom;
-  struct casereader *data;
-  struct ccase *c;
-
-  chart_geometry_init (lp, &geom);
-  chart_write_title (lp, &geom, _("Detrended Normal Q-Q Plot of %s"),
-                     plot->label);
-  chart_write_xlabel (lp, &geom, _("Observed Value"));
-  chart_write_ylabel (lp, &geom, _("Dev from Normal"));
-  chart_write_xscale (lp, &geom, plot->y_min, plot->y_max, 5);
-  chart_write_yscale (lp, &geom, plot->dns_min, plot->dns_max, 5);
-
-  data = casereader_clone (plot->data);
-  for (; (c = casereader_read (data)) != NULL; case_unref (c))
-    chart_datum (lp, &geom, 0, case_data_idx (c, NP_IDX_Y)->f,
-                 case_data_idx (c, NP_IDX_DNS)->f);
-  casereader_destroy (data);
-
-  chart_line (lp, &geom, 0, 0, plot->y_min, plot->y_max, CHART_DIM_X);
-
-  chart_geometry_free (lp);
-}
-
-static void
-np_plot_chart_destroy (struct chart *chart)
-{
-  struct np_plot_chart *plot = (struct np_plot_chart *) chart;
-
-  casereader_destroy (plot->data);
-  free (plot->label);
-  free (plot);
-}
-
-static const struct chart_class np_plot_chart_class =
-  {
-    np_plot_chart_draw,
-    np_plot_chart_destroy
-  };
-
-static const struct chart_class dnp_plot_chart_class =
-  {
-    dnp_plot_chart_draw,
-    np_plot_chart_destroy
-  };
 
 
 static void
@@ -481,20 +336,37 @@ show_npplot (const struct variable **dependent_var,
           ll != ll_null (&fctr->result_list);
           ll = ll_next (ll))
        {
-         struct string str;
+         struct string label;
          const struct factor_result *result =
            ll_data (ll, struct factor_result, ll);
+          struct chart *npp, *dnpp;
+          struct casereader *reader;
+          struct np *np;
 
-         ds_init_empty (&str);
-         ds_put_format (&str, "%s ", var_get_name (dependent_var[v]));
+         ds_init_empty (&label);
+         ds_put_format (&label, "%s ", var_get_name (dependent_var[v]));
+         factor_to_string (fctr, result, &label);
 
-         factor_to_string (fctr, result, &str);
+          np = (struct np *) result->metrics[v].np;
+          reader = casewriter_make_reader (np->writer);
+          npp = np_plot_create (np, reader, ds_cstr (&label));
+          dnpp = dnp_plot_create (np, reader, ds_cstr (&label));
 
-         np_plot ((struct np*) result->metrics[v].np, ds_cstr(&str));
+         ds_destroy (&label);
 
-         statistic_destroy ((struct statistic *)result->metrics[v].np);
+          if (npp == NULL || dnpp == NULL)
+            {
+              msg (MW, _("Not creating NP plot because data set is empty."));
+              chart_unref (npp);
+              chart_unref (dnpp);
+            }
+          else
+            {
+              chart_submit (npp);
+              chart_submit (dnpp);
+            }
 
-         ds_destroy (&str);
+         statistic_destroy (&np->parent.parent);
        }
     }
 }
index b2a65636b384352218b514b2f5d7a38714332b53..7be5705c4fe7995cc586d9eba12f824075f74079 100644 (file)
@@ -7,6 +7,8 @@ chart_sources = \
        src/output/charts/box-whisker.h \
        src/output/charts/cartesian.c \
        src/output/charts/cartesian.h \
+       src/output/charts/np-plot.c \
+       src/output/charts/np-plot.h \
        src/output/charts/piechart.c \
        src/output/charts/piechart.h \
        src/output/charts/plot-chart.h \
diff --git a/src/output/charts/np-plot.c b/src/output/charts/np-plot.c
new file mode 100644 (file)
index 0000000..df91add
--- /dev/null
@@ -0,0 +1,200 @@
+/* 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 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 <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/charts/np-plot.h>
+
+#include <gsl/gsl_cdf.h>
+
+#include <data/casereader.h>
+#include <data/casewriter.h>
+#include <libpspp/message.h>
+#include <math/np.h>
+#include <output/chart-provider.h>
+#include <output/charts/cartesian.h>
+#include <output/charts/plot-chart.h>
+
+#include "gl/minmax.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+/* An NP or DNP plot. */
+struct np_plot_chart
+  {
+    struct chart chart;
+    char *label;
+    struct casereader *data;
+
+    /* Copied directly from struct np. */
+    double y_min, y_max;
+    double dns_min, dns_max;
+
+    /* Calculated. */
+    double slope, intercept;
+    double y_first, y_last;
+    double x_lower, x_upper;
+    double slack;
+  };
+
+static const struct chart_class np_plot_chart_class;
+static const struct chart_class dnp_plot_chart_class;
+
+static struct chart *
+make_np_plot (const struct chart_class *class,
+              const struct np *np, const struct casereader *reader,
+              const char *label)
+{
+  struct np_plot_chart *npp;
+
+  if (np->n < 1.0)
+    return NULL;
+
+  npp = xmalloc (sizeof *npp);
+  chart_init (&npp->chart, class);
+  npp->label = xstrdup (label);
+  npp->data = casereader_clone (reader);
+  npp->y_min = np->y_min;
+  npp->y_max = np->y_max;
+  npp->dns_min = np->dns_min;
+  npp->dns_max = np->dns_max;
+
+  /* Slope and intercept of the ideal normal probability line. */
+  npp->slope = 1.0 / np->stddev;
+  npp->intercept = -np->mean / np->stddev;
+
+  npp->y_first = gsl_cdf_ugaussian_Pinv (1 / (np->n + 1));
+  npp->y_last = gsl_cdf_ugaussian_Pinv (np->n / (np->n + 1));
+
+  /* Need to make sure that both the scatter plot and the ideal fit into the
+     plot. */
+  npp->x_lower = MIN (np->y_min, (npp->y_first - npp->intercept) / npp->slope);
+  npp->x_upper = MAX (np->y_max, (npp->y_last  - npp->intercept) / npp->slope);
+  npp->slack = (npp->x_upper - npp->x_lower) * 0.05;
+
+  return &npp->chart;
+}
+
+/* Creates and returns a normal probability plot corresponding to
+   the calculations in NP and the data in READER, and label the
+   plot with LABEL.  The data in READER must have Y-values in
+   value index NP_IDX_Y and NS-values in value index NP_IDX_NS.
+
+   Returns a null pointer if the data set is empty.
+
+   The caller retains ownership of NP and READER. */
+struct chart *
+np_plot_create (const struct np *np, const struct casereader *reader,
+                const char *label)
+{
+  return make_np_plot (&np_plot_chart_class, np, reader, label);
+}
+
+/* Creates and returns a detrended normal probability plot
+   corresponding to the calculations in NP and the data in
+   READER, and label the plot with LABEL.  The data in READER
+   must have Y-values in value index NP_IDX_Y and DNS-values in
+   value index NP_IDX_DNS.
+
+   Returns a null pointer if the data set is empty.
+
+   The caller retains ownership of NP and READER. */
+struct chart *
+dnp_plot_create (const struct np *np, const struct casereader *reader,
+                 const char *label)
+{
+  return make_np_plot (&dnp_plot_chart_class, np, reader, label);
+}
+
+static void
+np_plot_chart_draw (const struct chart *chart, plPlotter *lp)
+{
+  const struct np_plot_chart *npp = (struct np_plot_chart *) chart;
+  struct chart_geometry geom;
+  struct casereader *data;
+  struct ccase *c;
+
+  chart_geometry_init (lp, &geom);
+  chart_write_title (lp, &geom, _("Normal Q-Q Plot of %s"), npp->label);
+  chart_write_xlabel (lp, &geom, _("Observed Value"));
+  chart_write_ylabel (lp, &geom, _("Expected Normal"));
+  chart_write_xscale (lp, &geom,
+                      npp->x_lower - npp->slack,
+                      npp->x_upper + npp->slack, 5);
+  chart_write_yscale (lp, &geom, npp->y_first, npp->y_last, 5);
+
+  data = casereader_clone (npp->data);
+  for (; (c = casereader_read (data)) != NULL; case_unref (c))
+    chart_datum (lp, &geom, 0,
+                 case_data_idx (c, NP_IDX_Y)->f,
+                 case_data_idx (c, NP_IDX_NS)->f);
+  casereader_destroy (data);
+
+  chart_line (lp, &geom, npp->slope, npp->intercept,
+              npp->y_first, npp->y_last, CHART_DIM_Y);
+
+  chart_geometry_free (lp);
+}
+
+static void
+dnp_plot_chart_draw (const struct chart *chart, plPlotter *lp)
+{
+  const struct np_plot_chart *dnpp = (struct np_plot_chart *) chart;
+  struct chart_geometry geom;
+  struct casereader *data;
+  struct ccase *c;
+
+  chart_geometry_init (lp, &geom);
+  chart_write_title (lp, &geom, _("Detrended Normal Q-Q Plot of %s"),
+                     dnpp->label);
+  chart_write_xlabel (lp, &geom, _("Observed Value"));
+  chart_write_ylabel (lp, &geom, _("Dev from Normal"));
+  chart_write_xscale (lp, &geom, dnpp->y_min, dnpp->y_max, 5);
+  chart_write_yscale (lp, &geom, dnpp->dns_min, dnpp->dns_max, 5);
+
+  data = casereader_clone (dnpp->data);
+  for (; (c = casereader_read (data)) != NULL; case_unref (c))
+    chart_datum (lp, &geom, 0, case_data_idx (c, NP_IDX_Y)->f,
+                 case_data_idx (c, NP_IDX_DNS)->f);
+  casereader_destroy (data);
+
+  chart_line (lp, &geom, 0, 0, dnpp->y_min, dnpp->y_max, CHART_DIM_X);
+
+  chart_geometry_free (lp);
+}
+
+static void
+np_plot_chart_destroy (struct chart *chart)
+{
+  struct np_plot_chart *npp = (struct np_plot_chart *) chart;
+
+  casereader_destroy (npp->data);
+  free (npp->label);
+  free (npp);
+}
+
+static const struct chart_class np_plot_chart_class =
+  {
+    np_plot_chart_draw,
+    np_plot_chart_destroy
+  };
+
+static const struct chart_class dnp_plot_chart_class =
+  {
+    dnp_plot_chart_draw,
+    np_plot_chart_destroy
+  };
diff --git a/src/output/charts/np-plot.h b/src/output/charts/np-plot.h
new file mode 100644 (file)
index 0000000..c974235
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_CHARTS_NP_PLOT_H
+#define OUTPUT_CHARTS_NP_PLOT_H 1
+
+struct casereader;
+struct np;
+
+struct chart *np_plot_create (const struct np *, const struct casereader *,
+                              const char *label);
+struct chart *dnp_plot_create (const struct np *, const struct casereader *,
+                               const char *label);
+
+#endif /* output/charts/np-plot.h */