Merge 'master' into 'psppsheet'.
[pspp] / src / language / stats / examine.c
index 069f807c0a17d40cae2deed032021228c4855250..0d260cffbf55ae130c056f2bcc0073a5ac8446d1 100644 (file)
@@ -1,6 +1,6 @@
 /*
   PSPP - a program for statistical analysis.
-  Copyright (C) 2012 Free Software Foundation, Inc.
+  Copyright (C) 2012, 2013  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
@@ -52,6 +52,7 @@
 
 #include "output/charts/boxplot.h"
 #include "output/charts/np-plot.h"
+#include "output/charts/spreadlevel-plot.h"
 #include "output/charts/plot-hist.h"
 
 #include "language/command.h"
 #define _(msgid) gettext (msgid)
 #define N_(msgid) msgid
 
+static void 
+append_value_name (const struct variable *var, const union value *val, struct string *str)
+{
+  var_append_value_name (var, val, str);
+  if ( var_is_value_missing (var, val, MV_ANY))
+    ds_put_cstr (str, _(" (missing)"));
+}
+
 enum bp_mode
   {
     BP_GROUPS,
@@ -95,7 +104,8 @@ struct examine
   size_t n_iacts;
   struct interaction **iacts;
 
-  enum mv_class exclude;
+  enum mv_class dep_excl;
+  enum mv_class fctr_excl;
 
   const struct dictionary *dict;
 
@@ -121,6 +131,8 @@ struct examine
   bool npplot;
   bool histogram;
   bool boxplot;
+  bool spreadlevel;
+  int sl_power;
 
   enum bp_mode boxplot_mode;
 
@@ -292,13 +304,19 @@ show_boxplot_grouped (const struct examine *cmd, int iact_idx)
           ds_init_empty (&label);
           for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
             {
+              struct string l;
               const struct variable *ivar = iact->vars[ivar_idx];
               const union value *val = case_data (c, ivar);
-              
-              ds_put_cstr (&label, var_to_string (ivar));
-              ds_put_cstr (&label, " = ");
-              var_append_value_name (ivar, val, &label);
-              ds_put_cstr (&label, "; ");
+              ds_init_empty (&l);
+
+              append_value_name (ivar, val, &l);
+              ds_ltrim (&l, ss_cstr (" "));
+
+              ds_put_substring (&label, l.ss);
+              if (ivar_idx < iact->n_vars - 1)
+                ds_put_cstr (&label, "; ");
+
+              ds_destroy (&l);
             }
 
           boxplot_add_box (boxplot, es[v].box_whisker, ds_cstr (&label));
@@ -356,7 +374,7 @@ show_boxplot_variabled (const struct examine *cmd, int iact_idx)
               
               ds_put_cstr (&label, var_to_string (ivar));
               ds_put_cstr (&label, " = ");
-              var_append_value_name (ivar, val, &label);
+              append_value_name (ivar, val, &label);
               ds_put_cstr (&label, "; ");
             }
 
@@ -423,7 +441,7 @@ show_npplot (const struct examine *cmd, int iact_idx)
                   
                   ds_put_cstr (&label, var_to_string (ivar));
                   ds_put_cstr (&label, " = ");
-                  var_append_value_name (ivar, val, &label);
+                  append_value_name (ivar, val, &label);
                   ds_put_cstr (&label, "; ");
                   
                 }
@@ -455,6 +473,58 @@ show_npplot (const struct examine *cmd, int iact_idx)
     }
 }
 
+static void
+show_spreadlevel (const struct examine *cmd, int iact_idx)
+{
+  const struct interaction *iact = cmd->iacts[iact_idx];
+  const size_t n_cats =  categoricals_n_count (cmd->cats, iact_idx);
+
+  int v;
+
+  /* Spreadlevel when there are no levels is not useful */
+  if (iact->n_vars == 0)
+    return;
+
+  for (v = 0; v < cmd->n_dep_vars; ++v)
+    {
+      int grp;
+      struct chart_item *sl;
+
+      struct string label;
+      ds_init_cstr (&label, 
+                   var_to_string (cmd->dep_vars[v]));
+
+      if (iact->n_vars > 0)
+       {
+         ds_put_cstr (&label, " (");
+         interaction_to_string (iact, &label);
+         ds_put_cstr (&label, ")");
+       }
+      
+      sl = spreadlevel_plot_create (ds_cstr (&label), cmd->sl_power);
+
+      for (grp = 0; grp < n_cats; ++grp)
+        {
+          const struct exploratory_stats *es =
+            categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, grp);
+
+         double median = percentile_calculate (es[v].quartiles[1], cmd->pc_alg);
+
+         double iqr = percentile_calculate (es[v].quartiles[2], cmd->pc_alg) -
+           percentile_calculate (es[v].quartiles[0], cmd->pc_alg);
+
+         spreadlevel_plot_add (sl, iqr, median);
+       }
+
+      if (sl == NULL)
+       msg (MW, _("Not creating spreadlevel chart for %s"), ds_cstr (&label));
+      else 
+       chart_item_submit (sl);
+
+      ds_destroy (&label);
+    }
+}
+
 
 static void
 show_histogram (const struct examine *cmd, int iact_idx)
@@ -479,6 +549,10 @@ show_histogram (const struct examine *cmd, int iact_idx)
             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, grp);
 
           struct string label;
+
+         if (es[v].histogram == NULL)
+           continue;
+
           ds_init_cstr (&label, 
                         var_to_string (cmd->dep_vars[v]));
 
@@ -492,7 +566,7 @@ show_histogram (const struct examine *cmd, int iact_idx)
                   
                   ds_put_cstr (&label, var_to_string (ivar));
                   ds_put_cstr (&label, " = ");
-                  var_append_value_name (ivar, val, &label);
+                  append_value_name (ivar, val, &label);
                   ds_put_cstr (&label, "; ");
                   
                 }
@@ -617,7 +691,7 @@ percentiles_report (const struct examine *cmd, int iact_idx)
                    {              
                      struct string str;
                      ds_init_empty (&str);
-                     var_append_value_name (ivar, val, &str);
+                     append_value_name (ivar, val, &str);
               
                      tab_text (t,
                                1 + ivar_idx,
@@ -785,7 +859,7 @@ descriptives_report (const struct examine *cmd, int iact_idx)
                 {              
                   struct string str;
                   ds_init_empty (&str);
-                  var_append_value_name (ivar, val, &str);
+                  append_value_name (ivar, val, &str);
               
                   tab_text (t,
                             1 + ivar_idx,
@@ -1101,7 +1175,7 @@ extremes_report (const struct examine *cmd, int iact_idx)
                 {              
                   struct string str;
                   ds_init_empty (&str);
-                  var_append_value_name (ivar, val, &str);
+                  append_value_name (ivar, val, &str);
               
                   tab_text (t,
                             1 + ivar_idx,
@@ -1165,13 +1239,12 @@ extremes_report (const struct examine *cmd, int iact_idx)
                             &F_8_0);
 
               tab_double (t,
-                          heading_columns + 1,
-                          heading_rows + v * rows_per_var + i * rows_per_cat + e,
-                          0,
-                          es->maxima[e].val,
-                          0);
-
-
+                         heading_columns + 1,
+                         heading_rows + v * rows_per_var + i * rows_per_cat + e,
+                         0,
+                         es->maxima[e].val,
+                         var_get_print_format (cmd->dep_vars[v]));
+                         
 
               tab_double (t,
                           heading_columns - 1,
@@ -1202,7 +1275,7 @@ extremes_report (const struct examine *cmd, int iact_idx)
                           heading_rows + v * rows_per_var + i * rows_per_cat + cmd->disp_extremes + e,
                           0,
                           es->minima[e].val,
-                          0);
+                          var_get_print_format (cmd->dep_vars[v]));
             }
         }
       free (prev_val);
@@ -1320,7 +1393,7 @@ summary_report (const struct examine *cmd, int iact_idx)
                      {              
                        struct string str;
                        ds_init_empty (&str);
-                       var_append_value_name (ivar, val, &str);
+                       append_value_name (ivar, val, &str);
               
                        tab_text (t,
                                  1 + ivar_idx, heading_rows + n_cats * v + i,
@@ -1394,25 +1467,6 @@ summary_report (const struct examine *cmd, int iact_idx)
   tab_submit (t);
 }
 
-
-/* Match a variable.
-   If the match succeeds, the variable will be placed in VAR.
-   Returns true if successful */
-static bool
-lex_match_variable (struct lexer *lexer, 
-                    const struct dictionary *dict, const struct variable **var)
-{
-  if (lex_token (lexer) !=  T_ID)
-
-    return false;
-
-  *var = parse_variable_const  (lexer, dict);
-
-  if ( *var == NULL)
-    return false;
-  return true;
-}
-
 /* Attempt to parse an interaction from LEXER */
 static struct interaction *
 parse_interaction (struct lexer *lexer, struct examine *ex)
@@ -1480,7 +1534,7 @@ update_n (const void *aux1, void *aux2 UNUSED, void *user_data,
       const struct variable *var = examine->dep_vars[v];
       const double x = case_data (c, var)->f;
       
-      if (var_is_value_missing (var, case_data (c, var), examine->exclude))
+      if (var_is_value_missing (var, case_data (c, var), examine->dep_excl))
         {
           es[v].missing += weight;
           continue;
@@ -1526,28 +1580,26 @@ calculate_n (const void *aux1, void *aux2 UNUSED, void *user_data)
     {
       int i;
       casenumber imin = 0;
-      double imax = es[v].cc;
+      casenumber imax;
       struct casereader *reader;
       struct ccase *c;
-      casenumber total_cases;
 
       if (examine->histogram)
         {
           /* Sturges Rule */
-          double bin_width = abs (es[v].minimum - es[v].maximum)
+          double bin_width = fabs (es[v].minimum - es[v].maximum)
             / (1 + log2 (es[v].cc))
             ;
 
-          bin_width = chart_rounded_tick (bin_width);
-
           es[v].histogram =
             histogram_create (bin_width, es[v].minimum, es[v].maximum);
         }
 
       es[v].sorted_reader = casewriter_make_reader (es[v].sorted_writer);
-      total_cases = casereader_count_cases (es[v].sorted_reader);
       es[v].sorted_writer = NULL;
 
+      imax = casereader_get_case_cnt (es[v].sorted_reader);
+
       es[v].maxima = pool_calloc (examine->pool, examine->calc_extremes, sizeof (*es[v].maxima));
       es[v].minima = pool_calloc (examine->pool, examine->calc_extremes, sizeof (*es[v].minima));
       for (i = 0; i < examine->calc_extremes; ++i)
@@ -1560,7 +1612,7 @@ calculate_n (const void *aux1, void *aux2 UNUSED, void *user_data)
            (c = casereader_read (reader)) != NULL; case_unref (c))
         {
           const double val = case_data_idx (c, EX_VAL)->f;
-          const double wt = case_data_idx (c, EX_WT)->f;   /* FIXME: What about fractional weights ??? */
+          const double wt = case_data_idx (c, EX_WT)->f;
 
           moments_pass_two (es[v].mom, val, wt);
 
@@ -1576,15 +1628,15 @@ calculate_n (const void *aux1, void *aux2 UNUSED, void *user_data)
                   min->val = val;
                   value_copy (&min->identity, case_data_idx (c, EX_ID), examine->id_width);
                 }
-              imin += wt;
+              imin ++;
             }
 
-          imax -= wt;
+          imax --;
           if (imax < examine->calc_extremes)
             {
               int x;
 
-              for (x = imax; x < imax + wt; ++x)
+              for (x = imax; x < imax + 1; ++x)
                 {
                   struct extremity *max;
 
@@ -1602,7 +1654,7 @@ calculate_n (const void *aux1, void *aux2 UNUSED, void *user_data)
       if (examine->calc_extremes > 0)
         {
           assert (es[v].minima[0].val == es[v].minimum);
-          assert (es[v].maxima[0].val == es[v].maximum);
+         assert (es[v].maxima[0].val == es[v].maximum);
         }
 
       {
@@ -1738,13 +1790,14 @@ run_examine (struct examine *cmd, struct casereader *input)
   struct payload payload;
   payload.create = create_n;
   payload.update = update_n;
-  payload.destroy = calculate_n;
+  payload.calculate = calculate_n;
+  payload.destroy = NULL;
   
   cmd->wv = dict_get_weight (cmd->dict);
 
   cmd->cats
     = categoricals_create (cmd->iacts, cmd->n_iacts,  
-                           cmd->wv, cmd->exclude);
+                           cmd->wv, cmd->dep_excl, cmd->fctr_excl);
 
   categoricals_set_payload (cmd->cats, &payload, cmd, NULL);
 
@@ -1760,14 +1813,12 @@ run_examine (struct examine *cmd, struct casereader *input)
       case_unref (c);
     }
 
-  /* FIXME: Filter out missing factor variables */
-
   /* Remove cases on a listwise basis if requested */
   if ( cmd->missing_pw == false)
     input = casereader_create_filter_missing (input,
                                               cmd->dep_vars,
                                               cmd->n_dep_vars,
-                                              cmd->exclude,
+                                              cmd->dep_excl,
                                               NULL,
                                               NULL);
 
@@ -1811,6 +1862,9 @@ run_examine (struct examine *cmd, struct casereader *input)
       if (cmd->npplot)
         show_npplot (cmd, i);
 
+      if (cmd->spreadlevel)
+        show_spreadlevel (cmd, i);
+
       if (cmd->descriptives)
         descriptives_report (cmd, i);
     }
@@ -1857,10 +1911,13 @@ cmd_examine (struct lexer *lexer, struct dataset *ds)
   examine.iacts = iacts_mem = pool_zalloc (examine.pool, sizeof (struct interaction *));
   examine.iacts[0] = interaction_create (NULL);
 
-  examine.exclude = MV_ANY;
+  examine.dep_excl = MV_ANY;
+  examine.fctr_excl = MV_ANY;
   examine.histogram = false;
   examine.npplot = false;
   examine.boxplot = false;
+  examine.spreadlevel = false;
+  examine.sl_power = 0;
   
   examine.dict = dataset_dict (ds);
 
@@ -2039,11 +2096,19 @@ cmd_examine (struct lexer *lexer, struct dataset *ds)
                 }
               else if (lex_match_id (lexer, "EXCLUDE"))
                 {
-                  examine.exclude = MV_ANY;
+                  examine.dep_excl = MV_ANY;
                 }
               else if (lex_match_id (lexer, "INCLUDE"))
                 {
-                  examine.exclude = MV_SYSTEM;
+                  examine.dep_excl = MV_SYSTEM;
+                }
+              else if (lex_match_id (lexer, "REPORT"))
+                {
+                  examine.fctr_excl = MV_NEVER;
+                }
+              else if (lex_match_id (lexer, "NOREPORT"))
+                {
+                  examine.fctr_excl = MV_ANY;
                 }
               else
                 {
@@ -2088,6 +2153,19 @@ cmd_examine (struct lexer *lexer, struct dataset *ds)
                 {
                   examine.histogram = true;
                 }
+              else if (lex_match_id (lexer, "SPREADLEVEL"))
+                {
+                 examine.spreadlevel = true;
+                 examine.sl_power = 0;
+                 if (lex_match (lexer, T_LPAREN))
+                   {
+                      examine.sl_power = lex_integer (lexer);
+
+                      lex_get (lexer);
+                      if (! lex_force_match (lexer, T_RPAREN))
+                        goto error;
+                   }
+                }
               else if (lex_match_id (lexer, "NONE"))
                 {
                   examine.histogram = false;
@@ -2146,6 +2224,7 @@ cmd_examine (struct lexer *lexer, struct dataset *ds)
     {
       examine.n_iacts--;
       examine.iacts = &iacts_mem[1];
+      interaction_destroy (iacts_mem[0]);
     }
 
 
@@ -2201,9 +2280,6 @@ cmd_examine (struct lexer *lexer, struct dataset *ds)
 
   caseproto_unref (examine.ex_proto);
 
-  for (i = 0; i < examine.n_iacts; ++i)
-    interaction_destroy (examine.iacts[i]);
-
   free (examine.ptiles);
   free (examine.dep_vars);
   pool_destroy (examine.pool);
@@ -2212,6 +2288,7 @@ cmd_examine (struct lexer *lexer, struct dataset *ds)
 
  error:
   caseproto_unref (examine.ex_proto);
+  examine.iacts = iacts_mem;
   for (i = 0; i < examine.n_iacts; ++i)
     interaction_destroy (examine.iacts[i]);
   free (examine.dep_vars);