oneway: Fix descriptives for multiple variables
[pspp] / src / language / stats / glm.q
index 07ee7ab53166fcbd917a79933f18eaf9761b8ea8..4de00e3bb9450ea90848e85ead17421771f8e58e 100644 (file)
@@ -23,7 +23,6 @@
 #include <stdlib.h>
 
 #include <data/case.h>
-#include <data/category.h>
 #include <data/casegrouper.h>
 #include <data/casereader.h>
 #include <data/dictionary.h>
 #include <language/data-io/file-handle.h>
 #include <language/lexer/lexer.h>
 #include <libpspp/compiler.h>
-#include <libpspp/hash.h>
 #include <libpspp/message.h>
-#include <math/covariance-matrix.h>
-#include <math/coefficient.h>
+#include <math/covariance.h>
+#include <math/categoricals.h>
 #include <math/linreg.h>
 #include <math/moments.h>
-#include <output/table.h>
+#include <output/tab.h>
 
 #include "xalloc.h"
 #include "gettext.h"
@@ -53,6 +51,7 @@
 /* (specification)
    "GLM" (glm_):
    *dependent=custom;
+   design=custom;
    by=varlist;
    with=varlist.
 */
@@ -60,6 +59,7 @@
 /* (functions) */
 static struct cmd_glm cmd;
 
+
 /*
   Moments for each of the variables used.
  */
@@ -83,19 +83,63 @@ static const struct variable **v_dependent;
  */
 static size_t n_dependent;
 
-#if 0
-/*
-  Return value for the procedure.
- */
-static int pspp_glm_rc = CMD_SUCCESS;
-#else
+size_t n_inter; /* Number of interactions. */
+size_t n_members; /* Number of memebr variables in an interaction. */ 
+
+struct interaction_variable **interactions;
+
 int cmd_glm (struct lexer *lexer, struct dataset *ds);
-#endif
 
 static bool run_glm (struct casereader *,
                     struct cmd_glm *,
                     const struct dataset *);
-
+/*
+  If the DESIGN subcommand was not specified, specify all possible
+  two-way interactions.
+ */
+static void
+check_interactions (struct dataset *ds, struct cmd_glm *cmd)
+{
+  size_t i;
+  size_t j;
+  size_t k = 0;
+  struct variable **interaction_vars;
+
+  /* 
+     User did not specify the design matrix, so we 
+     specify it here.
+  */
+  n_inter = (cmd->n_by + cmd->n_with) * (cmd->n_by + cmd->n_with - 1) / 2;
+  interactions = xnmalloc (n_inter, sizeof (*interactions));
+  interaction_vars = xnmalloc (2, sizeof (*interaction_vars));
+  for (i = 0; i < cmd->n_by; i++)
+    {
+      for (j = i + 1; j < cmd->n_by; j++)
+       {
+         interaction_vars[0] = cmd->v_by[i];
+         interaction_vars[1] = cmd->v_by[j];
+         interactions[k] = interaction_variable_create (interaction_vars, 2);
+         k++;
+       }
+      for (j = 0; j < cmd->n_with; j++)
+       {
+         interaction_vars[0] = cmd->v_by[i];
+         interaction_vars[1] = cmd->v_with[j];
+         interactions[k] = interaction_variable_create (interaction_vars, 2);
+         k++;
+       }
+    }
+  for (i = 0; i < cmd->n_with; i++)
+    {
+      for (j = i + 1; j < cmd->n_with; j++)
+       {
+         interaction_vars[0] = cmd->v_with[i];
+         interaction_vars[1] = cmd->v_with[j];
+         interactions[k] = interaction_variable_create (interaction_vars, 2);
+         k++;
+       }
+    }
+}
 int
 cmd_glm (struct lexer *lexer, struct dataset *ds)
 {
@@ -107,7 +151,11 @@ cmd_glm (struct lexer *lexer, struct dataset *ds)
   if (!parse_glm (lexer, ds, &cmd, NULL))
     return CMD_FAILURE;
 
-  /* Data pass. */
+  if (!lex_match_id (lexer, "DESIGN"))
+    {
+      check_interactions (ds, &cmd);
+    }
+   /* Data pass. */
   grouper = casegrouper_create_splits (proc_open (ds), dataset_dict (ds));
   while (casegrouper_get_next_group (grouper, &group))
     {
@@ -120,24 +168,80 @@ cmd_glm (struct lexer *lexer, struct dataset *ds)
   return ok ? CMD_SUCCESS : CMD_FAILURE;
 }
 
+static int
+parse_interactions (struct lexer *lexer, const struct variable **interaction_vars, int n_members,
+                   int max_members, struct dataset *ds)
+{
+  if (lex_match (lexer, '*'))
+    {
+      if (n_members > max_members)
+       {
+         max_members *= 2;
+         xnrealloc (interaction_vars, max_members, sizeof (*interaction_vars));
+       }
+      interaction_vars[n_members] = parse_variable (lexer, dataset_dict (ds));
+      parse_interactions (lexer, interaction_vars, n_members++, max_members, ds);
+    }
+  return n_members;
+}
+/* Parser for the design subcommand. */
+static int
+glm_custom_design (struct lexer *lexer, struct dataset *ds,
+                  struct cmd_glm *cmd UNUSED, void *aux UNUSED)
+{
+  size_t n_allocated = 2;
+  size_t n_members;
+  struct variable **interaction_vars;
+  struct variable *this_var;
+
+  interactions = xnmalloc (n_allocated, sizeof (*interactions));
+  n_inter = 1;
+  while (lex_token (lexer) != T_STOP && lex_token (lexer) != '.')
+    {
+      this_var = parse_variable (lexer, dataset_dict (ds));
+      if (lex_match (lexer, '('))
+       {
+         lex_force_match (lexer, ')');
+       }
+      else if (lex_match (lexer, '*'))
+       {
+         interaction_vars = xnmalloc (2 * n_inter, sizeof (*interaction_vars));
+         n_members = parse_interactions (lexer, interaction_vars, 1, 2 * n_inter, ds);
+         if (n_allocated < n_inter)
+           {
+             n_allocated *= 2;
+             xnrealloc (interactions, n_allocated, sizeof (*interactions));
+           }
+         interactions [n_inter - 1] = 
+           interaction_variable_create (interaction_vars, n_members);
+         n_inter++;
+         free (interaction_vars);
+       }
+    }
+  return 1;
+}
 /* Parser for the dependent sub command */
 static int
 glm_custom_dependent (struct lexer *lexer, struct dataset *ds,
                      struct cmd_glm *cmd UNUSED, void *aux UNUSED)
 {
   const struct dictionary *dict = dataset_dict (ds);
+  size_t i;
 
   if ((lex_token (lexer) != T_ID
        || dict_lookup_var (dict, lex_tokid (lexer)) == NULL)
       && lex_token (lexer) != T_ALL)
     return 2;
 
-  if (!parse_variables_const
-      (lexer, dict, &v_dependent, &n_dependent, PV_NONE))
+  if (!parse_variables_const (lexer, dict, &v_dependent, &n_dependent, PV_NONE))
     {
       free (v_dependent);
       return 0;
     }
+  for (i = 0; i < n_dependent; i++)
+    {
+      assert (var_is_numeric (v_dependent[i]));
+    }
   assert (n_dependent);
   if (n_dependent > 1)
     msg (SE, _("Multivariate GLM not yet supported"));
@@ -146,29 +250,13 @@ glm_custom_dependent (struct lexer *lexer, struct dataset *ds,
   return 1;
 }
 
-/*
-  COV is the covariance matrix for variables included in the
-  model. That means the dependent variable is in there, too.
- */
-static void
-coeff_init (pspp_linreg_cache * c, const struct design_matrix *cov)
-{
-  c->coeff = xnmalloc (cov->m->size2, sizeof (*c->coeff));
-  c->n_coeffs = cov->m->size2 - 1;
-  pspp_coeff_init (c->coeff, cov);
-}
-
-
-static pspp_linreg_cache *
-fit_model (const struct covariance_matrix *cov,
+static linreg *
+fit_model (const struct covariance *cov,
           const struct variable *dep_var, 
           const struct variable ** indep_vars, 
           size_t n_data, size_t n_indep)
 {
-  pspp_linreg_cache *result = NULL;
-  result = pspp_linreg_cache_alloc (dep_var, indep_vars, n_data, n_indep);
-  coeff_init (result, covariance_to_design (cov));
-  pspp_linreg_with_cov (cov, result);  
+  linreg *result = NULL;
   
   return result;
 }
@@ -179,17 +267,18 @@ run_glm (struct casereader *input,
         const struct dataset *ds)
 {
   casenumber row;
-  const struct variable **indep_vars;
-  const struct variable **all_vars;
+  const struct variable **numerics = NULL;
+  const struct variable **categoricals = NULL;
   int n_indep = 0;
-  pspp_linreg_cache *model = NULL; 
+  linreg *model = NULL; 
   pspp_linreg_opts lopts;
   struct ccase *c;
   size_t i;
-  size_t n_all_vars;
   size_t n_data;               /* Number of valid cases. */
+  size_t n_categoricals = 0;
+  size_t n_numerics;
   struct casereader *reader;
-  struct covariance_matrix *cov;
+  struct covariance *cov;
 
   c = casereader_peek (input, 0);
   if (c == NULL)
@@ -209,59 +298,100 @@ run_glm (struct casereader *input,
   lopts.get_depvar_mean_std = 1;
 
   lopts.get_indep_mean_std = xnmalloc (n_dependent, sizeof (int));
-  indep_vars = xnmalloc (cmd->n_by, sizeof *indep_vars);
-  n_all_vars = cmd->n_by + n_dependent;
-  all_vars = xnmalloc (n_all_vars, sizeof *all_vars);
 
-  for (i = 0; i < n_dependent; i++)
+
+  n_numerics = n_dependent;
+  for (i = 0; i < cmd->n_with; i++)
     {
-      all_vars[i] = v_dependent[i];
+      if (var_is_alpha (cmd->v_with[i]))
+       {
+         n_categoricals++;
+       }
+      else
+       {
+         n_numerics++;
+       }
     }
   for (i = 0; i < cmd->n_by; i++)
     {
-      indep_vars[i] = cmd->v_by[i];
-      all_vars[i + n_dependent] = cmd->v_by[i];
+      if (var_is_alpha (cmd->v_by[i]))
+       {
+         n_categoricals++;
+       }
+      else
+       {
+         n_numerics++;
+       }
     }
-  n_indep = cmd->n_by;
-
-  reader = casereader_clone (input);
-  reader = casereader_create_filter_missing (reader, indep_vars, n_indep,
-                                            MV_ANY, NULL, NULL);
-  reader = casereader_create_filter_missing (reader, v_dependent, 1,
-                                            MV_ANY, NULL, NULL);
-
-  if (n_indep > 0)
+  numerics = xnmalloc (n_numerics, sizeof *numerics);
+  categoricals = xnmalloc (n_categoricals, sizeof (*categoricals));
+  size_t j = 0;
+  size_t k = 0;
+  for (i = 0; i < cmd->n_by; i++)
     {
-      for (i = 0; i < n_all_vars; i++)
-       if (var_is_alpha (all_vars[i]))
-         cat_stored_values_create (all_vars[i]);
-      
-      cov = covariance_matrix_init (n_all_vars, all_vars, ONE_PASS, PAIRWISE, MV_ANY);
-      reader = casereader_create_counter (reader, &row, -1);
-      for (; (c = casereader_read (reader)) != NULL; case_unref (c))
+      if (var_is_alpha (cmd->v_by[i]))
        {
-         /* 
-            Accumulate the covariance matrix.
-         */
-         covariance_matrix_accumulate (cov, c);
-         n_data++;
+         categoricals[j] = cmd->v_by[i];
+         j++;
        }
-      covariance_matrix_compute (cov);
-
-      for (i = 0; i < n_dependent; i++)
+      else
+       {
+         numerics[k] = cmd->v_by[i];
+         k++;
+       }
+    }
+  for (i = 0; i < cmd->n_with; i++)
+    {
+      if (var_is_alpha (cmd->v_with[i]))
        {
-         model = fit_model (cov, v_dependent[i], indep_vars, n_data, n_indep);
-         pspp_linreg_cache_free (model);
+         categoricals[j] = cmd->v_with[i];
+         j++;
        }
+      else
+       {
+         numerics[k] = cmd->v_with[i];
+         k++;
+       }
+    }
+  for (i = 0; i < n_dependent; i++)
+    {
+      numerics[k] = v_dependent[i];
+      k++;
+    }
+
+  struct categoricals *cats =
+    categoricals_create (categoricals, n_categoricals,
+                        NULL, MV_NEVER,
+                        NULL, NULL, NULL, NULL);
+
+  cov = covariance_2pass_create (n_numerics, numerics,
+                                cats,
+                                NULL, MV_NEVER);
 
-      casereader_destroy (reader);
-      covariance_matrix_destroy (cov);
+  reader = casereader_clone (input);
+  reader = casereader_create_filter_missing (reader, numerics, n_numerics,
+                                            MV_ANY, NULL, NULL);
+  reader = casereader_create_filter_missing (reader, categoricals, n_categoricals,
+                                            MV_ANY, NULL, NULL);
+  struct casereader *r = casereader_clone (reader);
+
+  reader = casereader_create_counter (reader, &row, -1);
+  
+  for (; (c = casereader_read (reader)) != NULL; case_unref (c))
+    {
+      covariance_accumulate_pass1 (cov, c);
     }
-  else
+  for (; (c = casereader_read (r)) != NULL; case_unref (c))
     {
-      msg (SE, gettext ("No valid data found. This command was skipped."));
+      covariance_accumulate_pass2 (cov, c);
     }
-  free (indep_vars);
+
+  covariance_destroy (cov);
+  casereader_destroy (reader);
+  casereader_destroy (r);
+  
+  free (numerics);
+  free (categoricals);
   free (lopts.get_indep_mean_std);
   casereader_destroy (input);