Merge branch 'master' of ssh://jmd@git.sv.gnu.org/srv/git/pspp
authorJohn Darrington <john@darrington.wattle.id.au>
Sat, 1 Nov 2008 05:18:26 +0000 (14:18 +0900)
committerJohn Darrington <john@darrington.wattle.id.au>
Sat, 1 Nov 2008 05:18:26 +0000 (14:18 +0900)
13 files changed:
doc/utilities.texi
src/data/settings.c
src/language/dictionary/value-labels.c
src/language/stats/glm.q
src/language/tests/.gitignore [new file with mode: 0644]
src/libpspp/i18n.c
src/libpspp/i18n.h
src/math/covariance-matrix.c
src/math/covariance-matrix.h
src/math/design-matrix.c
src/math/design-matrix.h
tests/automake.mk
tests/expressions/valuelabel.sh

index 35ea20e2131d42cb4e660db4d29973a2a7408cea..d825b9abc4a5e0e6a676ecd2c258bbb5f7d06e77 100644 (file)
@@ -414,10 +414,13 @@ default.  Any real value may be assigned.
 
 @item DECIMAL
 @anchor{SET DECIMAL}
-The default DOT setting causes the decimal point character to be
-@samp{.} and the grouping character to be @samp{,}.  A setting of COMMA
+This value may be set to DOT or COMMA.
+Setting it to DOT causes the decimal point character to be
+@samp{.} and the grouping character to be @samp{,}.
+Setting it to COMMA
 causes the decimal point character to be @samp{,} and the grouping
 character to be @samp{.}.
+The default value is determined from the system locale.
 
 @item FORMAT
 Allows the default numeric input/output format to be specified.  The
index a87e3eff800f78a552232125fbec7af7ebe67527..ad509fb028059a6c6d3af86445a1bdb9e744ce08 100644 (file)
@@ -149,6 +149,8 @@ settings_init (int *width, int *length)
   settings_set_epoch (-1);
   i18n_init ();
   the_settings.styles = fmt_create ();
+
+  settings_set_decimal_char (get_system_decimal ());
 }
 
 void
index 4b7c03961783c0109b30c8e5a32bcb13b43658b3..39f544ec4b917fa2b517129a06ad6c252d5cc171 100644 (file)
@@ -163,11 +163,9 @@ get_label (struct lexer *lexer, struct variable **vars, size_t var_cnt)
        {
          if (!lex_is_number (lexer))
            {
-             lex_error (lexer, _("expecting integer"));
+             lex_error (lexer, _("expecting number"));
              return 0;
            }
-         if (!lex_is_integer (lexer))
-           msg (SW, _("Value label `%g' is not integer."), lex_tokval (lexer));
          value.f = lex_tokval (lexer);
        }
       lex_get (lexer);
index 39e4f1c5d207ab57c0d9735361bfc66cd36242fa..024b5429c345abc1a03bdbb3f29b771b0686d940 100644 (file)
@@ -37,6 +37,7 @@
 #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>
@@ -47,8 +48,6 @@
 #include "xalloc.h"
 #include "gettext.h"
 
-#define GLM_LARGE_DATA 10000
-
 /* (headers) */
 
 /* (specification)
@@ -152,7 +151,7 @@ glm_custom_dependent (struct lexer *lexer, struct dataset *ds,
   model. That means the dependent variable is in there, too.
  */
 static void
-coeff_init (pspp_linreg_cache * c, struct design_matrix *cov)
+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;
@@ -213,26 +212,39 @@ data_pass_one (struct casereader *input,
   return n_data;
 }
 
+static pspp_linreg_cache *
+fit_model (const struct design_matrix *cov, const struct moments1 **mom, 
+          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, cov);
+  pspp_linreg_with_cov (cov, result);  
+  
+  return result;
+}
+
 static bool
 run_glm (struct casereader *input,
         struct cmd_glm *cmd,
         const struct dataset *ds)
 {
-  pspp_linreg_cache *model = NULL; 
-  size_t i;
-  size_t j;
-  int n_indep = 0;
-  struct ccase c;
+  casenumber row;
   const struct variable **indep_vars;
   const struct variable **all_vars;
-  struct design_matrix *X;
-  struct moments_var **mom;
-  struct casereader *reader;
-  casenumber row;
+  int n_indep = 0;
+  pspp_linreg_cache *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. */
-
-  pspp_linreg_opts lopts;
+  struct casereader *reader;
+  struct design_matrix *cov;
+  struct hsh_table *cov_hash;
+  struct moments1 **mom;
 
   if (!casereader_peek (input, 0, &c))
     {
@@ -266,69 +278,47 @@ run_glm (struct casereader *input,
     }
   n_indep = cmd->n_by;
   mom = xnmalloc (n_all_vars, sizeof (*mom));
-
+  for (i = 0; i < n_all_vars; i++)
+    mom[i] = moments1_create (MOMENT_MEAN);
 
   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);
-  n_data = data_pass_one (casereader_clone (reader),
-                         (const struct variable **) all_vars, n_all_vars,
-                         mom);
 
-  if ((n_data > 0) && (n_indep > 0))
+  if (n_indep > 0)
     {
       for (i = 0; i < n_all_vars; i++)
        if (var_is_alpha (all_vars[i]))
          cat_stored_values_create (all_vars[i]);
       
-      X =
-       covariance_matrix_create (n_all_vars,
-                                 (const struct variable **) all_vars);
+      cov_hash = covariance_hsh_create (n_all_vars);
       reader = casereader_create_counter (reader, &row, -1);
       for (; casereader_read (reader, &c); case_destroy (&c))
        {
          /* 
             Accumulate the covariance matrix.
-          */
-         for (i = 0; i < n_all_vars; ++i)
-           {
-             const struct variable *v = all_vars[i];
-             const union value *val_v = case_data (&c, v);
-             if (var_is_alpha (all_vars[i]))
-               cat_value_update (all_vars[i], val_v);
-             for (j = i; j < n_all_vars; j++)
-               {
-                 const struct variable *w = all_vars[j];
-                 const union value *val_w = case_data (&c, w);
-                 covariance_pass_two (X, *mom[i]->mean, *mom[j]->mean,
-                                      (double) n_data,
-                                      v, w, val_v, val_w);
-               }
-           }
+         */
+         covariance_accumulate (cov_hash, mom, &c, all_vars, n_all_vars);
+         n_data++;
        }
-      model = pspp_linreg_cache_alloc (v_dependent[0], indep_vars, n_data, n_indep);
-      /*
-       For large data sets, use QR decomposition.
-      */
-      if (n_data > sqrt (n_indep) && n_data > GLM_LARGE_DATA)
+      cov = covariance_accumulator_to_matrix (cov_hash, mom, all_vars, n_all_vars, n_data);
+
+      hsh_destroy (cov_hash);
+      for (i = 0; i < n_dependent; i++)
        {
-         model->method = PSPP_LINREG_QR;
+         model = fit_model (cov, mom, v_dependent[i], indep_vars, n_data, n_indep);
+         pspp_linreg_cache_free (model);
        }
-      coeff_init (model, X);
-      pspp_linreg_with_cov (X, model);
+
       casereader_destroy (reader);
       for (i = 0; i < n_all_vars; i++)
        {
-         moments1_destroy (mom[i]->m);
-         free (mom[i]->mean);
-         free (mom[i]->variance);
-         free (mom[i]->weight);
-         free (mom[i]);
+         moments1_destroy (mom[i]);
        }
       free (mom);
-      covariance_matrix_destroy (X);
+      covariance_matrix_destroy (cov);
     }
   else
     {
@@ -336,7 +326,6 @@ run_glm (struct casereader *input,
     }
   free (indep_vars);
   free (lopts.get_indep_mean_std);
-  pspp_linreg_cache_free (model);
   casereader_destroy (input);
 
   return true;
diff --git a/src/language/tests/.gitignore b/src/language/tests/.gitignore
new file mode 100644 (file)
index 0000000..61e8b4e
--- /dev/null
@@ -0,0 +1 @@
+check-model.c
index 70780eff968a6ec62c071da23229a4ebaac49fcc..bda2676c86788eabcfa24a937f7802cbe19c955d 100644 (file)
 #include <localcharset.h>
 #include "xstrndup.h"
 
+#if HAVE_NL_LANGINFO
+#include <langinfo.h>
+#endif
+
 
 static char *locale = 0;
 static const char *charset;
@@ -213,3 +217,32 @@ i18n_done (void)
     }
 }
 
+
+
+
+/* Return the system local's idea of the
+   decimal seperator character */
+char
+get_system_decimal (void)
+{
+  char *radix_char = NULL;
+
+  char *ol = setlocale (LC_NUMERIC, NULL);
+  setlocale (LC_NUMERIC, "");
+
+#if HAVE_NL_LANGINFO
+  radix_char = nl_langinfo (RADIXCHAR);
+#else
+  {
+    char *buf = xmalloc (10);
+    snprintf (buf, 10, "%f", 2.5);
+    radix_char = &buf[1];
+  }
+#endif
+
+  /* We MUST leave LC_NUMERIC untouched, since it would
+     otherwise interfere with data_{in,out} */
+  setlocale (LC_NUMERIC, ol);
+  return *radix_char;
+}
+
index 0633ebc82f807b9fb1d89429c3bf99fa9699930c..db15bad86940a955f0a0a942af548a8a7b177edb 100644 (file)
@@ -36,5 +36,8 @@ enum conv_id
 char * recode_string (enum conv_id how,  const char *text, int len);
 
 
+/* Return the decimal separator according to the
+   system locale */
+char get_system_decimal (void);
 
 #endif /* i18n.h */
index f929a370cf5470456ba0c33018a7f292d2e4e3a4..817f7ed3158c7a29772af63845c8a2575de4bd9a 100644 (file)
 */
 #include <assert.h>
 #include <config.h>
+#include <data/case.h>
+#include <data/category.h>
 #include <data/variable.h>
 #include <data/value.h>
-#include "covariance-matrix.h"
-#include "moments.h"
+#include <libpspp/hash.h>
+#include <libpspp/hash-functions.h>
+#include <math/covariance-matrix.h>
+#include <math/moments.h>
+#include <string.h>
+#include <xalloc.h>
+
+/*
+  Structure used to accumulate the covariance matrix in a single data
+  pass.  Before passing the data, we do not know how many categories
+  there are in each categorical variable. Therefore we do not know the
+  size of the covariance matrix. To get around this problem, we
+  accumulate the elements of the covariance matrix in pointers to
+  COVARIANC_ACCUMULATOR. These values are then used to populate
+  the covariance matrix.
+ */
+struct covariance_accumulator
+{
+  const struct variable *v1;
+  const struct variable *v2;
+  double product;
+  const union value *val1;
+  const union value *val2;
+};
+
+static hsh_hash_func covariance_accumulator_hash;
+static unsigned int hash_numeric_alpha (const struct variable *, const struct variable *, 
+                                       const union value *, size_t);
+static hsh_compare_func covariance_accumulator_compare;
+static hsh_free_func covariance_accumulator_free;
 
 /*
   The covariances are stored in a DESIGN_MATRIX structure.
  */
 struct design_matrix *
-covariance_matrix_create (int n_variables, const struct variable *v_variables[])
+covariance_matrix_create (size_t n_variables, const struct variable *v_variables[])
 {
   return design_matrix_create (n_variables, v_variables, (size_t) n_variables);
 }
@@ -144,3 +174,363 @@ void covariance_pass_two (struct design_matrix *cov, double mean1, double mean2,
     }
 }
 
+static unsigned int
+covariance_accumulator_hash (const void *h, const void *aux)
+{
+  struct covariance_accumulator *ca = (struct covariance_accumulator *) h;
+  size_t *n_vars = (size_t *) aux;
+  size_t idx_max;
+  size_t idx_min;
+  const struct variable *v_min;
+  const struct variable *v_max;
+  const union value *val_min;
+  const union value *val_max;
+
+  /*
+    Order everything by the variables' indices. This ensures we get the
+    same key regardless of the order in which the variables are stored
+    and passed around.
+   */
+  v_min = (var_get_dict_index (ca->v1) < var_get_dict_index (ca->v2)) ? ca->v1 : ca->v2;
+  v_max = (ca->v1 == v_min) ? ca->v2 : ca->v1;
+
+  val_min = (v_min == ca->v1) ? ca->val1 : ca->val2;
+  val_max = (ca->val1 == val_min) ? ca->val2 : ca->val1;
+
+  idx_min = var_get_dict_index (v_min);
+  idx_max = var_get_dict_index (v_max);
+
+  if (var_is_numeric (v_max) && var_is_numeric (v_min))
+    {
+      return (*n_vars * idx_max + idx_min);
+    }
+  if (var_is_numeric (v_max) && var_is_alpha (v_min))
+    {
+      return hash_numeric_alpha (v_max, v_min, val_min, *n_vars);
+    }
+  if (var_is_alpha (v_max) && var_is_numeric (v_min))
+    {
+      return (hash_numeric_alpha (v_min, v_max, val_max, *n_vars));
+    }
+  if (var_is_alpha (v_max) && var_is_alpha (v_min))
+    {
+      unsigned int tmp;
+      char *x = xnmalloc (1 + var_get_width (v_max) + var_get_width (v_min), sizeof (*x));
+      strncpy (x, val_max->s, var_get_width (v_max));
+      strncat (x, val_min->s, var_get_width (v_min));
+      tmp = *n_vars * (*n_vars + 1 + idx_max)
+       + idx_min
+       + hsh_hash_string (x);
+      free (x);
+      return tmp;
+    }
+  return -1u;
+}
+
+/*
+  Make a hash table consisting of struct covariance_accumulators.
+  This allows the accumulation of the elements of a covariance matrix
+  in a single data pass. Call covariance_accumulate () for each case 
+  in the data.
+ */
+struct hsh_table *
+covariance_hsh_create (size_t n_vars)
+{
+  return hsh_create (n_vars * (n_vars + 1) / 2, covariance_accumulator_compare, 
+                    covariance_accumulator_hash, covariance_accumulator_free, &n_vars);
+}
+
+static void 
+covariance_accumulator_free (void *c_, const void *aux UNUSED)
+{
+  struct covariance_accumulator *c = c_;
+  assert (c != NULL);
+  free (c);
+}
+static int
+match_nodes (const struct covariance_accumulator *c, const struct variable *v1,
+            const struct variable *v2, const union value *val1,
+            const union value *val2)
+{
+  if (var_get_dict_index (v1) == var_get_dict_index (c->v1) && 
+      var_get_dict_index (v2) == var_get_dict_index (c->v2))
+    {
+      if (var_is_numeric (v1) && var_is_numeric (v2))
+       {
+         return 0;
+       }
+      if (var_is_numeric (v1) && var_is_alpha (v2))
+       {
+         if (compare_values (val2, c->val2, v2))
+           {
+             return 0;
+           }
+       }
+      if (var_is_alpha (v1) && var_is_numeric (v2))
+       {
+         if (compare_values (val1, c->val1, v1))
+           {
+             return 0;
+           }
+       }
+      if (var_is_alpha (v1) && var_is_alpha (v2))
+       {
+         if (compare_values (val1, c->val1, v1))
+           {
+             if (compare_values (val2, c->val2, v2))
+               {
+                 return 0;
+               }
+           }
+       }
+    }
+  else if (v2 == c->v1 && v1 == c->v2)
+    {
+      return -match_nodes (c, v2, v1, val2, val1);
+    }
+  return 1;
+}
+
+/*
+  This function is meant to be used as a comparison function for
+  a struct hsh_table in src/libpspp/hash.c.
+*/
+static int
+covariance_accumulator_compare (const void *a1_, const void *a2_, const void *aux UNUSED)
+{
+  const struct covariance_accumulator *a1 =  a1_;
+  const struct covariance_accumulator *a2 =  a2_;
+
+  if (a1 == NULL && a2 == NULL)
+    return 0;
+
+  if (a1 == NULL || a2 == NULL)
+    return 1;
+
+  return match_nodes (a1, a2->v1, a2->v2, a2->val1, a2->val2);
+}
+
+static unsigned int
+hash_numeric_alpha (const struct variable *v1, const struct variable *v2, 
+                   const union value *val, size_t n_vars)
+{
+  unsigned int result = -1u;
+  if (var_is_numeric (v1) && var_is_alpha (v2))
+    {
+      result = n_vars * ((n_vars + 1) + var_get_dict_index (v1))
+       + var_get_dict_index (v2) + hsh_hash_string (val->s);
+    }
+  else if (var_is_alpha (v1) && var_is_numeric (v2))
+    {
+      result = hash_numeric_alpha (v2, v1, val, n_vars);
+    }
+  return result;
+}
+
+
+static double
+update_product (const struct variable *v1, const struct variable *v2, const union value *val1,
+               const union value *val2)
+{
+  assert (v1 != NULL);
+  assert (v2 != NULL);
+  assert (val1 != NULL);
+  assert (val2 != NULL);
+  if (var_is_alpha (v1) && var_is_alpha (v2))
+    {
+      return 1.0;
+    }
+  if (var_is_numeric (v1) && var_is_numeric (v2))
+    {
+      return (val1->f * val2->f);
+    }
+  if (var_is_numeric (v1) && var_is_alpha (v2))
+    {
+      return (val1->f);
+    }
+  if (var_is_numeric (v2) && var_is_alpha (v1))
+    {
+      update_product (v2, v1, val2, val1);
+    }
+  return 0.0;
+}
+/*
+  Compute the covariance matrix in a single data-pass.
+ */
+void 
+covariance_accumulate (struct hsh_table *cov, struct moments1 **m,
+                      const struct ccase *ccase, const struct variable **vars,
+                      size_t n_vars)
+{
+  size_t i;
+  size_t j;
+  const union value *val;
+  struct covariance_accumulator *ca;
+  struct covariance_accumulator *entry;
+
+  assert (m != NULL);
+
+  for (i = 0; i < n_vars; ++i)
+    {
+      val = case_data (ccase, vars[i]);
+      if (var_is_alpha (vars[i]))
+       {
+         cat_value_update (vars[i], val);
+       }
+      else
+       {
+         moments1_add (m[i], val->f, 1.0);
+       }
+      for (j = i; j < n_vars; j++)
+       {
+         ca = xmalloc (sizeof (*ca));
+         ca->v1 = vars[i];
+         ca->v2 = vars[j];
+         ca->val1 = val;
+         ca->val2 = case_data (ccase, ca->v2);
+         ca->product = update_product (ca->v1, ca->v2, ca->val1, ca->val2);
+         entry = hsh_insert (cov, ca);
+         if (entry != NULL)
+           {
+             entry->product += ca->product;
+             /*
+               If ENTRY is null, CA was not already in the hash
+               hable, so we don't free it because it was just inserted.
+               If ENTRY was not null, CA is already in the hash table.
+               Unnecessary now, it must be freed here.
+             */
+             free (ca);
+           }
+       }
+    }
+}
+
+static void 
+covariance_matrix_insert (struct design_matrix *cov, const struct variable *v1,
+                         const struct variable *v2, const union value *val1, 
+                         const union value *val2, double product)
+{
+  size_t row;
+  size_t col;
+  size_t i;
+  const union value *tmp_val;
+
+  assert (cov != NULL);
+
+  row = design_matrix_var_to_column (cov, v1);
+  if (var_is_alpha (v1))
+    {
+      i = 0;
+      tmp_val = cat_subscript_to_value (i, v1);
+      while (!compare_values (tmp_val, val1, v1))
+       {
+         i++;
+         tmp_val = cat_subscript_to_value (i, v1);
+       }
+      row += i;
+      if (var_is_numeric (v2))
+       {
+         col = design_matrix_var_to_column (cov, v2);
+       }
+      else
+       {
+         col = design_matrix_var_to_column (cov, v2);
+         i = 0;
+         tmp_val = cat_subscript_to_value (i, v1);
+         while (!compare_values (tmp_val, val1, v1))
+           {
+             i++;
+             tmp_val = cat_subscript_to_value (i, v1);
+           } 
+         col += i;
+       }
+    }    
+  else
+    {
+      if (var_is_numeric (v2))
+       {
+         col = design_matrix_var_to_column (cov, v2);
+       }
+      else
+       {
+         covariance_matrix_insert (cov, v2, v1, val2, val1, product);
+       }
+    }
+  gsl_matrix_set (cov->m, row, col, product);
+  gsl_matrix_set (cov->m, col, row, product);
+}
+
+static double
+get_center (const struct variable *v, const union value *val, 
+           const struct variable **vars, const struct moments1 **m, size_t n_vars,
+           size_t ssize)
+{
+  size_t i = 0;
+
+  while ((var_get_dict_index (vars[i]) != var_get_dict_index(v)) && (i < n_vars))
+    {
+      i++;
+    }  
+  if (var_is_numeric (v))
+    {
+      double mean;
+      moments1_calculate (m[i], NULL, &mean, NULL, NULL, NULL);
+      return mean;
+    }
+  else 
+    {
+      i = cat_value_find (v, val);
+      return (cat_get_category_count (i, v) / ssize);
+    }
+  return 0.0;
+}
+
+/*
+  Subtract the product of the means.
+ */
+static double
+center_entry (const struct covariance_accumulator *ca, const struct variable **vars,
+             const struct moments1 **m, size_t n_vars, size_t ssize)
+{
+  double m1;
+  double m2;
+  double result = 0.0;
+  
+  m1 = get_center (ca->v1, ca->val1, vars, m, n_vars, ssize);
+  m2 = get_center (ca->v2, ca->val2, vars, m, n_vars, ssize);
+  result = ca->product - ssize * m1 * m2;
+  return result;
+}
+
+/*
+  The first moments in M should be stored in the order corresponding
+  to the order of VARS. So, for example, VARS[0] has its moments in
+  M[0], VARS[1] has its moments in M[1], etc.
+ */
+struct design_matrix *
+covariance_accumulator_to_matrix (struct hsh_table *cov, const struct moments1 **m,
+                                 const struct variable **vars, size_t n_vars, size_t ssize)
+{
+  double tmp;
+  struct covariance_accumulator *entry;
+  struct design_matrix *result = NULL;
+  struct hsh_iterator iter;
+  
+  result = covariance_matrix_create (n_vars, vars);
+
+  entry = hsh_first (cov, &iter);
+  
+  while (entry != NULL)
+    {
+      /*
+       We compute the centered, un-normalized covariance matrix.
+       */
+      tmp = center_entry (entry, vars, m, n_vars, ssize);
+      covariance_matrix_insert (result, entry->v1, entry->v2, entry->val1,
+                               entry->val2, tmp);
+      entry = hsh_next (cov, &iter);
+    }
+
+  return result;
+}
+
index 22f979c595c0baf76974fcba537cb7423bac9c52..573d3e7ec27b8a2fda2387cb8423e85e9f31991c 100644 (file)
 #ifndef COVARIANCE_MATRIX_H
 #define COVARIANCE_MATRIX_H
 
-#include "design-matrix.h"
+#include <math/design-matrix.h>
+
+struct moments1;
+struct ccase;
+struct hsh_table;
 
 struct design_matrix *
-covariance_matrix_create (int, const struct variable *[]);
+covariance_matrix_create (size_t, const struct variable *[]);
 
 void covariance_matrix_destroy (struct design_matrix *);
 
 void covariance_pass_two (struct design_matrix *, double,
                          double, double, const struct variable *, 
                          const struct variable *, const union value *, const union value *);
+void covariance_accumulate (struct hsh_table *, struct moments1 **,
+                           const struct ccase *, const struct variable **, size_t);
+struct hsh_table * covariance_hsh_create (size_t);
+struct design_matrix * covariance_accumulator_to_matrix (struct hsh_table *, const struct moments1 **,
+                                                        const struct variable **, size_t, size_t);
 #endif
index 4e6ccf17dc77a21fb39e30f093f5e3a5c8786c25..df68287b36e04e8077db317fa780cfeb648553b5 100644 (file)
@@ -54,10 +54,12 @@ design_matrix_create (int n_variables,
 
   dm = xmalloc (sizeof *dm);
   dm->vars = xnmalloc (n_variables, sizeof *dm->vars);
+  dm->n_cases = xnmalloc (n_variables, sizeof (*dm->n_cases));
   dm->n_vars = n_variables;
 
   for (i = 0; i < n_variables; i++)
     {
+      dm->n_cases[i] = 0;
       v = v_variables[i];
       assert ((dm->vars + i) != NULL);
       (dm->vars + i)->v = v;   /* Allows us to look up the variable from
@@ -79,6 +81,7 @@ design_matrix_create (int n_variables,
   dm->m = gsl_matrix_calloc (n_data, n_cols);
   col = 0;
 
+  
   return dm;
 }
 
@@ -87,6 +90,7 @@ design_matrix_destroy (struct design_matrix *dm)
 {
   free (dm->vars);
   gsl_matrix_free (dm->m);
+  free (dm->n_cases);
   free (dm);
 }
 
@@ -212,3 +216,46 @@ design_matrix_clone (const struct design_matrix *dm)
   return result;
 }
 
+/*
+  Increment the number of cases for V.
+ */
+void 
+design_matrix_increment_case_count (struct design_matrix *dm, const struct variable *v)
+{
+  size_t i;
+  assert (dm != NULL);
+  assert (dm->n_cases != NULL);
+  assert (v != NULL);
+  i = design_matrix_var_to_column (dm, v);
+  dm->n_cases[i]++;
+}
+
+/*
+  Set the number of cases for V.
+ */
+void 
+design_matrix_set_case_count (struct design_matrix *dm, const struct variable *v, size_t n)
+{
+  size_t i;
+  assert (dm != NULL);
+  assert (dm->n_cases != NULL);
+  assert (v != NULL);
+  i = design_matrix_var_to_column (dm, v);
+  dm->n_cases[i] = n;
+}
+
+/*
+  Get the number of cases for V.
+ */
+size_t 
+design_matrix_get_case_count (const struct design_matrix *dm, const struct variable *v)
+{
+  size_t i;
+  assert (dm != NULL);
+  assert (dm->n_cases != NULL);
+  assert (v != NULL);
+  i = design_matrix_var_to_column (dm, v);
+  return dm->n_cases[i];
+}
+
+  
index 8d56d010c5974f0ebf558dc3d12989f96e96f093..ba7f984218261d5db40af8c41a864b51c1af886a 100644 (file)
@@ -58,6 +58,9 @@ struct design_matrix
                                           design_matrix_var
                                           structure.
                                         */
+  size_t *n_cases; /* Element i is the number of valid cases for this
+                     variable.
+                   */
   size_t n_vars;
 };
 
@@ -82,5 +85,9 @@ size_t design_matrix_var_to_column (const struct design_matrix *,
 
 const struct variable *design_matrix_col_to_var (const struct design_matrix *,
                                           size_t);
+void design_matrix_increment_case_count (struct design_matrix *, const struct variable *);
 
+void design_matrix_set_case_count (struct design_matrix *, const struct variable *, size_t);
+
+size_t design_matrix_get_case_count (const struct design_matrix *, const struct variable *);
 #endif
index d8dd300ee3d3ede8cb75b529b57a928cc6ef54b9..082889cdedfe26d098dff3a4c9bd9196a59c7fe4 100644 (file)
@@ -6,6 +6,8 @@ TESTS_ENVIRONMENT += PERL='@PERL@' PG_CONFIG='@PG_CONFIG@'
 # Allow locale_charset to find charset.alias before running "make install".
 TESTS_ENVIRONMENT += CHARSETALIASDIR='$(abs_top_builddir)/gl'
 
+TESTS_ENVIRONMENT += LC_ALL=C
+
 dist_TESTS = \
        tests/command/aggregate.sh \
        tests/command/attributes.sh \
index 1c29a7a523f2a4ee42e4b3f5dc2f030d19f83c63..cb0f5ba3a04161a27ca712c4b43df36c7e83e2a8 100755 (executable)
@@ -60,6 +60,7 @@ cat > $TEMPDIR/valuelabel.stat <<EOF
 DATA LIST notable /n 1 s 2(a).
 VALUE LABELS /n 0 'Very dissatisfied'
                 1 'Dissatisfied'
+               1.5 'Slightly Peeved'
                 2 'Neutral'
                 3 'Satisfied'
                 4 'Very satisfied'.
@@ -82,6 +83,7 @@ BEGIN DATA.
 5f
 6g
 END DATA.
+
 EOF
 if [ $? -ne 0 ] ; then no_result ; fi