| /* Algorithm AS 51 Appl. Statist. (1972), vol. 21, p. 218 |
| original (C) Royal Statistical Society 1972 |
| |
| Performs an iterative proportional fit of the marginal totals of a |
| contingency table. |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| # include <config.h> |
| #endif |
| |
| #include <math.h> |
| |
| #include <stdio.h> |
| #include <R_ext/Memory.h> |
| #include <R_ext/Applic.h> |
| |
| #undef max |
| #undef min |
| #undef abs |
| #define max(a, b) ((a) < (b) ? (b) : (a)) |
| #define min(a, b) ((a) > (b) ? (b) : (a)) |
| #define abs(x) ((x) >= 0 ? (x) : -(x)) |
| |
| static void collap(int nvar, double *x, double *y, int locy, |
| int *dim, int *config); |
| static void adjust(int nvar, double *x, double *y, double *z, |
| int *locz, int *dim, int *config, double *d); |
| |
| /* Table of constant values */ |
| |
| static void |
| loglin(int nvar, int *dim, int ncon, int *config, int ntab, |
| double *table, double *fit, int *locmar, int nmar, double *marg, |
| int nu, double *u, double maxdev, int maxit, |
| double *dev, int *nlast, int *ifault) |
| { |
| // nvar could be zero (no-segfault test) |
| if (!nvar) error("no variables"); // not translated |
| int i, j, k, n, point, size, check[nvar], icon[nvar]; |
| double x, y, xmax; |
| |
| /* Parameter adjustments */ |
| --dim; |
| --locmar; |
| config -= nvar + 1; |
| --fit; |
| --table; |
| --marg; |
| --u; |
| --dev; |
| |
| /* Function body */ |
| |
| *ifault = 0; |
| *nlast = 0; |
| |
| /* Check validity of NVAR, the number of variables, and of maxit, |
| the maximum number of iterations */ |
| |
| if (nvar > 0 && maxit > 0) goto L10; |
| L5: |
| *ifault = 4; |
| return; |
| |
| /* Look at table and fit constants */ |
| |
| L10: |
| size = 1; |
| for (j = 1; j <= nvar; j++) { |
| if (dim[j] <= 0) goto L5; |
| size *= dim[j]; |
| } |
| if (size <= ntab) goto L40; |
| L35: |
| *ifault = 2; |
| return; |
| L40: |
| x = 0.; |
| y = 0.; |
| for (i = 1; i <= size; i++) { |
| if (table[i] < 0. || fit[i] < 0.) goto L5; |
| x += table[i]; |
| y += fit[i]; |
| } |
| |
| /* Make a preliminary adjustment to obtain the fit to an empty |
| configuration list */ |
| |
| if (y == 0.) goto L5; |
| x /= y; |
| for (i = 1; i <= size; i++) fit[i] = x * fit[i]; |
| if (ncon <= 0 || config[nvar + 1] == 0) return; |
| |
| /* Allocate marginal tables */ |
| |
| point = 1; |
| for (i = 1; i <= ncon; i++) { |
| /* A zero beginning a configuration indicates that the list is |
| completed */ |
| if (config[i * nvar + 1] == 0) goto L160; |
| /* Get marginal table size. While doing this task, see if the |
| configuration list contains duplications or elements out of |
| range. */ |
| size = 1; |
| for (j = 0; j < nvar; j++) check[j] = 0; |
| for (j = 1; j <= nvar; j++) { |
| k = config[j + i * nvar]; |
| /* A zero indicates the end of the string. */ |
| if (k == 0) goto L130; |
| /* See if element is valid. */ |
| if (k >= 0 && k <= nvar) goto L100; |
| L95: |
| *ifault = 1; |
| return; |
| /* Check for duplication */ |
| L100: |
| if (check[k - 1]) goto L95; |
| check[k - 1] = 1; |
| /* Get size */ |
| size *= dim[k]; |
| } |
| |
| /* Since U is used to store fitted marginals, size must not |
| exceed NU */ |
| L130: |
| if (size > nu) goto L35; |
| |
| /* LOCMAR points to marginal tables to be placed in MARG */ |
| locmar[i] = point; |
| point += size; |
| } |
| |
| /* Get N, number of valid configurations */ |
| |
| i = ncon + 1; |
| L160: |
| n = i - 1; |
| |
| /* See if MARG can hold all marginal tables */ |
| |
| if (point > nmar + 1) goto L35; |
| |
| /* Obtain marginal tables */ |
| |
| for (i = 1; i <= n; i++) { |
| for (j = 1; j <= nvar; j++) { |
| icon[j - 1] = config[j + i * nvar]; |
| } |
| collap(nvar, &table[1], &marg[1], locmar[i], &dim[1], icon); |
| } |
| |
| /* Perform iterations */ |
| |
| for (k = 1; k <= maxit; k++) { |
| /* XMAX is maximum deviation observed between fitted and true |
| marginal during a cycle */ |
| xmax = 0.; |
| for (i = 1; i <= n; i++) { |
| for (j = 1; j <= nvar; j++) icon[j - 1] = config[j + i * nvar]; |
| collap(nvar, &fit[1], &u[1], 1, &dim[1], icon); |
| adjust(nvar, &fit[1], &u[1], &marg[1], &locmar[i], &dim[1], icon, &xmax); |
| } |
| /* Test convergence */ |
| dev[k] = xmax; |
| if (xmax < maxdev) goto L240; |
| } |
| if (maxit > 1) goto L230; |
| *nlast = 1; |
| return; |
| |
| /* No convergence */ |
| L230: |
| *ifault = 3; |
| *nlast = maxit; |
| return; |
| |
| /* Normal termination */ |
| L240: |
| *nlast = k; |
| |
| return; |
| } |
| |
| /* Algorithm AS 51.1 Appl. Statist. (1972), vol. 21, p. 218 |
| |
| Computes a marginal table from a complete table. |
| All parameters are assumed valid without test. |
| |
| The larger table is X and the smaller one is Y. |
| */ |
| |
| void collap(int nvar, double *x, double *y, int locy, int *dim, int *config) |
| { |
| int i, j, k, l, n, locu, size[nvar + 1], coord[nvar]; |
| |
| /* Parameter adjustments */ |
| --config; |
| --dim; |
| --x; |
| --y; |
| |
| /* Initialize arrays */ |
| |
| size[0] = 1; |
| for (k = 1; k <= nvar; k++) { |
| l = config[k]; |
| if (l == 0) goto L20; |
| size[k] = size[k - 1] * dim[l]; |
| } |
| |
| /* Find number of variables in configuration */ |
| |
| k = nvar + 1; |
| L20: |
| n = k - 1; |
| |
| /* Initialize Y. First cell of marginal table is at Y(LOCY) and |
| table has SIZE(K) elements */ |
| |
| locu = locy + size[k - 1] - 1; |
| for (j = locy; j <= locu; j++) y[j] = 0.; |
| |
| /* Initialize coordinates */ |
| |
| for (k = 0; k < nvar; k++) coord[k] = 0; |
| |
| /* Find locations in tables */ |
| i = 1; |
| L60: |
| j = locy; |
| for (k = 1; k <= n; k++) { |
| l = config[k]; |
| j += coord[l - 1] * size[k - 1]; |
| } |
| y[j] += x[i]; |
| |
| /* Update coordinates */ |
| |
| i++; |
| for (k = 1; k <= nvar; k++) { |
| coord[k - 1]++; |
| if (coord[k - 1] < dim[k]) goto L60; |
| coord[k - 1] = 0; |
| } |
| |
| return; |
| } |
| |
| |
| /* Algorithm AS 51.2 Appl. Statist. (1972), vol. 21, p. 218 |
| |
| Makes proportional adjustment corresponding to CONFIG. |
| All parameters are assumed valid without test. |
| */ |
| |
| void adjust(int nvar, double *x, double *y, double *z, int *locz, |
| int *dim, int *config, double *d) |
| { |
| int i, j, k, l, n, size[nvar + 1], coord[nvar]; |
| double e; |
| |
| /* Parameter adjustments */ |
| --config; |
| --dim; |
| --x; |
| --y; |
| --z; |
| |
| /* Set size array */ |
| |
| size[0] = 1; |
| for (k = 1; k <= nvar; k++) { |
| l = config[k]; |
| if (l == 0) goto L20; |
| size[k] = size[k - 1] * dim[l]; |
| } |
| |
| /* Find number of variables in configuration */ |
| |
| k = nvar + 1; |
| L20: |
| n = k - 1; |
| |
| /* Test size of deviation */ |
| |
| l = size[k - 1]; |
| j = 1; |
| k = *locz; |
| for (i = 1; i <= l; i++) { |
| e = abs(z[k] - y[j]); |
| if (e > *d) { |
| *d = e; |
| } |
| j++; |
| k++; |
| } |
| |
| /* Initialize coordinates */ |
| |
| for (k = 0; k < nvar; k++) coord[k] = 0; |
| i = 1; |
| |
| /* Perform adjustment */ |
| |
| L50: |
| j = 0; |
| for (k = 1; k <= n; k++) { |
| l = config[k]; |
| j += coord[l - 1] * size[k - 1]; |
| } |
| k = j + *locz; |
| j++; |
| |
| /* Note that Y(J) should be non-negative */ |
| |
| if (y[j] <= 0.) x[i] = 0.; |
| if (y[j] > 0.) x[i] = x[i] * z[k] / y[j]; |
| |
| /* Update coordinates */ |
| |
| i++; |
| for (k = 1; k <= nvar; k++) { |
| coord[k - 1]++; |
| if (coord[k - 1] < dim[k]) goto L50; |
| coord[k - 1] = 0; |
| } |
| |
| return; |
| } |
| |
| #undef max |
| #undef min |
| #undef abs |
| |
| #include <R.h> |
| #include <Rinternals.h> |
| #include "statsR.h" |
| #ifdef ENABLE_NLS |
| #include <libintl.h> |
| #define _(String) dgettext ("stats", String) |
| #else |
| #define _(String) (String) |
| #endif |
| |
| SEXP LogLin(SEXP dtab, SEXP conf, SEXP table, SEXP start, |
| SEXP snmar, SEXP eps, SEXP iter) |
| { |
| int nvar = length(dtab), |
| ncon = ncols(conf), |
| ntab = length(table), |
| nmar = asInteger(snmar), |
| maxit = asInteger(iter), |
| nlast, ifault; |
| double maxdev = asReal(eps); |
| SEXP fit = PROTECT(TYPEOF(start) == REALSXP ? duplicate(start) : |
| coerceVector(start, REALSXP)), |
| locmar = PROTECT(allocVector(INTSXP, ncon)), |
| marg = PROTECT(allocVector(REALSXP, nmar)), |
| u = PROTECT(allocVector(REALSXP, ntab)), |
| dev = PROTECT(allocVector(REALSXP, maxit)); |
| dtab = PROTECT(coerceVector(dtab, INTSXP)); |
| conf = PROTECT(coerceVector(conf, INTSXP)); |
| table = PROTECT(coerceVector(table, REALSXP)); |
| loglin(nvar, INTEGER(dtab), ncon, INTEGER(conf), ntab, |
| REAL(table), REAL(fit), INTEGER(locmar), nmar, REAL(marg), |
| ntab, REAL(u), maxdev, maxit, REAL(dev), &nlast, &ifault); |
| switch(ifault) { |
| case 1: |
| case 2: |
| error(_("this should not happen")); break; |
| case 3: |
| warning(_("algorithm did not converge")); break; |
| case 4: |
| error(_("incorrect specification of 'table' or 'start'")); break; |
| default: |
| break; |
| } |
| |
| SEXP ans = PROTECT(allocVector(VECSXP, 3)), nm; |
| SET_VECTOR_ELT(ans, 0, fit); |
| SET_VECTOR_ELT(ans, 1, dev); |
| SET_VECTOR_ELT(ans, 2, ScalarInteger(nlast)); |
| nm = allocVector(STRSXP, 3); |
| setAttrib(ans, R_NamesSymbol, nm); |
| SET_STRING_ELT(nm, 0, mkChar("fit")); |
| SET_STRING_ELT(nm, 1, mkChar("dev")); |
| SET_STRING_ELT(nm, 2, mkChar("nlast")); |
| UNPROTECT(9); |
| return ans; |
| } |
| |