Rev 8457 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/* MONothetic Analysis --- MONAProgram for divisive hierarchical clustering of binary data,using association analysis.*//* mona.f -- translated by f2c (version 20031025).Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,http://www.netlib.org/f2c/libf2c.zipFurther transformed by$Id: f2c-clean,v 1.11 2012/05/04 19:34:33 maechler Exp $*/#include <R.h>// #include <Rinternals.h>#include <R_ext/Print.h>/* for diagnostics */#include <R_ext/Utils.h>/* for interrupting */// iabs(j) = |j|static int iabs(int j){return (j >= 0) ? (j) : -(j);}void clmona(int *nn, // = number of objectsint *pp, // = number of variablesint *x, /* x[i,j]: binary (0/1/NA) data (obs. i, var.j)where NA = missing values, are all values > 1 ;actually are NA == 2 when called from R's mona() */// int jerr[1] : verbose(ness) in {0, 1, ..}// Result / Output / Return Values:int *jerr, // error return code in {1,2,3,4,5}int *nban, // [1:nn]int *ner, // [1:nn]int *kwan, // [1:nn]int *lava, // [1:nn]int *jlack)// [1:pp] : jlack[j] := #{NA's in x[,j]}{int verbose = jerr[0];int j, j0, j1, jnat, jma = -1, jtel = -1, jtelz = -1, lama;/* Parameter adjustments */--lava;--kwan;--ner;--nban;--jlack;int x_dim1 = *nn,x_offset = 1 + x_dim1;x -= x_offset;#define X(i,j) x[i + j * x_dim1]/* Function Body */if(*pp < 2) {*jerr = 5; return; // not implemented currently (patches welcome!)}const int nhalf = (*nn + 1) / 2;// jptwe = (*pp + 4) / 5;Rboolean has_NA = FALSE;for (int l = 1; l <= *nn; ++l) {int n_miss = 0;for (j = 1; j <= *pp; ++j) {if (X(l,j) > 1) ++n_miss;}if (n_miss == *pp) { // all variables missing for this object*jerr = 1; return;}if(n_miss) {has_NA = TRUE; break;}}if (has_NA) { // -------------- Missing Values Treatment -----------int lack = 0;for (j = 1; j <= *pp; ++j) {j0 = 0;j1 = 0;for (int l = 1; l <= *nn; ++l) {if (X(l,j) == 0) ++j0;else if (X(l,j) == 1) ++j1;}jlack[j] = *nn - j0 - j1;if (jlack[j] != 0) {++lack;}if (jlack[j] >= nhalf) {// at least 50% of the objects have missing values for this variable*jerr = 2; return;}if (j0 == 0 || j1 == 0) {// all non missing values are identical for this variable*jerr = 3; return;}}if (lack == *pp) { /* all variables have missing values */*jerr = 4; return;}/* ---------- Filling in missing values --------------------- */for (j = 1; j <= *pp; ++j)if (jlack[j] != 0) {lama = -1;Rboolean syn = TRUE;for (int ja = 1; ja <= *pp; ++ja)if (jlack[ja] == 0) { /* no missing in x[, ja] */int a = 0,b = 0,c = 0,d = 0;for (int k = 1; k <= *nn; ++k) {if (X(k,j) != 1) {if (X(k,ja) == 0) ++a;else if (X(k,ja) == 1) ++b;} else { // x[...] == 1if (X(k,ja) == 0) ++c;else if (X(k,ja) == 1) ++d;}}int kal = a * d - b * c,kalf = iabs(kal);if (kalf >= lama) {lama = kalf;jma = ja;if (kal < 0)syn = FALSE;}}for (int k = 1; k <= *nn; ++k)if (X(k,j) > 1) { // missingif (syn) {X(k,j) = X(k,jma);} else {if (X(k,jma) == 1)X(k,j) = 0;if (X(k,jma) == 0)X(k,j) = 1;}}}} /* --- end of treating missing values ---- */*jerr = 0; // it may have had "verbose"/* initialization --------------------------- */for (int k = 1; k <= *nn; ++k) {kwan[k] = 0;ner[k] = k;lava[k] = 0;}int npass = 1; // number of passeskwan[1] = *nn;/* algorithm -------------------------------- */int nclu = 1;int ka = 1;/* --- Loop --- */L310:R_CheckUserInterrupt(); // (had infinite loop whenever pp == 1 !)int kb = ka + kwan[ka] - 1;if(verbose) Rprintf("Loop npass = %d: (ka,kb) = (%d,%d)\n", npass, ka, kb);lama = -1;jnat = *pp;for (j = 1; j <= *pp; ++j) {if (nclu == 1) {goto L330; // jump *inside* if(.) .. hmm...}j0 = 0;j1 = 0;for (int k = ka; k <= kb; ++k) {int n_k = ner[k];if (X(n_k,j) == 0) ++j0;else if (X(n_k,j) == 1) ++j1;}if (j1 != 0 && j0 != 0) {L330:--jnat;int a = 0,b = 0,c = 0,d = 0,lams = 0;for (int jb = 1; jb <= *pp; ++jb) {if (jb != j) { // FIXME: if (p == 1) have j == jb == 1 here// then this branch is never used ==> lama = -1 < lams = 0// but (a,b,c,d) will we remain unitialized// -Wall in Fortran did make us intialize them to 0a = 0;b = 0;c = 0;d = 0;for (int k = ka; k <= kb; ++k) {int n_k = ner[k];if (X(n_k,j) == 0) {if (X(n_k,jb) == 0) ++a;else if (X(n_k,jb) == 1) ++b;} else {if (X(n_k,jb) == 0) ++c;else if (X(n_k,jb) == 1) ++d;}}lams += iabs(a * d - b * c);}}if (lama < lams) {lama = lams;jtel = c + d;jtelz = a + b;jma = j;}}} // end -- for(j in 1:p)if(verbose) Rprintf(" for(j ..) -> jma=%d, jtel(.,z) = (%d, %d)", jma, jtel, jtelz);if (jnat < *pp) {// ---- Splitting -------------------------------int nzf, jtel2,// L375:nel = ner[ka];if (X(nel,jma) == 1) {nzf = 0;jtel2 = jtel;} else {nzf = 1;jtel2 = jtelz;}int jres = kb - ka + 1 - jtel2,km = ka + jtel2;if(verbose)Rprintf(" --> splitting: ka=%d, ner[ka]=%d => (nzf, jtel2, jres, km) = (%d, %d, %d, %d)\n",ka, nel, nzf, jtel2, jres, km);/*------- inner loop ------------------ */if(verbose >= 2) Rprintf(" inner loop: for(k in ka:km) use ner[k]: ");int k = ka,n_b = 0;do { // L378: almost for(k=ka; k < km; ++k) (but see 'continue' below)int n_k = ner[k];if(verbose >= 2) Rprintf(" %d", n_k);if (X(n_k,jma) == nzf) {int c = 0;for (int b = k; b <= kb; ++b) {n_b = ner[b];if (X(n_b,jma) != nzf) {c = b - 1;break; // goto L382;}}// L382:for (int a = k; a <= c; ++a) {int d = c + k - a;ner[d + 1] = ner[d];}ner[k] = n_b;continue; // the inner loop _without_ increasing 'k' !}++k;} while (k < km);if(verbose >= 2) Rprintf(" -> 'nelbb' = n_b = %d\n", n_b);/*------- end{inner loop} -- *//* L390: */++nclu;nban[km] = npass;kwan[ka] = jtel2;kwan[km] = jres;lava[km] = jma;ka += kwan[ka];} else { // jnat == *ppif(verbose) Rprintf(" --> _NO_ splitting\n");kwan[ka] = -kwan[ka];}if(verbose) Rprintf(" --> nclu = %d, kwan[ka] = %d\n", nclu, kwan[ka]);// L400:if (kb == *nn) {goto L500;}do {// L410:ka += iabs(kwan[ka]);if (ka > *nn) {goto L500;}} while (kwan[ka] < 2);goto L310;//-----------> LoopL500:++npass;for (ka = 1; ka <= *nn; ++ka) {if (kwan[ka] >= 2) {if(verbose) Rprintf("L500; found kwan[%d] = %d >= 2 ==> Loop again\n",ka, kwan[ka]);goto L310;//-----------> Loop}}return;} /* clmona_ */