FACTOR: Added "Scree Plots"
[pspp-builds.git] / src / language / stats / factor.c
index ca09aa5962e7f4fba8be9417ec6bd209f0412ad7..2dbf3b69143f88ad53a1515670000f42f09e0dc3 100644 (file)
@@ -46,6 +46,8 @@
 
 #include <output/table.h>
 
+#include <output/charts/scree.h>
+#include <output/chart.h>
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -70,6 +72,12 @@ enum extraction_method
     EXTRACTION_PAF,
   };
 
+enum plot_opts
+  {
+    PLOT_SCREE = 0x0001,
+    PLOT_ROTATION = 0x0002
+  };
+
 enum print_opts
   {
     PRINT_UNIVARIATE  = 0x0001,
@@ -100,6 +108,7 @@ struct cmd_factor
   enum mv_class exclude;
   enum print_opts print;
   enum extraction_method extraction;
+  enum plot_opts plot;
 
   /* Extraction Criteria */
   int n_factors;
@@ -116,6 +125,10 @@ struct idata
 {
   /* Intermediate values used in calculation */
 
+  const gsl_matrix *corr ;  /* The correlation matrix */
+  const gsl_matrix *cov ;   /* The covariance matrix */
+  const gsl_matrix *n ;     /* Matrix of number of samples */
+
   gsl_vector *eval ;  /* The eigenvalues */
   gsl_matrix *evec ;  /* The eigenvectors */
 
@@ -127,7 +140,7 @@ struct idata
 static struct idata *
 idata_alloc (size_t n_vars)
 {
-  struct idata *id = xmalloc (sizeof (*id));
+  struct idata *id = xzalloc (sizeof (*id));
 
   id->n_extractions = 0;
   id->msr = gsl_vector_alloc (n_vars);
@@ -280,7 +293,7 @@ ws_destroy (struct smr_workspace *ws)
    Return the square of the regression coefficient for VAR regressed against all other variables.
  */
 static double
-squared_multiple_correlation (const gsl_matrix *analysis_matrix, int var, struct smr_workspace *ws)
+squared_multiple_correlation (const gsl_matrix *corr, int var, struct smr_workspace *ws)
 {
   /* For an explanation of what this is doing, see 
      http://www.visualstatistics.net/Visual%20Statistics%20Multimedia/multiple_regression_analysis.htm
@@ -289,7 +302,7 @@ squared_multiple_correlation (const gsl_matrix *analysis_matrix, int var, struct
   int signum = 0;
   gsl_matrix_view rxx;
 
-  gsl_matrix_memcpy (ws->m, analysis_matrix);
+  gsl_matrix_memcpy (ws->m, corr);
 
   gsl_matrix_swap_rows (ws->m, 0, var);
   gsl_matrix_swap_columns (ws->m, 0, var);
@@ -522,6 +535,7 @@ cmd_factor (struct lexer *lexer, struct dataset *ds)
   factor.econverge = 0.001;
   factor.blank = 0;
   factor.sort = false;
+  factor.plot = 0;
 
   factor.wv = dict_get_weight (dict);
 
@@ -542,7 +556,6 @@ cmd_factor (struct lexer *lexer, struct dataset *ds)
     {
       lex_match (lexer, '/');
 
-#if FACTOR_FULLY_IMPLEMENTED
       if (lex_match_id (lexer, "PLOT"))
        {
           lex_match (lexer, '=');
@@ -550,10 +563,13 @@ cmd_factor (struct lexer *lexer, struct dataset *ds)
            {
              if (lex_match_id (lexer, "EIGEN"))
                {
+                 factor.plot |= PLOT_SCREE;
                }
+#if FACTOR_FULLY_IMPLEMENTED
              else if (lex_match_id (lexer, "ROTATION"))
                {
                }
+#endif
              else
                {
                  lex_error (lexer, NULL);
@@ -561,9 +577,7 @@ cmd_factor (struct lexer *lexer, struct dataset *ds)
                }
            }
        }
-      else
-#endif
-      if (lex_match_id (lexer, "METHOD"))
+      else if (lex_match_id (lexer, "METHOD"))
        {
           lex_match (lexer, '=');
           while (lex_token (lexer) != '.' && lex_token (lexer) != '/')
@@ -742,13 +756,17 @@ cmd_factor (struct lexer *lexer, struct dataset *ds)
              else if (lex_match_id (lexer, "AIC"))
                {
                }
+#endif
              else if (lex_match_id (lexer, "SIG"))
                {
+                 factor.print |= PRINT_SIG;
                }
-             else if (lex_match_id (lexer, "COVARIANCE"))
+             else if (lex_match_id (lexer, "CORRELATION"))
                {
+                 factor.print |= PRINT_CORRELATION;
                }
-             else if (lex_match_id (lexer, "CORRELATION"))
+#if FACTOR_FULLY_IMPLEMENTED
+             else if (lex_match_id (lexer, "COVARIANCE"))
                {
                }
 #endif
@@ -903,6 +921,22 @@ communality (struct idata *idata, int n, int n_factors)
 }
 
 
+static void
+show_scree (const struct cmd_factor *f, struct idata *idata)
+{
+  struct scree *s;
+  const char *label ;
+
+  if ( !(f->plot & PLOT_SCREE) )
+    return;
+
+
+  label = f->extraction == EXTRACTION_PC ? _("Component Number") : _("Factor Number");
+
+  s = scree_create (idata->eval, label);
+
+  chart_submit (scree_get_chart (s));
+}
 
 static void
 show_communalities (const struct cmd_factor * factor,
@@ -926,11 +960,11 @@ show_communalities (const struct cmd_factor * factor,
   if (nc <= 1)
     return;
 
-  t = tab_create (nc, nr, 0);
+  t = tab_create (nc, nr);
 
   tab_title (t, _("Communalities"));
 
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   tab_headers (t, heading_columns, 0, heading_rows, 0);
 
@@ -986,14 +1020,14 @@ show_factor_matrix (const struct cmd_factor *factor, struct idata *idata, const
   const int nc = heading_columns + n_factors;
   gsl_permutation *perm;
 
-  struct tab_table *t = tab_create (nc, nr, 0);
+  struct tab_table *t = tab_create (nc, nr);
 
   if ( factor->extraction == EXTRACTION_PC )
     tab_title (t, _("Component Matrix"));
   else 
     tab_title (t, _("Factor Matrix"));
 
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   tab_headers (t, heading_columns, 0, heading_rows, 0);
 
@@ -1030,11 +1064,6 @@ show_factor_matrix (const struct cmd_factor *factor, struct idata *idata, const
   tab_vline (t, TAL_2, heading_columns, 0, nr - 1);
 
 
-  {
-    gsl_vector_const_view r1 = gsl_matrix_const_row (fm, 0);
-    gsl_vector_const_view r2 = gsl_matrix_const_row (fm, 1);
-  }
-
   /* Initialise to the identity permutation */
   perm = gsl_permutation_calloc (factor->n_vars);
 
@@ -1103,11 +1132,11 @@ show_explained_variance (const struct cmd_factor * factor, struct idata *idata,
   if ( nc <= heading_columns)
     return;
 
-  t = tab_create (nc, nr, 0);
+  t = tab_create (nc, nr);
 
   tab_title (t, _("Total Variance Explained"));
 
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   tab_headers (t, heading_columns, 0, heading_rows, 0);
 
@@ -1216,18 +1245,155 @@ show_explained_variance (const struct cmd_factor * factor, struct idata *idata,
 }
 
 
+static void
+show_correlation_matrix (const struct cmd_factor *factor, const struct idata *idata)
+{
+  struct tab_table *t ;
+  size_t i, j;
+  int y_pos_corr = -1;
+  int y_pos_sig = -1;
+  int suffix_rows = 0;
+
+  const int heading_rows = 1;
+  const int heading_columns = 2;
+
+  int nc = heading_columns ;
+  int nr = heading_rows ;
+  int n_data_sets = 0;
+
+  if (factor->print & PRINT_CORRELATION)
+    {
+      y_pos_corr = n_data_sets;
+      n_data_sets++;
+      nc = heading_columns + factor->n_vars;
+    }
+
+  if (factor->print & PRINT_SIG)
+    {
+      y_pos_sig = n_data_sets;
+      n_data_sets++;
+      nc = heading_columns + factor->n_vars;
+    }
+
+  nr += n_data_sets * factor->n_vars;
+
+  if (factor->print & PRINT_DETERMINANT)
+    suffix_rows = 1;
+
+  /* If the table would contain only headings, don't bother rendering it */
+  if (nr <= heading_rows && suffix_rows == 0)
+    return;
+
+  t = tab_create (nc, nr + suffix_rows);
+
+  tab_title (t, _("Correlation Matrix"));
+
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
+
+  tab_hline (t, TAL_1, 0, nc - 1, heading_rows);
+
+  if (nr > heading_rows)
+    {
+      tab_headers (t, heading_columns, 0, heading_rows, 0);
+
+      tab_vline (t, TAL_2, 2, 0, nr - 1);
+
+      /* Outline the box */
+      tab_box (t,
+              TAL_2, TAL_2,
+              -1, -1,
+              0, 0,
+              nc - 1, nr - 1);
+
+      /* Vertical lines */
+      tab_box (t,
+              -1, -1,
+              -1, TAL_1,
+              heading_columns, 0,
+              nc - 1, nr - 1);
+
+
+      for (i = 0; i < factor->n_vars; ++i)
+       tab_text (t, heading_columns + i, 0, TAT_TITLE, var_to_string (factor->vars[i]));
+
+
+      for (i = 0 ; i < n_data_sets; ++i)
+       {
+         int y = heading_rows + i * factor->n_vars;
+         size_t v;
+         for (v = 0; v < factor->n_vars; ++v)
+           tab_text (t, 1, y + v, TAT_TITLE, var_to_string (factor->vars[v]));
+
+         tab_hline (t, TAL_1, 0, nc - 1, y);
+       }
+
+      if (factor->print & PRINT_CORRELATION)
+       {
+         const double y = heading_rows + y_pos_corr;
+         tab_text (t, 0, y, TAT_TITLE, _("Correlations"));
+
+         for (i = 0; i < factor->n_vars; ++i)
+           {
+             for (j = 0; j < factor->n_vars; ++j)
+               tab_double (t, heading_columns + i,  y + j, 0, gsl_matrix_get (idata->corr, i, j), NULL);
+           }
+       }
+
+      if (factor->print & PRINT_SIG)
+       {
+         const double y = heading_rows + y_pos_sig * factor->n_vars;
+         tab_text (t, 0, y, TAT_TITLE, _("Sig. 1-tailed"));
+
+         for (i = 0; i < factor->n_vars; ++i)
+           {
+             for (j = 0; j < factor->n_vars; ++j)
+               {
+                 double rho = gsl_matrix_get (idata->corr, i, j);
+                 double w = gsl_matrix_get (idata->n, i, j);
+
+                 if (i == j)
+                   continue;
+
+                 tab_double (t, heading_columns + i,  y + j, 0, significance_of_correlation (rho, w), NULL);
+               }
+           }
+       }
+    }
+
+  if (factor->print & PRINT_DETERMINANT)
+    {
+      int sign = 0;
+      double det = 0.0;
+
+      const int size = idata->corr->size1;
+      gsl_permutation *p = gsl_permutation_calloc (size);
+      gsl_matrix *tmp = gsl_matrix_calloc (size, size);
+      gsl_matrix_memcpy (tmp, idata->corr);
+
+      gsl_linalg_LU_decomp (tmp, p, &sign);
+      det = gsl_linalg_LU_det (tmp, sign);
+      gsl_permutation_free (p);
+      gsl_matrix_free (tmp);
+
+
+      tab_text (t, 0, nr, TAB_LEFT | TAT_TITLE, _("Determinant"));
+      tab_double (t, 1, nr, 0, det, NULL);
+    }
+
+  tab_submit (t);
+}
+
+
 
 static void
 do_factor (const struct cmd_factor *factor, struct casereader *r)
 {
   struct ccase *c;
-  const gsl_matrix *cov_matrix;
   const gsl_matrix *var_matrix;
   const gsl_matrix *mean_matrix;
-  const gsl_matrix *n_matrix;
 
   const gsl_matrix *analysis_matrix;
-  struct idata *idata;
+  struct idata *idata = idata_alloc (factor->n_vars);
 
   struct covariance *cov = covariance_create (factor->n_vars, factor->vars,
                                              factor->wv, factor->exclude);
@@ -1237,18 +1403,19 @@ do_factor (const struct cmd_factor *factor, struct casereader *r)
       covariance_accumulate (cov, c);
     }
 
-  cov_matrix = covariance_calculate (cov);
+  idata->cov = covariance_calculate (cov);
 
   var_matrix = covariance_moments (cov, MOMENT_VARIANCE);
   mean_matrix = covariance_moments (cov, MOMENT_MEAN);
-  n_matrix = covariance_moments (cov, MOMENT_NONE);
+  idata->n = covariance_moments (cov, MOMENT_NONE);
 
   if ( factor->method == METHOD_CORR)
     {
-      analysis_matrix = correlation_from_covariance (cov_matrix, var_matrix);
+      idata->corr = correlation_from_covariance (idata->cov, var_matrix);
+      analysis_matrix = idata->corr;
     }
   else
-    analysis_matrix = cov_matrix;
+    analysis_matrix = idata->cov;
 
   if ( factor->print & PRINT_UNIVARIATE)
     {
@@ -1262,9 +1429,9 @@ do_factor (const struct cmd_factor *factor, struct casereader *r)
 
       const int nr = heading_rows + factor->n_vars;
 
-      struct tab_table *t = tab_create (nc, nr, 0);
+      struct tab_table *t = tab_create (nc, nr);
       tab_title (t, _("Descriptive Statistics"));
-      tab_dim (t, tab_natural_dimensions, NULL);
+      tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
       tab_headers (t, heading_columns, 0, heading_rows, 0);
 
@@ -1296,54 +1463,13 @@ do_factor (const struct cmd_factor *factor, struct casereader *r)
 
          tab_double (t, 1, i + heading_rows, 0, gsl_matrix_get (mean_matrix, i, i), NULL);
          tab_double (t, 2, i + heading_rows, 0, sqrt (gsl_matrix_get (var_matrix, i, i)), NULL);
-         tab_double (t, 3, i + heading_rows, 0, gsl_matrix_get (n_matrix, i, i), wfmt);
+         tab_double (t, 3, i + heading_rows, 0, gsl_matrix_get (idata->n, i, i), wfmt);
        }
 
       tab_submit (t);
     }
 
-  if ( factor->print & PRINT_DETERMINANT)
-    {
-      const int nc = 2;
-      const int heading_columns = 0;
-      const int heading_rows = 0;
-      const int nr = 1;
-      struct tab_table *t ;
-
-      int sign = 0;
-      double det = 0.0;
-      const int size = analysis_matrix->size1;
-      gsl_permutation *p = gsl_permutation_calloc (size);
-      gsl_matrix *tmp = gsl_matrix_calloc (size, size);
-
-      gsl_matrix_memcpy (tmp, analysis_matrix);
-      gsl_linalg_LU_decomp (tmp, p, &sign);
-      det = gsl_linalg_LU_det (tmp, sign);
-      gsl_permutation_free (p);
-      gsl_matrix_free (tmp);
-
-      t = tab_create (nc, nr, 0);
-
-      if ( factor->method == METHOD_CORR)
-       tab_title (t, _("Correlation Matrix"));
-      else 
-       tab_title (t, _("Covariance Matrix"));
-
-      tab_dim (t, tab_natural_dimensions, NULL);
-
-      tab_headers (t, heading_columns, 0, heading_rows, 0);
-
-      tab_hline (t, TAL_1, 0, nc - 1, heading_rows);
-
-      tab_text (t, 0, 0, TAB_LEFT | TAT_TITLE, _("Determinant"));
-      tab_double (t, 1, 0, 0, det, NULL);
-
-      tab_submit (t);
-    }
-
-
-  idata = idata_alloc (factor->n_vars);
-
+  show_correlation_matrix (factor, idata);
 
 #if 1
   {
@@ -1417,6 +1543,8 @@ do_factor (const struct cmd_factor *factor, struct casereader *r)
 
     factor_matrix_workspace_free (fmw);
 
+    show_scree (factor, idata);
+
     show_factor_matrix (factor, idata, factor_matrix);
 
     gsl_vector_free (initial_communalities);