output: Introduce pivot tables.
[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, post_percentage},
209
210     {"MEAN",    N_("Mean"),    1, 0, NULL, calc_mom1, post_normalise, 0},
211     {"SUM",     N_("Sum"),     1, 0, NULL, calc_mom1, 0, 0},
212     {"MAXIMUM", N_("Maximum"), 1, 0, pre_low_extreme, calc_max, 0, 0},
213     {"MINIMUM", N_("Minimum"), 1, 0, pre_high_extreme, calc_min, 0, 0},
214   };
215
216 const int N_AG_FUNCS = sizeof (ag_func) / sizeof (ag_func[0]);
217
218 static bool
219 parse_function (struct lexer *lexer, struct graph *graph)
220 {
221   int i;
222   for (i = 0 ; i < N_AG_FUNCS; ++i)
223     {
224       if (lex_match_id (lexer, ag_func[i].name))
225         {
226           graph->agr = i;
227           break;
228         }
229     }
230   if (i == N_AG_FUNCS)
231     {
232       goto error;
233     }
234
235   graph->n_dep_vars = ag_func[i].arity;
236   if (ag_func[i].arity > 0)
237     {
238       int v;
239       if (!lex_force_match (lexer, T_LPAREN))
240         goto error;
241
242       graph->dep_vars = xzalloc (sizeof (graph->dep_vars) * graph->n_dep_vars);
243       for (v = 0; v < ag_func[i].arity; ++v)
244         {
245           graph->dep_vars[v] = parse_variable (lexer, graph->dict);
246           if (! graph->dep_vars[v])
247             goto error;
248         }
249
250       if (!lex_force_match (lexer, T_RPAREN))
251         goto error;
252     }
253
254   if (!lex_force_match (lexer, T_BY))
255     goto error;
256
257   graph->by_var[0] = parse_variable (lexer, graph->dict);
258   if (!graph->by_var[0])
259     {
260       goto error;
261     }
262   subcase_add_var (&graph->ordering, graph->by_var[0], SC_ASCEND);
263   graph->n_by_vars++;
264
265   if (lex_match (lexer, T_BY))
266     {
267       graph->by_var[1] = parse_variable (lexer, graph->dict);
268       if (!graph->by_var[1])
269         {
270           goto error;
271         }
272       subcase_add_var (&graph->ordering, graph->by_var[1], SC_ASCEND);
273       graph->n_by_vars++;
274     }
275
276   return true;
277
278  error:
279   lex_error (lexer, NULL);
280   return false;
281 }
282
283
284 static void
285 show_scatterplot (const struct graph *cmd, struct casereader *input)
286 {
287   struct string title;
288   struct scatterplot_chart *scatterplot;
289   bool byvar_overflow = false;
290
291   ds_init_empty (&title);
292
293   if (cmd->n_by_vars > 0)
294     {
295       ds_put_format (&title, _("%s vs. %s by %s"),
296                            var_to_string (cmd->dep_vars[1]),
297                            var_to_string (cmd->dep_vars[0]),
298                            var_to_string (cmd->by_var[0]));
299     }
300   else
301     {
302       ds_put_format (&title, _("%s vs. %s"),
303                      var_to_string (cmd->dep_vars[1]),
304                      var_to_string (cmd->dep_vars[0]));
305     }
306
307   scatterplot = scatterplot_create (input,
308                                     var_to_string(cmd->dep_vars[0]),
309                                     var_to_string(cmd->dep_vars[1]),
310                                     (cmd->n_by_vars > 0) ? cmd->by_var[0] : NULL,
311                                     &byvar_overflow,
312                                     ds_cstr (&title),
313                                     cmd->es[0].minimum, cmd->es[0].maximum,
314                                     cmd->es[1].minimum, cmd->es[1].maximum);
315   scatterplot_chart_submit (scatterplot);
316   ds_destroy (&title);
317
318   if (byvar_overflow)
319     {
320       msg (MW, _("Maximum number of scatterplot categories reached. "
321                  "Your BY variable has too many distinct values. "
322                  "The coloring of the plot will not be correct."));
323     }
324 }
325
326 static void
327 show_histogr (const struct graph *cmd, struct casereader *input)
328 {
329   struct histogram *histogram;
330   struct ccase *c;
331
332   if (cmd->es[0].cc <= 0)
333     {
334       casereader_destroy (input);
335       return;
336     }
337
338   {
339     /* Sturges Rule */
340     double bin_width = fabs (cmd->es[0].minimum - cmd->es[0].maximum)
341       / (1 + log2 (cmd->es[0].cc))
342       ;
343
344     histogram =
345       histogram_create (bin_width, cmd->es[0].minimum, cmd->es[0].maximum);
346   }
347
348   if (NULL == histogram)
349     {
350       casereader_destroy (input);
351       return;
352     }
353
354   for (;(c = casereader_read (input)) != NULL; case_unref (c))
355     {
356       const double x      = case_data_idx (c, HG_IDX_X)->f;
357       const double weight = case_data_idx (c, HG_IDX_WT)->f;
358       moments_pass_two (cmd->es[0].mom, x, weight);
359       histogram_add (histogram, x, weight);
360     }
361   casereader_destroy (input);
362
363
364   {
365     double n, mean, var;
366
367     struct string label;
368
369     ds_init_cstr (&label,
370                   var_to_string (cmd->dep_vars[0]));
371
372     moments_calculate (cmd->es[0].mom, &n, &mean, &var, NULL, NULL);
373
374     chart_item_submit
375       ( histogram_chart_create (histogram->gsl_hist,
376                                 ds_cstr (&label), n, mean,
377                                 sqrt (var), cmd->normal));
378
379     statistic_destroy (&histogram->parent);
380     ds_destroy (&label);
381   }
382 }
383
384 static void
385 cleanup_exploratory_stats (struct graph *cmd)
386 {
387   int v;
388
389   for (v = 0; v < cmd->n_dep_vars; ++v)
390     {
391       moments_destroy (cmd->es[v].mom);
392     }
393 }
394
395
396 static void
397 run_barchart (struct graph *cmd, struct casereader *input)
398 {
399   struct casegrouper *grouper;
400   struct casereader *group;
401   double ccc = 0.0;
402
403   if ( cmd->missing_pw == false)
404     input = casereader_create_filter_missing (input,
405                                               cmd->dep_vars,
406                                               cmd->n_dep_vars,
407                                               cmd->dep_excl,
408                                               NULL,
409                                               NULL);
410
411
412   input = sort_execute (input, &cmd->ordering);
413
414   struct freq **freqs = NULL;
415   int n_freqs = 0;
416
417   for (grouper = casegrouper_create_vars (input, cmd->by_var,
418                                           cmd->n_by_vars);
419        casegrouper_get_next_group (grouper, &group);
420        casereader_destroy (group))
421     {
422       int v;
423       struct ccase *c = casereader_peek (group, 0);
424
425       /* Deal with missing values in the categorical variables */
426       for (v = 0; v < cmd->n_by_vars; ++v)
427         {
428           if (var_is_value_missing (cmd->by_var[v], case_data (c, cmd->by_var[v]), cmd->fctr_excl) )
429             break;
430         }
431
432       if (v < cmd->n_by_vars)
433         {
434           case_unref (c);
435           continue;
436         }
437
438       freqs = xrealloc (freqs, sizeof (*freqs) * ++n_freqs);
439       freqs[n_freqs - 1] = xzalloc (sizeof (**freqs) +
440                                     sizeof (union value) * (cmd->n_by_vars - 1) );
441
442       if (ag_func[cmd->agr].cumulative && n_freqs >= 2)
443         freqs[n_freqs - 1]->count = freqs[n_freqs - 2]->count;
444       else
445         freqs[n_freqs - 1]->count = 0;
446       if (ag_func[cmd->agr].pre)
447         freqs[n_freqs - 1]->count = ag_func[cmd->agr].pre();
448
449
450       for (v = 0; v < cmd->n_by_vars; ++v)
451         {
452           value_clone (&freqs[n_freqs - 1]->values[v], case_data (c, cmd->by_var[v]),
453                        var_get_width (cmd->by_var[v])
454                        );
455         }
456       case_unref (c);
457
458       double cc = 0;
459       for (;(c = casereader_read (group)) != NULL; case_unref (c))
460         {
461           const double weight = dict_get_case_weight (cmd->dict,c,NULL);
462           const double x =  (cmd->n_dep_vars > 0) ? case_data (c, cmd->dep_vars[0])->f : SYSMIS;
463
464           cc += weight;
465
466           freqs[n_freqs - 1]->count
467             = ag_func[cmd->agr].calc (freqs[n_freqs - 1]->count, x, weight);
468         }
469
470       if (ag_func[cmd->agr].post)
471         freqs[n_freqs - 1]->count
472           = ag_func[cmd->agr].post (freqs[n_freqs - 1]->count, cc);
473
474       ccc += cc;
475     }
476
477   casegrouper_destroy (grouper);
478
479   for (int i = 0; i < n_freqs; ++i)
480     {
481       if (ag_func[cmd->agr].ppost)
482         freqs[i]->count = ag_func[cmd->agr].ppost (freqs[i]->count, ccc);
483     }
484
485
486   {
487     struct string label;
488     ds_init_empty (&label);
489
490     if (cmd->n_dep_vars > 0)
491       ds_put_format (&label, _("%s of %s"),
492                      ag_func[cmd->agr].description,
493                      var_get_name (cmd->dep_vars[0]));
494     else
495       ds_put_cstr (&label,
496                      ag_func[cmd->agr].description);
497
498     chart_item_submit (barchart_create (cmd->by_var, cmd->n_by_vars,
499                                         ds_cstr (&label), false,
500                                         freqs, n_freqs));
501
502     ds_destroy (&label);
503   }
504
505   for (int i = 0; i < n_freqs; ++i)
506     free (freqs[i]);
507
508   free (freqs);
509 }
510
511
512 static void
513 run_graph (struct graph *cmd, struct casereader *input)
514 {
515   struct ccase *c;
516   struct casereader *reader;
517   struct casewriter *writer;
518
519   cmd->es = pool_calloc (cmd->pool,cmd->n_dep_vars,sizeof(struct exploratory_stats));
520   for(int v=0;v<cmd->n_dep_vars;v++)
521     {
522       cmd->es[v].mom = moments_create (MOMENT_KURTOSIS);
523       cmd->es[v].cmin = DBL_MAX;
524       cmd->es[v].maximum = -DBL_MAX;
525       cmd->es[v].minimum =  DBL_MAX;
526     }
527   /* Always remove cases listwise. This is correct for */
528   /* the histogram because there is only one variable  */
529   /* and a simple bivariate scatterplot                */
530   /* if ( cmd->missing_pw == false)                    */
531     input = casereader_create_filter_missing (input,
532                                               cmd->dep_vars,
533                                               cmd->n_dep_vars,
534                                               cmd->dep_excl,
535                                               NULL,
536                                               NULL);
537
538   writer = autopaging_writer_create (cmd->gr_proto);
539
540   /* The case data is copied to a new writer        */
541   /* The setup of the case depends on the Charttype */
542   /* For Scatterplot x is assumed in dep_vars[0]    */
543   /*                 y is assumed in dep_vars[1]    */
544   /* For Histogram   x is assumed in dep_vars[0]    */
545   assert(SP_IDX_X == 0 && SP_IDX_Y == 1 && HG_IDX_X == 0);
546
547   for (;(c = casereader_read (input)) != NULL; case_unref (c))
548     {
549       struct ccase *outcase = case_create (cmd->gr_proto);
550       const double weight = dict_get_case_weight (cmd->dict,c,NULL);
551       if (cmd->chart_type == CT_HISTOGRAM)
552         case_data_rw_idx (outcase, HG_IDX_WT)->f = weight;
553       if (cmd->chart_type == CT_SCATTERPLOT && cmd->n_by_vars > 0)
554         value_copy (case_data_rw_idx (outcase, SP_IDX_BY),
555                     case_data (c, cmd->by_var[0]),
556                     var_get_width (cmd->by_var[0]));
557       for(int v=0;v<cmd->n_dep_vars;v++)
558         {
559           const struct variable *var = cmd->dep_vars[v];
560           const double x = case_data (c, var)->f;
561
562           if (var_is_value_missing (var, case_data (c, var), cmd->dep_excl))
563             {
564               cmd->es[v].missing += weight;
565               continue;
566             }
567           /* Magically v value fits to SP_IDX_X, SP_IDX_Y, HG_IDX_X */
568           case_data_rw_idx (outcase, v)->f = x;
569
570           if (x > cmd->es[v].maximum)
571             cmd->es[v].maximum = x;
572
573           if (x < cmd->es[v].minimum)
574             cmd->es[v].minimum =  x;
575
576           cmd->es[v].non_missing += weight;
577
578           moments_pass_one (cmd->es[v].mom, x, weight);
579
580           cmd->es[v].cc += weight;
581
582           if (cmd->es[v].cmin > weight)
583             cmd->es[v].cmin = weight;
584         }
585       casewriter_write (writer,outcase);
586     }
587
588   reader = casewriter_make_reader (writer);
589
590   switch (cmd->chart_type)
591     {
592     case CT_HISTOGRAM:
593       show_histogr (cmd,reader);
594       break;
595     case CT_SCATTERPLOT:
596       show_scatterplot (cmd,reader);
597       break;
598     default:
599       NOT_REACHED ();
600       break;
601     };
602
603   casereader_destroy (input);
604   cleanup_exploratory_stats (cmd);
605 }
606
607
608 int
609 cmd_graph (struct lexer *lexer, struct dataset *ds)
610 {
611   struct graph graph;
612
613   graph.missing_pw = false;
614
615   graph.pool = pool_create ();
616
617   graph.dep_excl = MV_ANY;
618   graph.fctr_excl = MV_ANY;
619
620   graph.dict = dataset_dict (ds);
621
622   graph.dep_vars = NULL;
623   graph.chart_type = CT_NONE;
624   graph.scatter_type = ST_BIVARIATE;
625   graph.n_by_vars = 0;
626   graph.gr_proto = caseproto_create ();
627
628   subcase_init_empty (&graph.ordering);
629
630   while (lex_token (lexer) != T_ENDCMD)
631     {
632       lex_match (lexer, T_SLASH);
633
634       if (lex_match_id (lexer, "HISTOGRAM"))
635         {
636           if (graph.chart_type != CT_NONE)
637             {
638               lex_error (lexer, _("Only one chart type is allowed."));
639               goto error;
640             }
641           graph.normal = false;
642           if (lex_match (lexer, T_LPAREN))
643             {
644               if (!lex_force_match_id (lexer, "NORMAL"))
645                 goto error;
646
647               if (!lex_force_match (lexer, T_RPAREN))
648                 goto error;
649
650               graph.normal = true;
651             }
652           if (!lex_force_match (lexer, T_EQUALS))
653             goto error;
654           graph.chart_type = CT_HISTOGRAM;
655           if (!parse_variables_const (lexer, graph.dict,
656                                       &graph.dep_vars, &graph.n_dep_vars,
657                                       PV_NO_DUPLICATE | PV_NUMERIC))
658             goto error;
659           if (graph.n_dep_vars > 1)
660             {
661               lex_error (lexer, _("Only one variable is allowed."));
662               goto error;
663             }
664         }
665       else if (lex_match_id (lexer, "BAR"))
666         {
667           if (graph.chart_type != CT_NONE)
668             {
669               lex_error (lexer, _("Only one chart type is allowed."));
670               goto error;
671             }
672           graph.chart_type = CT_BAR;
673           graph.bar_type = CBT_SIMPLE;
674
675           if (lex_match (lexer, T_LPAREN))
676             {
677               if (lex_match_id (lexer, "SIMPLE"))
678                 {
679                   /* This is the default anyway */
680                 }
681               else if (lex_match_id (lexer, "GROUPED"))
682                 {
683                   graph.bar_type = CBT_GROUPED;
684                   goto error;
685                 }
686               else if (lex_match_id (lexer, "STACKED"))
687                 {
688                   graph.bar_type = CBT_STACKED;
689                   lex_error (lexer, _("%s is not yet implemented."), "STACKED");
690                   goto error;
691                 }
692               else if (lex_match_id (lexer, "RANGE"))
693                 {
694                   graph.bar_type = CBT_RANGE;
695                   lex_error (lexer, _("%s is not yet implemented."), "RANGE");
696                   goto error;
697                 }
698               else
699                 {
700                   lex_error (lexer, NULL);
701                   goto error;
702                 }
703               if (!lex_force_match (lexer, T_RPAREN))
704                 goto error;
705             }
706
707           if (!lex_force_match (lexer, T_EQUALS))
708             goto error;
709
710           if (! parse_function (lexer, &graph))
711             goto error;
712         }
713       else if (lex_match_id (lexer, "SCATTERPLOT"))
714         {
715           if (graph.chart_type != CT_NONE)
716             {
717               lex_error (lexer, _("Only one chart type is allowed."));
718               goto error;
719             }
720           graph.chart_type = CT_SCATTERPLOT;
721           if (lex_match (lexer, T_LPAREN))
722             {
723               if (lex_match_id (lexer, "BIVARIATE"))
724                 {
725                   /* This is the default anyway */
726                 }
727               else if (lex_match_id (lexer, "OVERLAY"))
728                 {
729                   lex_error (lexer, _("%s is not yet implemented."),"OVERLAY");
730                   goto error;
731                 }
732               else if (lex_match_id (lexer, "MATRIX"))
733                 {
734                   lex_error (lexer, _("%s is not yet implemented."),"MATRIX");
735                   goto error;
736                 }
737               else if (lex_match_id (lexer, "XYZ"))
738                 {
739                   lex_error(lexer, _("%s is not yet implemented."),"XYZ");
740                   goto error;
741                 }
742               else
743                 {
744                   lex_error_expecting (lexer, "BIVARIATE", NULL);
745                   goto error;
746                 }
747               if (!lex_force_match (lexer, T_RPAREN))
748                 goto error;
749             }
750           if (!lex_force_match (lexer, T_EQUALS))
751             goto error;
752
753           if (!parse_variables_const (lexer, graph.dict,
754                                       &graph.dep_vars, &graph.n_dep_vars,
755                                       PV_NO_DUPLICATE | PV_NUMERIC))
756             goto error;
757
758           if (graph.scatter_type == ST_BIVARIATE && graph.n_dep_vars != 1)
759             {
760               lex_error(lexer, _("Only one variable is allowed."));
761               goto error;
762             }
763
764           if (!lex_force_match (lexer, T_WITH))
765             goto error;
766
767           if (!parse_variables_const (lexer, graph.dict,
768                                       &graph.dep_vars, &graph.n_dep_vars,
769                                       PV_NO_DUPLICATE | PV_NUMERIC | PV_APPEND))
770             goto error;
771
772           if (graph.scatter_type == ST_BIVARIATE && graph.n_dep_vars != 2)
773             {
774               lex_error (lexer, _("Only one variable is allowed."));
775               goto error;
776             }
777
778           if (lex_match (lexer, T_BY))
779             {
780               const struct variable *v = NULL;
781               if (!lex_match_variable (lexer,graph.dict,&v))
782                 {
783                   lex_error (lexer, _("Variable expected"));
784                   goto error;
785                 }
786               graph.by_var[0] = v;
787               graph.n_by_vars = 1;
788             }
789         }
790       else if (lex_match_id (lexer, "LINE"))
791         {
792           lex_error (lexer, _("%s is not yet implemented."),"LINE");
793           goto error;
794         }
795       else if (lex_match_id (lexer, "PIE"))
796         {
797           lex_error (lexer, _("%s is not yet implemented."),"PIE");
798           goto error;
799         }
800       else if (lex_match_id (lexer, "ERRORBAR"))
801         {
802           lex_error (lexer, _("%s is not yet implemented."),"ERRORBAR");
803           goto error;
804         }
805       else if (lex_match_id (lexer, "PARETO"))
806         {
807           lex_error (lexer, _("%s is not yet implemented."),"PARETO");
808           goto error;
809         }
810       else if (lex_match_id (lexer, "TITLE"))
811         {
812           lex_error (lexer, _("%s is not yet implemented."),"TITLE");
813           goto error;
814         }
815       else if (lex_match_id (lexer, "SUBTITLE"))
816         {
817           lex_error (lexer, _("%s is not yet implemented."),"SUBTITLE");
818           goto error;
819         }
820       else if (lex_match_id (lexer, "FOOTNOTE"))
821         {
822           lex_error (lexer, _("%s is not yet implemented."),"FOOTNOTE");
823           lex_error (lexer, _("FOOTNOTE is not implemented yet for GRAPH"));
824           goto error;
825         }
826       else if (lex_match_id (lexer, "MISSING"))
827         {
828           lex_match (lexer, T_EQUALS);
829
830           while (lex_token (lexer) != T_ENDCMD
831                  && lex_token (lexer) != T_SLASH)
832             {
833               if (lex_match_id (lexer, "LISTWISE"))
834                 {
835                   graph.missing_pw = false;
836                 }
837               else if (lex_match_id (lexer, "VARIABLE"))
838                 {
839                   graph.missing_pw = true;
840                 }
841               else if (lex_match_id (lexer, "EXCLUDE"))
842                 {
843                   graph.dep_excl = MV_ANY;
844                 }
845               else if (lex_match_id (lexer, "INCLUDE"))
846                 {
847                   graph.dep_excl = MV_SYSTEM;
848                 }
849               else if (lex_match_id (lexer, "REPORT"))
850                 {
851                   graph.fctr_excl = MV_NEVER;
852                 }
853               else if (lex_match_id (lexer, "NOREPORT"))
854                 {
855                   graph.fctr_excl = MV_ANY;
856                 }
857               else
858                 {
859                   lex_error (lexer, NULL);
860                   goto error;
861                 }
862             }
863         }
864       else
865         {
866           lex_error (lexer, NULL);
867           goto error;
868         }
869     }
870
871   switch (graph.chart_type)
872     {
873     case CT_SCATTERPLOT:
874       /* See scatterplot.h for the setup of the case prototype */
875       graph.gr_proto = caseproto_add_width (graph.gr_proto, 0); /* x value - SP_IDX_X*/
876       graph.gr_proto = caseproto_add_width (graph.gr_proto, 0); /* y value - SP_IDX_Y*/
877       /* The by_var contains the plot categories for the different xy plot colors */
878       if (graph.n_by_vars > 0) /* SP_IDX_BY */
879         graph.gr_proto = caseproto_add_width (graph.gr_proto, var_get_width(graph.by_var[0]));
880       break;
881     case CT_HISTOGRAM:
882       graph.gr_proto = caseproto_add_width (graph.gr_proto, 0); /* x value      */
883       graph.gr_proto = caseproto_add_width (graph.gr_proto, 0); /* weight value */
884       break;
885     case CT_BAR:
886       break;
887     case CT_NONE:
888       lex_error_expecting (lexer, "HISTOGRAM", "SCATTERPLOT", "BAR", NULL);
889       goto error;
890     default:
891       NOT_REACHED ();
892       break;
893     };
894
895   {
896     struct casegrouper *grouper;
897     struct casereader *group;
898     bool ok;
899
900     grouper = casegrouper_create_splits (proc_open (ds), graph.dict);
901     while (casegrouper_get_next_group (grouper, &group))
902       {
903         if (graph.chart_type == CT_BAR)
904           run_barchart (&graph, group);
905         else
906           run_graph (&graph, group);
907       }
908     ok = casegrouper_destroy (grouper);
909     ok = proc_commit (ds) && ok;
910   }
911
912   subcase_destroy (&graph.ordering);
913   free (graph.dep_vars);
914   pool_destroy (graph.pool);
915   caseproto_unref (graph.gr_proto);
916
917   return CMD_SUCCESS;
918
919  error:
920   subcase_destroy (&graph.ordering);
921   caseproto_unref (graph.gr_proto);
922   free (graph.dep_vars);
923   pool_destroy (graph.pool);
924
925   return CMD_FAILURE;
926 }