/* Copyright (C) 2003, 2004 Free Software Foundation, Inc. * * This file is part of Dap. * * Dap 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. * * Dap 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 Dap. If not, see . */ #include #include #include #include "sbs.h" extern char sbstmp[TOKENLEN + 1]; /* name of current temp dataset */ extern int sbslineno; /* Translate call to GLM. step starts after "proc glm" */ void glmtrans(char *step, FILE *dapfile) { int s; /* index to step */ int sincr; /* increment for s */ static char setname[TOKENLEN + 1]; int modelstart; /* starting position of model */ static char response[TOKENLEN + 1]; /* name of response variable */ int resplen; /* length of response variable name */ int e; /* index to step, for stepping through effects of the model */ int lsmeans; /* 0 = no lsmeans statement, > 0 is start of lsmeans statement. */ static char test[TOKENLEN + 1]; /* dunnett, tukey, or lsd */ static char level[TOKENLEN + 1]; /* alpha requested for lsmeans */ int term; /* starting position of term in numerator of ftest */ int nomatch; /* lsmeans term doesn't match ftest term */ int classstart; /* start of class variable list */ int nbyvars; /* number of by vars: need to know this for contrasts */ int classvar; /* index of class var in contrast */ int c; /* index to class vars */ static char classname[TOKENLEN + 1]; /* name of class var in contrast */ unsigned int contrastterm; /* value for _term_ variable for contrasts */ int *coeff; /* coefficients for the effect */ int ncoeff; /* number of coefficients for contrast */ int minus; /* coeff has minus sign? (read as separate token) */ int coeffsum; /* sum of coefficients for contrast */ if (!getoption(step, "data", setname, 1)) strcpy(setname, sbstmp); if ((modelstart = findstatement(step, "model"))) resplen = linecpy(response, step + modelstart); else { fprintf(stderr, "sbstrans: before %d: missing model statement in proc glm\n", sbslineno); exit(1); } if (!(classstart = findstatement(step, "class"))) { fprintf(stderr, "sbstrans: before %d: missing class statement in proc glm\n", sbslineno); exit(1); } if ((s = findstatement(step, "by"))) { for (nbyvars = 0; step[s] && step[s] != ';'; nbyvars++) s += linecpy((char *) NULL, step + s) + 1; } else nbyvars = 0; /* we have to sort by the class vars and to get means, etc. */ fprintf(dapfile, "sort(\"%s\", \"", setname); copylist(step, "by", dapfile); copylist(step, "class", dapfile); fputs("\", \"\");\n", dapfile); /* we have to get means and vars */ fprintf(dapfile, "means(\"%s.srt\", \"%s\", \"N MEAN VAR\", \"", setname, response); copylist(step, "by", dapfile); copylist(step, "class", dapfile); fputs("\");\n", dapfile); /* now we can start the ANOVA and test the whole model */ fprintf(dapfile, "effects(\"%s.srt.mns\", \"%s ", setname, response); copylist(step, "class", dapfile); fputs("\", \"", dapfile); s = modelstart + resplen + 1; if (step[s] != '=') { fprintf(stderr, "sbstrans: before %d: missing = in model statement in proc glm\n", sbslineno); exit(1); } for (s += 2; step[s] && step[s] != '/' && step[s] != ';'; s++) { if (step[s] == '\n') putc(' ', dapfile); else putc(step[s], dapfile); } fputs("\", \"", dapfile); copylist(step, "by", dapfile); fputs("\");\n", dapfile); /* now we've run the model, it's time to test each effect in it */ /* we need a separate call to ftest for each effect and check for lsmeans statements */ /* first get the first lsmeans statement */ if ((lsmeans = findstatement(step, "lsmeans"))) { /* see if it specifies an error term */ if (getoption(step + lsmeans, "e", (char *) NULL, 1)) lsmeans = 0; /* not going to use it */ else /* need to get test name, alpha */ { for (s = lsmeans; step[s] && step[s] != '/' && step[s] != ';'; s++) ; /* get to options */ test[0] = '\0'; /* haven't found one yet... */ if (step[s] == '/') { for (s += 2; step[s] && step[s] != ';'; ) { s += linecpy(test, step + s) + 1; upper(test); if (strcmp(test, "DUNNETT") && strcmp(test, "TUKEY") && strcmp(test, "LSD")) test[0] = '\0'; /* wasn't, after all */ } } if (!test[0]) { fprintf(stderr, "sbstrans: before %d: no test specified in lsmeans statement in proc glm\n", sbslineno); exit(1); } if (!getoption(step + lsmeans, "alpha", level, 1)) strcpy(level, "0.05"); } } /* now we're ready to test the terms in the model, one-by-one */ e = modelstart + resplen + 3; while (step[e] && step[e] != '/' && step[e] != ';') { fprintf(dapfile, "ftest(\"%s.srt.mns.con\", \"%s ", setname, response); copylist(step, "class", dapfile); fputs("\", \"", dapfile); /* here comes the numerator */ term = e; /* mark this place for lsmeans */ while (step[e] && step[e] != '\n') { putc(step[e], dapfile); e++; /* now see if it's a crossed or nested effect */ if (step[e] == '\n' && step[e + 1] == '*') /* then need to keep copying */ { putc('*', dapfile); e += 3; /* get to next variable */ term = 0; /* mark as crossed: lsmeans can't handle these yet */ } } fputs("\", \"\", \"", dapfile); /* null denominator */ copylist(step, "by", dapfile); fputs("\");\n", dapfile); e++; /* position at next term */ if (lsmeans && term) /* term is position of numerator in ftest just run */ { for (s = lsmeans; step[s] && step[s] != '/' && step[s] != ';' && (nomatch = linecmp(step + s, step + term)); s += linecpy((char *) NULL, step + s) + 1) ; /* search for ftest effect in lsmeans statement */ if (!nomatch) { fprintf(dapfile, "lsmeans(\"%s.srt.mns.tst\", \"%s\", %s, \"%s ", setname, test, level, response); copylist(step, "class", dapfile); fputs("\", \"", dapfile); for (s = term; step[s] && step[s] != '\n'; s++) putc(step[s], dapfile); fputs("\", \"", dapfile); copylist(step, "by", dapfile); fputs("\", \"s12\");\n", dapfile); } } } /* now do specific test request */ for (s = 0; (sincr = findstatement(step + s, "test")); ) { s += sincr; fprintf(dapfile, "ftest(\"%s.srt.mns.con\", \"%s ", setname, response); copylist(step, "class", dapfile); fputs("\", \"", dapfile); if (!step[s] || linecmp(step + s, "h") || linecmp(step + s + 2, "=")) { fprintf(stderr, "sbstrans: before %d: missing h= in test statement in proc glm\n", sbslineno); exit(1); } for (s += 4; step[s] && (linecmp(step + s, "e") || (step[s + 2] && linecmp(step + s + 2, "="))); s++) s += putlines(step + s, dapfile, '\n'); /* putlines puts a space */ if (!step[s] || linecmp(step + s, "e") || linecmp(step + s + 2, "=")) { fprintf(stderr, "sbstrans: before %d: missing e= in test statement in proc glm\n", sbslineno); exit(1); } fputs("\", \"", dapfile); s += 4; s += putlines(step + s, dapfile, ';'); if (step[s] != ';') { fprintf(stderr, "sbstrans: before %d: extra characters after e= in test statement in proc glm\n", sbslineno); exit(1); } fputs("\", \"", dapfile); copylist(step, "by", dapfile); fputs("\");\n", dapfile); } /* Now do contrasts: each contrast statement runs a separate ftest */ /* First set up array for coefficient values: this is an overestimate; so? */ /* And it's unncessary if there's no contrast statement */ coeff = (int *) malloc(sizeof(int) * strlen(step) / 2); /* First set up array for coefficient values: this is an overestimate; so? */ for (s = 0; (sincr = findstatement(step + s, "contrast")); ) { s += sincr; if (step[s] != '"') { fprintf(stderr, "sbstrans: before %d: missing \"LABEL\" in contrast statement in proc glm\n", sbslineno); exit(1); } fputs("title(\"", dapfile); for (s++; step[s] && step[s] != '"'; s++) /* can't use putlines because we want the newlines */ putc(step[s], dapfile); if (step[s] != '"') { fprintf(stderr, "sbstrans: before %d: no terminating \" in contrast statement label in proc glm\n", sbslineno); exit(1); } fputs("\");\n", dapfile); s += 2; /* on to effect and values */ /* We need to revise the .con file "by hand" for ftest */ /* We need to set up _term_ for the class variable specified in the contrast. */ /* Find class variable referenced */ for (contrastterm = 0x1, e = classstart, classvar = 0; step[e] && step[e] != ';'; contrastterm = (contrastterm << 1), classvar++) { if (!linecmp(step + s, step + e)) break; else e += linecpy((char *) NULL, step + e) + 1; } classvar++; /* now is 1, 2, ..., 3 */ s += linecpy(classname, step + s) + 1; /* should have coefficients now */ for (ncoeff = 0, coeffsum = 0; num(step[s]) || step[s] == '+' || step[s] == '-'; ncoeff++) { if (step[s] == '+' || step[s] == '-') { minus = (step[s] == '-'); s += 2; } else minus = 0; if (sscanf(step + s, "%d", coeff + ncoeff) != 1) { fprintf(stderr, "sbstrans: before %d: invalid coefficient in contrast statement in proc glm\n", sbslineno); exit(1); } if (minus) coeff[ncoeff] = -coeff[ncoeff]; coeffsum += coeff[ncoeff]; s += linecpy((char *) NULL, step + s) + 1; } if (coeffsum) { fprintf(stderr, "sbstrans: before %d: coefficients sum to nonzero in contrast statement in proc glm\n", sbslineno); exit(1); } if (step[s] == '/') /* on to e=, if any */ s += 2; /* We'll start with the file that effects created */ fprintf(dapfile, "inset(\"%s.srt.mns.con\")\n{\n", setname); fprintf(dapfile, "char _type_[9];\ndouble %s;\nint _n_, _term_;\n", response); fprintf(dapfile, "int _partv_[%d];\n", nbyvars + classvar); fprintf(dapfile, "int _c_, _more_, _contr1_;\ndouble _coeff_[%d];\n", ncoeff); fprintf(dapfile, "outset(\"%s.srt.mns.con.con\", \"\");\n", setname); fputs("dap_list(\"", dapfile); copylist(step, "by", dapfile); for (e = classstart, c = 0; c < classvar; c++) e += putlines(step + e, dapfile, '\n') + 1; fprintf(dapfile, "\", _partv_, %d);\n", nbyvars + classvar); /* now we need to set the coefficient array values */ for (c = 0; c < ncoeff; c++) fprintf(dapfile, "_coeff_[%d] = %d.0;\n", c, coeff[c]); /* first write out N, MEAN, VAR */ fputs("for (_c_ = 0, _contr1_ = 1, _more_ = step(); _more_; )\n{\n", dapfile); fprintf(dapfile, "if (dap_newpart(_partv_, %d))\n_c_ = 0;\n", nbyvars + classvar - 1); fprintf(dapfile, "else if (dap_newpart(_partv_, %d))\n_c_++;\n", nbyvars + classvar); fputs("output();\nstep();\noutput();\nstep();\noutput();\n", dapfile); /* need to include ERROR lines, but skip CONTR lines for specified effect */ fputs("while ((_more_ = step()))\n{\n", dapfile); fputs("if (!strcmp(_type_, \"ERROR\"))\noutput();\n", dapfile); fputs("else if (!strcmp(_type_, \"CONTR\"))\n{\n", dapfile); fprintf(dapfile, "if (_term_ == %d)\n{\n", contrastterm); /* if 1st, change to contrast */ fprintf(dapfile, "if (_contr1_)\n{\n_contr1_ = 0;\n_term_ = %d;\n", contrastterm); fprintf(dapfile, "if (_c_ < %d)\n", ncoeff); fprintf(dapfile, "%s = _coeff_[_c_];\nelse\n%s = 0.0;\n", response, response); fputs("output();\n}\n}\n", dapfile); fputs("else\noutput();\n}\n", dapfile); fputs("else if (!strcmp(_type_, \"LSMEAN\"))\noutput();\n", dapfile); fputs("else\n\{\n_contr1_ = 1;\nbreak;\n}\n}\n}\n}\n", dapfile); /* at another cell */ fprintf(dapfile, "ftest(\"%s.srt.mns.con.con\", \"%s ", setname, response); copylist(step, "class", dapfile); fprintf(dapfile, "\", \"%s\", \"", classname); if ((sincr = getoption(step + s, "e", (char *) NULL, 1))) { s += sincr; s += putlines(step + s, dapfile, ';'); } if (!linecmp(step + s, ";")) s += 2; else { fprintf(stderr, "sbstrans: before %d: missing ; at end of contrast statement in proc glm\n", sbslineno); exit(1); } fputs("\", \"", dapfile); copylist(step, "by", dapfile); fputs("\");\ntitle(NULL);\n", dapfile); } free(coeff); /* now do lsmeans statement(s) that we haven't done yet, if any */ while ((s = findstatement(step + lsmeans, "lsmeans"))) /* find next lsmeans statement */ { lsmeans += s; /* first get position of denominator, if any */ e = lsmeans + getoption(step + lsmeans, "e", (char *) NULL, 1); if (!getoption(step + lsmeans, "alpha", level, 1)) strcpy(level, "0.05"); /* get test type */ test[0] = '\0'; /* in case we never find one */ for (s = lsmeans; step[s] && step[s] != '/' && step[s] != ';'; s++) ; if (step[s] == '/') { for (s += 2; step[s] && step[s] != ';'; ) { s += linecpy(test, step + s) + 1; upper(test); if (strcmp(test, "DUNNETT") && strcmp(test, "TUKEY") && strcmp(test, "LSD")) test[0] = '\0'; /* wasn't, after all */ } } if (!test[0]) { fprintf(stderr, "sbstrans: before %d: no test specified in lsmeans statement in proc glm\n", sbslineno); exit(1); } upper(test); for (s = lsmeans; step[s] && step[s] != '/' && step[s] != ';'; s++) { /* one term at a time */ fprintf(dapfile, "ftest(\"%s.srt.mns.con\", \"%s ", setname, response); copylist(step, "class", dapfile); fputs("\", \"", dapfile); /* here comes the numerator */ term = s; /* mark this place for call to lsmeans */ s += putlines(step + s, dapfile, '\n'); fputs("\", \"", dapfile); if (e > lsmeans) /* put denominator */ { for (s = e; step[s] && step[s] != ';' && linecmp(step + s, "alpha") && linecmp(step + s, "dunnett") && linecmp(step + s, "tukey") && linecmp(step + s, "lsd"); s += putlines(step + s, dapfile, '\n') + 1) ; } fputs("\", \"", dapfile); copylist(step, "by", dapfile); fputs("\");\n", dapfile); /* now the lsmeans statement */ fprintf(dapfile, "lsmeans(\"%s.srt.mns.tst\", \"%s\", %s, \"%s ", setname, test, level, response); copylist(step, "class", dapfile); fputs("\", \"", dapfile); s = term + putlines(step + term, dapfile, '\n'); fputs("\", \"", dapfile); copylist(step, "by", dapfile); fputs("\", \"s12\");\n", dapfile); } } } /* Translate call to LOGISTIC. step starts after "proc logistic" */ void logistictrans(char *step, FILE *dapfile) { int s; /* index to step */ char setname[TOKENLEN + 1]; char outname[TOKENLEN + 1]; if (!getoption(step, "data", setname, 1)) strcpy(setname, sbstmp); fprintf(dapfile, "logreg(\"%s\", \"", setname); if ((s = findstatement(step, "model"))) { /* response variable */ s += putlines(step + s, dapfile, '\n') + 1; /* only one response variable allowed */ putc('/', dapfile); /* both forms use / in Dap */ if (step[s] == '/') /* we have the events / trials form */ { s += 2; s += putlines(step + s, dapfile, '\n') + 1; } else putc('1', dapfile); /* binary response */ if (step[s] == '=') { fputs("\", \"\", \"", dapfile); /* x0-var-list is empty */ s += 2; s += putlines(step + s, dapfile, ';'); fputs("\", \"", dapfile); /* closes the x1 variable list */ copylist(step, "by", dapfile); fputs("\", NULL, 0.95);\n", dapfile); } else { fprintf(stderr, "sbstrans: before %d: missing = in model statement in proc logistic.\n", sbslineno); exit(1); } } else { fprintf(stderr, "sbstrans: before %d: missing model statement in proc logistic.\n", sbslineno); exit(1); } if (getoption(step, "outest", outname, 1)) { fprintf(dapfile, "dataset(\"%s.cov\", \"%s\", \"RENAME\");\n", setname, outname); strcpy(sbstmp, outname); } } /* Translate call to NPAR1WAY. step starts after "proc npar1way" */ void npar1waytrans(char *step, FILE *dapfile) { int s; /* index to step */ char setname[TOKENLEN + 1]; char classname[TOKENLEN + 1]; /* name of class variable */ if (!getoption(step, "data", setname, 1)) strcpy(setname, sbstmp); if ((s = findstatement(step, "class"))) linecpy(classname, step + s); else { fprintf(stderr, "sbstrans: before %d: missing class statement in proc npar1way.\n", sbslineno); exit(1); } if ((s = findstatement(step, "var"))) { while (step[s] && step[s] != ';') /* if there are multiple variables, then... */ { /* need to run nonparam for each */ fprintf(dapfile, "nonparam(\"%s\", \"", setname); while (step[s] && step[s] != '\n') { putc(step[s], dapfile); s++; } fprintf(dapfile, " %s\", \"", classname); copylist(step, "by", dapfile); fputs("\");\n", dapfile); s++; } if (step[s] != ';') { fprintf(stderr, "sbstrans: before %d: missing ; at end of proc npar1way.\n", sbslineno); exit(1); } } else { fprintf(stderr, "sbstrans: before %d: missing var statement in proc npar1way.\n", sbslineno); exit(1); } } /* Translate call to REG. step starts after "proc reg" */ void regtrans(char *step, FILE *dapfile) { int s; /* index to step */ char setname[TOKENLEN + 1]; char outname[TOKENLEN + 1]; int isadd; /* is there an add statement? */ if (!getoption(step, "data", setname, 1)) strcpy(setname, sbstmp); if (findstatement(step, "plot")) /* we're going to call plotlinreg */ { if (isby(step) >= 0) countparts(step, setname, dapfile); else fputs("_sbspictcnt_[_sbsnpicts_] = 1;\n", dapfile); fprintf(dapfile, "_sbspict_[_sbsnpicts_] = plotlinreg(\"%s\", \"", setname); if ((s = findstatement(step, "model"))) { /* response variable: must be only 1 */ while (step[s] && step[s] != '\n') { putc(step[s], dapfile); s++; } s++; if (step[s] == '=') { fputs("\", \"", dapfile); for (s += 2; step[s] && step[s] != '\n'; s++) putc(step[s], dapfile); if (step[s + 1] != ';') { fprintf(stderr, "sbstrans: before %d: only one explanatory variable allowed in model statement in proc reg with plotting\n", sbslineno); exit(1); } fputs("\", \"==\", \"", dapfile); copylist(step, "by", dapfile); fputs("\", _sbspictcnt_[_sbsnpicts_], 0.95);\n", dapfile); fputs("_sbspictpage_[_sbsnpicts_++] = 4;\n", dapfile); sbshaspicts = 1; } else { fprintf(stderr, "sbstrans: before %d: only one response variable allowed in model statement in proc reg with plotting\n", sbslineno); exit(1); } } else { fprintf(stderr, "sbstrans: before %d: missing model statement in proc reg.\n", sbslineno); exit(1); } } else /* just use linreg */ { fprintf(dapfile, "linreg(\"%s\", \"", setname); if ((s = findstatement(step, "model"))) { /* response variables */ s += putlines(step + s, dapfile, '='); if (step[s] == '=') { fputs("\", \"", dapfile); if (!(isadd = findstatement(step, "add"))) /* reduced model is intercept only */ fputs("\", \"", dapfile); /* close x0-variables list */ s += 2; s += putlines(step + s, dapfile, ';'); fputs("\", \"", dapfile); /* either closes the x0 or x1 variable list */ if (isadd) /* put in the x1-variables */ { copylist(step, "add", dapfile); fputs("\", \"", dapfile); /* closes the x1 variable list */ } copylist(step, "by", dapfile); fputs("\", NULL, 0.95);\n", dapfile); } else { fprintf(stderr, "sbstrans: before %d: missing = in model statement in proc reg.\n", sbslineno); exit(1); } } else { fprintf(stderr, "sbstrans: before %d: missing model statement in proc reg.\n", sbslineno); exit(1); } } if (getoption(step, "outest", outname, 1)) { fprintf(dapfile, "dataset(\"%s.cov\", \"%s\", \"RENAME\");\n", setname, outname); strcpy(sbstmp, outname); } } /* Translate call to DAP. step starts after "proc dap" */ void daptrans(char *step, FILE *dapfile) { int s; /* index to step */ int brace; /* level of braces nesting */ s = 0; if (step[s] != ';') { fprintf(stderr, "sbstrans: before %d: no options allowed for proc dap\n", sbslineno); exit(1); } s += 2; if (step[s] == '{') { fputs("{\n", dapfile); for (s += 2, brace = 1; brace && step[s]; s++) { if (step[s] == '\n') putc(' ', dapfile); else putc(step[s], dapfile); if (step[s] == ';' || step[s] == '{' || step[s] == '}') putc('\n', dapfile); if (step[s] == '{') brace++; else if (step[s] == '}') --brace; } if (brace) { fprintf(stderr, "sbstrans: before %d: missing } in proc dap\n", sbslineno); exit(1); } putc('\n', dapfile); } else { s += putlines(step + s, dapfile, ';'); if (step[s] != ';') { fprintf(stderr, "sbstrans: before %d: missing ; in proc dap\n", sbslineno); exit(1); } fputs(";\n", dapfile); } }