sysfile-info.c tab.c tab.h temporary.c stat.h \
title.c val.h val-labs.c value-labels.c value-labels.h \
var-labs.c var.h vars-atr.c vars-prs.c vector.c version.c version.h \
-vfm.c vfm.h vfmP.h weight.c
+vfm.c vfm.h vfmP.h weight.c \
+barchart.c \
+box-whisker.c \
+cartesian.c \
+chart.c \
+chart.h \
+histogram.c \
+piechart.c
pspp_LDADD = ../lib/julcal/libjulcal.a \
../lib/misc/libmisc.a \
+ -lplot \
@LIBINTL@
version.c:
--- /dev/null
+/* PSPP - computes sample statistics.
+ Copyright (C) 2004 Free Software Foundation, Inc.
+ Written by John Darrington <john@darrington.wattle.id.au>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+
+#include <stdio.h>
+#include <plot.h>
+#include <stdarg.h>
+#include <math.h>
+#include "chart.h"
+
+#define CATAGORIES 6
+#define SUB_CATAGORIES 3
+
+
+
+static const double x_min = 0;
+static const double x_max = 15.0;
+
+static const char *cat_labels[] =
+ {
+ "Age",
+ "Intelligence",
+ "Wealth",
+ "Emotional",
+ "cat 5",
+ "cat 6",
+ "cat 7",
+ "cat 8",
+ "cat 9",
+ "cat 10",
+ "cat 11"
+ };
+
+
+
+
+/* Subcatagories */
+static const double data1[] =
+{
+ 28,83,
+ 34,
+ 29,13,
+ 9,4,
+ 3,3,
+ 2,0,
+ 1,0,
+ 0,
+ 1,1
+};
+
+
+static const double data2[] =
+{
+ 45,13,
+ 9,4,
+ 3,43,
+ 2,0,
+ 1,20,
+ 0,0,
+ 1,1,
+ 0,0
+};
+
+static const double data3[] =
+ {
+ 23,18,
+ 0, 45,23, 9, 40, 24,4, 8
+ };
+
+
+static const char subcat_name[]="Gender";
+
+
+struct subcat {
+ const double *data;
+ char *label;
+};
+
+static const struct subcat sub_catagory[SUB_CATAGORIES] =
+ {
+ {data1, "male"},
+ {data2, "female"},
+ {data3, "47xxy"}
+ };
+
+
+
+static const double y_min = 0;
+static const double y_max = 120.0;
+static const double y_tick = 20.0;
+
+
+
+static void write_legend(struct chart *chart) ;
+
+
+void
+draw_barchart(struct chart *ch, const char *title,
+ const char *xlabel, const char *ylabel, enum bar_opts opt)
+{
+
+ double d;
+ int i;
+
+ double interval_size = fabs(ch->data_right - ch->data_left) / ( CATAGORIES );
+
+ double bar_width = interval_size / 1.1 ;
+
+ if ( opt != BAR_STACKED )
+ bar_width /= SUB_CATAGORIES;
+
+ double ordinate_scale = fabs(ch->data_top - ch->data_bottom) / fabs(y_max - y_min) ;
+
+ /* Move to data bottom-left */
+ pl_move_r(ch->lp, ch->data_left, ch->data_bottom);
+
+ pl_savestate_r(ch->lp);
+ pl_filltype_r(ch->lp,1);
+
+ /* Draw the data */
+ for (i = 0 ; i < CATAGORIES ; ++i )
+ {
+ int sc;
+ double ystart=0.0;
+ double x = i * interval_size;
+
+ pl_savestate_r(ch->lp);
+
+ draw_tick (ch, TICK_ABSCISSA, x + (interval_size/2 ),
+ cat_labels[i]);
+
+ for(sc = 0 ; sc < SUB_CATAGORIES ; ++sc )
+ {
+
+ pl_savestate_r(ch->lp);
+ pl_fillcolorname_r(ch->lp,data_colour[sc]);
+
+ switch ( opt )
+ {
+ case BAR_GROUPED:
+ pl_fboxrel_r(ch->lp,
+ x + (sc * bar_width ), 0,
+ x + (sc + 1) * bar_width,
+ sub_catagory[sc].data[i] * ordinate_scale );
+ break;
+
+
+ case BAR_STACKED:
+
+ pl_fboxrel_r(ch->lp,
+ x, ystart,
+ x + bar_width,
+ ystart + sub_catagory[sc].data[i] * ordinate_scale );
+
+ ystart += sub_catagory[sc].data[i] * ordinate_scale ;
+
+ break;
+
+ default:
+ break;
+ }
+ pl_restorestate_r(ch->lp);
+ }
+
+ pl_restorestate_r(ch->lp);
+ }
+ pl_restorestate_r(ch->lp);
+
+ for ( d = y_min; d <= y_max ; d += y_tick )
+ {
+
+ draw_tick (ch, TICK_ORDINATE,
+ (d - y_min ) * ordinate_scale, "%g", d);
+
+ }
+
+ /* Write the abscissa label */
+ pl_move_r(ch->lp,ch->data_left, ch->abscissa_top);
+ pl_alabel_r(ch->lp,0,'t',xlabel);
+
+
+ /* Write the ordinate label */
+ pl_savestate_r(ch->lp);
+ pl_move_r(ch->lp,ch->data_bottom, ch->ordinate_right);
+ pl_textangle_r(ch->lp,90);
+ pl_alabel_r(ch->lp,0,0,ylabel);
+ pl_restorestate_r(ch->lp);
+
+
+ chart_write_title(ch, title);
+
+ write_legend(ch);
+
+
+}
+
+
+
+
+
+static void
+write_legend(struct chart *chart)
+{
+ int sc;
+
+ pl_savestate_r(chart->lp);
+
+ pl_filltype_r(chart->lp,1);
+
+ pl_move_r(chart->lp, chart->legend_left,
+ chart->data_bottom + chart->font_size * SUB_CATAGORIES * 1.5);
+
+ pl_alabel_r(chart->lp,0,'b',subcat_name);
+
+ for (sc = 0 ; sc < SUB_CATAGORIES ; ++sc )
+ {
+ pl_fmove_r(chart->lp,
+ chart->legend_left,
+ chart->data_bottom + chart->font_size * sc * 1.5);
+
+ pl_savestate_r(chart->lp);
+ pl_fillcolorname_r(chart->lp,data_colour[sc]);
+ pl_fboxrel_r (chart->lp,
+ 0,0,
+ chart->font_size, chart->font_size);
+ pl_restorestate_r(chart->lp);
+
+ pl_fmove_r(chart->lp,
+ chart->legend_left + chart->font_size * 1.5,
+ chart->data_bottom + chart->font_size * sc * 1.5);
+
+ pl_alabel_r(chart->lp,'l','b',sub_catagory[sc].label);
+ }
+
+
+ pl_restorestate_r(chart->lp);
+}
--- /dev/null
+/* PSPP - computes sample statistics.
+ Copyright (C) 2004 Free Software Foundation, Inc.
+ Written by John Darrington <john@darrington.wattle.id.au>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+
+#include "chart.h"
+#include <math.h>
+
+/* Draw a box-and-whiskers plot
+*/
+
+struct data_stats
+{
+ double ptile0 ;
+ double ptile25 ;
+ double median ;
+ double ptile75 ;
+
+ double ptile100;
+
+ double outlier ;
+};
+
+
+const struct data_stats stats1 = {
+ 40,
+ 45,
+ 54,
+ 60,
+ 70,
+
+ 33
+};
+
+const struct data_stats stats2 = {
+ 30,
+ 40,
+ 45,
+ 54,
+ 60,
+
+
+ 72
+};
+
+
+
+
+
+static const double y_min = 25;
+static const double y_max = 75;
+static const double y_tick = 10;
+
+
+
+#define min(A,B) ((A>B)?B:A)
+
+
+void draw_box_and_whiskers(struct chart *ch,
+ double box_centre, const struct data_stats *s,
+ const char *name);
+
+
+static double ordinate_scale;
+
+void
+draw_box_whisker_chart(struct chart *ch, const char *title)
+{
+ double d;
+
+ ordinate_scale = fabs(ch->data_top - ch->data_bottom) / fabs(y_max - y_min) ;
+
+
+ chart_write_title(ch, title);
+
+
+
+ /* Move to data bottom-left */
+ pl_move_r(ch->lp,
+ ch->data_left, ch->data_bottom);
+
+ for ( d = y_min; d <= y_max ; d += y_tick )
+ {
+ draw_tick (ch, TICK_ORDINATE, (d - y_min ) * ordinate_scale, "%g", d);
+ }
+
+ draw_box_and_whiskers(ch,
+ ch->data_left + 1.0/4.0 * (ch->data_right - ch->data_left) ,
+ &stats1,"Stats1"
+ );
+
+ draw_box_and_whiskers(ch,
+ ch->data_left + 3.0/4.0 * (ch->data_right - ch->data_left),
+ &stats2,"Stats2"
+ );
+
+
+}
+
+
+void
+draw_box_and_whiskers(struct chart *ch,
+ double box_centre, const struct data_stats *s,
+ const char *name)
+{
+
+ const double box_width = (ch->data_right - ch->data_left) / 4.0;
+
+ const double box_left = box_centre - box_width / 2.0;
+
+ const double box_right = box_centre + box_width / 2.0;
+
+
+ const double box_bottom =
+ ch->data_bottom + ( s->ptile25 - y_min ) * ordinate_scale;
+
+
+ const double box_top =
+ ch->data_bottom + ( s->ptile75 - y_min ) * ordinate_scale;
+
+
+ const double iq_range = s->ptile75 - s->ptile25;
+
+ const double bottom_whisker =
+ ch->data_bottom + (min(s->ptile0,s->ptile25 + iq_range*1.5) - y_min ) *
+ ordinate_scale;
+
+ const double top_whisker =
+ ch->data_bottom + (min(s->ptile100,s->ptile75 + iq_range*1.5) - y_min ) *
+ ordinate_scale;
+
+ pl_savestate_r(ch->lp);
+
+
+ /* Draw the box */
+ pl_savestate_r(ch->lp);
+ pl_fillcolorname_r(ch->lp,ch->fill_colour);
+ pl_filltype_r(ch->lp,1);
+ pl_fbox_r(ch->lp,
+ box_left,
+ box_bottom,
+ box_right,
+ box_top);
+
+ pl_restorestate_r(ch->lp);
+
+
+
+ /* Draw the median */
+ pl_savestate_r(ch->lp);
+ pl_linewidth_r(ch->lp,5);
+ pl_fline_r(ch->lp,
+ box_left,
+ ch->data_bottom + ( s->median - y_min ) * ordinate_scale,
+ box_right,
+ ch->data_bottom + ( s->median - y_min ) * ordinate_scale);
+ pl_restorestate_r(ch->lp);
+
+
+ /* Draw the bottom whisker */
+ pl_fline_r(ch->lp,
+ box_left,
+ bottom_whisker,
+ box_right,
+ bottom_whisker);
+
+ /* Draw top whisker */
+ pl_fline_r(ch->lp,
+ box_left,
+ top_whisker,
+ box_right,
+ top_whisker);
+
+
+ /* Draw centre line.
+ (bottom half) */
+ pl_fline_r(ch->lp,
+ box_centre, bottom_whisker,
+ box_centre, box_bottom);
+
+ /* (top half) */
+ pl_fline_r(ch->lp,
+ box_centre, top_whisker,
+ box_centre, box_top);
+
+
+ /* Draw an outlier */
+ pl_fcircle_r(ch->lp,
+ box_centre,
+ ch->data_bottom + (s->outlier - y_min ) * ordinate_scale,
+ 5);
+
+ pl_moverel_r(ch->lp, 10,0);
+ pl_alabel_r(ch->lp,'l','c',"123");
+
+
+ /* Draw tick mark on x axis */
+ draw_tick(ch, TICK_ABSCISSA, box_centre - ch->data_left, name);
+
+ pl_restorestate_r(ch->lp);
+
+}
+
--- /dev/null
+/* PSPP - computes sample statistics.
+ Copyright (C) 2004 Free Software Foundation, Inc.
+ Written by John Darrington <john@darrington.wattle.id.au>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+
+#include <math.h>
+#include "chart.h"
+#include <assert.h>
+
+
+
+static const double y_min = 15;
+static const double y_max = 120.0;
+static const double y_tick = 20.0;
+
+
+
+static const double x_min = -11.0;
+static const double x_max = 19.0;
+static const double x_tick = 5.0;
+
+
+struct datum
+{
+ double x;
+ double y;
+};
+
+
+static const struct datum data1[]=
+ {
+ { -8.0, 29 },
+ { -3.7, 45 },
+ { -3.3, 67 },
+ { -0.8, 89 },
+ { -0.2, 93 },
+ { 1.0, 100},
+ { 2.3, 103},
+ { 4.0, 103.4},
+ { 5.2, 104},
+ { 5.9, 106},
+ { 10.3, 106},
+ { 13.8, 108},
+ { 15.8, 109},
+ };
+
+
+
+
+static const struct datum data2[]=
+ {
+ { -9.1, 20 },
+ { -8.2, 17 },
+ { -5.0, 19 },
+ { -3.7, 25 },
+ { -1.6, 49 },
+ { -1.3, 61 },
+ { -1.1, 81 },
+ { 3.5, 91},
+ { 5.4, 93},
+ { 9.3, 94},
+ { 14.3, 92}
+ };
+
+
+
+
+struct dataset
+{
+ const struct datum *data;
+ int n_data;
+ char *label;
+};
+
+
+
+#define DATASETS 2
+
+static const struct dataset dataset[DATASETS] =
+ {
+ {data1, 13, "male"},
+ {data2, 11, "female"},
+ };
+
+
+
+typedef void (*plot_func) (struct chart *ch, const struct dataset *dataset);
+
+
+void plot_line(struct chart *ch, const struct dataset *dataset);
+
+void plot_scatter(struct chart *ch, const struct dataset *dataset);
+
+
+
+static void
+write_legend(struct chart *chart, const char *heading, int n);
+
+void draw_cartesian(struct chart *ch, const char *title,
+ const char *xlabel, const char *ylabel, plot_func pf);
+
+
+
+void
+draw_scatterplot(struct chart *ch, const char *title,
+ const char *xlabel, const char *ylabel)
+{
+ draw_cartesian(ch, title, xlabel, ylabel, plot_scatter);
+}
+
+
+void
+draw_lineplot(struct chart *ch, const char *title,
+ const char *xlabel, const char *ylabel)
+{
+ draw_cartesian(ch, title, xlabel, ylabel, plot_scatter);
+}
+
+
+void
+draw_cartesian(struct chart *ch, const char *title,
+ const char *xlabel, const char *ylabel, plot_func pf)
+{
+ double x;
+ double y;
+
+
+ int d;
+
+
+ const double ordinate_scale =
+ fabs(ch->data_top - ch->data_bottom)
+ / fabs(y_max - y_min) ;
+
+
+ const double abscissa_scale =
+ fabs(ch->data_right - ch->data_left)
+ /
+ fabs(x_max - x_min);
+
+
+ /* Move to data bottom-left */
+ pl_move_r(ch->lp, ch->data_left, ch->data_bottom);
+
+ pl_savestate_r(ch->lp);
+
+
+ for(x = x_tick * ceil(x_min / x_tick ) ;
+ x < x_max;
+ x += x_tick )
+ draw_tick (ch, TICK_ABSCISSA, (x - x_min) * abscissa_scale, "%g", x);
+
+ for(y = y_tick * ceil(y_min / y_tick ) ;
+ y < y_max;
+ y += y_tick )
+ draw_tick (ch, TICK_ORDINATE, (y - y_min) * ordinate_scale, "%g", y);
+
+ pl_savestate_r(ch->lp);
+
+ for (d = 0 ; d < DATASETS ; ++d )
+ {
+ pl_pencolorname_r(ch->lp,data_colour[d]);
+ pf(ch, &dataset[d]);
+ }
+
+ pl_restorestate_r(ch->lp);
+
+ /* Write the abscissa label */
+ pl_move_r(ch->lp,ch->data_left, ch->abscissa_top);
+ pl_alabel_r(ch->lp,0,'t',xlabel);
+
+
+ /* Write the ordinate label */
+ pl_savestate_r(ch->lp);
+ pl_move_r(ch->lp,ch->data_bottom, ch->ordinate_right);
+ pl_textangle_r(ch->lp,90);
+ pl_alabel_r(ch->lp,0,0,ylabel);
+ pl_restorestate_r(ch->lp);
+
+
+ chart_write_title(ch, title);
+
+ write_legend(ch,"Key:",DATASETS);
+
+ pl_restorestate_r(ch->lp);
+
+}
+
+
+
+
+static void
+write_legend(struct chart *chart, const char *heading,
+ int n)
+{
+ int ds;
+
+ pl_savestate_r(chart->lp);
+
+ pl_filltype_r(chart->lp,1);
+
+ pl_move_r(chart->lp, chart->legend_left,
+ chart->data_bottom + chart->font_size * n * 1.5);
+
+ pl_alabel_r(chart->lp,0,'b',heading);
+
+ for (ds = 0 ; ds < n ; ++ds )
+ {
+ pl_fmove_r(chart->lp,
+ chart->legend_left,
+ chart->data_bottom + chart->font_size * ds * 1.5);
+
+ pl_savestate_r(chart->lp);
+ pl_fillcolorname_r(chart->lp,data_colour[ds]);
+ pl_fboxrel_r (chart->lp,
+ 0,0,
+ chart->font_size, chart->font_size);
+ pl_restorestate_r(chart->lp);
+
+ pl_fmove_r(chart->lp,
+ chart->legend_left + chart->font_size * 1.5,
+ chart->data_bottom + chart->font_size * ds * 1.5);
+
+ pl_alabel_r(chart->lp,'l','b',dataset[ds].label);
+ }
+
+
+ pl_restorestate_r(chart->lp);
+}
+
+
+
+void
+plot_line(struct chart *ch, const struct dataset *dataset)
+{
+ int i;
+
+ const struct datum *data = dataset->data;
+
+ const double ordinate_scale =
+ fabs(ch->data_top - ch->data_bottom)
+ / fabs(y_max - y_min) ;
+
+
+ const double abscissa_scale =
+ fabs(ch->data_right - ch->data_left)
+ /
+ fabs(x_max - x_min);
+
+
+ for( i = 0 ; i < dataset->n_data ; ++i )
+ {
+ const double x =
+ (data[i].x - x_min) * abscissa_scale + ch->data_left ;
+ const double y =
+ (data[i].y - y_min) * ordinate_scale + ch->data_bottom;
+
+ if (i == 0 )
+ pl_move_r(ch->lp, x, y );
+ else
+ pl_fcont_r(ch->lp, x, y);
+ }
+ pl_endpath_r(ch->lp);
+
+}
+
+
+
+
+void
+plot_scatter(struct chart *ch, const struct dataset *dataset)
+{
+ int i;
+
+ const struct datum *data = dataset->data;
+
+ const double ordinate_scale =
+ fabs(ch->data_top - ch->data_bottom)
+ / fabs(y_max - y_min) ;
+
+
+ const double abscissa_scale =
+ fabs(ch->data_right - ch->data_left)
+ /
+ fabs(x_max - x_min);
+
+
+ for( i = 0 ; i < dataset->n_data ; ++i )
+ {
+ const double x =
+ (data[i].x - x_min) * abscissa_scale + ch->data_left ;
+ const double y =
+ (data[i].y - y_min) * ordinate_scale + ch->data_bottom;
+
+ pl_fmarker_r(ch->lp, x, y, 6, 15);
+ }
+
+}
--- /dev/null
+/* PSPP - computes sample statistics.
+ Copyright (C) 2004 Free Software Foundation, Inc.
+ Written by John Darrington <john@darrington.wattle.id.au>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+
+#include <stdio.h>
+#include <plot.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+#include <assert.h>
+#include <math.h>
+
+#include "chart.h"
+
+
+const char *data_colour[] = {
+ "brown",
+ "red",
+ "orange",
+ "yellow",
+ "green",
+ "blue",
+ "violet",
+ "grey",
+ "pink"
+};
+
+
+
+int
+chart_initialise(struct chart *chart)
+{
+
+ chart->pl_params = pl_newplparams();
+
+ chart->lp = pl_newpl_r ("X",0,stdout,stderr,chart->pl_params);
+
+ if (pl_openpl_r (chart->lp) < 0) /* open Plotter */
+ return 1;
+
+ pl_fspace_r (chart->lp, 0.0, 0.0, 1000.0, 1000.0); /* set coordinate system */
+ pl_flinewidth_r (chart->lp, 0.25); /* set line thickness */
+ pl_pencolorname_r (chart->lp, "black");
+
+ pl_erase_r (chart->lp); /* erase graphics display */
+ pl_filltype_r(chart->lp,0);
+
+
+
+ pl_savestate_r(chart->lp);
+
+ /* Set default chartetry */
+ chart->data_top = 900;
+ chart->data_right = 800;
+ chart->data_bottom = 120;
+ chart->data_left = 150;
+ chart->abscissa_top = 70;
+ chart->ordinate_right = 120;
+ chart->title_bottom = 920;
+ chart->legend_left = 810;
+ chart->legend_right = 1000;
+ chart->font_size = 0;
+ strcpy(chart->fill_colour,"red");
+
+
+ /* Get default font size */
+ if ( !chart->font_size)
+ chart->font_size = pl_fontsize_r(chart->lp, -1);
+
+ /* Draw the data area */
+ pl_box_r(chart->lp,
+ chart->data_left, chart->data_bottom,
+ chart->data_right, chart->data_top);
+
+ return 0;
+
+}
+
+
+
+/* Draw a tick mark at position
+ If label is non zero, then print it at the tick mark
+*/
+void
+draw_tick(struct chart *chart,
+ enum tick_orientation orientation,
+ double position,
+ const char *label, ...)
+{
+ const int tickSize = 10;
+
+ pl_savestate_r(chart->lp);
+
+ pl_move_r(chart->lp, chart->data_left, chart->data_bottom);
+
+ if ( orientation == TICK_ABSCISSA )
+ pl_flinerel_r(chart->lp, position, 0, position, -tickSize);
+ else if (orientation == TICK_ORDINATE )
+ pl_flinerel_r(chart->lp, 0, position, -tickSize, position);
+ else
+ assert(0);
+
+ if ( label ) {
+ char buf[10];
+ va_list ap;
+ va_start(ap,label);
+ vsnprintf(buf,10,label,ap);
+
+ if ( orientation == TICK_ABSCISSA )
+ pl_alabel_r(chart->lp, 'c','t', buf);
+ else if (orientation == TICK_ORDINATE )
+ {
+ if ( fabs(position) < DBL_EPSILON )
+ pl_moverel_r(chart->lp, 0, 10);
+
+ pl_alabel_r(chart->lp, 'r','c', buf);
+ }
+
+ va_end(ap);
+ }
+
+ pl_restorestate_r(chart->lp);
+}
+
+
+
+
+void
+chart_write_title(struct chart *chart, const char *title)
+{
+ /* Write the title */
+ pl_savestate_r(chart->lp);
+ pl_ffontsize_r(chart->lp,chart->font_size * 1.5);
+ pl_move_r(chart->lp,chart->data_left, chart->title_bottom);
+ pl_alabel_r(chart->lp,0,0,title);
+ pl_restorestate_r(chart->lp);
+}
+
+
+
+void
+chart_finalise(struct chart *chart)
+{
+ pl_restorestate_r(chart->lp);
+
+ if (pl_closepl_r (chart->lp) < 0) /* close Plotter */
+ {
+ fprintf (stderr, "Couldn't close Plotter\n");
+ }
+
+
+ pl_deletepl_r(chart->lp);
+
+ pl_deleteplparams(chart->pl_params);
+
+}
+
--- /dev/null
+/* PSPP - computes sample statistics.
+ Copyright (C) 2004 Free Software Foundation, Inc.
+ Written by John Darrington <john@darrington.wattle.id.au>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+
+#ifndef CHART_H
+#define CHART_H
+
+#include <stdio.h>
+#include <plot.h>
+#include "var.h"
+
+
+/* Array of standard colour names */
+extern const char *data_colour[];
+
+
+struct chart {
+
+ plPlotter *lp ;
+ plPlotterParams *pl_params;
+
+ /* The geometry of the chart
+ See diagram at the foot of this file.
+ */
+
+ int data_top ;
+ int data_right ;
+ int data_bottom;
+ int data_left ;
+
+ int abscissa_top;
+
+ int ordinate_right ;
+
+ int title_bottom ;
+
+ int legend_left ;
+ int legend_right ;
+
+
+ /* Default font size for the plot (if zero, then use plotter default) */
+ int font_size;
+
+ char fill_colour[10];
+
+};
+
+
+int chart_initialise(struct chart *ch);
+
+void chart_finalise(struct chart *ch);
+
+
+
+void chart_write_title(struct chart *ch,
+ const char *title);
+
+enum tick_orientation {
+ TICK_ABSCISSA=0,
+ TICK_ORDINATE
+};
+
+void draw_tick(struct chart *ch, enum tick_orientation orientation,
+ double position, const char *label, ...);
+
+
+
+enum bar_opts {
+ BAR_GROUPED = 0,
+ BAR_STACKED,
+ BAR_RANGE
+};
+
+
+void draw_barchart(struct chart *ch, const char *title,
+ const char *xlabel, const char *ylabel, enum bar_opts opt);
+
+void draw_box_whisker_chart(struct chart *ch, const char *title);
+
+
+
+struct normal_curve
+{
+ double N ;
+ double mean ;
+ double stddev ;
+};
+
+
+void draw_histogram(struct chart *ch,
+ const struct variable *v,
+ const char *title,
+ struct normal_curve *norm,
+ int show_normal);
+
+
+
+void draw_piechart(struct chart *ch, const struct variable *v);
+
+void draw_scatterplot(struct chart *ch, const char *title,
+ const char *xlabel, const char *ylabel);
+
+
+void draw_lineplot(struct chart *ch, const char *title,
+ const char *xlabel, const char *ylabel);
+
+
+
+#endif
+
+#if 0
+The anatomy of a chart is as follows.
+
++-------------------------------------------------------------+
+| +----------------------------------+ |
+| | | |
+| | Title | |
+| | | |
+| +----------------------------------+ |
+|+----------++----------------------------------++-----------+|
+|| || || ||
+|| || || ||
+|| || || ||
+|| || || ||
+|| || || ||
+|| || || ||
+|| || || ||
+|| || || ||
+|| || || ||
+|| || || ||
+|| Ordinate || Data || Legend ||
+|| || || ||
+|| || || ||
+|| || || ||
+|| || || ||
+|| || || ||
+|| || || ||
+|| || || ||
+|| || || ||
+|| || || ||
+|| || || ||
+|+----------++----------------------------------++-----------+| --
+| +----------------------------------+ | - ^ data_bottom
+| | Abscissa | | ^ |
+| | | | | abscissa_top
+| +----------------------------------+ | v v
++-------------------------------------------------------------+ ----
+
+ordinate_right || |
+| | || |
+|<--------->| || |
+| | || |
+| data_left | || |
+|<---------->| || |
+| || |
+| data_right || |
+|<--------------------------------------------->|| |
+| legend_left | |
+|<---------------------------------------------->| |
+| legend_right |
+|<---------------------------------------------------------->|
+
+#endif
#include "var.h"
#include "vfm.h"
#include "settings.h"
+#include "chart.h"
#include "debug-print.h"
barchart(ba_)=:minimum(d:min),
:maximum(d:max),
scale:freq(*n:freq,"%s>0")/percent(*n:pcnt,"%s>0");
+ piechart(pie_)=:minimum(d:min),
+ :maximum(d:max),
+ missing:missing/!nomissing;
histogram(hi_)=:minimum(d:min),
:maximum(d:max),
scale:freq(*n:freq,"%s>0")/percent(*n:pcnt,"%s>0"),
GFT_NONE, /* Don't draw graphs. */
GFT_BAR, /* Draw bar charts. */
GFT_HIST, /* Draw histograms. */
+ GFT_PIE, /* Draw piechart */
GFT_HBAR /* Draw bar charts or histograms at our discretion. */
};
static struct cmd_frequencies cmd;
/* Summary of the barchart, histogram, and hbar subcommands. */
-static int chart; /* NONE/BAR/HIST/HBAR. */
+/* FIXME: These should not be mututally exclusive */
+static int chart; /* NONE/BAR/HIST/HBAR/PIE. */
static double min, max; /* Minimum, maximum on y axis. */
static int format; /* FREQ/PERCENT: Scaling of y axis. */
static double scale, incr; /* FIXME */
static void determine_charts (void);
+static void calc_stats (struct variable * v, double d[frq_n_stats]);
+
static void precalc (void *);
static int calc (struct ccase *, void *);
static void postcalc (void *);
static void
determine_charts (void)
{
- int count = (!!cmd.sbc_histogram) + (!!cmd.sbc_barchart) + (!!cmd.sbc_hbar);
+ int count = (!!cmd.sbc_histogram) + (!!cmd.sbc_barchart) +
+ (!!cmd.sbc_hbar) + (!!cmd.sbc_piechart);
if (!count)
{
chart = GFT_HIST;
else if (cmd.sbc_barchart)
chart = GFT_BAR;
+ else if (cmd.sbc_piechart)
+ chart = GFT_PIE;
else
chart = GFT_HBAR;
format = FRQ_PERCENT;
scale = cmd.ba_pcnt;
}
- if (cmd.hi_norm)
+ if (cmd.hi_norm != FRQ_NONORMAL )
normal = 1;
if (cmd.hi_incr == FRQ_INCREMENT)
incr = cmd.hi_inc;
{
case FRQM_GENERAL:
{
+
/* General mode. */
struct freq **fpp = (struct freq **) hsh_probe (ft->data, val);
-
+
if (*fpp != NULL)
(*fpp)->c += weight;
else
if (n_stats)
dump_statistics (v, !dumped_freq_tab);
+
+ if ( chart == GFT_HIST)
+ {
+ struct chart ch;
+ double d[frq_n_stats];
+ struct frequencies_proc *frq = &v->p.frq;
+
+ struct normal_curve norm;
+ norm.N = frq->tab.total_cases ;
+
+ calc_stats(v,d);
+ norm.mean = d[frq_mean];
+ norm.stddev = d[frq_stddev];
+
+ chart_initialise(&ch);
+ draw_histogram(&ch, v_variables[i], "HISTOGRAM",&norm,normal);
+ chart_finalise(&ch);
+ }
+
+
+ if ( chart == GFT_PIE)
+ {
+ struct chart ch;
+
+ chart_initialise(&ch);
+
+ draw_piechart(&ch, v_variables[i]);
+
+ chart_finalise(&ch);
+ }
+
+
cleanup_freq_tab (v);
+
}
}
tab_title (t, 1, "%s: %s", v->name, v->label ? v->label : "");
tab_submit (t);
+
}
/* Sets the widths of all the columns and heights of all the rows in
--- /dev/null
+/* PSPP - computes sample statistics.
+ Copyright (C) 2004 Free Software Foundation, Inc.
+ Written by John Darrington <john@darrington.wattle.id.au>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#include <stdio.h>
+#include <plot.h>
+#include <stdarg.h>
+#include <math.h>
+#include "hash.h"
+
+#include "var.h"
+#include "chart.h"
+
+/* Number of bins in which to divide data */
+#define BINS 15
+
+/* The approximate no of ticks on the y axis */
+#define YTICKS 10
+
+#define M_PI ( 22.0 / 7.0 )
+
+
+static double gaussian(double x, double mu, double sigma ) ;
+
+
+static double
+gaussian(double x, double mu, double sigma )
+{
+ return (exp( - (( x - mu )* (x - mu) / (2.0 * sigma * sigma) ))
+ / ( sigma * sqrt( M_PI * 2.0) )) ;
+}
+
+
+/* Adjust tick to be a sensible value */
+void adjust_tick(double *tick);
+
+
+/* Write the legend of the chart */
+static void
+write_legend(struct chart *ch, struct normal_curve *norm)
+{
+ char buf[100];
+ pl_savestate_r(ch->lp);
+
+ sprintf(buf,"N = %.2f",norm->N);
+ pl_move_r(ch->lp, ch->legend_left, ch->data_bottom);
+ pl_alabel_r(ch->lp,0,'b',buf);
+
+ sprintf(buf,"Mean = %.1f",norm->mean);
+ pl_fmove_r(ch->lp,ch->legend_left,ch->data_bottom + ch->font_size * 1.5);
+ pl_alabel_r(ch->lp,0,'b',buf);
+
+ sprintf(buf,"Std. Dev = %.2f",norm->stddev);
+ pl_fmove_r(ch->lp,ch->legend_left,ch->data_bottom + ch->font_size * 1.5 * 2);
+ pl_alabel_r(ch->lp,0,'b',buf);
+
+ pl_restorestate_r(ch->lp);
+}
+
+
+
+
+/* Draw a histogram.
+ If show_normal is non zero then superimpose a normal curve
+*/
+void
+draw_histogram(struct chart *ch,
+ const struct variable *var,
+ const char *title,
+ struct normal_curve *norm,
+ int show_normal)
+{
+
+ double d;
+ int count;
+
+ double x_min = DBL_MAX;
+ double x_max = -DBL_MAX;
+ double y_min = DBL_MAX;
+ double y_max = -DBL_MAX;
+
+ double y_tick ;
+
+
+ double ordinate_values[BINS];
+
+ const struct freq_tab *frq_tab = &var->p.frq.tab ;
+
+ struct hsh_iterator hi;
+ struct hsh_table *fh = frq_tab->data;
+ struct freq *frq;
+
+ double interval_size = fabs(ch->data_right - ch->data_left) / ( BINS );
+
+ /* Find out the extremes of the x value
+ FIXME: These don't need to be calculated here, since the
+ calling routine should know them */
+
+ for ( frq = hsh_first(fh,&hi); frq != 0; frq = hsh_next(fh,&hi) )
+ {
+ if ( frq->v.f < x_min ) x_min = frq->v.f ;
+ if ( frq->v.f > x_max ) x_max = frq->v.f ;
+ }
+
+ double x_interval = fabs(x_max - x_min) / ( BINS - 1);
+
+
+ double abscissa_scale =
+ fabs( (ch->data_right - ch->data_left) / (x_max - x_min) ) ;
+
+
+ /* Find out the bin values */
+ for ( count = 0, d = x_min; d <= x_max ; d += x_interval, ++count )
+ {
+
+ double y = 0;
+
+ for ( frq = hsh_first(fh,&hi); frq != 0; frq = hsh_next(fh,&hi) )
+ {
+ if ( frq->v.f >= d && frq->v.f < d + x_interval )
+ y += frq->c;
+ }
+
+ ordinate_values[count] = y ;
+
+ if ( y > y_max ) y_max = y ;
+ if ( y < y_min ) y_min = y;
+ }
+
+ y_tick = ( y_max - y_min ) / (double) (YTICKS - 1) ;
+
+ adjust_tick(&y_tick);
+
+ y_min = floor( y_min / y_tick ) * y_tick ;
+ y_max = ceil( y_max / y_tick ) * y_tick ;
+
+ double ordinate_scale =
+ fabs(ch->data_top - ch->data_bottom) / fabs(y_max - y_min) ;
+
+
+ /* Move to data bottom-left */
+ pl_move_r(ch->lp, ch->data_left, ch->data_bottom);
+
+ pl_savestate_r(ch->lp);
+ pl_fillcolorname_r(ch->lp, ch->fill_colour);
+ pl_filltype_r(ch->lp,1);
+
+ /* Draw the histogram */
+ for ( count = 0, d = x_min; d <= x_max ; d += x_interval, ++count )
+ {
+ const double x = count * interval_size ;
+ pl_savestate_r(ch->lp);
+
+ draw_tick (ch, TICK_ABSCISSA, x + (interval_size / 2.0 ), "%.1f",d);
+
+ pl_fboxrel_r(ch->lp, x, 0, x + interval_size,
+ ordinate_values[count] * ordinate_scale );
+
+ pl_restorestate_r(ch->lp);
+
+ }
+ pl_restorestate_r(ch->lp);
+
+ /* Put the y axis on */
+ for ( d = y_min; d <= y_max ; d += y_tick )
+ {
+ draw_tick (ch, TICK_ORDINATE, (d - y_min ) * ordinate_scale, "%g", d);
+ }
+
+ /* Write the abscissa label */
+ pl_move_r(ch->lp,ch->data_left, ch->abscissa_top);
+ pl_alabel_r(ch->lp,0,'t',var->label ? var->label : var->name);
+
+
+ /* Write the ordinate label */
+ pl_savestate_r(ch->lp);
+ pl_move_r(ch->lp,ch->data_bottom, ch->ordinate_right);
+ pl_textangle_r(ch->lp,90);
+ pl_alabel_r(ch->lp,0,0,"Frequency");
+
+ pl_restorestate_r(ch->lp);
+
+
+ chart_write_title(ch, title);
+
+
+ /* Write the legend */
+ write_legend(ch,norm);
+
+
+ if ( show_normal )
+ {
+ /* Draw the normal curve */
+ double d;
+
+ pl_move_r(ch->lp, ch->data_left, ch->data_bottom);
+ for( d = ch->data_left;
+ d <= ch->data_right ;
+ d += (ch->data_right - ch->data_left) / 100.0)
+ {
+ const double x = (d - ch->data_left - interval_size / 2.0 ) /
+ abscissa_scale + x_min ;
+
+ pl_fcont_r(ch->lp, d,
+ ch->data_bottom +
+ norm->N * gaussian(x, norm->mean, norm->stddev)
+ * ordinate_scale);
+
+ }
+ pl_endpath_r(ch->lp);
+ }
+
+}
+
+
+
+double
+log10(double x)
+{
+ return log(x) / log(10.0) ;
+}
+
+
+/* Adjust tick to be a sensible value */
+void
+adjust_tick(double *tick)
+{
+ int i;
+ const double standard_ticks[] = {1, 2, 5};
+
+ const double factor = pow(10,ceil(log10(standard_ticks[0] / *tick))) ;
+
+ for (i = 2 ; i >=0 ; --i)
+ {
+ if ( *tick > standard_ticks[i] / factor )
+ {
+ *tick = standard_ticks[i] / factor ;
+ break;
+ }
+ }
+
+ }
+
--- /dev/null
+/* PSPP - draws pie charts of sample statistics
+
+Copyright (C) 2004 Free Software Foundation, Inc.
+Written by John Darrington <john@darrington.wattle.id.au>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
+
+
+#include "chart.h"
+#include <float.h>
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+#include "value-labels.h"
+
+
+/* Pie charts of course need to know Pi :) */
+#define M_PI ( 22.0 / 7.0 )
+
+
+#define min(A,B) ((A>B)?B:A)
+
+
+/* Draw a single slice of the pie */
+void
+draw_segment(struct chart *ch,
+ double centre_x, double centre_y,
+ double radius,
+ double start_angle, double segment_angle,
+ const char *colour) ;
+
+
+/* Draw a pie chart */
+void
+draw_piechart(struct chart *ch, const struct variable *var)
+{
+ int i;
+
+ const struct freq_tab *frq_tab = &var->p.frq.tab ;
+
+ const int n_data = frq_tab->n_valid;
+ const double left_label = ch->data_left +
+ (ch->data_right - ch->data_left)/10.0;
+
+ const double right_label = ch->data_right -
+ (ch->data_right - ch->data_left)/10.0;
+
+ const double centre_x = (ch->data_right + ch->data_left ) / 2.0 ;
+ const double centre_y = (ch->data_top + ch->data_bottom ) / 2.0 ;
+
+ const double radius = min(
+ 5.0 / 12.0 * (ch->data_top - ch->data_bottom),
+ 1.0 / 4.0 * (ch->data_right - ch->data_left)
+ );
+
+
+ chart_write_title(ch, var->label ? var->label: var->name);
+
+
+ for (i = 0 ; i < n_data ; ++i )
+ {
+ static double angle=0.0;
+ const struct freq frq = frq_tab->valid[i];
+
+ const double segment_angle =
+ frq.c / frq_tab->valid_cases * 2 * M_PI ;
+
+ char *label = val_labs_find (var->val_labs, frq.v );
+ if ( !label )
+ {
+ static char l[20];
+ snprintf(l,20,"%g",frq.v.f);
+ label = l;
+ }
+
+ const double label_x = centre_x -
+ radius * sin(angle + segment_angle/2.0);
+
+ const double label_y = centre_y +
+ radius * cos(angle + segment_angle/2.0);
+
+ /* Fill the segment */
+ draw_segment(ch,
+ centre_x, centre_y, radius,
+ angle, segment_angle,
+ data_colour[i]);
+
+ /* Now add the labels */
+ if ( label_x < centre_x )
+ {
+ pl_line_r(ch->lp, label_x, label_y,
+ left_label, label_y );
+ pl_moverel_r(ch->lp,0,5);
+ pl_alabel_r(ch->lp,0,0,label);
+ }
+ else
+ {
+ pl_line_r(ch->lp,
+ label_x, label_y,
+ right_label, label_y
+ );
+ pl_moverel_r(ch->lp,0,5);
+ pl_alabel_r(ch->lp,'r',0,label);
+ }
+
+ angle += segment_angle;
+
+ }
+
+ /* Draw an outline to the pie */
+ pl_filltype_r(ch->lp,0);
+ pl_fcircle_r (ch->lp, centre_x, centre_y, radius);
+
+}
+
+
+
+void
+fill_segment(struct chart *ch,
+ double x0, double y0,
+ double radius,
+ double start_angle, double segment_angle) ;
+
+
+/* Fill a segment with the current fill colour */
+void
+fill_segment(struct chart *ch,
+ double x0, double y0,
+ double radius,
+ double start_angle, double segment_angle)
+{
+
+ const double start_x = x0 - radius * sin(start_angle);
+ const double start_y = y0 + radius * cos(start_angle);
+
+ const double stop_x =
+ x0 - radius * sin(start_angle + segment_angle);
+
+ const double stop_y =
+ y0 + radius * cos(start_angle + segment_angle);
+
+ assert(segment_angle <= 2 * M_PI);
+ assert(segment_angle >= 0);
+
+ if ( segment_angle > M_PI )
+ {
+ /* Then we must draw it in two halves */
+ fill_segment(ch, x0, y0, radius, start_angle, segment_angle / 2.0 );
+ fill_segment(ch, x0, y0, radius, start_angle + segment_angle / 2.0,
+ segment_angle / 2.0 );
+ }
+ else
+ {
+ pl_move_r(ch->lp, x0, y0);
+
+ pl_cont_r(ch->lp, stop_x, stop_y);
+ pl_cont_r(ch->lp, start_x, start_y);
+
+ pl_arc_r(ch->lp,
+ x0, y0,
+ stop_x, stop_y,
+ start_x, start_y
+ );
+
+ pl_endpath_r(ch->lp);
+ }
+}
+
+
+
+/* Draw a single slice of the pie */
+void
+draw_segment(struct chart *ch,
+ double x0, double y0,
+ double radius,
+ double start_angle, double segment_angle,
+ const char *colour)
+{
+ const double start_x = x0 - radius * sin(start_angle);
+ const double start_y = y0 + radius * cos(start_angle);
+
+ pl_savestate_r(ch->lp);
+
+ pl_savestate_r(ch->lp);
+ pl_colorname_r(ch->lp, colour);
+
+ pl_pentype_r(ch->lp,1);
+ pl_filltype_r(ch->lp,1);
+
+ fill_segment(ch, x0, y0, radius, start_angle, segment_angle);
+ pl_restorestate_r(ch->lp);
+
+ /* Draw line dividing segments */
+ pl_pentype_r(ch->lp, 1);
+ pl_fline_r(ch->lp, x0, y0, start_x, start_y);
+
+
+ pl_restorestate_r(ch->lp);
+}