c077b8723def9395f567944a6d2d9559f8cf7373
[pspp-builds.git] / src / output / charts / np-plot.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2004, 2008, 2009 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include <output/charts/np-plot.h>
20
21 #include <gsl/gsl_cdf.h>
22
23 #include <data/casereader.h>
24 #include <data/casewriter.h>
25 #include <libpspp/cast.h>
26 #include <libpspp/message.h>
27 #include <math/np.h>
28 #include <output/chart-provider.h>
29 #include <output/charts/cartesian.h>
30 #include <output/charts/plot-chart.h>
31
32 #include "gl/minmax.h"
33
34 #include "gettext.h"
35 #define _(msgid) gettext (msgid)
36
37 /* An NP or DNP plot. */
38 struct np_plot_chart
39   {
40     struct chart chart;
41     char *label;
42     struct casereader *data;
43
44     /* Copied directly from struct np. */
45     double y_min, y_max;
46     double dns_min, dns_max;
47
48     /* Calculated. */
49     double slope, intercept;
50     double y_first, y_last;
51     double x_lower, x_upper;
52     double slack;
53   };
54
55 static const struct chart_class np_plot_chart_class;
56 static const struct chart_class dnp_plot_chart_class;
57
58 static struct chart *
59 make_np_plot (const struct chart_class *class,
60               const struct np *np, const struct casereader *reader,
61               const char *label)
62 {
63   struct np_plot_chart *npp;
64
65   if (np->n < 1.0)
66     return NULL;
67
68   npp = xmalloc (sizeof *npp);
69   chart_init (&npp->chart, class);
70   npp->label = xstrdup (label);
71   npp->data = casereader_clone (reader);
72   npp->y_min = np->y_min;
73   npp->y_max = np->y_max;
74   npp->dns_min = np->dns_min;
75   npp->dns_max = np->dns_max;
76
77   /* Slope and intercept of the ideal normal probability line. */
78   npp->slope = 1.0 / np->stddev;
79   npp->intercept = -np->mean / np->stddev;
80
81   npp->y_first = gsl_cdf_ugaussian_Pinv (1 / (np->n + 1));
82   npp->y_last = gsl_cdf_ugaussian_Pinv (np->n / (np->n + 1));
83
84   /* Need to make sure that both the scatter plot and the ideal fit into the
85      plot. */
86   npp->x_lower = MIN (np->y_min, (npp->y_first - npp->intercept) / npp->slope);
87   npp->x_upper = MAX (np->y_max, (npp->y_last  - npp->intercept) / npp->slope);
88   npp->slack = (npp->x_upper - npp->x_lower) * 0.05;
89
90   return &npp->chart;
91 }
92
93 /* Creates and returns a normal probability plot corresponding to
94    the calculations in NP and the data in READER, and label the
95    plot with LABEL.  The data in READER must have Y-values in
96    value index NP_IDX_Y and NS-values in value index NP_IDX_NS.
97
98    Returns a null pointer if the data set is empty.
99
100    The caller retains ownership of NP and READER. */
101 struct chart *
102 np_plot_create (const struct np *np, const struct casereader *reader,
103                 const char *label)
104 {
105   return make_np_plot (&np_plot_chart_class, np, reader, label);
106 }
107
108 /* Creates and returns a detrended normal probability plot
109    corresponding to the calculations in NP and the data in
110    READER, and label the plot with LABEL.  The data in READER
111    must have Y-values in value index NP_IDX_Y and DNS-values in
112    value index NP_IDX_DNS.
113
114    Returns a null pointer if the data set is empty.
115
116    The caller retains ownership of NP and READER. */
117 struct chart *
118 dnp_plot_create (const struct np *np, const struct casereader *reader,
119                  const char *label)
120 {
121   return make_np_plot (&dnp_plot_chart_class, np, reader, label);
122 }
123
124 static void
125 np_plot_chart_draw (const struct chart *chart, cairo_t *cr,
126                     struct chart_geometry *geom)
127 {
128   const struct np_plot_chart *npp = UP_CAST (chart, struct np_plot_chart,
129                                              chart);
130   struct casereader *data;
131   struct ccase *c;
132
133   chart_write_title (cr, geom, _("Normal Q-Q Plot of %s"), npp->label);
134   chart_write_xlabel (cr, geom, _("Observed Value"));
135   chart_write_ylabel (cr, geom, _("Expected Normal"));
136   chart_write_xscale (cr, geom,
137                       npp->x_lower - npp->slack,
138                       npp->x_upper + npp->slack, 5);
139   chart_write_yscale (cr, geom, npp->y_first, npp->y_last, 5);
140
141   data = casereader_clone (npp->data);
142   for (; (c = casereader_read (data)) != NULL; case_unref (c))
143     chart_datum (cr, geom, 0,
144                  case_data_idx (c, NP_IDX_Y)->f,
145                  case_data_idx (c, NP_IDX_NS)->f);
146   casereader_destroy (data);
147
148   chart_line (cr, geom, npp->slope, npp->intercept,
149               npp->y_first, npp->y_last, CHART_DIM_Y);
150 }
151
152 static void
153 dnp_plot_chart_draw (const struct chart *chart, cairo_t *cr,
154                      struct chart_geometry *geom)
155 {
156   const struct np_plot_chart *dnpp = UP_CAST (chart, struct np_plot_chart,
157                                               chart);
158   struct casereader *data;
159   struct ccase *c;
160
161   chart_write_title (cr, geom, _("Detrended Normal Q-Q Plot of %s"),
162                      dnpp->label);
163   chart_write_xlabel (cr, geom, _("Observed Value"));
164   chart_write_ylabel (cr, geom, _("Dev from Normal"));
165   chart_write_xscale (cr, geom, dnpp->y_min, dnpp->y_max, 5);
166   chart_write_yscale (cr, geom, dnpp->dns_min, dnpp->dns_max, 5);
167
168   data = casereader_clone (dnpp->data);
169   for (; (c = casereader_read (data)) != NULL; case_unref (c))
170     chart_datum (cr, geom, 0, case_data_idx (c, NP_IDX_Y)->f,
171                  case_data_idx (c, NP_IDX_DNS)->f);
172   casereader_destroy (data);
173
174   chart_line (cr, geom, 0, 0, dnpp->y_min, dnpp->y_max, CHART_DIM_X);
175 }
176
177 static void
178 np_plot_chart_destroy (struct chart *chart)
179 {
180   struct np_plot_chart *npp = UP_CAST (chart, struct np_plot_chart, chart);
181   casereader_destroy (npp->data);
182   free (npp->label);
183   free (npp);
184 }
185
186 static const struct chart_class np_plot_chart_class =
187   {
188     np_plot_chart_draw,
189     np_plot_chart_destroy
190   };
191
192 static const struct chart_class dnp_plot_chart_class =
193   {
194     dnp_plot_chart_draw,
195     np_plot_chart_destroy
196   };