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