Logo Search packages:      
Sourcecode: gretl version File versions

plotspec.c

/* 
 *  gretl -- Gnu Regression, Econometrics and Time-series Library
 *  Copyright (C) 2001 Allin Cottrell and Riccardo "Jack" Lucchetti
 * 
 *  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 "libgretl.h"
#include "plotspec.h"

/**
 * plotspec_label_init:
 * @lbl: pointer to gnuplot label struct.
 * 
 * Initializes the label.
 */

void plotspec_label_init (GPT_LABEL *lbl)
{
    lbl->text[0] = '\0';
    lbl->just = GP_JUST_LEFT;
    lbl->pos[0] = NADBL;
    lbl->pos[1] = NADBL;
}

GPT_SPEC *plotspec_new (void)
{
    GPT_SPEC *spec;
    int i;

    spec = malloc(sizeof *spec);
    if (spec == NULL) {
      return NULL;
    }

    spec->lines = NULL;

    for (i=0; i<4; i++) {
      spec->titles[i][0] = 0;
    }

    *spec->xvarname = '\0';
    *spec->yvarname = '\0';

    spec->literal = NULL;
    spec->n_literal = 0;

    for (i=0; i<MAX_PLOT_LABELS; i++) {
      plotspec_label_init(&(spec->labels[i]));
    }

    spec->xtics[0] = 0;
    spec->mxtics[0] = 0;
    spec->fname[0] = 0;
    strcpy(spec->keyspec, "left top");

    for (i=0; i<4; i++) {
      spec->range[i][0] = NADBL;
      spec->range[i][1] = NADBL;
      if (i < 3) {
          spec->logbase[i] = 0.0;
      }
    }

    spec->b_ols = NULL;
    spec->b_quad = NULL;
    spec->b_inv = NULL;

    spec->code = PLOT_REGULAR;
    spec->flags = 0;
    spec->fit = PLOT_FIT_NONE;
    spec->fp = NULL;
    spec->data = NULL;
    spec->markers = NULL;
    spec->n_markers = 0;
    spec->labeled = NULL;
    spec->ptr = NULL;
    spec->reglist = NULL;
    spec->n_lines = 0;
    spec->nobs = 0;
    spec->okobs = 0;
    spec->pd = 0;
    spec->boxwidth = 0;
    spec->samples = 0;

    spec->termtype = GP_TERM_NONE;

    return spec;
}

void plotspec_destroy (GPT_SPEC *spec)
{
    if (spec == NULL) {
      return;
    }

    if (spec->lines != NULL) {
      free(spec->lines);
    }

    if (spec->data != NULL) {
      free(spec->data);
    }

    if (spec->reglist != NULL) {
      free(spec->reglist);
    }

    if (spec->literal != NULL) {
      free_strings_array(spec->literal, spec->n_literal);
    }

    if (spec->markers != NULL) {
      free_strings_array(spec->markers, spec->n_markers);
    }

    if (spec->labeled != NULL) {
      free(spec->labeled);
    }

    gretl_matrix_free(spec->b_ols);
    gretl_matrix_free(spec->b_quad);
    gretl_matrix_free(spec->b_inv);

    free(spec);
}

int plotspec_add_line (GPT_SPEC *spec)
{
    GPT_LINE *lines;
    int n = spec->n_lines;

    lines = realloc(spec->lines, (n + 1) * sizeof *lines);
    if (lines == NULL) {
      return E_ALLOC;
    }

    spec->lines = lines;
    spec->n_lines += 1;

    lines[n].varnum = 0;
    lines[n].title[0] = 0;
    lines[n].formula[0] = 0;
    lines[n].style[0] = 0;
    lines[n].scale[0] = 0;
    lines[n].yaxis = 1;
    lines[n].type = 0;
    lines[n].width = 1;
    lines[n].ncols = 0;

    return 0;
}

static char *escape_quotes (const char *s)
{
    if (strchr(s, '"') == NULL) {
      return NULL;
    } else {
      int qcount = 0;
      char *ret, *r;
      const char *p = s;

      while (*p) {
          if (*p == '"') qcount++;
          p++;
      }

      ret = malloc(strlen(s) + 1 + qcount);
      if (ret == NULL) {
          return NULL;
      }

      r = ret;
      while (*s) {
          if (*s == '"') {
            *r++ = '\\';
            *r++ = '"';
          } else {
            *r++ = *s;
          }
          s++;
      }
      *r = 0;

      return ret;
    }
}

const char *gp_justification_string (int j)
{
    if (j == GP_JUST_LEFT) {
      return "left";
    } else if (j == GP_JUST_CENTER) {
      return "center";
    } else if (j == GP_JUST_RIGHT) {
      return "right";
    } else {
      return "left";
    }
}

static void 
print_plot_labelspec (const GPT_LABEL *lbl, FILE *fp)
{
    char *label = escape_quotes(lbl->text);

    fprintf(fp, "set label \"%s\" ", (label != NULL)? 
          label : lbl->text);

    gretl_push_c_numeric_locale();

    fprintf(fp, "at %g,%g %s%s\n", 
          lbl->pos[0], lbl->pos[1],
          gp_justification_string(lbl->just),
          gnuplot_label_front_string());

    gretl_pop_c_numeric_locale();

    if (label != NULL) {
      free(label);
    }
}

static int print_data_labels (const GPT_SPEC *spec, FILE *fp)
{
    const double *x, *y;
    double xrange, yrange;
    double yoff;
    int t;

    if (spec->n_lines > 2 || 
      spec->lines[0].ncols != 2 || 
      spec->markers == NULL) {
      return 1;
    }

    x = spec->data;
    y = x + spec->nobs;

    xrange = spec->range[0][1] - spec->range[0][0];
    yrange = spec->range[1][1] - spec->range[1][0];

    if (xrange == 0.0 || yrange == 0.0) {
      double ymin = 1.0e+16, ymax = -1.0e+16;
      double xmin = 1.0e+16, xmax = -1.0e+16;

      for (t=0; t<spec->nobs; t++) {
          if (!na(x[t]) && !na(y[t])) {
            if (yrange == 0.0) {
                if (y[t] < ymin) {
                  ymin = y[t];
                } else if (y[t] > ymax) {
                  ymax = y[t];
                }
            }
            if (xrange == 0.0) {
                if (x[t] < xmin) {
                  xmin = x[t];
                } else if (x[t] > xmax) {
                  xmax = x[t];
                }
            }           
          }
      }
      if (yrange == 0.0) {
          yrange = ymax - ymin;
      }
      if (xrange == 0.0) {
          xrange = xmax - xmin;
      }
    }   

    yoff = 0.03 * yrange;

    fputs("# printing data labels\n", fp);

    gretl_push_c_numeric_locale();

    for (t=0; t<spec->nobs; t++) {
      double xoff = 0.0;

      if (spec->labeled != NULL && !spec->labeled[t]) {
          /* printing only specified labels */
          continue;
      }

      if (!na(x[t]) && !na(y[t])) {
          if (x[t] > .90 * xrange) {
            xoff = -.02 * xrange;
          }
          fprintf(fp, "set label \"%s\" at %.8g,%.8g\n", spec->markers[t],
                x[t] + xoff, y[t] + yoff);
      }
    }

    gretl_pop_c_numeric_locale();

    return 0;
}

void print_auto_fit_string (FitType fit, FILE *fp)
{
    if (fit == PLOT_FIT_OLS) {
      fputs("# plot includes automatic fit: OLS\n", fp);
    } else if (fit == PLOT_FIT_QUADRATIC) {
      fputs("# plot includes automatic fit: quadratic\n", fp);
    } else if (fit == PLOT_FIT_INVERSE) {
      fputs("# plot includes automatic fit: inverse\n", fp);
    } else if (fit == PLOT_FIT_LOESS) {
      fputs("# plot includes automatic fit: loess\n", fp);
    }
}

void print_plot_ranges_etc (const GPT_SPEC *spec, FILE *fp)
{
    const char *rstrs[] = {
      "x", "y", "y2", "t"
    };
    int i;

    gretl_push_c_numeric_locale();

    for (i=0; i<4; i++) {
      if (i < 3 && spec->logbase[i] > 0.0) {
          fprintf(fp, "set logscale %s %g\n", rstrs[i], spec->logbase[i]);
      }

      if (na(spec->range[i][0]) || na(spec->range[i][1]) ||
          spec->range[i][0] == spec->range[i][1]) {
          continue;
      }

      if ((i == 2 && !(spec->flags & GPT_Y2AXIS)) ||
          (i == 3 && !(spec->flags & GPT_PARAMETRIC))) {
          continue;
      }

      fprintf(fp, "set %srange [%.7g:%.7g]\n", rstrs[i], 
            spec->range[i][0], spec->range[i][1]);

      if (i == 4 && spec->code == PLOT_PROB_DIST && spec->samples == 0) {
          int ns = spec->range[i][1] - spec->range[i][0] + 1;

          fprintf(fp, "set samples %d\n", ns);
      }
    }

    if (spec->boxwidth > 0 && spec->boxwidth < 1) {
      fprintf(fp, "set boxwidth %.3f\n", (double) spec->boxwidth);
    }

    gretl_pop_c_numeric_locale();
}

#define show_fit(s) (s->fit == PLOT_FIT_OLS || \
                     s->fit == PLOT_FIT_QUADRATIC || \
                     s->fit == PLOT_FIT_INVERSE || \
                     s->fit == PLOT_FIT_LOESS)

int plotspec_print (const GPT_SPEC *spec, FILE *fp)
{
    int i, k, t;
    int png = get_png_output(spec);
    int mono = (spec->flags & GPT_MONO);
    int started_data_lines = 0;
    int n_lines = spec->n_lines;
    double et, yt;
    double *x[4];
    int any_y2 = 0;
    int miss = 0;

    if (spec->flags & GPT_FIT_HIDDEN) {
      n_lines--;
    }

    if (spec->pd > 0) {
      fprintf(fp, "# timeseries %d", spec->pd);
      if (spec->flags & GPT_LETTERBOX) {
          fputs(" (letterbox)\n", fp);
      } else {
          fputc('\n', fp);
      }
    }

    if (!mono && gnuplot_has_rgb()) {
      write_plot_line_styles(spec->code, fp);
    }

    if (!string_is_blank(spec->titles[0])) {
      fprintf(fp, "set title \"%s\"\n", spec->titles[0]);
    }

    if (!string_is_blank(spec->titles[1])) {
      fprintf(fp, "set xlabel \"%s\"\n", spec->titles[1]);
    }

    if (!string_is_blank(spec->titles[2])) {
      fprintf(fp, "set ylabel \"%s\"\n", spec->titles[2]);
    }

    if ((spec->flags & GPT_Y2AXIS) && !string_is_blank(spec->titles[3])) {
      fprintf(fp, "set y2label \"%s\"\n", spec->titles[3]);
    }

    for (i=0; i<MAX_PLOT_LABELS; i++) {
      if (!string_is_blank(spec->labels[i].text)) {
          print_plot_labelspec(&(spec->labels[i]), fp);
      }
    }

    if (spec->flags & GPT_XZEROAXIS) {
      fputs("set xzeroaxis\n", fp);
    }

    if (spec->flags & GPT_YZEROAXIS) {
      fputs("set yzeroaxis\n", fp);
    }    

    if (spec->flags & GPT_PARAMETRIC) {
      fputs("set parametric\n", fp);
      if (spec->samples > 0) {
          fprintf(fp, "set samples %d\n", spec->samples);
      }
    }

    gnuplot_missval_string(fp);

    if (strcmp(spec->keyspec, "none") == 0) {
      fputs("set nokey\n", fp);
    } else {
      fprintf(fp, "set key %s\n", spec->keyspec);
    }

    print_plot_ranges_etc(spec, fp);

    /* customized xtics? */
    if (!string_is_blank(spec->xtics)) {
      fprintf(fp, "set xtics %s\n", spec->xtics);
    }
    if (!string_is_blank(spec->mxtics)) {
      fprintf(fp, "set mxtics %s\n", spec->mxtics);
    }

    if (spec->flags & GPT_Y2AXIS) {
      /* using two y axes */
      fputs("set ytics nomirror\n", fp);
      fputs("set y2tics\n", fp);
    } else if (spec->flags & GPT_MINIMAL_BORDER) {
      /* suppressing part of border */
      fputs("set border 3\n", fp);
      if (string_is_blank(spec->xtics)) {
          fputs("set xtics nomirror\n", fp);
      }
      fputs("set ytics nomirror\n", fp);
    }

    /* in case of plots that are editable (see gui client), it is
       important to write out the comment string that identifies the
       sort of graph, so that it will be recognized by type when
       it is redisplayed */

    write_plot_type_string(spec->code, fp);

    if (spec->n_literal > 0) {
      fprintf(fp, "# literal lines = %d\n", spec->n_literal);
      for (i=0; i<spec->n_literal; i++) {
          if (spec->literal[i] != NULL && *spec->literal[i] != '\0') {
            fprintf(fp, "%s\n", spec->literal[i]);
          } else {
            fputs("# empty line!\n", fp);
          }
      }
    }

    if (show_fit(spec)) {
      print_auto_fit_string(spec->fit, fp);
    }

    if ((spec->code == PLOT_FREQ_SIMPLE ||
       spec->code == PLOT_FREQ_NORMAL ||
       spec->code == PLOT_FREQ_GAMMA) && gnuplot_has_style_fill()) {
      if (mono) {
          fputs("set style fill solid 0.3\n", fp);
      } else {
          fputs("set style fill solid 0.6\n", fp);
      }
    } else if (spec->code == PLOT_FORECAST) {
      fputs("set style fill solid 0.4\n", fp);
    }

    if (spec->flags & GPT_ALL_MARKERS) {
      print_data_labels(spec, fp);
    }

    fputs("plot \\\n", fp);

    for (i=0; i<n_lines; i++) {
      if ((spec->flags & GPT_Y2AXIS) && spec->lines[i].yaxis != 1) {
          any_y2 = 1;
          break;
      }
    }

    for (i=0; i<n_lines; i++) {

      if (strcmp(spec->lines[i].scale, "NA")) {
          if (!strcmp(spec->lines[i].scale, "1.0")) {
            fputs("'-' using 1", fp);
            if (spec->lines[i].ncols == 2) {
                fputs(":($2)", fp);
            } else {
                for (k=2; k<=spec->lines[i].ncols; k++) {
                  fprintf(fp, ":%d", k);
                }
            }
            fputc(' ', fp);
          } else {
            fprintf(fp, "'-' using 1:($2*%s) ", 
                  spec->lines[i].scale);
          }
      } else {
          fprintf(fp, "%s ", spec->lines[i].formula); 
      }

      if ((spec->flags & GPT_Y2AXIS) && spec->lines[i].yaxis != 1) {
          fprintf(fp, "axes x1y%d ", spec->lines[i].yaxis);
      }

      fprintf(fp, "title \"%s", spec->lines[i].title);

      if (any_y2) {
          if (spec->lines[i].yaxis == 1) {
            fprintf(fp, " (%s)\" ", G_("left"));
          } else {
            fprintf(fp, " (%s)\" ", G_("right"));
          }
      } else {
          fputs("\" ", fp);
      }

      fprintf(fp, "w %s", spec->lines[i].style);

      if (spec->lines[i].type != 0) {
          fprintf(fp, " lt %d", spec->lines[i].type);
      }
      if (spec->lines[i].width != 1) {
          fprintf(fp, " lw %d", spec->lines[i].width);
      }

      if (i == n_lines - 1) {
          fputc('\n', fp);
      } else {
          fputs(", \\\n", fp);
      }
    } 

    miss = 0;

    /* supply the data to gnuplot inline */

    gretl_push_c_numeric_locale();

    for (i=0; i<spec->n_lines; i++) { 
      int j;

      if (spec->lines[i].ncols == 0) {
          continue;
      }

      if (!started_data_lines) {
          x[0] = spec->data;
          /* see below for subsequent adjustment of x[1] */
          x[1] = x[0] + spec->nobs;
          started_data_lines = 1;
      } 

      x[2] = x[1] + spec->nobs;
      x[3] = x[2] + spec->nobs;

      for (t=0; t<spec->nobs; t++) {
          /* print x-axis value */
          if (na(x[0][t])) {
            fputs("? ", fp);
            miss = 1;
          } else {
            fprintf(fp, "%.8g ", x[0][t]);
          }

          /* conversion, if needed, between (y, ydelta) and
             (ylow, yhigh)
           */
          if (spec->lines[i].ncols == 3) {
            if (spec->flags & GPT_FILL_SWITCH) { 
                yt = x[1][t];
                et = x[2][t];
                if (!na(yt) && !na(et)) {
                  x[1][t] = yt - et;
                  x[2][t] = yt + et;
                }
            } else if (spec->flags & GPT_ERR_SWITCH) {
                if (!na(x[1][t]) && !na(x[2][t])) {
                  et = (x[2][t] - x[1][t]) / 2.0;
                  yt = x[1][t] + et;
                  x[1][t] = yt;
                  x[2][t] = et;
                }
            }
          } 

          /* print y-axis value(s) */
          for (j=1; j<spec->lines[i].ncols; j++) {
            if (na(x[j][t])) {
                fputs("? ", fp);
                miss = 1;
            } else {
                fprintf(fp, "%.8g ",  x[j][t]);
            }
          }

          if (spec->markers != NULL && i == 0) {
            fprintf(fp, " # %s", spec->markers[t]);
          }

          fputc('\n', fp);
      }

      fputs("e\n", fp);

      x[1] += (spec->lines[i].ncols - 1) * spec->nobs;
    }

    gretl_pop_c_numeric_locale();

    if (png && gnuplot_has_bbox()) {
      print_plot_bounding_box_request(fp);
    }

    return miss;
}

static int set_loess_fit (GPT_SPEC *spec, int d, double q, gretl_matrix *x,
                    gretl_matrix *y, gretl_matrix *yh)
{
    int t, T = gretl_vector_get_length(y);
    double *data;

    data = realloc(spec->data, 3 * T * sizeof *data);
    if (data == NULL) {
      return E_ALLOC;
    }

    for (t=0; t<T; t++) {
      data[t] = x->val[t];
      data[t+T] = y->val[t];
      data[t+2*T] = yh->val[t];
    }

    spec->data = data;
    spec->nobs = spec->okobs = T;

    sprintf(spec->lines[1].title, G_("loess fit, d = %d, q = %g"), d, q);
    strcpy(spec->lines[1].scale, "1.0");
    strcpy(spec->lines[1].style, "lines");
    spec->lines[1].ncols = 2;

    spec->fit = PLOT_FIT_LOESS;

    return 0;
}

static void set_fitted_line (GPT_SPEC *spec, FitType f)
{
    char *formula = spec->lines[1].formula;
    char *title = spec->lines[1].title;
    const double *b;

    if (f == PLOT_FIT_OLS) {
      b = spec->b_ols->val;
      sprintf(title, "Y = %#.3g %c %#.3gX", b[0],
            (b[1] > 0)? '+' : '-', fabs(b[1]));
      gretl_push_c_numeric_locale();
      sprintf(formula, "%g + %g*x", b[0], b[1]);
      gretl_pop_c_numeric_locale();
    } else if (f == PLOT_FIT_QUADRATIC) {
      b = spec->b_quad->val;
      sprintf(title, "Y = %#.3g %c %#.3gX %c %#.3gX^2", b[0],
            (b[1] > 0)? '+' : '-', fabs(b[1]),
            (b[2] > 0)? '+' : '-', fabs(b[2]));
      gretl_push_c_numeric_locale();
      sprintf(formula, "%g + %g*x + %g*x**2", b[0], b[1], b[2]);
      gretl_pop_c_numeric_locale();
    } else if (f == PLOT_FIT_INVERSE) {
      b = spec->b_inv->val;
      sprintf(title, "Y = %#.3g %c %#.3g(1/X)", b[0],
            (b[1] > 0)? '+' : '-', fabs(b[1]));
      gretl_push_c_numeric_locale();
      sprintf(formula, "%g + %g/x", b[0], b[1]);
      gretl_pop_c_numeric_locale();
    }

    strcpy(spec->lines[1].scale, "NA");
    strcpy(spec->lines[1].style, "lines");
    spec->lines[1].ncols = 0;

    spec->fit = f;
}

int plotspec_add_fit (GPT_SPEC *spec, FitType f)
{
    gretl_matrix *y = NULL;
    gretl_matrix *X = NULL;
    gretl_matrix *b = NULL;
    gretl_matrix *yh = NULL;
    const double *py;
    const double *px;
    int T = spec->okobs;
    double q = 0.5;
    int d = 1;
    int i, t, k;
    int err = 0;

    if ((f == PLOT_FIT_OLS && spec->b_ols != NULL) ||
      (f == PLOT_FIT_QUADRATIC && spec->b_quad != NULL) ||
      (f == PLOT_FIT_INVERSE && spec->b_inv != NULL)) {
      /* just activate existing setup */
      set_fitted_line(spec, f);
      return 0;
    }
      
    if (f == PLOT_FIT_OLS || f == PLOT_FIT_INVERSE) {
      k = 2;
    } else if (f == PLOT_FIT_QUADRATIC) {
      k = 3;
    } else if (f == PLOT_FIT_LOESS) {
      k = 1;
    } else {
      return E_DATA;
    }

    y = gretl_column_vector_alloc(T);
    X = gretl_matrix_alloc(T, k);
    if (y == NULL || X == NULL) {
      err = E_ALLOC;
      goto bailout;
    }

    if (f != PLOT_FIT_LOESS) {
      b = gretl_column_vector_alloc(k);
      if (b == NULL) {
          err = E_ALLOC;
          goto bailout;
      }
    }

    px = spec->data;
    py = px + spec->nobs;

    i = 0;
    for (t=0; t<spec->nobs; t++) {
      if (!na(py[t]) && !na(px[t])) {
          y->val[i] = py[t];
          if (f == PLOT_FIT_LOESS) {
            gretl_matrix_set(X, i, 0, px[t]);
          } else {
            gretl_matrix_set(X, i, 0, 1.0);
            if (f == PLOT_FIT_INVERSE) {
                gretl_matrix_set(X, i, 1, 1.0 / px[t]);
            } else {
                gretl_matrix_set(X, i, 1, px[t]);
            }
            if (f == PLOT_FIT_QUADRATIC) {
                gretl_matrix_set(X, i, 2, px[t] * px[t]);
            }
          }
          i++;
      }
    }

    if (f == PLOT_FIT_LOESS) {
      err = sort_pairs_by_x(X, y, NULL, spec->markers);
      if (!err) {
          yh = loess_fit(X, y, d, q, OPT_R, &err);
      }
    } else {
      err = gretl_matrix_ols(y, X, b, NULL, NULL, NULL);
    }

    if (!err && spec->n_lines == 1) {
      err = plotspec_add_line(spec);
    }

    if (!err) {
      if (f == PLOT_FIT_OLS) {
          spec->b_ols = b;
          b = NULL;
      } else if (f == PLOT_FIT_QUADRATIC) {
          spec->b_quad = b;
          b = NULL;
      } else if (f == PLOT_FIT_INVERSE) {
          spec->b_inv = b;
          b = NULL;
      }         
    }

    if (!err) {
      if (f == PLOT_FIT_LOESS) {
          set_loess_fit(spec, d, q, X, y, yh);
      } else {
          set_fitted_line(spec, f);
      }
    }

 bailout:
    
    gretl_matrix_free(y);
    gretl_matrix_free(X);
    gretl_matrix_free(b);
    gretl_matrix_free(yh);

    return err;
}

Generated by  Doxygen 1.6.0   Back to index