2 PSPP - a program for statistical analysis.
3 Copyright (C) 2012, 2013, 2015, 2019 Free Software Foundation, Inc.
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.
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.
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/>.
20 * This module implements the graph command
26 #include "gl/xalloc.h"
27 #include <gsl/gsl_cdf.h>
29 #include "libpspp/assertion.h"
30 #include "libpspp/message.h"
31 #include "libpspp/pool.h"
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"
43 #include "data/format.h"
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"
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"
62 #define _(msgid) gettext (msgid)
63 #define N_(msgid) msgid
95 /* Variable index for histogram case */
102 struct exploratory_stats
115 /* The minimum weight */
125 const struct variable
**dep_vars
;
126 struct exploratory_stats
*es
;
128 enum mv_class dep_excl
;
129 enum mv_class fctr_excl
;
131 const struct dictionary
*dict
;
135 /* ------------ Graph ---------------- */
136 bool normal
; /* For histograms, draw the normal curve */
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];
144 struct subcase ordering
; /* Ordering for aggregation */
145 int agr
; /* Index into ag_func */
147 /* A caseproto that contains the plot data */
148 struct caseproto
*gr_proto
;
155 calc_mom1 (double acc
, double x
, double w
)
161 calc_mom0 (double acc
, double x UNUSED
, double w
)
167 pre_low_extreme (void)
173 calc_max (double acc
, double x
, double w UNUSED
)
175 return (acc
> x
) ? acc
: x
;
179 pre_high_extreme (void)
185 calc_min (double acc
, double x
, double w UNUSED
)
187 return (acc
< x
) ? acc
: x
;
191 post_normalise (double acc
, double cc
)
197 post_percentage (double acc
, double ccc
)
199 return acc
/ ccc
* 100.0;
203 const struct ag_func ag_func
[] =
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,
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},
217 const int N_AG_FUNCS
= sizeof (ag_func
) / sizeof (ag_func
[0]);
220 parse_function (struct lexer
*lexer
, struct graph
*graph
)
223 for (i
= 0 ; i
< N_AG_FUNCS
; ++i
)
225 if (lex_match_id (lexer
, ag_func
[i
].name
))
236 graph
->n_dep_vars
= ag_func
[i
].arity
;
237 if (ag_func
[i
].arity
> 0)
240 if (!lex_force_match (lexer
, T_LPAREN
))
243 graph
->dep_vars
= xcalloc (graph
->n_dep_vars
, sizeof (graph
->dep_vars
));
244 for (v
= 0; v
< ag_func
[i
].arity
; ++v
)
246 graph
->dep_vars
[v
] = parse_variable (lexer
, graph
->dict
);
247 if (! graph
->dep_vars
[v
])
251 if (!lex_force_match (lexer
, T_RPAREN
))
255 if (!lex_force_match (lexer
, T_BY
))
258 graph
->by_var
[0] = parse_variable (lexer
, graph
->dict
);
259 if (!graph
->by_var
[0])
263 subcase_add_var (&graph
->ordering
, graph
->by_var
[0], SC_ASCEND
);
266 if (lex_match (lexer
, T_BY
))
268 graph
->by_var
[1] = parse_variable (lexer
, graph
->dict
);
269 if (!graph
->by_var
[1])
273 subcase_add_var (&graph
->ordering
, graph
->by_var
[1], SC_ASCEND
);
280 lex_error (lexer
, NULL
);
286 show_scatterplot (const struct graph
*cmd
, struct casereader
*input
)
289 struct scatterplot_chart
*scatterplot
;
290 bool byvar_overflow
= false;
292 ds_init_empty (&title
);
294 if (cmd
->n_by_vars
> 0)
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]));
303 ds_put_format (&title
, _("%s vs. %s"),
304 var_to_string (cmd
->dep_vars
[1]),
305 var_to_string (cmd
->dep_vars
[0]));
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]
315 cmd
->es
[0].minimum
, cmd
->es
[0].maximum
,
316 cmd
->es
[1].minimum
, cmd
->es
[1].maximum
);
317 scatterplot_chart_submit (scatterplot
);
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."));
329 show_histogr (const struct graph
*cmd
, struct casereader
*input
)
331 struct histogram
*histogram
;
334 if (cmd
->es
[0].cc
<= 0)
336 casereader_destroy (input
);
342 double bin_width
= fabs (cmd
->es
[0].minimum
- cmd
->es
[0].maximum
)
343 / (1 + log2 (cmd
->es
[0].cc
))
347 histogram_create (bin_width
, cmd
->es
[0].minimum
, cmd
->es
[0].maximum
);
350 if (NULL
== histogram
)
352 casereader_destroy (input
);
356 for (;(c
= casereader_read (input
)) != NULL
; case_unref (c
))
358 const double x
= case_num_idx (c
, HG_IDX_X
);
359 const double weight
= case_num_idx (c
, HG_IDX_WT
);
360 moments_pass_two (cmd
->es
[0].mom
, x
, weight
);
361 histogram_add (histogram
, x
, weight
);
363 casereader_destroy (input
);
371 ds_init_cstr (&label
,
372 var_to_string (cmd
->dep_vars
[0]));
374 moments_calculate (cmd
->es
[0].mom
, &n
, &mean
, &var
, NULL
, NULL
);
377 (histogram_chart_create (histogram
->gsl_hist
,
378 ds_cstr (&label
), n
, mean
,
379 sqrt (var
), cmd
->normal
));
381 statistic_destroy (&histogram
->parent
);
387 cleanup_exploratory_stats (struct graph
*cmd
)
391 for (v
= 0; v
< cmd
->n_dep_vars
; ++v
)
393 moments_destroy (cmd
->es
[v
].mom
);
399 run_barchart (struct graph
*cmd
, struct casereader
*input
)
401 struct casegrouper
*grouper
;
402 struct casereader
*group
;
405 if (cmd
->missing_pw
== false)
406 input
= casereader_create_filter_missing (input
,
414 input
= sort_execute (input
, &cmd
->ordering
);
416 struct freq
**cells
= NULL
;
419 struct hmap columns
= HMAP_INITIALIZER (columns
);
420 assert (cmd
->n_by_vars
<= 2);
421 for (grouper
= casegrouper_create_vars (input
, cmd
->by_var
,
423 casegrouper_get_next_group (grouper
, &group
);
424 casereader_destroy (group
))
427 struct ccase
*c
= casereader_peek (group
, 0);
429 /* Deal with missing values in the categorical variables */
430 for (v
= 0; v
< cmd
->n_by_vars
; ++v
)
432 if (var_is_value_missing (cmd
->by_var
[v
],
433 case_data (c
, cmd
->by_var
[v
]))
438 if (v
< cmd
->n_by_vars
)
444 cells
= xrealloc (cells
, sizeof (*cells
) * ++n_cells
);
445 cells
[n_cells
- 1] = xzalloc (sizeof (**cells
)
446 + sizeof (union value
)
447 * (cmd
->n_by_vars
- 1));
449 if (ag_func
[cmd
->agr
].cumulative
&& n_cells
>= 2)
450 cells
[n_cells
- 1]->count
= cells
[n_cells
- 2]->count
;
452 cells
[n_cells
- 1]->count
= 0;
453 if (ag_func
[cmd
->agr
].pre
)
454 cells
[n_cells
- 1]->count
= ag_func
[cmd
->agr
].pre();
456 if (cmd
->n_by_vars
> 1)
458 const union value
*vv
= case_data (c
, cmd
->by_var
[1]);
459 const double weight
= dict_get_case_weight (cmd
->dict
, c
, NULL
);
460 int v1_width
= var_get_width (cmd
->by_var
[1]);
461 size_t hash
= value_hash (vv
, v1_width
, 0);
463 struct freq
*fcol
= NULL
;
464 HMAP_FOR_EACH_WITH_HASH (fcol
, struct freq
, node
, hash
, &columns
)
465 if (value_equal (vv
, &fcol
->values
[0], v1_width
))
469 fcol
->count
+= weight
;
472 fcol
= xzalloc (sizeof *fcol
);
473 fcol
->count
= weight
;
474 value_clone (&fcol
->values
[0], vv
, v1_width
);
475 hmap_insert (&columns
, &fcol
->node
, hash
);
479 for (v
= 0; v
< cmd
->n_by_vars
; ++v
)
481 value_clone (&cells
[n_cells
- 1]->values
[v
],
482 case_data (c
, cmd
->by_var
[v
]),
483 var_get_width (cmd
->by_var
[v
]));
488 for (;(c
= casereader_read (group
)) != NULL
; case_unref (c
))
490 const double weight
= dict_get_case_weight (cmd
->dict
,c
,NULL
);
491 const double x
= (cmd
->n_dep_vars
> 0)
492 ? case_num (c
, cmd
->dep_vars
[0]) : SYSMIS
;
496 cells
[n_cells
- 1]->count
497 = ag_func
[cmd
->agr
].calc (cells
[n_cells
- 1]->count
, x
, weight
);
500 if (ag_func
[cmd
->agr
].post
)
501 cells
[n_cells
- 1]->count
502 = ag_func
[cmd
->agr
].post (cells
[n_cells
- 1]->count
, cc
);
507 casegrouper_destroy (grouper
);
509 for (int i
= 0; i
< n_cells
; ++i
)
511 if (ag_func
[cmd
->agr
].ppost
)
513 struct freq
*cell
= cells
[i
];
514 if (cmd
->n_by_vars
> 1)
516 const union value
*vv
= &cell
->values
[1];
518 int v1_width
= var_get_width (cmd
->by_var
[1]);
519 size_t hash
= value_hash (vv
, v1_width
, 0);
521 struct freq
*fcol
= NULL
;
522 HMAP_FOR_EACH_WITH_HASH (fcol
, struct freq
, node
, hash
, &columns
)
523 if (value_equal (vv
, &fcol
->values
[0], v1_width
))
526 cell
->count
= ag_func
[cmd
->agr
].ppost (cell
->count
, fcol
->count
);
529 cell
->count
= ag_func
[cmd
->agr
].ppost (cell
->count
, ccc
);
533 if (cmd
->n_by_vars
> 1)
535 struct freq
*col_cell
;
537 HMAP_FOR_EACH_SAFE (col_cell
, next
, struct freq
, node
, &columns
)
540 value_destroy (col_cell
->values
, var_get_width (cmd
->by_var
[1]));
544 hmap_destroy (&columns
);
548 ds_init_empty (&label
);
550 if (cmd
->n_dep_vars
> 0)
551 ds_put_format (&label
, _("%s of %s"),
552 ag_func
[cmd
->agr
].description
,
553 var_get_name (cmd
->dep_vars
[0]));
556 ag_func
[cmd
->agr
].description
);
558 chart_submit (barchart_create (cmd
->by_var
, cmd
->n_by_vars
,
559 ds_cstr (&label
), false,
565 for (int i
= 0; i
< n_cells
; ++i
)
573 run_graph (struct graph
*cmd
, struct casereader
*input
)
576 struct casereader
*reader
;
577 struct casewriter
*writer
;
579 cmd
->es
= pool_calloc (cmd
->pool
,cmd
->n_dep_vars
, sizeof *cmd
->es
);
580 for(int v
=0;v
<cmd
->n_dep_vars
;v
++)
582 cmd
->es
[v
].mom
= moments_create (MOMENT_KURTOSIS
);
583 cmd
->es
[v
].cmin
= DBL_MAX
;
584 cmd
->es
[v
].maximum
= -DBL_MAX
;
585 cmd
->es
[v
].minimum
= DBL_MAX
;
587 /* Always remove cases listwise. This is correct for */
588 /* the histogram because there is only one variable */
589 /* and a simple bivariate scatterplot */
590 /* if (cmd->missing_pw == false) */
591 input
= casereader_create_filter_missing (input
,
598 writer
= autopaging_writer_create (cmd
->gr_proto
);
600 /* The case data is copied to a new writer */
601 /* The setup of the case depends on the Charttype */
602 /* For Scatterplot x is assumed in dep_vars[0] */
603 /* y is assumed in dep_vars[1] */
604 /* For Histogram x is assumed in dep_vars[0] */
605 assert(SP_IDX_X
== 0 && SP_IDX_Y
== 1 && HG_IDX_X
== 0);
607 for (;(c
= casereader_read (input
)) != NULL
; case_unref (c
))
609 struct ccase
*outcase
= case_create (cmd
->gr_proto
);
610 const double weight
= dict_get_case_weight (cmd
->dict
,c
,NULL
);
611 if (cmd
->chart_type
== CT_HISTOGRAM
)
612 *case_num_rw_idx (outcase
, HG_IDX_WT
) = weight
;
613 if (cmd
->chart_type
== CT_SCATTERPLOT
&& cmd
->n_by_vars
> 0)
614 value_copy (case_data_rw_idx (outcase
, SP_IDX_BY
),
615 case_data (c
, cmd
->by_var
[0]),
616 var_get_width (cmd
->by_var
[0]));
617 for(int v
=0;v
<cmd
->n_dep_vars
;v
++)
619 const struct variable
*var
= cmd
->dep_vars
[v
];
620 const double x
= case_num (c
, var
);
622 if (var_is_value_missing (var
, case_data (c
, var
)) & cmd
->dep_excl
)
624 cmd
->es
[v
].missing
+= weight
;
627 /* Magically v value fits to SP_IDX_X, SP_IDX_Y, HG_IDX_X */
628 *case_num_rw_idx (outcase
, v
) = x
;
630 if (x
> cmd
->es
[v
].maximum
)
631 cmd
->es
[v
].maximum
= x
;
633 if (x
< cmd
->es
[v
].minimum
)
634 cmd
->es
[v
].minimum
= x
;
636 cmd
->es
[v
].non_missing
+= weight
;
638 moments_pass_one (cmd
->es
[v
].mom
, x
, weight
);
640 cmd
->es
[v
].cc
+= weight
;
642 if (cmd
->es
[v
].cmin
> weight
)
643 cmd
->es
[v
].cmin
= weight
;
645 casewriter_write (writer
,outcase
);
648 reader
= casewriter_make_reader (writer
);
650 switch (cmd
->chart_type
)
653 show_histogr (cmd
,reader
);
656 show_scatterplot (cmd
,reader
);
663 casereader_destroy (input
);
664 cleanup_exploratory_stats (cmd
);
669 cmd_graph (struct lexer
*lexer
, struct dataset
*ds
)
673 graph
.missing_pw
= false;
675 graph
.pool
= pool_create ();
677 graph
.dep_excl
= MV_ANY
;
678 graph
.fctr_excl
= MV_ANY
;
680 graph
.dict
= dataset_dict (ds
);
682 graph
.dep_vars
= NULL
;
683 graph
.chart_type
= CT_NONE
;
684 graph
.scatter_type
= ST_BIVARIATE
;
686 graph
.gr_proto
= caseproto_create ();
688 subcase_init_empty (&graph
.ordering
);
690 while (lex_token (lexer
) != T_ENDCMD
)
692 lex_match (lexer
, T_SLASH
);
694 if (lex_match_id (lexer
, "HISTOGRAM"))
696 if (graph
.chart_type
!= CT_NONE
)
698 lex_error (lexer
, _("Only one chart type is allowed."));
701 graph
.normal
= false;
702 if (lex_match (lexer
, T_LPAREN
))
704 if (!lex_force_match_id (lexer
, "NORMAL"))
707 if (!lex_force_match (lexer
, T_RPAREN
))
712 if (!lex_force_match (lexer
, T_EQUALS
))
714 graph
.chart_type
= CT_HISTOGRAM
;
715 if (!parse_variables_const (lexer
, graph
.dict
,
716 &graph
.dep_vars
, &graph
.n_dep_vars
,
717 PV_NO_DUPLICATE
| PV_NUMERIC
))
719 if (graph
.n_dep_vars
> 1)
721 lex_error (lexer
, _("Only one variable is allowed."));
725 else if (lex_match_id (lexer
, "BAR"))
727 if (graph
.chart_type
!= CT_NONE
)
729 lex_error (lexer
, _("Only one chart type is allowed."));
732 graph
.chart_type
= CT_BAR
;
733 graph
.bar_type
= CBT_SIMPLE
;
735 if (lex_match (lexer
, T_LPAREN
))
737 if (lex_match_id (lexer
, "SIMPLE"))
739 /* This is the default anyway */
741 else if (lex_match_id (lexer
, "GROUPED"))
743 graph
.bar_type
= CBT_GROUPED
;
746 else if (lex_match_id (lexer
, "STACKED"))
748 graph
.bar_type
= CBT_STACKED
;
749 lex_error (lexer
, _("%s is not yet implemented."), "STACKED");
752 else if (lex_match_id (lexer
, "RANGE"))
754 graph
.bar_type
= CBT_RANGE
;
755 lex_error (lexer
, _("%s is not yet implemented."), "RANGE");
760 lex_error (lexer
, NULL
);
763 if (!lex_force_match (lexer
, T_RPAREN
))
767 if (!lex_force_match (lexer
, T_EQUALS
))
770 if (! parse_function (lexer
, &graph
))
773 else if (lex_match_id (lexer
, "SCATTERPLOT"))
775 if (graph
.chart_type
!= CT_NONE
)
777 lex_error (lexer
, _("Only one chart type is allowed."));
780 graph
.chart_type
= CT_SCATTERPLOT
;
781 if (lex_match (lexer
, T_LPAREN
))
783 if (lex_match_id (lexer
, "BIVARIATE"))
785 /* This is the default anyway */
787 else if (lex_match_id (lexer
, "OVERLAY"))
789 lex_error (lexer
, _("%s is not yet implemented."),"OVERLAY");
792 else if (lex_match_id (lexer
, "MATRIX"))
794 lex_error (lexer
, _("%s is not yet implemented."),"MATRIX");
797 else if (lex_match_id (lexer
, "XYZ"))
799 lex_error(lexer
, _("%s is not yet implemented."),"XYZ");
804 lex_error_expecting (lexer
, "BIVARIATE");
807 if (!lex_force_match (lexer
, T_RPAREN
))
810 if (!lex_force_match (lexer
, T_EQUALS
))
813 if (!parse_variables_const (lexer
, graph
.dict
,
814 &graph
.dep_vars
, &graph
.n_dep_vars
,
815 PV_NO_DUPLICATE
| PV_NUMERIC
))
818 if (graph
.scatter_type
== ST_BIVARIATE
&& graph
.n_dep_vars
!= 1)
820 lex_error(lexer
, _("Only one variable is allowed."));
824 if (!lex_force_match (lexer
, T_WITH
))
827 if (!parse_variables_const (lexer
, graph
.dict
,
828 &graph
.dep_vars
, &graph
.n_dep_vars
,
829 PV_NO_DUPLICATE
| PV_NUMERIC
| PV_APPEND
))
832 if (graph
.scatter_type
== ST_BIVARIATE
&& graph
.n_dep_vars
!= 2)
834 lex_error (lexer
, _("Only one variable is allowed."));
838 if (lex_match (lexer
, T_BY
))
840 const struct variable
*v
= NULL
;
841 if (!lex_match_variable (lexer
,graph
.dict
,&v
))
843 lex_error (lexer
, _("Variable expected"));
850 else if (lex_match_id (lexer
, "LINE"))
852 lex_error (lexer
, _("%s is not yet implemented."),"LINE");
855 else if (lex_match_id (lexer
, "PIE"))
857 lex_error (lexer
, _("%s is not yet implemented."),"PIE");
860 else if (lex_match_id (lexer
, "ERRORBAR"))
862 lex_error (lexer
, _("%s is not yet implemented."),"ERRORBAR");
865 else if (lex_match_id (lexer
, "PARETO"))
867 lex_error (lexer
, _("%s is not yet implemented."),"PARETO");
870 else if (lex_match_id (lexer
, "TITLE"))
872 lex_error (lexer
, _("%s is not yet implemented."),"TITLE");
875 else if (lex_match_id (lexer
, "SUBTITLE"))
877 lex_error (lexer
, _("%s is not yet implemented."),"SUBTITLE");
880 else if (lex_match_id (lexer
, "FOOTNOTE"))
882 lex_error (lexer
, _("%s is not yet implemented."),"FOOTNOTE");
883 lex_error (lexer
, _("FOOTNOTE is not implemented yet for GRAPH"));
886 else if (lex_match_id (lexer
, "MISSING"))
888 lex_match (lexer
, T_EQUALS
);
890 while (lex_token (lexer
) != T_ENDCMD
891 && lex_token (lexer
) != T_SLASH
)
893 if (lex_match_id (lexer
, "LISTWISE"))
895 graph
.missing_pw
= false;
897 else if (lex_match_id (lexer
, "VARIABLE"))
899 graph
.missing_pw
= true;
901 else if (lex_match_id (lexer
, "EXCLUDE"))
903 graph
.dep_excl
= MV_ANY
;
905 else if (lex_match_id (lexer
, "INCLUDE"))
907 graph
.dep_excl
= MV_SYSTEM
;
909 else if (lex_match_id (lexer
, "REPORT"))
913 else if (lex_match_id (lexer
, "NOREPORT"))
915 graph
.fctr_excl
= MV_ANY
;
919 lex_error (lexer
, NULL
);
926 lex_error (lexer
, NULL
);
931 switch (graph
.chart_type
)
934 /* See scatterplot.h for the setup of the case prototype */
936 /* x value - SP_IDX_X*/
937 graph
.gr_proto
= caseproto_add_width (graph
.gr_proto
, 0);
939 /* y value - SP_IDX_Y*/
940 graph
.gr_proto
= caseproto_add_width (graph
.gr_proto
, 0);
941 /* The by_var contains the plot categories for the different xy
943 if (graph
.n_by_vars
> 0) /* SP_IDX_BY */
944 graph
.gr_proto
= caseproto_add_width (graph
.gr_proto
,
945 var_get_width(graph
.by_var
[0]));
949 graph
.gr_proto
= caseproto_add_width (graph
.gr_proto
, 0);
951 graph
.gr_proto
= caseproto_add_width (graph
.gr_proto
, 0);
956 lex_error_expecting (lexer
, "HISTOGRAM", "SCATTERPLOT", "BAR");
964 struct casegrouper
*grouper
;
965 struct casereader
*group
;
968 grouper
= casegrouper_create_splits (proc_open (ds
), graph
.dict
);
969 while (casegrouper_get_next_group (grouper
, &group
))
971 if (graph
.chart_type
== CT_BAR
)
972 run_barchart (&graph
, group
);
974 run_graph (&graph
, group
);
976 ok
= casegrouper_destroy (grouper
);
977 ok
= proc_commit (ds
) && ok
;
980 subcase_uninit (&graph
.ordering
);
981 free (graph
.dep_vars
);
982 pool_destroy (graph
.pool
);
983 caseproto_unref (graph
.gr_proto
);
988 subcase_uninit (&graph
.ordering
);
989 caseproto_unref (graph
.gr_proto
);
990 free (graph
.dep_vars
);
991 pool_destroy (graph
.pool
);