Changes since 0.8.4:
- * The FREQUENCIES command can now generate barcharts.
+ * The FREQUENCIES and CROSSTABS commands can now generate barcharts.
* The FACTOR command can now perform PROMAX rotations.
The @subcmd{PIECHART} subcommand adds a pie chart for each variable to the data. Each
slice represents one value, with the size of the slice proportional to
the value's frequency. By default, all non-missing values are given
-slices. The @subcmd{MINIMUM} and @subcmd{MAXIMUM} keywords can be used to limit the
-displayed slices to a given range of values. The @subcmd{MISSING} keyword adds
-slices for missing values.
-
-The @subcmd{FREQ} and @subcmd{PERCENT} options on @subcmd{HISTOGRAM} and @subcmd{PIECHART} are accepted
-but not currently honoured.
+slices.
+The @subcmd{MINIMUM} and @subcmd{MAXIMUM} keywords can be used to limit the
+displayed slices to a given range of values.
+The keyword @subcmd{NOMISSING} causes missing values to be omitted from the
+piechart. This is the default.
+If instead, @subcmd{MISSING} is specified, then a single slice
+will be included representing all system missing and user-missing cases.
+
+@cindex bar chart
+The @subcmd{BARCHART} subcommand produces a bar chart for each variable.
+The @subcmd{MINIMUM} and @subcmd{MAXIMUM} keywords can be used to omit
+categories whose counts which lie outside the specified limits.
+The @subcmd{FREQ} option (default) causes the ordinate to display the frequency
+of each category, whereas the @subcmd{PERCENT} option will display relative
+percentages.
+
+The @subcmd{FREQ} and @subcmd{PERCENT} options on @subcmd{HISTOGRAM} and
+@subcmd{PIECHART} are accepted but not currently honoured.
@node EXAMINE
@section EXAMINE
ASRESIDUAL,ALL,NONE@}
/STATISTICS=@{CHISQ,PHI,CC,LAMBDA,UC,BTAU,CTAU,RISK,GAMMA,D,
KAPPA,ETA,CORR,ALL,NONE@}
+ /BARCHART
(Integer mode.)
/VARIABLES=@var{var_list} (@var{low},@var{high})@dots{}
@samp{/STATISTICS} without any settings selects CHISQ. If the
@subcmd{STATISTICS} subcommand is not given, no statistics are calculated.
+@cindex bar chart
+The @samp{/BARCHART} subcommand produces a clustered bar chart for the first two
+variables on each table.
+If a table has more than two variables, the counts for the third and subsequent levels
+will be aggregated and the chart will be produces as if there were only two variables.
+
+
@strong{Please note:} Currently the implementation of @cmd{CROSSTABS} has the
-following bugs:
+following limitations:
@itemize @bullet
@item
if (bst->cutpoint != SYSMIS)
{
- if ( cat1[v].value.f >= value )
+ if ( cat1[v].values[0].f >= value )
cat1[v].count += w;
else
cat2[v].count += w;
}
else
{
- if ( SYSMIS == cat1[v].value.f )
+ if ( SYSMIS == cat1[v].values[0].f )
{
- cat1[v].value.f = value;
+ cat1[v].values[0].f = value;
cat1[v].count = w;
}
- else if ( cat1[v].value.f == value )
+ else if ( cat1[v].values[0].f == value )
cat1[v].count += w;
- else if ( SYSMIS == cat2[v].value.f )
+ else if ( SYSMIS == cat2[v].values[0].f )
{
- cat2[v].value.f = value;
+ cat2[v].values[0].f = value;
cat2[v].count = w;
}
- else if ( cat2[v].value.f == value )
+ else if ( cat2[v].values[0].f == value )
cat2[v].count += w;
else if ( bst->category1 == SYSMIS)
msg (ME, _("Variable %s is not dichotomous"), var_get_name (var));
cat[i] = xnmalloc (ost->n_vars, sizeof *cat[i]);
for (v = 0; v < ost->n_vars; v++)
{
- cat[i][v].value.f = value;
+ cat[i][v].values[0].f = value;
cat[i][v].count = 0;
}
}
}
else
{
- var_append_value_name (var, &cat[0][v].value, &catstr[0]);
- var_append_value_name (var, &cat[1][v].value, &catstr[1]);
+ var_append_value_name (var, cat[0][v].values, &catstr[0]);
+ var_append_value_name (var, cat[1][v].values, &catstr[1]);
}
tab_hline (table, TAL_1, 0, tab_nc (table) -1, 1 + v * 3);
{
struct string str;
double exp;
- const union value *observed_value = &ff[i]->value;
+ const union value *observed_value = &ff[i]->values[0];
ds_init_empty (&str);
var_append_value_name (var, observed_value, &str);
struct string str;
double exp;
- const union value *observed_value = &ff[i]->value;
+ const union value *observed_value = &ff[i]->values[0];
ds_init_empty (&str);
var_append_value_name (ost->vars[v], observed_value, &str);
#include "data/value-labels.h"
#include "data/variable.h"
#include "language/command.h"
+#include "language/stats/freq.h"
#include "language/dictionary/split-file.h"
#include "language/lexer/lexer.h"
#include "language/lexer/variable-parser.h"
#include "libpspp/pool.h"
#include "libpspp/str.h"
#include "output/tab.h"
+#include "output/chart-item.h"
+#include "output/charts/barchart.h"
#include "gl/minmax.h"
#include "gl/xalloc.h"
tabl:!tables/notables,
box:!box/nobox,
pivot:!pivot/nopivot;
+ +barchart=;
+cells[cl_]=count,expected,row,column,total,residual,sresidual,
asresidual,all,none;
+statistics[st_]=chisq,phi,cc,lambda,uc,none,btau,ctau,risk,gamma,d,
/* Number of directional statistics. */
#define N_DIRECTIONAL 13
-/* A single table entry for general mode. */
-struct table_entry
- {
- struct hmap_node node; /* Entry in hash table. */
- double freq; /* Frequency count. */
- union value values[1]; /* Values. */
- };
-
-static size_t
-table_entry_size (size_t n_values)
-{
- return (offsetof (struct table_entry, values)
- + n_values * sizeof (union value));
-}
/* Indexes into the 'vars' member of struct pivot_table and
struct crosstab member. */
/* Data. */
struct hmap data;
- struct table_entry **entries;
+ struct freq **entries;
size_t n_entries;
/* Column values, number of columns. */
enum { INTEGER, GENERAL } mode;
enum mv_class exclude;
bool pivot;
+ bool barchart;
bool bad_warn;
struct fmt_spec weight_format;
}
proc.mode = proc.n_variables ? INTEGER : GENERAL;
-
+ proc.barchart = cmd.sbc_barchart > 0;
proc.descending = cmd.val == CRS_DVALUE;
tabulate_integer_case (struct pivot_table *pt, const struct ccase *c,
double weight)
{
- struct table_entry *te;
+ struct freq *te;
size_t hash;
int j;
hash = hash_int (case_num (c, pt->vars[j]), hash);
}
- HMAP_FOR_EACH_WITH_HASH (te, struct table_entry, node, hash, &pt->data)
+ HMAP_FOR_EACH_WITH_HASH (te, struct freq, node, hash, &pt->data)
{
for (j = 0; j < pt->n_vars; j++)
if ((int) case_num (c, pt->vars[j]) != (int) te->values[j].f)
goto no_match;
/* Found an existing entry. */
- te->freq += weight;
+ te->count += weight;
return;
no_match: ;
/* No existing entry. Create a new one. */
te = xmalloc (table_entry_size (pt->n_vars));
- te->freq = weight;
+ te->count = weight;
for (j = 0; j < pt->n_vars; j++)
te->values[j].f = (int) case_num (c, pt->vars[j]);
hmap_insert (&pt->data, &te->node, hash);
tabulate_general_case (struct pivot_table *pt, const struct ccase *c,
double weight)
{
- struct table_entry *te;
+ struct freq *te;
size_t hash;
int j;
hash = value_hash (case_data (c, var), var_get_width (var), hash);
}
- HMAP_FOR_EACH_WITH_HASH (te, struct table_entry, node, hash, &pt->data)
+ HMAP_FOR_EACH_WITH_HASH (te, struct freq, node, hash, &pt->data)
{
for (j = 0; j < pt->n_vars; j++)
{
}
/* Found an existing entry. */
- te->freq += weight;
+ te->count += weight;
return;
no_match: ;
/* No existing entry. Create a new one. */
te = xmalloc (table_entry_size (pt->n_vars));
- te->freq = weight;
+ te->count = weight;
for (j = 0; j < pt->n_vars; j++)
{
const struct variable *var = pt->vars[j];
\f
/* Post-data reading calculations. */
-static int compare_table_entry_vars_3way (const struct table_entry *a,
- const struct table_entry *b,
+static int compare_table_entry_vars_3way (const struct freq *a,
+ const struct freq *b,
const struct pivot_table *pt,
int idx0, int idx1);
static int compare_table_entry_3way (const void *ap_, const void *bp_,
/* Convert hash tables into sorted arrays of entries. */
for (pt = &proc->pivots[0]; pt < &proc->pivots[proc->n_pivots]; pt++)
{
- struct table_entry *e;
+ struct freq *e;
size_t i;
pt->n_entries = hmap_count (&pt->data);
pt->entries = xnmalloc (pt->n_entries, sizeof *pt->entries);
i = 0;
- HMAP_FOR_EACH (e, struct table_entry, node, &pt->data)
+ HMAP_FOR_EACH (e, struct freq, node, &pt->data)
pt->entries[i++] = e;
hmap_destroy (&pt->data);
sort (pt->entries, pt->n_entries, sizeof *pt->entries,
proc->descending ? compare_table_entry_3way_inv : compare_table_entry_3way,
pt);
+
}
make_summary_table (proc);
output_pivot_table (proc, &subset);
}
}
+ if (proc->barchart)
+ chart_item_submit
+ (barchart_create (pt->vars, pt->n_vars, _("Count"), pt->entries, pt->n_entries));
}
/* Free output and prepare for next split file. */
}
static int
-compare_table_entry_var_3way (const struct table_entry *a,
- const struct table_entry *b,
+compare_table_entry_var_3way (const struct freq *a,
+ const struct freq *b,
const struct pivot_table *pt,
int idx)
{
}
static int
-compare_table_entry_vars_3way (const struct table_entry *a,
- const struct table_entry *b,
+compare_table_entry_vars_3way (const struct freq *a,
+ const struct freq *b,
const struct pivot_table *pt,
int idx0, int idx1)
{
return 0;
}
-/* Compare the struct table_entry at *AP to the one at *BP and
+/* Compare the struct freq at *AP to the one at *BP and
return a strcmp()-type result. */
static int
compare_table_entry_3way (const void *ap_, const void *bp_, const void *pt_)
{
- const struct table_entry *const *ap = ap_;
- const struct table_entry *const *bp = bp_;
- const struct table_entry *a = *ap;
- const struct table_entry *b = *bp;
+ const struct freq *const *ap = ap_;
+ const struct freq *const *bp = bp_;
+ const struct freq *a = *ap;
+ const struct freq *b = *bp;
const struct pivot_table *pt = pt_;
int cmp;
return pt->n_vars - 1;
else
{
- const struct table_entry *a = pt->entries[row];
- const struct table_entry *b = pt->entries[row - 1];
+ const struct freq *a = pt->entries[row];
+ const struct freq *b = pt->entries[row - 1];
int col;
for (col = pt->n_vars - 1; col >= 0; col--)
valid = 0.;
for (i = 0; i < pt->n_entries; i++)
- valid += pt->entries[i]->freq;
+ valid += pt->entries[i]->count;
n[0] = valid;
n[1] = pt->missing;
const int row_var_width = var_get_width (x->vars[ROW_VAR]);
int col, row;
double *mp;
- struct table_entry **p;
+ struct freq **p;
mp = x->mat;
col = row = 0;
for (p = x->entries; p < &x->entries[x->n_entries]; p++)
{
- const struct table_entry *te = *p;
+ const struct freq *te = *p;
while (!value_equal (&x->rows[row], &te->values[ROW_VAR], row_var_width))
{
col++;
}
- *mp++ = te->freq;
+ *mp++ = te->count;
if (++col >= x->n_cols)
{
col = 0;
for (row1 = row0 + 1; row1 < pt->n_entries; row1++)
{
- struct table_entry *a = pt->entries[row0];
- struct table_entry *b = pt->entries[row1];
+ struct freq *a = pt->entries[row0];
+ struct freq *b = pt->entries[row1];
if (compare_table_entry_vars_3way (a, b, pt, 2, pt->n_vars) != 0)
break;
}
hmapx_init (&set);
for (i = 0; i < pt->n_entries; i++)
{
- const struct table_entry *te = pt->entries[i];
+ const struct freq *te = pt->entries[i];
const union value *value = &te->values[var_idx];
size_t hash = value_hash (value, width, 0);
#include "libpspp/array.h"
#include "libpspp/compiler.h"
+struct freq *
+freq_clone (const struct freq *in, int values, int *widths)
+{
+ int i;
+ struct freq *f = xmalloc (sizeof (struct freq) +
+ (sizeof (union value) * (values - 1)));
+
+ f->node = in->node;
+ f->count = in->count;
+ for (i = 0; i < values; ++i)
+ {
+ value_init (&f->values[i], widths[i]);
+ value_copy (&f->values[i], &in->values[i], widths[i]);
+ }
+
+ return f;
+}
+
+void
+freq_destroy (struct freq *f, int values, int *widths)
+{
+ int i;
+ for (i = 0; i < values; ++i)
+ {
+ value_destroy (&f->values[i], widths[i]);
+ }
+
+ free (f);
+}
+
+
+
void
freq_hmap_destroy (struct hmap *hmap, int width)
{
struct freq *f, *next;
- HMAP_FOR_EACH_SAFE (f, next, struct freq, hmap_node, hmap)
+ HMAP_FOR_EACH_SAFE (f, next, struct freq, node, hmap)
{
- value_destroy (&f->value, width);
- hmap_delete (hmap, &f->hmap_node);
+ value_destroy (&f->values[0], width);
+ hmap_delete (hmap, &f->node);
free (f);
}
hmap_destroy (hmap);
{
struct freq *f;
- HMAP_FOR_EACH_WITH_HASH (f, struct freq, hmap_node, hash, hmap)
- if (value_equal (value, &f->value, width))
+ HMAP_FOR_EACH_WITH_HASH (f, struct freq, node, hash, hmap)
+ if (value_equal (value, &f->values[0], width))
return f;
return NULL;
const union value *value, int width, size_t hash)
{
struct freq *f = xmalloc (sizeof *f);
- value_clone (&f->value, value, width);
+ value_clone (&f->values[0], value, width);
f->count = 0;
- hmap_insert (hmap, &f->hmap_node, hash);
+ hmap_insert (hmap, &f->node, hash);
return f;
}
-static int
+int
compare_freq_ptr_3way (const void *a_, const void *b_, const void *width_)
{
const struct freq *const *ap = a_;
const struct freq *const *bp = b_;
const int *widthp = width_;
- return value_compare_3way (&(*ap)->value, &(*bp)->value, *widthp);
+ return value_compare_3way (&(*ap)->values[0], &(*bp)->values[0], *widthp);
}
struct freq **
entries = xnmalloc (n_entries, sizeof *entries);
i = 0;
- HMAP_FOR_EACH (f, struct freq, hmap_node, hmap)
+ HMAP_FOR_EACH (f, struct freq, node, hmap)
entries[i++] = f;
assert (i == n_entries);
n_freqs = hmap_count (hmap);
freqs = xnmalloc (n_freqs, sizeof *freqs);
i = 0;
- HMAP_FOR_EACH (f, struct freq, hmap_node, hmap)
+ HMAP_FOR_EACH (f, struct freq, node, hmap)
freqs[i++] = *f;
assert (i == n_freqs);
/* PSPP - a program for statistical analysis.
- Copyright (C) 2006, 2010 Free Software Foundation, Inc.
+ Copyright (C) 2006, 2010, 2015 Free Software Foundation, Inc.
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
/* Frequency table entry. */
struct freq
{
- struct hmap_node hmap_node; /* Element in hash table. */
- union value value; /* The value. */
+ struct hmap_node node; /* Element in hash table. */
double count; /* The number of occurrences of the value. */
+ union value values[1]; /* The value. */
};
+
+struct freq *freq_clone (const struct freq *, int values, int *widths);
+void freq_destroy (struct freq *f, int values, int *widths);
+
+
+static inline size_t
+table_entry_size (size_t n_values)
+{
+ return (offsetof (struct freq, values)
+ + n_values * sizeof (union value));
+}
+
+
+int compare_freq_ptr_3way (const void *a_, const void *b_, const void *width_);
+
+
+
void freq_hmap_destroy (struct hmap *, int width);
struct freq *freq_hmap_search (struct hmap *, const union value *, int width,
struct freq **freq_hmap_sort (struct hmap *, int width);
struct freq *freq_hmap_extract (struct hmap *);
+
+
+
#endif /* language/stats/freq.h */
const struct freq_tab *frq_tab);
static void do_barchart(const struct frq_chart *bar,
- const struct variable *var,
+ const struct variable **var,
const struct freq_tab *frq_tab);
static void dump_statistics (const struct frq_proc *frq,
}
else
{
- int cmp = value_compare_3way (&a->value, &b->value, aux->width);
+ int cmp = value_compare_3way (a->values, b->values, aux->width);
return aux->ascending_value ? cmp : -cmp;
}
}
valid_percent = f->count / ft->valid_cases * 100.0;
cum_total += valid_percent;
- label = var_lookup_value_label (vf->var, &f->value);
+ label = var_lookup_value_label (vf->var, f->values);
if (label != NULL)
tab_text (t, 0, r, TAB_LEFT, label);
- tab_value (t, 1, r, TAB_NONE, &f->value, vf->var, NULL);
+ tab_value (t, 1, r, TAB_NONE, f->values, vf->var, NULL);
tab_double (t, 2, r, TAB_NONE, f->count, NULL, RC_WEIGHT);
tab_double (t, 3, r, TAB_NONE, percent, NULL, RC_OTHER);
tab_double (t, 4, r, TAB_NONE, valid_percent, NULL, RC_OTHER);
cum_freq += f->count;
- label = var_lookup_value_label (vf->var, &f->value);
+ label = var_lookup_value_label (vf->var, f->values);
if (label != NULL)
tab_text (t, 0, r, TAB_LEFT, label);
- tab_value (t, 1, r, TAB_NONE, &f->value, vf->var, NULL);
+ tab_value (t, 1, r, TAB_NONE, f->values, vf->var, NULL);
tab_double (t, 2, r, TAB_NONE, f->count, NULL, RC_WEIGHT);
tab_double (t, 3, r, TAB_NONE,
f->count / ft->total_cases * 100.0, NULL, RC_OTHER);
break;
if (tp + 1 < rank || f + 1 >= ft->missing)
- pc->value = f->value.f;
+ pc->value = f->values[0].f;
else
- pc->value = calc_percentile (pc->p, W, f->value.f, f[1].value.f);
+ pc->value = calc_percentile (pc->p, W, f->values[0].f, f[1].values[0].f);
}
}
for (; percentile_idx < frq->n_percentiles; percentile_idx++)
{
struct percentile *pc = &frq->percentiles[percentile_idx];
- pc->value = ft->valid[ft->n_valid - 1].value.f;
+ pc->value = ft->valid[ft->n_valid - 1].values[0].f;
}
}
const struct freq *f = f_;
const struct variable *v = v_;
- return !var_is_value_missing (v, &f->value, MV_ANY);
+ return !var_is_value_missing (v, f->values, MV_ANY);
}
do_piechart(frq->pie, vf->var, &vf->tab);
if (frq->bar)
- do_barchart(frq->bar, vf->var, &vf->tab);
+ do_barchart(frq->bar, &vf->var, &vf->tab);
cleanup_freq_tab (vf);
}
double pie_min = -DBL_MAX;
double pie_max = DBL_MAX;
- bool pie_missing = false;
+ bool pie_missing = true;
double bar_min = -DBL_MAX;
double bar_max = DBL_MAX;
{
if (lex_match_id (lexer, "TABLE"))
{
-
}
else if (lex_match_id (lexer, "NOTABLE"))
{
frq.bar = xmalloc (sizeof *frq.bar);
frq.bar->x_min = bar_min;
frq.bar->x_max = bar_max;
- frq.bar->include_missing = true;
+ frq.bar->include_missing = false;
frq.bar->y_scale = bar_freq ? FRQ_FREQ : FRQ_PERCENT;
}
for (i = 0; i < ft->n_valid; i++)
{
const struct freq *f = &ft->valid[i];
- if (chart_includes_value (frq->hist, var, &f->value))
+ if (chart_includes_value (frq->hist, var, f->values))
{
- x_min = MIN (x_min, f->value.f);
- x_max = MAX (x_max, f->value.f);
+ x_min = MIN (x_min, f->values[0].f);
+ x_max = MAX (x_max, f->values[0].f);
valid_freq += f->count;
}
}
for (i = 0; i < ft->n_valid; i++)
{
const struct freq *f = &ft->valid[i];
- if (chart_includes_value (frq->hist, var, &f->value))
- histogram_add (histogram, f->value.f, f->count);
+ if (chart_includes_value (frq->hist, var, f->values))
+ histogram_add (histogram, f->values[0].f, f->count);
}
return histogram;
}
-static int
-add_slice (const struct frq_chart *pie, const struct freq *freq,
- const struct variable *var, struct slice *slice)
+
+/* Allocate an array of struct freqs and fill them from the data in FRQ_TAB,
+ according to the parameters of CATCHART
+ N_SLICES will contain the number of slices allocated.
+ The caller is responsible for freeing slices
+*/
+static struct freq *
+pick_cat_counts (const struct frq_chart *catchart,
+ const struct freq_tab *frq_tab,
+ int *n_slicesp)
{
- if (chart_includes_value (pie, var, &freq->value))
+ int n_slices = 0;
+ int i;
+ struct freq *slices = xnmalloc (frq_tab->n_valid + frq_tab->n_missing, sizeof *slices);
+
+ for (i = 0; i < frq_tab->n_valid; i++)
+ {
+ const struct freq *f = &frq_tab->valid[i];
+ if (f->count > catchart->x_max)
+ continue;
+
+ if (f->count < catchart->x_min)
+ continue;
+
+ slices[n_slices] = *f;
+
+ n_slices++;
+ }
+
+ if (catchart->include_missing)
{
- ds_init_empty (&slice->label);
- var_append_value_name (var, &freq->value, &slice->label);
- slice->magnitude = freq->count;
- return 1;
+ for (i = 0; i < frq_tab->n_missing; i++)
+ {
+ const struct freq *f = &frq_tab->missing[i];
+ slices[n_slices].count += f->count;
+
+ if (i == 0)
+ slices[n_slices].values[0] = f->values[0];
+ }
+
+ if (frq_tab->n_missing > 0)
+ n_slices++;
}
- else
- return 0;
+
+ *n_slicesp = n_slices;
+ return slices;
}
-/* Allocate an array of slices and fill them from the data in frq_tab
- n_slices will contain the number of slices allocated.
+
+/* Allocate an array of struct freqs and fill them from the data in FRQ_TAB,
+ according to the parameters of CATCHART
+ N_SLICES will contain the number of slices allocated.
The caller is responsible for freeing slices
*/
-static struct slice *
-freq_tab_to_slice_array(const struct frq_chart *catchart,
- const struct freq_tab *frq_tab,
- const struct variable *var,
- int *n_slicesp)
+static struct freq **
+pick_cat_counts_ptr (const struct frq_chart *catchart,
+ const struct freq_tab *frq_tab,
+ int *n_slicesp)
{
- struct slice *slices;
- int n_slices;
+ int n_slices = 0;
int i;
- double total = 0;
-
- slices = xnmalloc (frq_tab->n_valid + frq_tab->n_missing, sizeof *slices);
- n_slices = 0;
-
+ struct freq **slices = xnmalloc (frq_tab->n_valid + frq_tab->n_missing, sizeof *slices);
for (i = 0; i < frq_tab->n_valid; i++)
{
- const struct freq *f = &frq_tab->valid[i];
- total += f->count;
+ struct freq *f = &frq_tab->valid[i];
if (f->count > catchart->x_max)
continue;
if (f->count < catchart->x_min)
continue;
-
- n_slices += add_slice (catchart, f, var, &slices[n_slices]);
+
+ slices[n_slices] = f;
+
+ n_slices++;
}
- if (catchart->y_scale == FRQ_PERCENT)
- for (i = 0; i < frq_tab->n_valid; i++)
- {
- slices[i].magnitude /= total;
- slices[i].magnitude *= 100.00;
- }
-
- for (i = 0; i < frq_tab->n_missing; i++)
- n_slices += add_slice (catchart, &frq_tab->missing[i], var, &slices[n_slices]);
+ if (catchart->include_missing)
+ {
+ for (i = 0; i < frq_tab->n_missing; i++)
+ {
+ const struct freq *f = &frq_tab->missing[i];
+ if (i == 0)
+ {
+ slices[n_slices] = xmalloc (sizeof (struct freq));
+ slices[n_slices]->values[0] = f->values[0];
+ }
+
+ slices[n_slices]->count += f->count;
+
+ }
+ }
*n_slicesp = n_slices;
return slices;
}
+
static void
do_piechart(const struct frq_chart *pie, const struct variable *var,
const struct freq_tab *frq_tab)
{
- struct slice *slices;
- int n_slices, i;
-
- slices = freq_tab_to_slice_array (pie, frq_tab, var, &n_slices);
+ int n_slices;
+ struct freq *slices = pick_cat_counts (pie, frq_tab, &n_slices);
if (n_slices < 2)
msg (SW, _("Omitting pie chart for %s, which has only %d unique values."),
msg (SW, _("Omitting pie chart for %s, which has over 50 unique values."),
var_get_name (var));
else
- chart_item_submit (piechart_create (var_to_string(var), slices, n_slices));
+ chart_item_submit (piechart_create (var, slices, n_slices));
- for (i = 0; i < n_slices; i++)
- ds_destroy (&slices[i].label);
free (slices);
}
static void
-do_barchart(const struct frq_chart *bar, const struct variable *var,
+do_barchart(const struct frq_chart *bar, const struct variable **var,
const struct freq_tab *frq_tab)
{
- struct slice *slices;
- int n_slices, i;
-
- slices = freq_tab_to_slice_array (bar, frq_tab, var, &n_slices);
+ int n_slices;
+ struct freq **slices = pick_cat_counts_ptr (bar, frq_tab, &n_slices);
- chart_item_submit (barchart_create (var_to_string (var),
+ chart_item_submit (barchart_create (var, 1,
(bar->y_scale == FRQ_FREQ) ? _("Count") : _("Percent"),
slices, n_slices));
-
- for (i = 0; i < n_slices; i++)
- ds_destroy (&slices[i].label);
free (slices);
}
if (most_often < f->count)
{
most_often = f->count;
- X_mode = f->value.f;
+ X_mode = f->values[0].f;
}
else if (most_often == f->count)
{
/* Calculate moments. */
m = moments_create (MOMENT_KURTOSIS);
for (f = ft->valid; f < ft->missing; f++)
- moments_pass_one (m, f->value.f, f->count);
+ moments_pass_one (m, f->values[0].f, f->count);
for (f = ft->valid; f < ft->missing; f++)
- moments_pass_two (m, f->value.f, f->count);
+ moments_pass_two (m, f->values[0].f, f->count);
moments_calculate (m, NULL, &d[FRQ_ST_MEAN], &d[FRQ_ST_VARIANCE],
&d[FRQ_ST_SKEWNESS], &d[FRQ_ST_KURTOSIS]);
moments_destroy (m);
/* Formulae below are taken from _SPSS Statistical Algorithms_. */
- d[FRQ_ST_MINIMUM] = ft->valid[0].value.f;
- d[FRQ_ST_MAXIMUM] = ft->valid[ft->n_valid - 1].value.f;
+ d[FRQ_ST_MINIMUM] = ft->valid[0].values[0].f;
+ d[FRQ_ST_MAXIMUM] = ft->valid[ft->n_valid - 1].values[0].f;
d[FRQ_ST_MODE] = X_mode;
d[FRQ_ST_RANGE] = d[FRQ_ST_MAXIMUM] - d[FRQ_ST_MINIMUM];
d[FRQ_ST_SUM] = d[FRQ_ST_MEAN] * W;
#define CHART_GEOMETRY_H
struct decimal;
-void chart_rounded_tick(double tick, struct decimal *);
+void chart_rounded_tick (double tick, struct decimal *);
void chart_get_scale (double high, double low,
- struct decimal *lower, struct decimal *interval, int *n_ticks);
+ struct decimal *lower, struct decimal *interval, int *n_ticks);
#endif
struct xrchart_geometry *);
void xrchart_draw_piechart (const struct chart_item *, cairo_t *,
struct xrchart_geometry *);
+void xrchart_draw_barchart (const struct chart_item *, cairo_t *,
+ struct xrchart_geometry *);
void xrchart_draw_histogram (const struct chart_item *, cairo_t *,
struct xrchart_geometry *);
void xrchart_draw_np_plot (const struct chart_item *, cairo_t *,
#include "output/charts/piechart.h"
#include <math.h>
+#include "data/variable.h"
#include "output/cairo-chart.h"
#define _(msgid) gettext (msgid)
+
static void
-draw_bar (cairo_t *cr, const struct xrchart_geometry *geom,
- const struct barchart *bc, int bar)
+abscissa_label (const struct barchart *bc, cairo_t *cr,
+ struct xrchart_geometry *geom,
+ const union value *prev,
+ double x_pos,
+ double width,
+ int n_last_cat)
{
- const double width =
- (geom->axis[SCALE_ABSCISSA].data_max - geom->axis[SCALE_ABSCISSA].data_min)
- /
- (double) bc->n_bars ;
-
- const double x_pos =
- (geom->axis[SCALE_ABSCISSA].data_max - geom->axis[SCALE_ABSCISSA].data_min) *
- bar
- /
- (double) bc->n_bars ;
-
-
- double height = geom->axis[SCALE_ORDINATE].scale * bc->bars[bar].magnitude;
-
- cairo_rectangle (cr,
- geom->axis[SCALE_ABSCISSA].data_min + x_pos + width * 0.1,
- geom->axis[SCALE_ORDINATE].data_min,
- width * 0.8, height);
- cairo_save (cr);
- cairo_set_source_rgb (cr,
- geom->fill_colour.red / 255.0,
- geom->fill_colour.green / 255.0,
- geom->fill_colour.blue / 255.0);
- cairo_fill_preserve (cr);
- cairo_restore (cr);
- cairo_stroke (cr);
-
- draw_tick (cr, geom, SCALE_ABSCISSA, true,
- x_pos + width / 2.0, "%s", ds_cstr (&bc->bars[bar].label));
+ struct category *foo = NULL;
+ size_t hash = value_hash (prev, bc->widths[0], 0);
+ HMAP_FOR_EACH_WITH_HASH (foo, struct category, node, hash, &bc->primaries)
+ {
+ if (value_equal (&foo->val, prev, bc->widths[0]))
+ break;
+ }
+
+ draw_tick (cr, geom, SCALE_ABSCISSA, false,
+ x_pos - (width * n_last_cat) / 2.0,
+ "%s", ds_cstr (&foo->label));
}
+
+
void
xrchart_draw_barchart (const struct chart_item *chart_item, cairo_t *cr,
struct xrchart_geometry *geom)
struct barchart *bc = to_barchart (chart_item);
int i;
- xrchart_write_title (cr, geom, _("BARCHART"));
+ xrchart_write_title (cr, geom, _("Bar Chart"));
xrchart_write_ylabel (cr, geom, bc->ylabel);
xrchart_write_xlabel (cr, geom, chart_item_get_title (chart_item));
xrchart_write_yscale (cr, geom, 0, bc->largest);
- for (i = 0; i < bc->n_bars; i++)
+ const double abscale = geom->axis[SCALE_ABSCISSA].data_max - geom->axis[SCALE_ABSCISSA].data_min;
+ const double width = abscale / (double) (bc->n_nzcats + bc->n_pcats);
+
+ double x_pos = 0.5 * width;
+ union value *prev = NULL;
+
+ if (bc->ss)
{
- draw_bar (cr, geom, bc, i);
+ const int blob_size = 13;
+ const int height = blob_size * (hmap_count (&bc->secondaries) * 2);
+
+ cairo_rectangle (cr,
+ geom->axis[SCALE_ABSCISSA].data_max + 10,
+ geom->axis[SCALE_ORDINATE].data_max - height,
+ 100, height);
+
+ cairo_stroke (cr);
+
+ int ypos = blob_size * 1.5;
+ for (i = 0 ; i < hmap_count (&bc->secondaries) ; ++i)
+ {
+ const struct category *foo = bc->ss[i];
+
+ cairo_move_to (cr,
+ geom->axis[SCALE_ABSCISSA].data_max + (1.5 * blob_size) + 20,
+ geom->axis[SCALE_ORDINATE].data_max - ypos);
+
+ xrchart_label (cr, 'l', 'b', geom->font_size, ds_cstr (&foo->label));
+
+ cairo_rectangle (cr,
+ geom->axis[SCALE_ABSCISSA].data_max + 20,
+ geom->axis[SCALE_ORDINATE].data_max - ypos,
+ blob_size, blob_size);
+
+ cairo_save (cr);
+ cairo_set_source_rgb (cr,
+ data_colour[foo->idx].red / 255.0,
+ data_colour[foo->idx].green / 255.0,
+ data_colour[foo->idx].blue / 255.0);
+ cairo_fill_preserve (cr);
+
+ cairo_restore (cr);
+
+ cairo_stroke (cr);
+
+ ypos += blob_size * 2;
+ }
}
+
+ int n_last_cat = 0;
+ for (i = 0; i < bc->n_nzcats; i++)
+ {
+ double height = geom->axis[SCALE_ORDINATE].scale * bc->cats[i]->count;
+
+ if (prev && !value_equal (prev, &bc->cats[i]->values[0], bc->widths[0]))
+ {
+ abscissa_label (bc, cr, geom, prev, x_pos, width, n_last_cat);
+
+ x_pos += width;
+ n_last_cat = 0;
+ }
+
+ cairo_rectangle (cr,
+ geom->axis[SCALE_ABSCISSA].data_min + x_pos,
+ geom->axis[SCALE_ORDINATE].data_min,
+ width, height);
+ cairo_save (cr);
+
+
+ int cidx = 0;
+ if (bc->ss)
+ {
+ struct category *foo;
+ size_t hash = value_hash (&bc->cats[i]->values[1], bc->widths[1], 0);
+ HMAP_FOR_EACH_WITH_HASH (foo, struct category, node, hash, &bc->secondaries)
+ {
+ if (value_equal (&foo->val, &bc->cats[i]->values[1], bc->widths[1]))
+ {
+ cidx = foo->idx;
+ break;
+ }
+ }
+ }
+
+ cairo_set_source_rgb (cr,
+ data_colour[cidx].red / 255.0,
+ data_colour[cidx].green / 255.0,
+ data_colour[cidx].blue / 255.0);
+ cairo_fill_preserve (cr);
+
+ cairo_restore (cr);
+ cairo_stroke (cr);
+
+ x_pos += width;
+
+ prev = &bc->cats[i]->values[0];
+ n_last_cat ++;
+ }
+
+ abscissa_label (bc, cr, geom, prev, x_pos, width, n_last_cat);
}
#include "libpspp/cast.h"
#include "libpspp/str.h"
+#include "libpspp/array.h"
#include "output/chart-item-provider.h"
#include "gl/xalloc.h"
+#include "data/variable.h"
+#include "language/stats/freq.h"
+
+
+static int
+compare_category_3way (const void *a_, const void *b_, const void *bc_)
+{
+ const struct category *const*a = a_;
+ const struct category *const*b = b_;
+ const struct barchart *bc = bc_;
+
+ return value_compare_3way (&(*a)->val, &(*b)->val, var_get_width (bc->var[1]));
+}
+
+
+static unsigned int
+hash_freq_2level_ptr (const void *a_, const void *bc_)
+{
+ const struct freq *const *ap = a_;
+ const struct barchart *bc = bc_;
+
+ size_t hash = value_hash (&(*ap)->values[0], bc->widths[0], 0);
+
+ if (bc->n_vars > 1)
+ hash = value_hash (&(*ap)->values[1], bc->widths[1], hash);
+
+ return hash;
+}
+
+
+static int
+compare_freq_2level_ptr_3way (const void *a_, const void *b_, const void *bc_)
+{
+ const struct freq *const *ap = a_;
+ const struct freq *const *bp = b_;
+ const struct barchart *bc = bc_;
+
+ const int level0 = value_compare_3way (&(*ap)->values[0], &(*bp)->values[0], bc->widths[0]);
+
+ if (level0 == 0 && bc->n_vars > 1)
+ return value_compare_3way (&(*ap)->values[1], &(*bp)->values[1], bc->widths[1]);
+
+ return level0;
+}
+
+
/* Creates and returns a chart that will render a barchart with
- the given TITLE and the N_BARS described in BARS. */
+ the given TITLE and the N_BARS described in CATS. */
struct chart_item *
-barchart_create (const char *title, const char *ylabel, const struct slice *bars, int n_bars)
+barchart_create (const struct variable **var, int n_vars,
+ const char *ylabel,
+ struct freq *const *cats, int n_cats)
{
struct barchart *bar;
int i;
- bar = xmalloc (sizeof *bar);
- chart_item_init (&bar->chart_item, &barchart_class, title);
- bar->bars = xnmalloc (n_bars, sizeof *bar->bars);
- bar->largest = 0;
+ const int pidx = 0;
+ const int sidx = 1;
+
+
+ int width = var_get_width (var[pidx]);
+
+ assert (n_vars >= 1);
+
+ bar = xzalloc (sizeof *bar);
+ bar->var = var;
+ bar->n_vars = n_vars;
+ bar->n_nzcats = n_cats;
+ chart_item_init (&bar->chart_item, &barchart_class, var_to_string (var[pidx]));
+
+ bar->largest = -1;
bar->ylabel = strdup (ylabel);
- for (i = 0; i < n_bars; i++)
+
{
- const struct slice *src = &bars[i];
- struct slice *dst = &bar->bars[i];
+ int idx = 0;
+ hmap_init (&bar->primaries);
+
+ /*
+ Iterate the categories and create a hash table of the primary categories.
+ We need to do this to find out how many there are and to cache the labels.
+ */
+ for (i = 0; i < n_cats; i++)
+ {
+ const struct freq *src = cats[i];
+ size_t hash = value_hash (&src->values[pidx], width, 0);
+
+ struct category *foo;
+ int flag = 0;
+ HMAP_FOR_EACH_WITH_HASH (foo, struct category, node, hash, &bar->primaries)
+ {
+ if (value_equal (&foo->val, &src->values[pidx], width))
+ {
+ flag = 1;
+ break;
+ }
+ }
- ds_init_string (&dst->label, &src->label);
- dst->magnitude = src->magnitude;
- if (dst->magnitude > bar->largest)
- bar->largest = dst->magnitude;
+ if (!flag)
+ {
+ struct category *s = xzalloc (sizeof *s);
+ s->idx = idx++;
+ value_init (&s->val, var_get_width (var[pidx]));
+ value_copy (&s->val, &src->values[pidx], var_get_width (var[pidx]));
+ ds_init_empty (&s->label);
+ var_append_value_name (var[pidx], &s->val, &s->label);
+
+ hmap_insert (&bar->primaries, &s->node, hash);
+ }
+ }
+
+ bar->n_pcats = hmap_count (&bar->primaries);
+ }
+
+ if (n_vars > 1)
+ {
+ hmap_init (&bar->secondaries);
+ int idx = 0;
+ /* Iterate the categories, and create a hash table of secondary categories */
+ for (i = 0; i < n_cats; i++)
+ {
+ struct freq *src = cats[i];
+
+ struct category *foo;
+ int flag = 0;
+ size_t hash = value_hash (&src->values[sidx], var_get_width (var[sidx]), 0);
+ HMAP_FOR_EACH_WITH_HASH (foo, struct category, node, hash, &bar->secondaries)
+ {
+ if (value_equal (&foo->val, &src->values[sidx], var_get_width (var[sidx])))
+ {
+ flag = 1;
+ break;
+ }
+ }
+
+ if (!flag)
+ {
+ struct category *s = xzalloc (sizeof *s);
+ s->idx = idx++;
+ value_init (&s->val, var_get_width (var[sidx]));
+ value_copy (&s->val, &src->values[sidx], var_get_width (var[sidx]));
+ ds_init_empty (&s->label);
+ var_append_value_name (var[sidx], &s->val, &s->label);
+
+ hmap_insert (&bar->secondaries, &s->node, hash);
+ bar->ss = xrealloc (bar->ss, idx * sizeof *bar->ss);
+ bar->ss[idx - 1] = s;
+ }
+ }
+
+ int n_category = hmap_count (&bar->secondaries);
+
+ sort (bar->ss, n_category, sizeof *bar->ss,
+ compare_category_3way, bar);
}
- bar->n_bars = n_bars;
+
+
+ /* Deep copy. Not necessary for cmd line, but essential for the GUI,
+ since an expose callback will access these structs which may not
+ exist.
+ */
+ bar->cats = xcalloc (n_cats, sizeof *bar->cats);
+
+ bar->widths[0] = var_get_width (bar->var[0]);
+ if (n_vars > 1)
+ bar->widths[1] = var_get_width (bar->var[1]);
+
+ {
+ struct hmap level2table;
+ hmap_init (&level2table);
+ int x = 0;
+
+ for (i = 0; i < n_cats; i++)
+ {
+ struct freq *c = cats[i];
+
+ struct freq *foo;
+ int flag = 0;
+ size_t hash = hash_freq_2level_ptr (&c, bar);
+ HMAP_FOR_EACH_WITH_HASH (foo, struct freq, node, hash, &level2table)
+ {
+ if (0 == compare_freq_2level_ptr_3way (&foo, &c, bar))
+ {
+ foo->count += c->count;
+
+ if (foo->count > bar->largest)
+ bar->largest = foo->count;
+
+ flag = 1;
+ break;
+ }
+ }
+
+ if (!flag)
+ {
+ struct freq *aggregated_freq = freq_clone (c, n_vars, bar->widths);
+ hmap_insert (&level2table, &aggregated_freq->node, hash);
+
+ if (c->count > bar->largest)
+ bar->largest = aggregated_freq->count;
+
+ bar->cats[x++] = aggregated_freq;
+ }
+ }
+
+ bar->n_nzcats = hmap_count (&level2table);
+ hmap_destroy (&level2table);
+ }
+
+ sort (bar->cats, bar->n_nzcats, sizeof *bar->cats,
+ compare_freq_2level_ptr_3way, bar);
+
return &bar->chart_item;
}
+static void
+destroy_cat_map (struct hmap *m)
+{
+ struct category *foo = NULL;
+ struct category *next = NULL;
+ HMAP_FOR_EACH_SAFE (foo, next, struct category, node, m)
+ {
+ ds_destroy (&foo->label);
+ free (foo);
+ }
+
+ hmap_destroy (m);
+}
+
static void
barchart_destroy (struct chart_item *chart_item)
{
struct barchart *bar = to_barchart (chart_item);
+
int i;
- for (i = 0; i < bar->n_bars; i++)
+ destroy_cat_map (&bar->primaries);
+ if (bar->ss)
+ {
+ destroy_cat_map (&bar->secondaries);
+ }
+
+ for (i = 0; i < bar->n_nzcats; i++)
{
- struct slice *slice = &bar->bars[i];
- ds_destroy (&slice->label);
+ freq_destroy (bar->cats[i], bar->n_vars, bar->widths);
}
+
+ free (bar->cats);
free (bar->ylabel);
- free (bar->bars);
+ free (bar->ss);
free (bar);
}
#define BARCHART_H
#include "libpspp/str.h"
+#include "libpspp/hmap.h"
+#include "data/value.h"
#include "output/chart-item.h"
+
+struct category
+{
+ struct hmap_node node;
+ int idx; /* Unique zero based index */
+ struct string label; /* The label to be displayed for this category */
+ union value val; /* The value of this category */
+};
+
+
struct barchart
{
struct chart_item chart_item;
- struct slice *bars;
- int n_bars;
+
+ /* The categories */
+ struct freq **cats;
+
+ /* The total number of categories (regardless of level) */
+ int n_nzcats;
+
+ /* The number of primary categories */
+ int n_pcats;
+
+ /* The largest count of all the categories */
double largest;
+
+ /* The label for the ordinate (vertical axis) */
char *ylabel;
+
+ /* The variables holding the categorical values */
+ const struct variable **var;
+ int n_vars;
+
+ int widths[2];
+
+ /* A hash table of struct category indexed by VAL */
+ struct hmap primaries;
+
+ /* A hash table of struct category indexed by VAL */
+ struct hmap secondaries;
+
+
+ /* A array of pointers to the members of the above hmap,
+ sorted by VAL */
+ struct category **ss;
};
-struct chart_item *barchart_create (const char *title, const char *ylabel,
- const struct slice *, int n_bars);
+
+struct variable;
+struct freq;
+
+struct chart_item *barchart_create (const struct variable **, int n_vars,
+ const char *ylabel,
+ struct freq *const *, int n_cats);
\f
/* This boilerplate for barchart, a subclass of chart_item, was
autogenerated by mk-class-boilerplate. */
#include "libpspp/cast.h"
#include "libpspp/str.h"
+#include "data/variable.h"
#include "output/chart-item-provider.h"
#include "gl/xalloc.h"
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
+
/* Creates and returns a chart that will render a piechart with
- the given TITLE and the N_SLICES described in SLICES. */
+ the of VAR and the N_SLICES described in SLICES. */
struct chart_item *
-piechart_create (const char *title, const struct slice *slices, int n_slices)
+piechart_create (const struct variable *var, const struct freq *slices, int n_slices)
{
struct piechart *pie;
int i;
pie = xmalloc (sizeof *pie);
- chart_item_init (&pie->chart_item, &piechart_class, title);
+ chart_item_init (&pie->chart_item, &piechart_class, var_to_string (var));
pie->slices = xnmalloc (n_slices, sizeof *pie->slices);
for (i = 0; i < n_slices; i++)
{
- const struct slice *src = &slices[i];
+ const struct freq *src = &slices[i];
struct slice *dst = &pie->slices[i];
- ds_init_string (&dst->label, &src->label);
+ ds_init_empty (&dst->label);
+
+ if ( var_is_value_missing (var, &src->values[0], MV_ANY))
+ ds_assign_cstr (&dst->label, _("*MISSING*"));
+ else
+ var_append_value_name (var, &src->values[0], &dst->label);
/* Chomp any whitespace from the RHS of the label.
Doing this ensures that those labels to the right
of the pie, appear right justified. */
ds_rtrim (&dst->label, ss_cstr (" \t"));
- dst->magnitude = src->magnitude;
+ ds_ltrim (&dst->label, ss_cstr (" \t"));
+ dst->magnitude = src->count;
}
pie->n_slices = n_slices;
return &pie->chart_item;
#include "libpspp/str.h"
#include "output/chart-item.h"
+#include "language/stats/freq.h"
struct piechart
{
double magnitude;
};
-struct chart_item *piechart_create (const char *title,
- const struct slice *, int n_slices);
+struct variable;
+
+struct chart_item *piechart_create (const struct variable *var,
+ const struct freq *, int n_slices);
\f
/* This boilerplate for piechart, a subclass of chart_item, was
autogenerated by mk-class-boilerplate. */
,,y Dependent,.184,.019,7.890,. @&t@
])
AT_CLEANUP
+
+
+
+AT_SETUP([CROSSTABS barchart])
+AT_DATA([bc.sps], [dnl
+SET FORMAT=F8.3.
+
+DATA LIST LIST NOTABLE /x (a20) y (f8) z (f8) w (f8) .
+BEGIN DATA.
+This 1 0 416
+That 2 0 121
+Other 2 0 335
+This 2 0 231
+That 3 0 112
+Other 4 0 130
+This 1 1 160
+That 2 1 211
+Other 2 1 352
+This 2 1 212
+That 3 1 121
+Other 4 1 101
+END DATA.
+
+WEIGHT BY w.
+
+CROSSTABS
+ /table x BY y BY z
+ /table x BY y
+ /barchart.
+])
+
+AT_CHECK([pspp -O format=txt -o xxx bc.sps], [0], [ignore])
+
+AT_CHECK([test -e xxx-1.png], [0], [ignore])
+AT_CHECK([test -e xxx-2.png], [0], [ignore])
+
+AT_CHECK([diff xxx-1.png xxx-2.png], [0], [ignore])
+
+AT_CLEANUP
#include <stdlib.h>
#include "math/chart-geometry.h"
#include "math/decimal.h"
-
+#include "libpspp/compiler.h"
const double in[20] =
{
};
int
-main ()
+main (int argc UNUSED, char **argv UNUSED)
{
int i;
for (i = 0; i < 20; ++i)
#include <assert.h>
#include <string.h>
+#include "libpspp/compiler.h"
+
#include "math/decimal.h"
+#include "math/chart-geometry.h"
#include <limits.h>
#include <float.h>
#include <math.h>
-void
+#if 0
+static void
dump_scale (const struct decimal *low, const struct decimal *interval, int n_ticks)
{
int i;
decimal_add (&tick, interval);
}
}
+#endif
-void
+static void
test_range (double low, double high)
{
int n_ticks = 0;
struct decimal interval;
struct decimal lower;
-
chart_get_scale (high, low,
&lower, &interval, &n_ticks);
int
-main (int argc, char **argv)
+main (int argc UNUSED, char **argv UNUSED)
{
test_range (0.2, 11);
test_range (-0.2, 11);
#include <assert.h>
#include <string.h>
+#include "libpspp/compiler.h"
#include "math/decimal.h"
#include <limits.h>
#include <float.h>
This function is used purely for testing, and need not and is not intended
to be efficient.
*/
-char *
+static char *
canonicalise_string (const char *s)
{
char *out;
/* Tests both the decimal_to_string function, and the decimal_input_from_string
function */
-void
+static void
test_run (const char *input)
{
struct decimal test;
}
-void
+static void
test_can (const char *in, const char *soll)
{
char *ist = canonicalise_string (in);
}
-void
+#if 0
+static void
dump_scale (const struct decimal *low, const struct decimal *interval, int n_ticks)
{
int i;
decimal_add (&tick, interval);
}
}
+#endif
-void
+static void
test_ceil (double x)
{
struct decimal dx;
assert (act == expected);
}
-void
+static void
test_floor (double x)
{
struct decimal dx;
}
-void
+static void
test_addition (const struct decimal *one_, const struct decimal *two)
{
struct decimal one = *one_;
- double d1 = decimal_to_double (&one);
- double d2 = decimal_to_double (two);
decimal_add (&one, two);
}
-void
+static void
test_multiplication (const struct decimal *d, int m)
{
char b1[256];
int
-main (int argc, char **argv)
+main (int argc UNUSED, char **argv UNUSED)
{
/* Test that our canonicalise function works for all corner cases we
can think of. */
AT_CHECK([pspp -o pspp.txt xxx.sps], [0], [ignore])
AT_CLEANUP
+
+
+
+AT_SETUP([CROSSTABS charts])
+AT_DATA([xxx.sps],[
+DATA LIST LIST /nationality (A10) religion (A20) gender (A8).
+BEGIN DATA.
+Australian Sikh Male
+Australian Sikh Male
+Australian Sikh Male
+Australian Sikh Male
+British Zoroastrian Female
+British Buddist Female
+British Buddist Female
+British Zoroastrian Female
+German Muslim Male
+German Christian Male
+German Christian Female
+German Christian Male
+German Zoroastrian Female
+German Sikh Female
+German Muslim Female
+German Pastafarian Female
+German "Jedi Knight" Female
+Belgian Sikh Male
+French Muslim Male
+French Muslim Male
+French Christian Male
+END DATA.
+
+
+CROSSTABS
+ /tables = nationality by religion by gender
+ /tables = nationality by religion
+ /tables = religion by gender
+ /tables = nationality by religion by gender
+ /barchart.
+])
+
+
+AT_CHECK([pspp -o pspp.txt xxx.sps], [0], [ignore])
+
+AT_CLEANUP