Added implementation for the Cochran Q test
authorJohn Darrington <john@darrington.wattle.id.au>
Thu, 28 Oct 2010 13:24:05 +0000 (15:24 +0200)
committerJohn Darrington <john@darrington.wattle.id.au>
Thu, 28 Oct 2010 13:24:05 +0000 (15:24 +0200)
src/language/stats/automake.mk
src/language/stats/cochran.c [new file with mode: 0644]
src/language/stats/cochran.h [new file with mode: 0644]
src/language/stats/npar.c

index 32a5c228f2aa965a0d818c1ac0c0190522c16ff6..01bdf8861865d34b42e3e9cfd88d82e2279d7b1c 100644 (file)
@@ -15,6 +15,7 @@ language_stats_sources = \
        src/language/stats/autorecode.c \
        src/language/stats/binomial.c src/language/stats/binomial.h \
        src/language/stats/chisquare.c src/language/stats/chisquare.h \
+       src/language/stats/cochran.c src/language/stats/cochran.h \
        src/language/stats/correlations.c \
        src/language/stats/descriptives.c \
        src/language/stats/factor.c \
diff --git a/src/language/stats/cochran.c b/src/language/stats/cochran.c
new file mode 100644 (file)
index 0000000..3b6af22
--- /dev/null
@@ -0,0 +1,244 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2010 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
+   the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include "cochran.h"
+#include "npar.h"
+#include <stdbool.h>
+#include <libpspp/cast.h>
+#include <libpspp/message.h>
+#include <libpspp/misc.h>
+
+#include <data/procedure.h>
+#include <data/dictionary.h>
+#include <data/variable.h>
+#include <data/val-type.h>
+#include <data/casereader.h>
+
+#include <gsl/gsl_cdf.h>
+
+#include <data/format.h>
+#include <output/tab.h>
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+
+struct cochran
+{
+  double success;
+  double failure;
+
+  double *hits;
+  double *misses;
+
+  const struct dictionary *dict;
+  double cc;
+  double df;
+  double q;
+};
+
+static void show_freqs_box (const struct one_sample_test *ost, const struct cochran *ch);
+static void show_sig_box (const struct cochran *ch);
+
+void
+cochran_execute (const struct dataset *ds,
+             struct casereader *input,
+             enum mv_class exclude,
+             const struct npar_test *test,
+             bool exact UNUSED, double timer UNUSED)
+{
+  struct one_sample_test *ct = UP_CAST (test, struct one_sample_test, parent);
+  int v;
+  struct cochran ch;
+  const struct dictionary *dict = dataset_dict (ds);
+  const struct variable *weight = dict_get_weight (dict);
+
+  struct ccase *c;
+  double rowsq = 0;
+  ch.cc = 0.0;
+  ch.dict = dict;
+  ch.success = SYSMIS;
+  ch.failure = SYSMIS;
+  ch.hits = xcalloc (ct->n_vars, sizeof *ch.hits);
+  ch.misses = xcalloc (ct->n_vars, sizeof *ch.misses);
+
+  for (; (c = casereader_read (input)); case_unref (c))
+    {
+      double case_hits = 0.0;
+      const double w = weight ? case_data (c, weight)->f: 1.0;
+      for (v = 0; v < ct->n_vars; ++v)
+       {
+         const struct variable *var = ct->vars[v];
+         const union value *val = case_data (c, var);
+
+         if ( var_is_value_missing (var, val, exclude))
+           continue;
+
+         if ( ch.success == SYSMIS)
+           {
+             ch.success = val->f;
+           }
+         else if (ch.failure == SYSMIS && val->f != ch.success)
+           {
+             ch.failure = val->f;
+           }
+         if ( ch.success == val->f)
+           {
+             ch.hits[v] += w;
+             case_hits += w;
+           }
+         else if ( ch.failure == val->f)
+           {
+             ch.misses[v] += w;
+           }
+         else
+           {
+             msg (MW, _("More than two values encountered.  Cochran Q test will not be run."));
+             goto finish;
+           }
+       }
+      ch.cc += w;
+      rowsq += pow2 (case_hits);
+    }
+  casereader_destroy (input);
+  
+  {
+    double c_l = 0;
+    double c_l2 = 0;
+    for (v = 0; v < ct->n_vars; ++v)
+      {
+       c_l += ch.hits[v];
+       c_l2 += pow2 (ch.hits[v]);
+      }
+
+    ch.q = ct->n_vars * c_l2;
+    ch.q -= pow2 (c_l);
+    ch.q *= ct->n_vars - 1;
+
+    ch.q /= ct->n_vars * c_l - rowsq;
+  
+    ch.df = ct->n_vars - 1;
+  }
+
+  show_freqs_box (ct, &ch);
+  show_sig_box (&ch);
+
+ finish:
+
+  free (ch.hits);
+  free (ch.misses);
+}
+
+static void
+show_freqs_box (const struct one_sample_test *ost, const struct cochran *ct)
+{
+  int i;
+  const struct variable *weight = dict_get_weight (ct->dict);
+  const struct fmt_spec *wfmt = weight ? var_get_print_format (weight) : &F_8_0;
+
+  const int row_headers = 1;
+  const int column_headers = 2;
+  struct tab_table *table =
+    tab_create (row_headers + 2, column_headers + ost->n_vars);
+
+  tab_headers (table, row_headers, 0, column_headers, 0);
+
+  tab_title (table, _("Frequencies"));
+
+  /* Vertical lines inside the box */
+  tab_box (table, 1, 0, -1, TAL_1,
+          row_headers, 0, tab_nc (table) - 1, tab_nr (table) - 1 );
+
+  /* Box around the table */
+  tab_box (table, TAL_2, TAL_2, -1, -1,
+          0,  0, tab_nc (table) - 1, tab_nr (table) - 1 );
+
+  tab_joint_text (table, 1, 0, 2, 0,
+                 TAT_TITLE | TAB_CENTER, _("Value"));
+
+  tab_text_format (table, 1, 1, 0, _("Success (%g)"), ct->success);
+  tab_text_format (table, 2, 1, 0, _("Failure (%g)"), ct->failure);
+
+  tab_hline (table, TAL_2, 0, tab_nc (table) - 1, column_headers);
+  tab_vline (table, TAL_2, row_headers, 0, tab_nr (table) - 1);
+
+  for (i = 0 ; i < ost->n_vars ; ++i)
+    {
+      tab_text (table, 0, column_headers + i,
+               TAB_LEFT, var_to_string (ost->vars[i]));
+
+      tab_double (table, 1, column_headers + i, 0,
+                 ct->hits[i], wfmt);
+
+      tab_double (table, 2, column_headers + i, 0,
+                 ct->misses[i], wfmt);
+    }
+
+  tab_submit (table);
+}
+
+
+
+static void
+show_sig_box (const struct cochran *ch)
+{
+  const struct variable *weight = dict_get_weight (ch->dict);
+  const struct fmt_spec *wfmt = weight ? var_get_print_format (weight) : &F_8_0;
+
+  const int row_headers = 1;
+  const int column_headers = 0;
+  struct tab_table *table =
+    tab_create (row_headers + 1, column_headers + 4);
+
+  tab_headers (table, row_headers, 0, column_headers, 0);
+
+  tab_title (table, _("Test Statistics"));
+
+  tab_text (table,  0, column_headers,
+           TAT_TITLE | TAB_LEFT , _("N"));
+
+  tab_text (table,  0, 1 + column_headers,
+           TAT_TITLE | TAB_LEFT , _("Cochran's Q"));
+
+  tab_text (table,  0, 2 + column_headers,
+           TAT_TITLE | TAB_LEFT, _("df"));
+
+  tab_text (table,  0, 3 + column_headers,
+           TAT_TITLE | TAB_LEFT, _("Asymp. Sig."));
+
+  /* Box around the table */
+  tab_box (table, TAL_2, TAL_2, -1, -1,
+          0,  0, tab_nc (table) - 1, tab_nr (table) - 1 );
+
+  tab_hline (table, TAL_2, 0, tab_nc (table) -1, column_headers);
+  tab_vline (table, TAL_2, row_headers, 0, tab_nr (table) - 1);
+
+  tab_double (table, 1, column_headers, 
+             0, ch->cc, wfmt);
+
+  tab_double (table, 1, column_headers + 1, 
+             0, ch->q, 0);
+
+  tab_double (table, 1, column_headers + 2, 
+             0, ch->df, &F_8_0);
+
+  tab_double (table, 1, column_headers + 3, 
+             0, gsl_cdf_chisq_Q (ch->q, ch->df), 
+             0);
+
+  tab_submit (table);
+}
diff --git a/src/language/stats/cochran.h b/src/language/stats/cochran.h
new file mode 100644 (file)
index 0000000..1ea7505
--- /dev/null
@@ -0,0 +1,34 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2010 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
+   the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>. */
+
+#if !cochran_h
+#define cochran_h 1
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <language/stats/npar.h>
+
+
+
+void cochran_execute (const struct dataset *ds,
+                     struct casereader *input,
+                     enum mv_class exclude,
+                     const struct npar_test *test,
+                     bool,
+                     double);
+
+
+#endif
index 053f021d3f20e2df6f9d9a47428e36b984923837..6bac48de913974ef42635aa78d7c4e2a427dc684 100644 (file)
@@ -46,6 +46,7 @@
 #include <language/lexer/value-parser.h>
 #include <language/stats/binomial.h>
 #include <language/stats/chisquare.h>
+#include <language/stats/cochran.h>
 #include <language/stats/runs.h>
 #include <language/stats/friedman.h>
 #include <language/stats/kruskal-wallis.h>
@@ -79,6 +80,7 @@ struct cmd_npar_tests
     /* Count variables indicating how many
        of the subcommands have been given. */
     int chisquare;
+    int cochran;
     int binomial;
     int wilcoxon;
     int sign;
@@ -123,6 +125,7 @@ static int npar_chisquare (struct lexer *, struct dataset *, struct npar_specs *
 static int npar_binomial (struct lexer *, struct dataset *,  struct npar_specs *);
 static int npar_runs (struct lexer *, struct dataset *, struct npar_specs *);
 static int npar_friedman (struct lexer *, struct dataset *, struct npar_specs *);
+static int npar_cochran (struct lexer *, struct dataset *, struct npar_specs *);
 static int npar_wilcoxon (struct lexer *, struct dataset *, struct npar_specs *);
 static int npar_sign (struct lexer *, struct dataset *, struct npar_specs *);
 static int npar_kruskal_wallis (struct lexer *, struct dataset *, struct npar_specs *);
@@ -139,6 +142,7 @@ parse_npar_tests (struct lexer *lexer, struct dataset *ds, struct cmd_npar_tests
 {
   npt->binomial = 0;
   npt->chisquare = 0;
+  npt->cochran = 0;
   npt->friedman = 0;
   npt->kruskal_wallis = 0;
   npt->mann_whitney = 0;
@@ -152,7 +156,23 @@ parse_npar_tests (struct lexer *lexer, struct dataset *ds, struct cmd_npar_tests
   memset (npt->a_statistics, 0, sizeof npt->a_statistics);
   for (;;)
     {
-      if (lex_match_hyphenated_word (lexer, "FRIEDMAN"))
+      if (lex_match_hyphenated_word (lexer, "COCHRAN"))
+       {
+          npt->cochran++;
+          switch (npar_cochran (lexer, ds, nps))
+            {
+            case 0:
+              goto lossage;
+            case 1:
+              break;
+            case 2:
+              lex_error (lexer, NULL);
+              goto lossage;
+            default:
+              NOT_REACHED ();
+            }
+       }
+      else if (lex_match_hyphenated_word (lexer, "FRIEDMAN"))
        {
           npt->friedman++;
           switch (npar_friedman (lexer, ds, nps))
@@ -611,6 +631,35 @@ npar_friedman (struct lexer *lexer, struct dataset *ds,
   return 1;
 }
 
+static int
+npar_cochran (struct lexer *lexer, struct dataset *ds,
+              struct npar_specs *specs)
+{
+  struct one_sample_test *ft = pool_alloc (specs->pool, sizeof (*ft)); 
+  struct npar_test *nt = &ft->parent;
+
+  nt->execute = cochran_execute;
+  nt->insert_variables = one_sample_insert_variables;
+
+  lex_match (lexer, '=');
+
+  if (!parse_variables_const_pool (lexer, specs->pool, dataset_dict (ds),
+                                  &ft->vars, &ft->n_vars,
+                                  PV_NO_SCRATCH | PV_NO_DUPLICATE | PV_NUMERIC))
+    {
+      return 2;
+    }
+
+  specs->n_tests++;
+  specs->test = pool_realloc (specs->pool,
+                             specs->test,
+                             sizeof (*specs->test) * specs->n_tests);
+
+  specs->test[specs->n_tests - 1] = nt;
+
+  return 1;
+}
+
 
 static int
 npar_chisquare (struct lexer *lexer, struct dataset *ds,