graph.c: Whitespace changes only. Fold some very long lines.
[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 "gettext.h"
62 #define _(msgid) gettext (msgid)
63 #define N_(msgid) msgid
64
65 enum chart_type
66   {
67     CT_NONE,
68     CT_BAR,
69     CT_LINE,
70     CT_PIE,
71     CT_ERRORBAR,
72     CT_HILO,
73     CT_HISTOGRAM,
74     CT_SCATTERPLOT,
75     CT_PARETO
76   };
77
78 enum scatter_type
79   {
80     ST_BIVARIATE,
81     ST_OVERLAY,
82     ST_MATRIX,
83     ST_XYZ
84   };
85
86 enum  bar_type
87   {
88     CBT_SIMPLE,
89     CBT_GROUPED,
90     CBT_STACKED,
91     CBT_RANGE
92   };
93
94
95 /* Variable index for histogram case */
96 enum
97   {
98     HG_IDX_X,
99     HG_IDX_WT
100   };
101
102 struct exploratory_stats
103 {
104   double missing;
105   double non_missing;
106
107   struct moments *mom;
108
109   double minimum;
110   double maximum;
111
112   /* Total weight */
113   double cc;
114
115   /* The minimum weight */
116   double cmin;
117 };
118
119
120 struct graph
121 {
122   struct pool *pool;
123
124   size_t n_dep_vars;
125   const struct variable **dep_vars;
126   struct exploratory_stats *es;
127
128   enum mv_class dep_excl;
129   enum mv_class fctr_excl;
130
131   const struct dictionary *dict;
132
133   bool missing_pw;
134
135   /* ------------ Graph ---------------- */
136   bool normal; /* For histograms, draw the normal curve */
137
138   enum chart_type chart_type;
139   enum scatter_type scatter_type;
140   enum bar_type bar_type;
141   const struct variable *by_var[2];
142   size_t n_by_vars;
143
144   struct subcase ordering; /* Ordering for aggregation */
145   int agr; /* Index into ag_func */
146
147   /* A caseproto that contains the plot data */
148   struct caseproto *gr_proto;
149 };
150
151
152
153
154 static double
155 calc_mom1 (double acc, double x, double w)
156 {
157   return acc + x * w;
158 }
159
160 static double
161 calc_mom0 (double acc, double x UNUSED, double w)
162 {
163   return acc + w;
164 }
165
166 static double
167 pre_low_extreme (void)
168 {
169   return -DBL_MAX;
170 }
171
172 static double
173 calc_max (double acc, double x, double w UNUSED)
174 {
175   return (acc > x) ? acc : x;
176 }
177
178 static double
179 pre_high_extreme (void)
180 {
181   return DBL_MAX;
182 }
183
184 static double
185 calc_min (double acc, double x, double w UNUSED)
186 {
187   return (acc < x) ? acc : x;
188 }
189
190 static double
191 post_normalise (double acc, double cc)
192 {
193   return acc / cc;
194 }
195
196 static double
197 post_percentage (double acc, double ccc)
198 {
199   return acc / ccc * 100.0;
200 }
201
202
203 const struct ag_func ag_func[] =
204   {
205     {"COUNT",   N_("Count"),      0, 0, NULL, calc_mom0, 0, 0},
206     {"PCT",     N_("Percentage"), 0, 0, NULL, calc_mom0, 0, post_percentage},
207     {"CUFREQ",  N_("Cumulative Count"),   0, 1, NULL, calc_mom0, 0, 0},
208     {"CUPCT",   N_("Cumulative Percent"), 0, 1, NULL, calc_mom0, 0,
209      post_percentage},
210
211     {"MEAN",    N_("Mean"),    1, 0, NULL, calc_mom1, post_normalise, 0},
212     {"SUM",     N_("Sum"),     1, 0, NULL, calc_mom1, 0, 0},
213     {"MAXIMUM", N_("Maximum"), 1, 0, pre_low_extreme, calc_max, 0, 0},
214     {"MINIMUM", N_("Minimum"), 1, 0, pre_high_extreme, calc_min, 0, 0},
215   };
216
217 const int N_AG_FUNCS = sizeof (ag_func) / sizeof (ag_func[0]);
218
219 static bool
220 parse_function (struct lexer *lexer, struct graph *graph)
221 {
222   int i;
223   for (i = 0 ; i < N_AG_FUNCS; ++i)
224     {
225       if (lex_match_id (lexer, ag_func[i].name))
226         {
227           graph->agr = i;
228           break;
229         }
230     }
231   if (i == N_AG_FUNCS)
232     {
233       goto error;
234     }
235
236   graph->n_dep_vars = ag_func[i].arity;
237   if (ag_func[i].arity > 0)
238     {
239       int v;
240       if (!lex_force_match (lexer, T_LPAREN))
241         goto error;
242
243       graph->dep_vars = xzalloc (sizeof (graph->dep_vars) * graph->n_dep_vars);
244       for (v = 0; v < ag_func[i].arity; ++v)
245         {
246           graph->dep_vars[v] = parse_variable (lexer, graph->dict);
247           if (! graph->dep_vars[v])
248             goto error;
249         }
250
251       if (!lex_force_match (lexer, T_RPAREN))
252         goto error;
253     }
254
255   if (!lex_force_match (lexer, T_BY))
256     goto error;
257
258   graph->by_var[0] = parse_variable (lexer, graph->dict);
259   if (!graph->by_var[0])
260     {
261       goto error;
262     }
263   subcase_add_var (&graph->ordering, graph->by_var[0], SC_ASCEND);
264   graph->n_by_vars++;
265
266   if (lex_match (lexer, T_BY))
267     {
268       graph->by_var[1] = parse_variable (lexer, graph->dict);
269       if (!graph->by_var[1])
270         {
271           goto error;
272         }
273       subcase_add_var (&graph->ordering, graph->by_var[1], SC_ASCEND);
274       graph->n_by_vars++;
275     }
276
277   return true;
278
279  error:
280   lex_error (lexer, NULL);
281   return false;
282 }
283
284
285 static void
286 show_scatterplot (const struct graph *cmd, struct casereader *input)
287 {
288   struct string title;
289   struct scatterplot_chart *scatterplot;
290   bool byvar_overflow = false;
291
292   ds_init_empty (&title);
293
294   if (cmd->n_by_vars > 0)
295     {
296       ds_put_format (&title, _("%s vs. %s by %s"),
297                            var_to_string (cmd->dep_vars[1]),
298                            var_to_string (cmd->dep_vars[0]),
299                            var_to_string (cmd->by_var[0]));
300     }
301   else
302     {
303       ds_put_format (&title, _("%s vs. %s"),
304                      var_to_string (cmd->dep_vars[1]),
305                      var_to_string (cmd->dep_vars[0]));
306     }
307
308   scatterplot = scatterplot_create (input,
309                                     var_to_string(cmd->dep_vars[0]),
310                                     var_to_string(cmd->dep_vars[1]),
311                                     (cmd->n_by_vars > 0) ? cmd->by_var[0]
312                                                          : 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],
431                                     case_data (c, cmd->by_var[v]),
432                                     cmd->fctr_excl))
433             break;
434         }
435
436       if (v < cmd->n_by_vars)
437         {
438           case_unref (c);
439           continue;
440         }
441
442       freqs = xrealloc (freqs, sizeof (*freqs) * ++n_freqs);
443       freqs[n_freqs - 1] = xzalloc (sizeof (**freqs)
444                                     + sizeof (union value)
445                                     * (cmd->n_by_vars - 1));
446
447       if (ag_func[cmd->agr].cumulative && n_freqs >= 2)
448         freqs[n_freqs - 1]->count = freqs[n_freqs - 2]->count;
449       else
450         freqs[n_freqs - 1]->count = 0;
451       if (ag_func[cmd->agr].pre)
452         freqs[n_freqs - 1]->count = ag_func[cmd->agr].pre();
453
454
455       for (v = 0; v < cmd->n_by_vars; ++v)
456         {
457           value_clone (&freqs[n_freqs - 1]->values[v],
458                        case_data (c, cmd->by_var[v]),
459                        var_get_width (cmd->by_var[v]));
460         }
461       case_unref (c);
462
463       double cc = 0;
464       for (;(c = casereader_read (group)) != NULL; case_unref (c))
465         {
466           const double weight = dict_get_case_weight (cmd->dict,c,NULL);
467           const double x = (cmd->n_dep_vars > 0)
468             ? case_data (c, cmd->dep_vars[0])->f : SYSMIS;
469
470           cc += weight;
471
472           freqs[n_freqs - 1]->count
473             = ag_func[cmd->agr].calc (freqs[n_freqs - 1]->count, x, weight);
474         }
475
476       if (ag_func[cmd->agr].post)
477         freqs[n_freqs - 1]->count
478           = ag_func[cmd->agr].post (freqs[n_freqs - 1]->count, cc);
479
480       ccc += cc;
481     }
482
483   casegrouper_destroy (grouper);
484
485   for (int i = 0; i < n_freqs; ++i)
486     {
487       if (ag_func[cmd->agr].ppost)
488         freqs[i]->count = ag_func[cmd->agr].ppost (freqs[i]->count, ccc);
489     }
490
491
492   {
493     struct string label;
494     ds_init_empty (&label);
495
496     if (cmd->n_dep_vars > 0)
497       ds_put_format (&label, _("%s of %s"),
498                      ag_func[cmd->agr].description,
499                      var_get_name (cmd->dep_vars[0]));
500     else
501       ds_put_cstr (&label,
502                      ag_func[cmd->agr].description);
503
504     chart_item_submit (barchart_create (cmd->by_var, cmd->n_by_vars,
505                                         ds_cstr (&label), false,
506                                         freqs, n_freqs));
507
508     ds_destroy (&label);
509   }
510
511   for (int i = 0; i < n_freqs; ++i)
512     free (freqs[i]);
513
514   free (freqs);
515 }
516
517
518 static void
519 run_graph (struct graph *cmd, struct casereader *input)
520 {
521   struct ccase *c;
522   struct casereader *reader;
523   struct casewriter *writer;
524
525   cmd->es = pool_calloc (cmd->pool,cmd->n_dep_vars, sizeof *cmd->es);
526   for(int v=0;v<cmd->n_dep_vars;v++)
527     {
528       cmd->es[v].mom = moments_create (MOMENT_KURTOSIS);
529       cmd->es[v].cmin = DBL_MAX;
530       cmd->es[v].maximum = -DBL_MAX;
531       cmd->es[v].minimum =  DBL_MAX;
532     }
533   /* Always remove cases listwise. This is correct for */
534   /* the histogram because there is only one variable  */
535   /* and a simple bivariate scatterplot                */
536   /* if ( cmd->missing_pw == false)                    */
537     input = casereader_create_filter_missing (input,
538                                               cmd->dep_vars,
539                                               cmd->n_dep_vars,
540                                               cmd->dep_excl,
541                                               NULL,
542                                               NULL);
543
544   writer = autopaging_writer_create (cmd->gr_proto);
545
546   /* The case data is copied to a new writer        */
547   /* The setup of the case depends on the Charttype */
548   /* For Scatterplot x is assumed in dep_vars[0]    */
549   /*                 y is assumed in dep_vars[1]    */
550   /* For Histogram   x is assumed in dep_vars[0]    */
551   assert(SP_IDX_X == 0 && SP_IDX_Y == 1 && HG_IDX_X == 0);
552
553   for (;(c = casereader_read (input)) != NULL; case_unref (c))
554     {
555       struct ccase *outcase = case_create (cmd->gr_proto);
556       const double weight = dict_get_case_weight (cmd->dict,c,NULL);
557       if (cmd->chart_type == CT_HISTOGRAM)
558         case_data_rw_idx (outcase, HG_IDX_WT)->f = weight;
559       if (cmd->chart_type == CT_SCATTERPLOT && cmd->n_by_vars > 0)
560         value_copy (case_data_rw_idx (outcase, SP_IDX_BY),
561                     case_data (c, cmd->by_var[0]),
562                     var_get_width (cmd->by_var[0]));
563       for(int v=0;v<cmd->n_dep_vars;v++)
564         {
565           const struct variable *var = cmd->dep_vars[v];
566           const double x = case_data (c, var)->f;
567
568           if (var_is_value_missing (var, case_data (c, var), cmd->dep_excl))
569             {
570               cmd->es[v].missing += weight;
571               continue;
572             }
573           /* Magically v value fits to SP_IDX_X, SP_IDX_Y, HG_IDX_X */
574           case_data_rw_idx (outcase, v)->f = x;
575
576           if (x > cmd->es[v].maximum)
577             cmd->es[v].maximum = x;
578
579           if (x < cmd->es[v].minimum)
580             cmd->es[v].minimum =  x;
581
582           cmd->es[v].non_missing += weight;
583
584           moments_pass_one (cmd->es[v].mom, x, weight);
585
586           cmd->es[v].cc += weight;
587
588           if (cmd->es[v].cmin > weight)
589             cmd->es[v].cmin = weight;
590         }
591       casewriter_write (writer,outcase);
592     }
593
594   reader = casewriter_make_reader (writer);
595
596   switch (cmd->chart_type)
597     {
598     case CT_HISTOGRAM:
599       show_histogr (cmd,reader);
600       break;
601     case CT_SCATTERPLOT:
602       show_scatterplot (cmd,reader);
603       break;
604     default:
605       NOT_REACHED ();
606       break;
607     };
608
609   casereader_destroy (input);
610   cleanup_exploratory_stats (cmd);
611 }
612
613
614 int
615 cmd_graph (struct lexer *lexer, struct dataset *ds)
616 {
617   struct graph graph;
618
619   graph.missing_pw = false;
620
621   graph.pool = pool_create ();
622
623   graph.dep_excl = MV_ANY;
624   graph.fctr_excl = MV_ANY;
625
626   graph.dict = dataset_dict (ds);
627
628   graph.dep_vars = NULL;
629   graph.chart_type = CT_NONE;
630   graph.scatter_type = ST_BIVARIATE;
631   graph.n_by_vars = 0;
632   graph.gr_proto = caseproto_create ();
633
634   subcase_init_empty (&graph.ordering);
635
636   while (lex_token (lexer) != T_ENDCMD)
637     {
638       lex_match (lexer, T_SLASH);
639
640       if (lex_match_id (lexer, "HISTOGRAM"))
641         {
642           if (graph.chart_type != CT_NONE)
643             {
644               lex_error (lexer, _("Only one chart type is allowed."));
645               goto error;
646             }
647           graph.normal = false;
648           if (lex_match (lexer, T_LPAREN))
649             {
650               if (!lex_force_match_id (lexer, "NORMAL"))
651                 goto error;
652
653               if (!lex_force_match (lexer, T_RPAREN))
654                 goto error;
655
656               graph.normal = true;
657             }
658           if (!lex_force_match (lexer, T_EQUALS))
659             goto error;
660           graph.chart_type = CT_HISTOGRAM;
661           if (!parse_variables_const (lexer, graph.dict,
662                                       &graph.dep_vars, &graph.n_dep_vars,
663                                       PV_NO_DUPLICATE | PV_NUMERIC))
664             goto error;
665           if (graph.n_dep_vars > 1)
666             {
667               lex_error (lexer, _("Only one variable is allowed."));
668               goto error;
669             }
670         }
671       else if (lex_match_id (lexer, "BAR"))
672         {
673           if (graph.chart_type != CT_NONE)
674             {
675               lex_error (lexer, _("Only one chart type is allowed."));
676               goto error;
677             }
678           graph.chart_type = CT_BAR;
679           graph.bar_type = CBT_SIMPLE;
680
681           if (lex_match (lexer, T_LPAREN))
682             {
683               if (lex_match_id (lexer, "SIMPLE"))
684                 {
685                   /* This is the default anyway */
686                 }
687               else if (lex_match_id (lexer, "GROUPED"))
688                 {
689                   graph.bar_type = CBT_GROUPED;
690                   goto error;
691                 }
692               else if (lex_match_id (lexer, "STACKED"))
693                 {
694                   graph.bar_type = CBT_STACKED;
695                   lex_error (lexer, _("%s is not yet implemented."), "STACKED");
696                   goto error;
697                 }
698               else if (lex_match_id (lexer, "RANGE"))
699                 {
700                   graph.bar_type = CBT_RANGE;
701                   lex_error (lexer, _("%s is not yet implemented."), "RANGE");
702                   goto error;
703                 }
704               else
705                 {
706                   lex_error (lexer, NULL);
707                   goto error;
708                 }
709               if (!lex_force_match (lexer, T_RPAREN))
710                 goto error;
711             }
712
713           if (!lex_force_match (lexer, T_EQUALS))
714             goto error;
715
716           if (! parse_function (lexer, &graph))
717             goto error;
718         }
719       else if (lex_match_id (lexer, "SCATTERPLOT"))
720         {
721           if (graph.chart_type != CT_NONE)
722             {
723               lex_error (lexer, _("Only one chart type is allowed."));
724               goto error;
725             }
726           graph.chart_type = CT_SCATTERPLOT;
727           if (lex_match (lexer, T_LPAREN))
728             {
729               if (lex_match_id (lexer, "BIVARIATE"))
730                 {
731                   /* This is the default anyway */
732                 }
733               else if (lex_match_id (lexer, "OVERLAY"))
734                 {
735                   lex_error (lexer, _("%s is not yet implemented."),"OVERLAY");
736                   goto error;
737                 }
738               else if (lex_match_id (lexer, "MATRIX"))
739                 {
740                   lex_error (lexer, _("%s is not yet implemented."),"MATRIX");
741                   goto error;
742                 }
743               else if (lex_match_id (lexer, "XYZ"))
744                 {
745                   lex_error(lexer, _("%s is not yet implemented."),"XYZ");
746                   goto error;
747                 }
748               else
749                 {
750                   lex_error_expecting (lexer, "BIVARIATE", NULL);
751                   goto error;
752                 }
753               if (!lex_force_match (lexer, T_RPAREN))
754                 goto error;
755             }
756           if (!lex_force_match (lexer, T_EQUALS))
757             goto error;
758
759           if (!parse_variables_const (lexer, graph.dict,
760                                       &graph.dep_vars, &graph.n_dep_vars,
761                                       PV_NO_DUPLICATE | PV_NUMERIC))
762             goto error;
763
764           if (graph.scatter_type == ST_BIVARIATE && graph.n_dep_vars != 1)
765             {
766               lex_error(lexer, _("Only one variable is allowed."));
767               goto error;
768             }
769
770           if (!lex_force_match (lexer, T_WITH))
771             goto error;
772
773           if (!parse_variables_const (lexer, graph.dict,
774                                       &graph.dep_vars, &graph.n_dep_vars,
775                                       PV_NO_DUPLICATE | PV_NUMERIC | PV_APPEND))
776             goto error;
777
778           if (graph.scatter_type == ST_BIVARIATE && graph.n_dep_vars != 2)
779             {
780               lex_error (lexer, _("Only one variable is allowed."));
781               goto error;
782             }
783
784           if (lex_match (lexer, T_BY))
785             {
786               const struct variable *v = NULL;
787               if (!lex_match_variable (lexer,graph.dict,&v))
788                 {
789                   lex_error (lexer, _("Variable expected"));
790                   goto error;
791                 }
792               graph.by_var[0] = v;
793               graph.n_by_vars = 1;
794             }
795         }
796       else if (lex_match_id (lexer, "LINE"))
797         {
798           lex_error (lexer, _("%s is not yet implemented."),"LINE");
799           goto error;
800         }
801       else if (lex_match_id (lexer, "PIE"))
802         {
803           lex_error (lexer, _("%s is not yet implemented."),"PIE");
804           goto error;
805         }
806       else if (lex_match_id (lexer, "ERRORBAR"))
807         {
808           lex_error (lexer, _("%s is not yet implemented."),"ERRORBAR");
809           goto error;
810         }
811       else if (lex_match_id (lexer, "PARETO"))
812         {
813           lex_error (lexer, _("%s is not yet implemented."),"PARETO");
814           goto error;
815         }
816       else if (lex_match_id (lexer, "TITLE"))
817         {
818           lex_error (lexer, _("%s is not yet implemented."),"TITLE");
819           goto error;
820         }
821       else if (lex_match_id (lexer, "SUBTITLE"))
822         {
823           lex_error (lexer, _("%s is not yet implemented."),"SUBTITLE");
824           goto error;
825         }
826       else if (lex_match_id (lexer, "FOOTNOTE"))
827         {
828           lex_error (lexer, _("%s is not yet implemented."),"FOOTNOTE");
829           lex_error (lexer, _("FOOTNOTE is not implemented yet for GRAPH"));
830           goto error;
831         }
832       else if (lex_match_id (lexer, "MISSING"))
833         {
834           lex_match (lexer, T_EQUALS);
835
836           while (lex_token (lexer) != T_ENDCMD
837                  && lex_token (lexer) != T_SLASH)
838             {
839               if (lex_match_id (lexer, "LISTWISE"))
840                 {
841                   graph.missing_pw = false;
842                 }
843               else if (lex_match_id (lexer, "VARIABLE"))
844                 {
845                   graph.missing_pw = true;
846                 }
847               else if (lex_match_id (lexer, "EXCLUDE"))
848                 {
849                   graph.dep_excl = MV_ANY;
850                 }
851               else if (lex_match_id (lexer, "INCLUDE"))
852                 {
853                   graph.dep_excl = MV_SYSTEM;
854                 }
855               else if (lex_match_id (lexer, "REPORT"))
856                 {
857                   graph.fctr_excl = MV_NEVER;
858                 }
859               else if (lex_match_id (lexer, "NOREPORT"))
860                 {
861                   graph.fctr_excl = MV_ANY;
862                 }
863               else
864                 {
865                   lex_error (lexer, NULL);
866                   goto error;
867                 }
868             }
869         }
870       else
871         {
872           lex_error (lexer, NULL);
873           goto error;
874         }
875     }
876
877   switch (graph.chart_type)
878     {
879     case CT_SCATTERPLOT:
880       /* See scatterplot.h for the setup of the case prototype */
881
882       /* x value - SP_IDX_X*/
883       graph.gr_proto = caseproto_add_width (graph.gr_proto, 0);
884
885       /* y value - SP_IDX_Y*/
886       graph.gr_proto = caseproto_add_width (graph.gr_proto, 0);
887       /* The by_var contains the plot categories for the different xy
888          plot colors */
889       if (graph.n_by_vars > 0) /* SP_IDX_BY */
890         graph.gr_proto = caseproto_add_width (graph.gr_proto,
891                                               var_get_width(graph.by_var[0]));
892       break;
893     case CT_HISTOGRAM:
894       /* x value      */
895       graph.gr_proto = caseproto_add_width (graph.gr_proto, 0);
896       /* weight value */
897       graph.gr_proto = caseproto_add_width (graph.gr_proto, 0);
898       break;
899     case CT_BAR:
900       break;
901     case CT_NONE:
902       lex_error_expecting (lexer, "HISTOGRAM", "SCATTERPLOT", "BAR", NULL);
903       goto error;
904     default:
905       NOT_REACHED ();
906       break;
907     };
908
909   {
910     struct casegrouper *grouper;
911     struct casereader *group;
912     bool ok;
913
914     grouper = casegrouper_create_splits (proc_open (ds), graph.dict);
915     while (casegrouper_get_next_group (grouper, &group))
916       {
917         if (graph.chart_type == CT_BAR)
918           run_barchart (&graph, group);
919         else
920           run_graph (&graph, group);
921       }
922     ok = casegrouper_destroy (grouper);
923     ok = proc_commit (ds) && ok;
924   }
925
926   subcase_destroy (&graph.ordering);
927   free (graph.dep_vars);
928   pool_destroy (graph.pool);
929   caseproto_unref (graph.gr_proto);
930
931   return CMD_SUCCESS;
932
933  error:
934   subcase_destroy (&graph.ordering);
935   caseproto_unref (graph.gr_proto);
936   free (graph.dep_vars);
937   pool_destroy (graph.pool);
938
939   return CMD_FAILURE;
940 }