578efc1e756feec5755582798b380e792813c05b
[pspp] / src / language / stats / graph.c
1 /*
2   PSPP - a program for statistical analysis.
3   Copyright (C) 2012, 2013, 2015 Free Software Foundation, Inc.
4
5   This program is free software: you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation, either version 3 of the License, or
8   (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /*
20  * This module implements the graph command
21  */
22
23 #include <config.h>
24
25 #include <math.h>
26 #include "gl/xalloc.h"
27 #include <gsl/gsl_cdf.h>
28
29 #include "libpspp/assertion.h"
30 #include "libpspp/message.h"
31 #include "libpspp/pool.h"
32
33
34 #include "data/dataset.h"
35 #include "data/dictionary.h"
36 #include "data/casegrouper.h"
37 #include "data/casereader.h"
38 #include "data/casewriter.h"
39 #include "data/caseproto.h"
40 #include "data/subcase.h"
41
42
43 #include "data/format.h"
44
45 #include "math/chart-geometry.h"
46 #include "math/histogram.h"
47 #include "math/moments.h"
48 #include "math/sort.h"
49 #include "math/order-stats.h"
50 #include "output/charts/plot-hist.h"
51 #include "output/charts/scatterplot.h"
52 #include "output/charts/barchart.h"
53
54 #include "language/command.h"
55 #include "language/lexer/lexer.h"
56 #include "language/lexer/value-parser.h"
57 #include "language/lexer/variable-parser.h"
58 #include "language/stats/freq.h"
59 #include "language/stats/chart-category.h"
60
61 #include "output/tab.h"
62
63 #include "gettext.h"
64 #define _(msgid) gettext (msgid)
65 #define N_(msgid) msgid
66
67 enum chart_type
68   {
69     CT_NONE,
70     CT_BAR,
71     CT_LINE,
72     CT_PIE,
73     CT_ERRORBAR,
74     CT_HILO,
75     CT_HISTOGRAM,
76     CT_SCATTERPLOT,
77     CT_PARETO
78   };
79
80 enum scatter_type
81   {
82     ST_BIVARIATE,
83     ST_OVERLAY,
84     ST_MATRIX,
85     ST_XYZ
86   };
87
88 enum  bar_type
89   {
90     CBT_SIMPLE,
91     CBT_GROUPED,
92     CBT_STACKED,
93     CBT_RANGE
94   };
95
96
97 /* Variable index for histogram case */
98 enum
99   {
100     HG_IDX_X,
101     HG_IDX_WT
102   };
103
104 struct exploratory_stats
105 {
106   double missing;
107   double non_missing;
108
109   struct moments *mom;
110
111   double minimum;
112   double maximum;
113
114   /* Total weight */
115   double cc;
116
117   /* The minimum weight */
118   double cmin;
119 };
120
121
122 struct graph
123 {
124   struct pool *pool;
125
126   size_t n_dep_vars;
127   const struct variable **dep_vars;
128   struct exploratory_stats *es;
129
130   enum mv_class dep_excl;
131   enum mv_class fctr_excl;
132
133   const struct dictionary *dict;
134
135   bool missing_pw;
136
137   /* ------------ Graph ---------------- */
138   bool normal; /* For histograms, draw the normal curve */
139
140   enum chart_type chart_type;
141   enum scatter_type scatter_type;
142   enum bar_type bar_type;
143   const struct variable *by_var[2];
144   size_t n_by_vars;
145
146   struct subcase ordering; /* Ordering for aggregation */
147   int agr; /* Index into ag_func */
148
149   /* A caseproto that contains the plot data */
150   struct caseproto *gr_proto;
151 };
152
153
154
155
156 static double
157 calc_mom1 (double acc, double x, double w)
158 {
159   return acc + x * w;
160 }
161
162 static double
163 calc_mom0 (double acc, double x UNUSED, double w)
164 {
165   return acc + w;
166 }
167
168 static double
169 pre_low_extreme (void)
170 {
171   return -DBL_MAX;
172 }
173
174 static double
175 calc_max (double acc, double x, double w UNUSED)
176 {
177   return (acc > x) ? acc : x;
178 }
179
180 static double
181 pre_high_extreme (void)
182 {
183   return DBL_MAX;
184 }
185
186 static double
187 calc_min (double acc, double x, double w UNUSED)
188 {
189   return (acc < x) ? acc : x;
190 }
191
192 static double
193 post_normalise (double acc, double cc)
194 {
195   return acc / cc;
196 }
197
198 static double
199 post_percentage (double acc, double ccc)
200 {
201   return acc / ccc * 100.0;
202 }
203
204
205 const struct ag_func ag_func[] =
206   {
207     {"COUNT",   N_("Count"),      0, 0, NULL, calc_mom0, 0, 0},
208     {"PCT",     N_("Percentage"), 0, 0, NULL, calc_mom0, 0, post_percentage},
209     {"CUFREQ",  N_("Cumulative Count"),   0, 1, NULL, calc_mom0, 0, 0},
210     {"CUPCT",   N_("Cumulative Percent"), 0, 1, NULL, calc_mom0, 0, post_percentage},
211
212     {"MEAN",    N_("Mean"),    1, 0, NULL, calc_mom1, post_normalise, 0},
213     {"SUM",     N_("Sum"),     1, 0, NULL, calc_mom1, 0, 0},
214     {"MAXIMUM", N_("Maximum"), 1, 0, pre_low_extreme, calc_max, 0, 0},
215     {"MINIMUM", N_("Minimum"), 1, 0, pre_high_extreme, calc_min, 0, 0},
216   };
217
218 const int N_AG_FUNCS = sizeof (ag_func) / sizeof (ag_func[0]);
219
220 static bool
221 parse_function (struct lexer *lexer, struct graph *graph)
222 {
223   int i;
224   for (i = 0 ; i < N_AG_FUNCS; ++i)
225     {
226       if (lex_match_id (lexer, ag_func[i].name))
227         {
228           graph->agr = i;
229           break;
230         }
231     }
232   if (i == N_AG_FUNCS)
233     {
234       goto error;
235     }
236
237   graph->n_dep_vars = ag_func[i].arity;
238   if (ag_func[i].arity > 0)
239     {
240       int v;
241       if (!lex_force_match (lexer, T_LPAREN))
242         goto error;
243
244       graph->dep_vars = xzalloc (sizeof (graph->dep_vars) * graph->n_dep_vars);
245       for (v = 0; v < ag_func[i].arity; ++v)
246         {
247           graph->dep_vars[v] = parse_variable (lexer, graph->dict);
248           if (! graph->dep_vars[v])
249             goto error;
250         }
251
252       if (!lex_force_match (lexer, T_RPAREN))
253         goto error;
254     }
255
256   if (!lex_force_match (lexer, T_BY))
257     goto error;
258
259   graph->by_var[0] = parse_variable (lexer, graph->dict);
260   if (!graph->by_var[0])
261     {
262       goto error;
263     }
264   subcase_add_var (&graph->ordering, graph->by_var[0], SC_ASCEND);
265   graph->n_by_vars++;
266
267   if (lex_match (lexer, T_BY))
268     {
269       graph->by_var[1] = parse_variable (lexer, graph->dict);
270       if (!graph->by_var[1])
271         {
272           goto error;
273         }
274       subcase_add_var (&graph->ordering, graph->by_var[1], SC_ASCEND);
275       graph->n_by_vars++;
276     }
277
278   return true;
279
280  error:
281   lex_error (lexer, NULL);
282   return false;
283 }
284
285
286 static void
287 show_scatterplot (const struct graph *cmd, struct casereader *input)
288 {
289   struct string title;
290   struct scatterplot_chart *scatterplot;
291   bool byvar_overflow = false;
292
293   ds_init_empty (&title);
294
295   if (cmd->n_by_vars > 0)
296     {
297       ds_put_format (&title, _("%s vs. %s by %s"),
298                            var_to_string (cmd->dep_vars[1]),
299                            var_to_string (cmd->dep_vars[0]),
300                            var_to_string (cmd->by_var[0]));
301     }
302   else
303     {
304       ds_put_format (&title, _("%s vs. %s"),
305                      var_to_string (cmd->dep_vars[1]),
306                      var_to_string (cmd->dep_vars[0]));
307     }
308
309   scatterplot = scatterplot_create (input,
310                                     var_to_string(cmd->dep_vars[0]),
311                                     var_to_string(cmd->dep_vars[1]),
312                                     (cmd->n_by_vars > 0) ? cmd->by_var[0] : NULL,
313                                     &byvar_overflow,
314                                     ds_cstr (&title),
315                                     cmd->es[0].minimum, cmd->es[0].maximum,
316                                     cmd->es[1].minimum, cmd->es[1].maximum);
317   scatterplot_chart_submit (scatterplot);
318   ds_destroy (&title);
319
320   if (byvar_overflow)
321     {
322       msg (MW, _("Maximum number of scatterplot categories reached. "
323                  "Your BY variable has too many distinct values. "
324                  "The coloring of the plot will not be correct."));
325     }
326 }
327
328 static void
329 show_histogr (const struct graph *cmd, struct casereader *input)
330 {
331   struct histogram *histogram;
332   struct ccase *c;
333
334   if (cmd->es[0].cc <= 0)
335     {
336       casereader_destroy (input);
337       return;
338     }
339
340   {
341     /* Sturges Rule */
342     double bin_width = fabs (cmd->es[0].minimum - cmd->es[0].maximum)
343       / (1 + log2 (cmd->es[0].cc))
344       ;
345
346     histogram =
347       histogram_create (bin_width, cmd->es[0].minimum, cmd->es[0].maximum);
348   }
349
350   if (NULL == histogram)
351     {
352       casereader_destroy (input);
353       return;
354     }
355
356   for (;(c = casereader_read (input)) != NULL; case_unref (c))
357     {
358       const double x      = case_data_idx (c, HG_IDX_X)->f;
359       const double weight = case_data_idx (c, HG_IDX_WT)->f;
360       moments_pass_two (cmd->es[0].mom, x, weight);
361       histogram_add (histogram, x, weight);
362     }
363   casereader_destroy (input);
364
365
366   {
367     double n, mean, var;
368
369     struct string label;
370
371     ds_init_cstr (&label,
372                   var_to_string (cmd->dep_vars[0]));
373
374     moments_calculate (cmd->es[0].mom, &n, &mean, &var, NULL, NULL);
375
376     chart_item_submit
377       ( histogram_chart_create (histogram->gsl_hist,
378                                 ds_cstr (&label), n, mean,
379                                 sqrt (var), cmd->normal));
380
381     statistic_destroy (&histogram->parent);
382     ds_destroy (&label);
383   }
384 }
385
386 static void
387 cleanup_exploratory_stats (struct graph *cmd)
388 {
389   int v;
390
391   for (v = 0; v < cmd->n_dep_vars; ++v)
392     {
393       moments_destroy (cmd->es[v].mom);
394     }
395 }
396
397
398 static void
399 run_barchart (struct graph *cmd, struct casereader *input)
400 {
401   struct casegrouper *grouper;
402   struct casereader *group;
403   double ccc = 0.0;
404
405   if ( cmd->missing_pw == false)
406     input = casereader_create_filter_missing (input,
407                                               cmd->dep_vars,
408                                               cmd->n_dep_vars,
409                                               cmd->dep_excl,
410                                               NULL,
411                                               NULL);
412
413
414   input = sort_execute (input, &cmd->ordering);
415
416   struct freq **freqs = NULL;
417   int n_freqs = 0;
418
419   for (grouper = casegrouper_create_vars (input, cmd->by_var,
420                                           cmd->n_by_vars);
421        casegrouper_get_next_group (grouper, &group);
422        casereader_destroy (group))
423     {
424       int v;
425       struct ccase *c = casereader_peek (group, 0);
426
427       /* Deal with missing values in the categorical variables */
428       for (v = 0; v < cmd->n_by_vars; ++v)
429         {
430           if (var_is_value_missing (cmd->by_var[v], case_data (c, cmd->by_var[v]), cmd->fctr_excl) )
431             break;
432         }
433
434       if (v < cmd->n_by_vars)
435         {
436           case_unref (c);
437           continue;
438         }
439
440       freqs = xrealloc (freqs, sizeof (*freqs) * ++n_freqs);
441       freqs[n_freqs - 1] = xzalloc (sizeof (**freqs) +
442                                     sizeof (union value) * (cmd->n_by_vars - 1) );
443
444       if (ag_func[cmd->agr].cumulative && n_freqs >= 2)
445         freqs[n_freqs - 1]->count = freqs[n_freqs - 2]->count;
446       else
447         freqs[n_freqs - 1]->count = 0;
448       if (ag_func[cmd->agr].pre)
449         freqs[n_freqs - 1]->count = ag_func[cmd->agr].pre();
450
451
452       for (v = 0; v < cmd->n_by_vars; ++v)
453         {
454           value_clone (&freqs[n_freqs - 1]->values[v], case_data (c, cmd->by_var[v]),
455                        var_get_width (cmd->by_var[v])
456                        );
457         }
458       case_unref (c);
459
460       double cc = 0;
461       for (;(c = casereader_read (group)) != NULL; case_unref (c))
462         {
463           const double weight = dict_get_case_weight (cmd->dict,c,NULL);
464           const double x =  (cmd->n_dep_vars > 0) ? case_data (c, cmd->dep_vars[0])->f : SYSMIS;
465
466           cc += weight;
467
468           freqs[n_freqs - 1]->count
469             = ag_func[cmd->agr].calc (freqs[n_freqs - 1]->count, x, weight);
470         }
471
472       if (ag_func[cmd->agr].post)
473         freqs[n_freqs - 1]->count
474           = ag_func[cmd->agr].post (freqs[n_freqs - 1]->count, cc);
475
476       ccc += cc;
477     }
478
479   casegrouper_destroy (grouper);
480
481   for (int i = 0; i < n_freqs; ++i)
482     {
483       if (ag_func[cmd->agr].ppost)
484         freqs[i]->count = ag_func[cmd->agr].ppost (freqs[i]->count, ccc);
485     }
486
487
488   {
489     struct string label;
490     ds_init_empty (&label);
491
492     if (cmd->n_dep_vars > 0)
493       ds_put_format (&label, _("%s of %s"),
494                      ag_func[cmd->agr].description,
495                      var_get_name (cmd->dep_vars[0]));
496     else
497       ds_put_cstr (&label,
498                      ag_func[cmd->agr].description);
499
500     chart_item_submit (barchart_create (cmd->by_var, cmd->n_by_vars,
501                                         ds_cstr (&label), false,
502                                         freqs, n_freqs));
503
504     ds_destroy (&label);
505   }
506
507   for (int i = 0; i < n_freqs; ++i)
508     free (freqs[i]);
509
510   free (freqs);
511 }
512
513
514 static void
515 run_graph (struct graph *cmd, struct casereader *input)
516 {
517   struct ccase *c;
518   struct casereader *reader;
519   struct casewriter *writer;
520
521   cmd->es = pool_calloc (cmd->pool,cmd->n_dep_vars,sizeof(struct exploratory_stats));
522   for(int v=0;v<cmd->n_dep_vars;v++)
523     {
524       cmd->es[v].mom = moments_create (MOMENT_KURTOSIS);
525       cmd->es[v].cmin = DBL_MAX;
526       cmd->es[v].maximum = -DBL_MAX;
527       cmd->es[v].minimum =  DBL_MAX;
528     }
529   /* Always remove cases listwise. This is correct for */
530   /* the histogram because there is only one variable  */
531   /* and a simple bivariate scatterplot                */
532   /* if ( cmd->missing_pw == false)                    */
533     input = casereader_create_filter_missing (input,
534                                               cmd->dep_vars,
535                                               cmd->n_dep_vars,
536                                               cmd->dep_excl,
537                                               NULL,
538                                               NULL);
539
540   writer = autopaging_writer_create (cmd->gr_proto);
541
542   /* The case data is copied to a new writer        */
543   /* The setup of the case depends on the Charttype */
544   /* For Scatterplot x is assumed in dep_vars[0]    */
545   /*                 y is assumed in dep_vars[1]    */
546   /* For Histogram   x is assumed in dep_vars[0]    */
547   assert(SP_IDX_X == 0 && SP_IDX_Y == 1 && HG_IDX_X == 0);
548
549   for (;(c = casereader_read (input)) != NULL; case_unref (c))
550     {
551       struct ccase *outcase = case_create (cmd->gr_proto);
552       const double weight = dict_get_case_weight (cmd->dict,c,NULL);
553       if (cmd->chart_type == CT_HISTOGRAM)
554         case_data_rw_idx (outcase, HG_IDX_WT)->f = weight;
555       if (cmd->chart_type == CT_SCATTERPLOT && cmd->n_by_vars > 0)
556         value_copy (case_data_rw_idx (outcase, SP_IDX_BY),
557                     case_data (c, cmd->by_var[0]),
558                     var_get_width (cmd->by_var[0]));
559       for(int v=0;v<cmd->n_dep_vars;v++)
560         {
561           const struct variable *var = cmd->dep_vars[v];
562           const double x = case_data (c, var)->f;
563
564           if (var_is_value_missing (var, case_data (c, var), cmd->dep_excl))
565             {
566               cmd->es[v].missing += weight;
567               continue;
568             }
569           /* Magically v value fits to SP_IDX_X, SP_IDX_Y, HG_IDX_X */
570           case_data_rw_idx (outcase, v)->f = x;
571
572           if (x > cmd->es[v].maximum)
573             cmd->es[v].maximum = x;
574
575           if (x < cmd->es[v].minimum)
576             cmd->es[v].minimum =  x;
577
578           cmd->es[v].non_missing += weight;
579
580           moments_pass_one (cmd->es[v].mom, x, weight);
581
582           cmd->es[v].cc += weight;
583
584           if (cmd->es[v].cmin > weight)
585             cmd->es[v].cmin = weight;
586         }
587       casewriter_write (writer,outcase);
588     }
589
590   reader = casewriter_make_reader (writer);
591
592   switch (cmd->chart_type)
593     {
594     case CT_HISTOGRAM:
595       show_histogr (cmd,reader);
596       break;
597     case CT_SCATTERPLOT:
598       show_scatterplot (cmd,reader);
599       break;
600     default:
601       NOT_REACHED ();
602       break;
603     };
604
605   casereader_destroy (input);
606   cleanup_exploratory_stats (cmd);
607 }
608
609
610 int
611 cmd_graph (struct lexer *lexer, struct dataset *ds)
612 {
613   struct graph graph;
614
615   graph.missing_pw = false;
616
617   graph.pool = pool_create ();
618
619   graph.dep_excl = MV_ANY;
620   graph.fctr_excl = MV_ANY;
621
622   graph.dict = dataset_dict (ds);
623
624   graph.dep_vars = NULL;
625   graph.chart_type = CT_NONE;
626   graph.scatter_type = ST_BIVARIATE;
627   graph.n_by_vars = 0;
628   graph.gr_proto = caseproto_create ();
629
630   subcase_init_empty (&graph.ordering);
631
632   while (lex_token (lexer) != T_ENDCMD)
633     {
634       lex_match (lexer, T_SLASH);
635
636       if (lex_match_id (lexer, "HISTOGRAM"))
637         {
638           if (graph.chart_type != CT_NONE)
639             {
640               lex_error (lexer, _("Only one chart type is allowed."));
641               goto error;
642             }
643           graph.normal = false;
644           if (lex_match (lexer, T_LPAREN))
645             {
646               if (!lex_force_match_id (lexer, "NORMAL"))
647                 goto error;
648
649               if (!lex_force_match (lexer, T_RPAREN))
650                 goto error;
651
652               graph.normal = true;
653             }
654           if (!lex_force_match (lexer, T_EQUALS))
655             goto error;
656           graph.chart_type = CT_HISTOGRAM;
657           if (!parse_variables_const (lexer, graph.dict,
658                                       &graph.dep_vars, &graph.n_dep_vars,
659                                       PV_NO_DUPLICATE | PV_NUMERIC))
660             goto error;
661           if (graph.n_dep_vars > 1)
662             {
663               lex_error (lexer, _("Only one variable is allowed."));
664               goto error;
665             }
666         }
667       else if (lex_match_id (lexer, "BAR"))
668         {
669           if (graph.chart_type != CT_NONE)
670             {
671               lex_error (lexer, _("Only one chart type is allowed."));
672               goto error;
673             }
674           graph.chart_type = CT_BAR;
675           graph.bar_type = CBT_SIMPLE;
676
677           if (lex_match (lexer, T_LPAREN))
678             {
679               if (lex_match_id (lexer, "SIMPLE"))
680                 {
681                   /* This is the default anyway */
682                 }
683               else if (lex_match_id (lexer, "GROUPED"))
684                 {
685                   graph.bar_type = CBT_GROUPED;
686                   goto error;
687                 }
688               else if (lex_match_id (lexer, "STACKED"))
689                 {
690                   graph.bar_type = CBT_STACKED;
691                   lex_error (lexer, _("%s is not yet implemented."), "STACKED");
692                   goto error;
693                 }
694               else if (lex_match_id (lexer, "RANGE"))
695                 {
696                   graph.bar_type = CBT_RANGE;
697                   lex_error (lexer, _("%s is not yet implemented."), "RANGE");
698                   goto error;
699                 }
700               else
701                 {
702                   lex_error (lexer, NULL);
703                   goto error;
704                 }
705               if (!lex_force_match (lexer, T_RPAREN))
706                 goto error;
707             }
708
709           if (!lex_force_match (lexer, T_EQUALS))
710             goto error;
711
712           if (! parse_function (lexer, &graph))
713             goto error;
714         }
715       else if (lex_match_id (lexer, "SCATTERPLOT"))
716         {
717           if (graph.chart_type != CT_NONE)
718             {
719               lex_error (lexer, _("Only one chart type is allowed."));
720               goto error;
721             }
722           graph.chart_type = CT_SCATTERPLOT;
723           if (lex_match (lexer, T_LPAREN))
724             {
725               if (lex_match_id (lexer, "BIVARIATE"))
726                 {
727                   /* This is the default anyway */
728                 }
729               else if (lex_match_id (lexer, "OVERLAY"))
730                 {
731                   lex_error (lexer, _("%s is not yet implemented."),"OVERLAY");
732                   goto error;
733                 }
734               else if (lex_match_id (lexer, "MATRIX"))
735                 {
736                   lex_error (lexer, _("%s is not yet implemented."),"MATRIX");
737                   goto error;
738                 }
739               else if (lex_match_id (lexer, "XYZ"))
740                 {
741                   lex_error(lexer, _("%s is not yet implemented."),"XYZ");
742                   goto error;
743                 }
744               else
745                 {
746                   lex_error_expecting (lexer, "BIVARIATE", NULL);
747                   goto error;
748                 }
749               if (!lex_force_match (lexer, T_RPAREN))
750                 goto error;
751             }
752           if (!lex_force_match (lexer, T_EQUALS))
753             goto error;
754
755           if (!parse_variables_const (lexer, graph.dict,
756                                       &graph.dep_vars, &graph.n_dep_vars,
757                                       PV_NO_DUPLICATE | PV_NUMERIC))
758             goto error;
759
760           if (graph.scatter_type == ST_BIVARIATE && graph.n_dep_vars != 1)
761             {
762               lex_error(lexer, _("Only one variable is allowed."));
763               goto error;
764             }
765
766           if (!lex_force_match (lexer, T_WITH))
767             goto error;
768
769           if (!parse_variables_const (lexer, graph.dict,
770                                       &graph.dep_vars, &graph.n_dep_vars,
771                                       PV_NO_DUPLICATE | PV_NUMERIC | PV_APPEND))
772             goto error;
773
774           if (graph.scatter_type == ST_BIVARIATE && graph.n_dep_vars != 2)
775             {
776               lex_error (lexer, _("Only one variable is allowed."));
777               goto error;
778             }
779
780           if (lex_match (lexer, T_BY))
781             {
782               const struct variable *v = NULL;
783               if (!lex_match_variable (lexer,graph.dict,&v))
784                 {
785                   lex_error (lexer, _("Variable expected"));
786                   goto error;
787                 }
788               graph.by_var[0] = v;
789               graph.n_by_vars = 1;
790             }
791         }
792       else if (lex_match_id (lexer, "LINE"))
793         {
794           lex_error (lexer, _("%s is not yet implemented."),"LINE");
795           goto error;
796         }
797       else if (lex_match_id (lexer, "PIE"))
798         {
799           lex_error (lexer, _("%s is not yet implemented."),"PIE");
800           goto error;
801         }
802       else if (lex_match_id (lexer, "ERRORBAR"))
803         {
804           lex_error (lexer, _("%s is not yet implemented."),"ERRORBAR");
805           goto error;
806         }
807       else if (lex_match_id (lexer, "PARETO"))
808         {
809           lex_error (lexer, _("%s is not yet implemented."),"PARETO");
810           goto error;
811         }
812       else if (lex_match_id (lexer, "TITLE"))
813         {
814           lex_error (lexer, _("%s is not yet implemented."),"TITLE");
815           goto error;
816         }
817       else if (lex_match_id (lexer, "SUBTITLE"))
818         {
819           lex_error (lexer, _("%s is not yet implemented."),"SUBTITLE");
820           goto error;
821         }
822       else if (lex_match_id (lexer, "FOOTNOTE"))
823         {
824           lex_error (lexer, _("%s is not yet implemented."),"FOOTNOTE");
825           lex_error (lexer, _("FOOTNOTE is not implemented yet for GRAPH"));
826           goto error;
827         }
828       else if (lex_match_id (lexer, "MISSING"))
829         {
830           lex_match (lexer, T_EQUALS);
831
832           while (lex_token (lexer) != T_ENDCMD
833                  && lex_token (lexer) != T_SLASH)
834             {
835               if (lex_match_id (lexer, "LISTWISE"))
836                 {
837                   graph.missing_pw = false;
838                 }
839               else if (lex_match_id (lexer, "VARIABLE"))
840                 {
841                   graph.missing_pw = true;
842                 }
843               else if (lex_match_id (lexer, "EXCLUDE"))
844                 {
845                   graph.dep_excl = MV_ANY;
846                 }
847               else if (lex_match_id (lexer, "INCLUDE"))
848                 {
849                   graph.dep_excl = MV_SYSTEM;
850                 }
851               else if (lex_match_id (lexer, "REPORT"))
852                 {
853                   graph.fctr_excl = MV_NEVER;
854                 }
855               else if (lex_match_id (lexer, "NOREPORT"))
856                 {
857                   graph.fctr_excl = MV_ANY;
858                 }
859               else
860                 {
861                   lex_error (lexer, NULL);
862                   goto error;
863                 }
864             }
865         }
866       else
867         {
868           lex_error (lexer, NULL);
869           goto error;
870         }
871     }
872
873   switch (graph.chart_type)
874     {
875     case CT_SCATTERPLOT:
876       /* See scatterplot.h for the setup of the case prototype */
877       graph.gr_proto = caseproto_add_width (graph.gr_proto, 0); /* x value - SP_IDX_X*/
878       graph.gr_proto = caseproto_add_width (graph.gr_proto, 0); /* y value - SP_IDX_Y*/
879       /* The by_var contains the plot categories for the different xy plot colors */
880       if (graph.n_by_vars > 0) /* SP_IDX_BY */
881         graph.gr_proto = caseproto_add_width (graph.gr_proto, var_get_width(graph.by_var[0]));
882       break;
883     case CT_HISTOGRAM:
884       graph.gr_proto = caseproto_add_width (graph.gr_proto, 0); /* x value      */
885       graph.gr_proto = caseproto_add_width (graph.gr_proto, 0); /* weight value */
886       break;
887     case CT_BAR:
888       break;
889     case CT_NONE:
890       lex_error_expecting (lexer, "HISTOGRAM", "SCATTERPLOT", "BAR", NULL);
891       goto error;
892     default:
893       NOT_REACHED ();
894       break;
895     };
896
897   {
898     struct casegrouper *grouper;
899     struct casereader *group;
900     bool ok;
901
902     grouper = casegrouper_create_splits (proc_open (ds), graph.dict);
903     while (casegrouper_get_next_group (grouper, &group))
904       {
905         if (graph.chart_type == CT_BAR)
906           run_barchart (&graph, group);
907         else
908           run_graph (&graph, group);
909       }
910     ok = casegrouper_destroy (grouper);
911     ok = proc_commit (ds) && ok;
912   }
913
914   subcase_destroy (&graph.ordering);
915   free (graph.dep_vars);
916   pool_destroy (graph.pool);
917   caseproto_unref (graph.gr_proto);
918
919   return CMD_SUCCESS;
920
921  error:
922   subcase_destroy (&graph.ordering);
923   caseproto_unref (graph.gr_proto);
924   free (graph.dep_vars);
925   pool_destroy (graph.pool);
926
927   return CMD_FAILURE;
928 }