def regression_warp(nu, beta, y, alpha): """ calculates optimal warping for function linear regression :param nu: numpy ndarray of shape (M,N) of M functions with N samples :param beta: numpy ndarray of shape (M,N) of M functions with N samples :param y: numpy ndarray of shape (1,N) of M functions with N samples responses :param alpha: numpy scalar :rtype: numpy array :return gamma_new: warping function """ T = beta.shape[1] betanu = cf.q_to_curve(nu) betaM, O_M, tauM = cf.find_rotation_and_seed_coord(betanu, beta) q = cf.curve_to_q(betaM) gam_M = cf.optimum_reparam_curve(nu, q) betaM = cf.group_action_by_gamma_coord(betaM, gam_M) qM = cf.curve_to_q(betaM) y_M = cf.innerprod_q2(qM, nu) betam, O_m, taum = cf.find_rotation_and_seed_coord(-1 * betanu, beta) q = cf.curve_to_q(betam) gam_m = cf.optimum_reparam_curve(-1 * nu, q) betam = cf.group_action_by_gamma_coord(betam, gam_m) qm = cf.curve_to_q(betam) y_m = cf.innerprod_q2(qm, nu) if y > alpha + y_M: O_hat = O_M gamma_new = gam_M tau = tauM elif y < alpha + y_m: O_hat = O_m gamma_new = gam_m tau = taum else: gamma_new, O_hat, tau = cf.curve_zero_crossing(y - alpha, beta, nu, y_M, y_m, gam_M, gam_m) return(gamma_new, O_hat, tau)
def oc_srvf_align(beta, mode='O'): """ This claculates the mean of a set of curves and aligns them :param beta: numpy ndarray of shape (n, M, N) describing N curves in R^M :param mode: Open ('O') or closed curve ('C') (default 'O') :rtype: tuple of numpy array :return betan: aligned curves :return qn: aligned srvf :return betamean: mean curve :return mu: mean srvf """ n, T, N = beta.shape # find mean mu, betamean, v, q = curve_karcher_mean(beta, mode=mode) qn = zeros((n, T, N)) betan = zeros((n, T, N)) centroid2 = cf.calculatecentroid(betamean) betamean = betamean - tile(centroid2, [T, 1]).T q_mu = cf.curve_to_q(betamean) # align to mean for ii in range(0, N): beta1 = beta[:, :, ii] centroid1 = cf.calculatecentroid(beta1) beta1 = beta1 - tile(centroid1, [T, 1]).T # Iteratively optimize over SO(n) x Gamma for i in range(0, 1): # Optimize over SO(n) beta1, O_hat, tau = cf.find_rotation_and_seed_coord(betamean, beta1) q1 = cf.curve_to_q(beta1) # Optimize over Gamma gam = cf.optimum_reparam_curve(q1, q_mu, 0.0) gamI = uf.invertGamma(gam) # Applying optimal re-parameterization to the second curve beta1 = cf.group_action_by_gamma_coord(beta1, gamI) # Optimize over SO(n) beta1, O_hat, tau = cf.find_rotation_and_seed_coord(betamean, beta1) qn[:, :, ii] = cf.curve_to_q(beta1) betan[:, :, ii] = beta1 align_results = collections.namedtuple('align', ['betan', 'qn', 'betamean', 'mu']) out = align_results(betan, qn, betamean, q_mu) return out
def align_sub(beta_mean, q_mu, beta1): # Iteratively optimize over SO(n) x Gamma for i in range(0, 1): # Optimize over SO(n) beta1, O_hat, tau = cf.find_rotation_and_seed_coord(beta_mean, beta1) q1 = cf.curve_to_q(beta1) # Optimize over Gamma gam = cf.optimum_reparam_curve(q1, q_mu, 0.0) gamI = uf.invertGamma(gam) # Applying optimal re-parameterization to the second curve beta1 = cf.group_action_by_gamma_coord(beta1, gamI) # Optimize over SO(n) beta1, O_hat, tau = cf.find_rotation_and_seed_coord(beta_mean, beta1) return (beta1, q1, gamI)
def srvf_align(self, rotation=True, parallel=False, cores=-1, method="DP"): """ This aligns a set of curves to the mean and computes mean if not computed :param rotation: compute optimal rotation (default = T) :param parallel: run in parallel (default = F) :param cores: number of cores for parallel (default = -1 (all)) :param method: method to apply optimization (default="DP") options are "DP" or "RBFGS" """ n, T, N = self.beta.shape modes = ['O', 'C'] mode = [i for i, x in enumerate(modes) if x == self.mode] if len(mode) == 0: mode = 0 else: mode = mode[0] # find mean if not hasattr(self, 'beta_mean'): self.karcher_mean() self.qn = zeros((n, T, N)) self.betan = zeros((n, T, N)) self.gams = zeros((T, N)) centroid2 = cf.calculatecentroid(self.beta_mean) self.beta_mean = self.beta_mean - tile(centroid2, [T, 1]).T # align to mean out = Parallel(n_jobs=-1)(delayed(cf.find_rotation_and_seed_unique)( self.q_mean, self.q[:, :, n], mode, rotation, method) for n in range(N)) for ii in range(0, N): self.gams[:, ii] = out[ii][2] self.qn[:, :, ii] = out[ii][0] btmp = out[ii][1].dot(self.beta[:, :, ii]) self.betan[:, :, ii] = cf.group_action_by_gamma_coord(btmp, out[ii][2]) return
def oc_elastic_prediction(beta, model, y=None): """ This function identifies a regression model with phase-variablity using elastic methods :param beta: numpy ndarray of shape (M,N) of M functions with N samples :param model: identified model from elastic_regression :param y: truth, optional used to calculate SSE :rtype: tuple of numpy array :return alpha: alpha parameter of model :return beta: beta(t) of model :return fn: aligned functions - numpy ndarray of shape (M,N) of M functions with N samples :return qn: aligned srvfs - similar structure to fn :return gamma: calculated warping functions :return q: original training SRSFs :return B: basis matrix :return b: basis coefficients :return SSE: sum of squared error """ T = model.q.shape[1] n = beta.shape[2] N = model.q.shape[2] q, beta = preproc_open_curve(beta, T) if model.type == 'oclinear' or model.type == 'oclogistic': y_pred = np.zeros(n) elif model.type == 'ocmlogistic': m = model.n_classes y_pred = np.zeros((n, m)) for ii in range(0, n): diff = model.q - q[:, :, ii][:, :, np.newaxis] # dist = np.linalg.norm(np.abs(diff), axis=(0, 1)) ** 2 dist = np.zeros(N) for jj in range(0, N): dist[jj] = np.linalg.norm(np.abs(diff[:, :, jj])) ** 2 if model.type == 'oclinear' or model.type == 'oclogistic': # beta1 = cf.shift_f(beta[:, :, ii], int(model.tau[dist.argmin()])) beta1 = beta[:, :, ii] else: beta1 = beta[:, :, ii] beta1 = model.O[:, :, dist.argmin()].dot(beta1) beta1 = cf.group_action_by_gamma_coord(beta1, model.gamma[:, dist.argmin()]) q_tmp = cf.curve_to_q(beta1) if model.type == 'oclinear': y_pred[ii] = model.alpha + cf.innerprod_q2(q_tmp, model.nu) elif model.type == 'oclogistic': y_pred[ii] = model.alpha + cf.innerprod_q2(q_tmp, model.nu) elif model.type == 'ocmlogistic': for jj in range(0, m): y_pred[ii, jj] = model.alpha[jj] + cf.innerprod_q2(q_tmp, model.nu[:, :, jj]) if y is None: if model.type == 'oclinear': SSE = None elif model.type == 'oclogistic': y_pred = phi(y_pred) y_labels = np.ones(n) y_labels[y_pred < 0.5] = -1 PC = None elif model.type == 'ocmlogistic': y_pred = phi(y_pred.ravel()) y_pred = y_pred.reshape(n, m) y_labels = y_pred.argmax(axis=1) + 1 PC = None else: if model.type == 'oclinear': SSE = sum((y - y_pred) ** 2) elif model.type == 'oclogistic': y_pred = phi(y_pred) y_labels = np.ones(n) y_labels[y_pred < 0.5] = -1 TP = sum(y[y_labels == 1] == 1) FP = sum(y[y_labels == -1] == 1) TN = sum(y[y_labels == -1] == -1) FN = sum(y[y_labels == 1] == -1) PC = (TP + TN) / float(TP + FP + FN + TN) elif model.type == 'ocmlogistic': y_pred = phi(y_pred.ravel()) y_pred = y_pred.reshape(n, m) y_labels = y_pred.argmax(axis=1) + 1 PC = np.zeros(m) cls_set = np.arange(1, m + 1) for ii in range(0, m): cls_sub = np.delete(cls_set, ii) TP = sum(y[y_labels == (ii + 1)] == (ii + 1)) FP = sum(y[np.in1d(y_labels, cls_sub)] == (ii + 1)) TN = sum(y[np.in1d(y_labels, cls_sub)] == y_labels[np.in1d(y_labels, cls_sub)]) FN = sum(np.in1d(y[y_labels == (ii + 1)], cls_sub)) PC[ii] = (TP + TN) / float(TP + FP + FN + TN) PC = sum(y == y_labels) / float(y_labels.size) if model.type == 'oclinear': prediction = collections.namedtuple('prediction', ['y_pred', 'SSE']) out = prediction(y_pred, SSE) elif model.type == 'oclogistic': prediction = collections.namedtuple('prediction', ['y_prob', 'y_labels', 'PC']) out = prediction(y_pred, y_labels, PC) elif model.type == 'ocmlogistic': prediction = collections.namedtuple('prediction', ['y_prob', 'y_labels', 'PC']) out = prediction(y_pred, y_labels, PC) return out
def oc_elastic_mlogistic(beta, y, B=None, df=20, T=100, max_itr=30, cores=-1, deltaO=.003, deltag=.003): """ This function identifies a multinomial logistic regression model with phase-variability using elastic methods for open curves :param beta: numpy ndarray of shape (n, M, N) describing N curves in R^M :param y: numpy array of labels {1,2,...,m} for m classes :param B: optional matrix describing Basis elements :param df: number of degrees of freedom B-spline (default 20) :param T: number of desired samples along curve (default 100) :param max_itr: maximum number of iterations (default 20) :param cores: number of cores for parallel processing (default all) :type beta: np.ndarray :rtype: tuple of numpy array :return alpha: alpha parameter of model :return nu: nu(t) of model :return betan: aligned curves - numpy ndarray of shape (n,T,N) :return O: calculated rotation matrices :return gamma: calculated warping functions :return B: basis matrix :return b: basis coefficients :return Loss: logistic loss """ n = beta.shape[0] N = beta.shape[2] time = np.linspace(0, 1, T) if n > 500: parallel = True elif T > 100: parallel = True else: parallel = True # Code labels m = y.max() Y = np.zeros((N, m), dtype=int) for ii in range(0, N): Y[ii, y[ii] - 1] = 1 # Create B-Spline Basis if none provided if B is None: B = bs(time, df=df, degree=4, include_intercept=True) Nb = B.shape[1] q, beta = preproc_open_curve(beta, T) qn = q.copy() beta0 = beta.copy() gamma = np.tile(np.linspace(0, 1, T), (N, 1)) gamma = gamma.transpose() O_hat = np.tile(np.eye(n), (N, 1, 1)).T itr = 1 LL = np.zeros(max_itr+1) while itr <= max_itr: print("Iteration: %d" % itr) Phi = np.ones((N, n * Nb + 1)) for ii in range(0, N): for jj in range(0, n): for kk in range(1, Nb + 1): Phi[ii, jj * Nb + kk] = trapz(qn[jj, :, ii] * B[:, kk - 1], time) # Find alpha and beta using l_bfgs b0 = np.zeros(m * (n * Nb + 1)) out = fmin_l_bfgs_b(mlogit_loss, b0, fprime=mlogit_gradient, args=(Phi, Y), pgtol=1e-10, maxiter=200, maxfun=250, factr=1e-30) b = out[0] B0 = b.reshape(n * Nb + 1, m) alpha = B0[0, :] nu = np.zeros((n, T, m)) for i in range(0, m): for j in range(0, n): nu[j, :, i] = B.dot(B0[(j * Nb + 1):((j + 1) * Nb + 1), i]) # compute the logistic loss LL[itr] = mlogit_loss(b, Phi, Y) # find gamma gamma_new = np.zeros((T, N)) if parallel: out = Parallel(n_jobs=cores)(delayed(mlogit_warp_grad)(alpha, nu, q[:, :, n], Y[n, :], deltaO=deltaO, deltag=deltag) for n in range(N)) for ii in range(0, N): gamma_new[:, ii] = out[ii][0] beta1n = cf.group_action_by_gamma_coord(out[ii][1].dot(beta0[:, :, ii]), out[ii][0]) beta[:, :, ii] = beta1n O_hat[:, :, ii] = out[ii][1] qn[:, :, ii] = cf.curve_to_q(beta[:, :, ii]) else: for ii in range(0, N): gammatmp, Otmp = mlogit_warp_grad(alpha, nu, q[:, :, ii], Y[ii, :], deltaO=deltaO, deltag=deltag) gamma_new[:, ii] = gammatmp beta1n = cf.group_action_by_gamma_coord(Otmp.dot(beta0[:, :, ii]), gammatmp) beta[:, :, ii] = beta1n O_hat[:, :, ii] = Otmp qn[:, :, ii] = cf.curve_to_q(beta[:, :, ii]) if norm(gamma - gamma_new) < 1e-5: break else: gamma = gamma_new.copy() itr += 1 model = collections.namedtuple('model', ['alpha', 'nu', 'betan', 'q', 'gamma', 'O', 'B', 'b', 'Loss', 'n_classes', 'type']) out = model(alpha, nu, beta, q, gamma_new, O_hat, B, b[1:-1], LL[1:itr], m, 'ocmlogistic') return out
def oc_elastic_regression(beta, y, B=None, df=40, T=200, max_itr=20, cores=-1): """ This function identifies a regression model for open curves using elastic methods :param beta: numpy ndarray of shape (n, M, N) describing N curves in R^M :param y: numpy array of N responses :param B: optional matrix describing Basis elements :param df: number of degrees of freedom B-spline (default 20) :param T: number of desired samples along curve (default 100) :param max_itr: maximum number of iterations (default 20) :param cores: number of cores for parallel processing (default all) :type beta: np.ndarray :rtype: tuple of numpy array :return alpha: alpha parameter of model :return beta: beta(t) of model :return fn: aligned functions - numpy ndarray of shape (M,N) of M functions with N samples :return qn: aligned srvfs - similar structure to fn :return gamma: calculated warping functions :return q: original training SRSFs :return B: basis matrix :return b: basis coefficients :return SSE: sum of squared error """ n = beta.shape[0] N = beta.shape[2] time = np.linspace(0, 1, T) if n > 500: parallel = True elif T > 100: parallel = True else: parallel = False # Create B-Spline Basis if none provided if B is None: B = bs(time, df=df, degree=4, include_intercept=True) Nb = B.shape[1] q, beta = preproc_open_curve(beta, T) beta0 = beta.copy() qn = q.copy() gamma = np.tile(np.linspace(0, 1, T), (N, 1)) gamma = gamma.transpose() O_hat = np.tile(np.eye(n), (N, 1, 1)).T itr = 1 SSE = np.zeros(max_itr) while itr <= max_itr: print("Iteration: %d" % itr) # align data # OLS using basis Phi = np.ones((N, n * Nb + 1)) for ii in range(0, N): for jj in range(0, n): for kk in range(1, Nb + 1): Phi[ii, jj * Nb + kk] = trapz(qn[jj, :, ii] * B[:, kk - 1], time) xx = dot(Phi.T, Phi) inv_xx = inv(xx) xy = dot(Phi.T, y) b = dot(inv_xx, xy) alpha = b[0] nu = np.zeros((n, T)) for ii in range(0, n): nu[ii, :] = B.dot(b[(ii * Nb + 1):((ii + 1) * Nb + 1)]) # compute the SSE int_X = np.zeros(N) for ii in range(0, N): int_X[ii] = cf.innerprod_q2(qn[:, :, ii], nu) SSE[itr - 1] = sum((y.reshape(N) - alpha - int_X) ** 2) # find gamma gamma_new = np.zeros((T, N)) if parallel: out = Parallel(n_jobs=cores)(delayed(regression_warp)(nu, beta0[:, :, n], y[n], alpha) for n in range(N)) for ii in range(0, N): gamma_new[:, ii] = out[ii][0] beta1n = cf.group_action_by_gamma_coord(out[ii][1].dot(beta0[:, :, ii]), out[ii][0]) beta[:, :, ii] = beta1n O_hat[:, :, ii] = out[ii][1] qn[:, :, ii] = cf.curve_to_q(beta[:, :, ii]) else: for ii in range(0, N): beta1 = beta0[:, :, ii] gammatmp, Otmp, tau = regression_warp(nu, beta1, y[ii], alpha) gamma_new[:, ii] = gammatmp beta1n = cf.group_action_by_gamma_coord(Otmp.dot(beta0[:, :, ii]), gammatmp) beta[:, :, ii] = beta1n O_hat[:, :, ii] = Otmp qn[:, :, ii] = cf.curve_to_q(beta[:, :, ii]) if np.abs(SSE[itr - 1] - SSE[itr - 2]) < 1e-15: break else: gamma = gamma_new itr += 1 tau = np.zeros(N) model = collections.namedtuple('model', ['alpha', 'nu', 'betan' 'q', 'gamma', 'O', 'tau', 'B', 'b', 'SSE', 'type']) out = model(alpha, nu, beta, q, gamma, O_hat, tau, B, b[1:-1], SSE[0:itr], 'oclinear') return out
def oc_elastic_logistic(beta, y, B=None, df=60, T=100, max_itr=40, cores=-1, deltaO=.1, deltag=.05, method=1): """ This function identifies a logistic regression model with phase-variablity using elastic methods for open curves :param beta: numpy ndarray of shape (n, M, N) describing N curves in R^M :param y: numpy array of N responses :param B: optional matrix describing Basis elements :param df: number of degrees of freedom B-spline (default 20) :param T: number of desired samples along curve (default 100) :param max_itr: maximum number of iterations (default 20) :param cores: number of cores for parallel processing (default all) :type beta: np.ndarray :rtype: tuple of numpy array :return alpha: alpha parameter of model :return nu: nu(t) of model :return betan: aligned curves - numpy ndarray of shape (n,T,N) :return O: calulated rotation matrices :return gamma: calculated warping functions :return B: basis matrix :return b: basis coefficients :return Loss: logistic loss """ n = beta.shape[0] N = beta.shape[2] time = np.linspace(0, 1, T) if n > 500: parallel = True elif T > 100: parallel = True else: parallel = True # Create B-Spline Basis if none provided if B is None: B = bs(time, df=df, degree=4, include_intercept=True) Nb = B.shape[1] q, beta = preproc_open_curve(beta, T) beta0 = beta.copy() qn = q.copy() gamma = np.tile(np.linspace(0, 1, T), (N, 1)) gamma = gamma.transpose() O_hat = np.tile(np.eye(n), (N, 1, 1)).T itr = 1 LL = np.zeros(max_itr + 1) while itr <= max_itr: print("Iteration: %d" % itr) Phi = np.ones((N, n * Nb + 1)) for ii in range(0, N): for jj in range(0, n): for kk in range(1, Nb + 1): Phi[ii, jj * Nb + kk] = trapz(qn[jj, :, ii] * B[:, kk - 1], time) # Find alpha and beta using l_bfgs b0 = np.zeros(n * Nb + 1) out = fmin_l_bfgs_b(logit_loss, b0, fprime=logit_gradient, args=(Phi, y), pgtol=1e-10, maxiter=200, maxfun=250, factr=1e-30) b = out[0] b = b/norm(b) # alpha_norm = b1[0] alpha = b[0] nu = np.zeros((n, T)) for ii in range(0, n): nu[ii, :] = B.dot(b[(ii * Nb + 1):((ii + 1) * Nb + 1)]) # compute the logistic loss LL[itr] = logit_loss(b, Phi, y) # find gamma gamma_new = np.zeros((T, N)) if parallel: out = Parallel(n_jobs=cores)(delayed(logistic_warp)(alpha, nu, q[:, :, ii], y[ii], deltaO=deltaO, deltag=deltag, method=method) for ii in range(N)) for ii in range(0, N): gamma_new[:, ii] = out[ii][0] beta1n = cf.group_action_by_gamma_coord(out[ii][1].dot(beta0[:, :, ii]), out[ii][0]) beta[:, :, ii] = beta1n O_hat[:, :, ii] = out[ii][1] if np.isinf(beta1n).any() or np.isnan(beta1n).any(): Tracer()() qn[:, :, ii] = cf.curve_to_q(beta[:, :, ii]) else: for ii in range(0, N): q1 = q[:, :, ii] gammatmp, Otmp, tautmp = logistic_warp(alpha, nu, q1, y[ii],deltaO=deltaO, deltag=deltag, method=method) gamma_new[:, ii] = gammatmp beta1n = cf.group_action_by_gamma_coord(Otmp.dot(beta0[:, :, ii]), gammatmp) beta[:, :, ii] = beta1n O_hat[:, :, ii] = Otmp qn[:, :, ii] = cf.curve_to_q(beta[:, :, ii]) if norm(gamma - gamma_new) < 1e-5: break else: gamma = gamma_new.copy() itr += 1 tau = np.zeros(N) model = collections.namedtuple('model', ['alpha', 'nu', 'betan', 'q', 'gamma', 'O', 'tau', 'B', 'b', 'Loss', 'type']) out = model(alpha, nu, beta, q, gamma_new, O_hat, tau, B, b[1:-1], LL[1:itr], 'oclogistic') return out
def calc_model(self, B=None, lam=0, df=40, T=200, max_itr=20, cores=-1): """ This function identifies a regression model for open curves using elastic methods :param B: optional matrix describing Basis elements :param lam: regularization parameter (default 0) :param df: number of degrees of freedom B-spline (default 20) :param T: number of desired samples along curve (default 100) :param max_itr: maximum number of iterations (default 20) :param cores: number of cores for parallel processing (default all) """ n = self.beta.shape[0] N = self.beta.shape[2] time = np.linspace(0, 1, T) if n > 500: parallel = True elif T > 100: parallel = True else: parallel = False binsize = np.diff(time) binsize = binsize.mean() # Create B-Spline Basis if none provided if B is None: B = bs(time, df=df, degree=4, include_intercept=True) Nb = B.shape[1] # second derivative for regularization Bdiff = np.zeros((T, Nb)) for ii in range(0, Nb): Bdiff[:, ii] = np.gradient(np.gradient(B[:, ii], binsize), binsize) q, beta = preproc_open_curve(self.beta, T) self.q = q beta0 = beta.copy() qn = q.copy() gamma = np.tile(np.linspace(0, 1, T), (N, 1)) gamma = gamma.transpose() O_hat = np.tile(np.eye(n), (N, 1, 1)).T itr = 1 self.SSE = np.zeros(max_itr) while itr <= max_itr: print("Iteration: %d" % itr) # align data # OLS using basis Phi = np.ones((N, n * Nb + 1)) for ii in range(0, N): for jj in range(0, n): for kk in range(1, Nb + 1): Phi[ii, jj * Nb + kk] = trapz(qn[jj, :, ii] * B[:, kk - 1], time) R = np.zeros((n * Nb + 1, n * Nb + 1)) for kk in range(0, n): for ii in range(1, Nb + 1): for jj in range(1, Nb + 1): R[kk * Nb + ii, kk * Nb + jj] = trapz( Bdiff[:, ii - 1] * Bdiff[:, jj - 1], time) xx = np.dot(Phi.T, Phi) inv_xx = inv(xx + lam * R) xy = np.dot(Phi.T, self.y) b = np.dot(inv_xx, xy) alpha = b[0] nu = np.zeros((n, T)) for ii in range(0, n): nu[ii, :] = B.dot(b[(ii * Nb + 1):((ii + 1) * Nb + 1)]) # compute the SSE int_X = np.zeros(N) for ii in range(0, N): int_X[ii] = cf.innerprod_q2(qn[:, :, ii], nu) self.SSE[itr - 1] = sum((self.y.reshape(N) - alpha - int_X)**2) # find gamma gamma_new = np.zeros((T, N)) if parallel: out = Parallel(n_jobs=cores)( delayed(regression_warp)(nu, q[:, :, n], self.y[n], alpha) for n in range(N)) for ii in range(0, N): gamma_new[:, ii] = out[ii][0] beta1n = cf.group_action_by_gamma_coord( out[ii][1].dot(beta0[:, :, ii]), out[ii][0]) beta[:, :, ii] = beta1n O_hat[:, :, ii] = out[ii][1] qn[:, :, ii] = cf.curve_to_q(beta1n)[0] else: for ii in range(0, N): q1 = q[:, :, ii] gammatmp, Otmp = regression_warp(nu, q1, self.y[ii], alpha) gamma_new[:, ii] = gammatmp beta1n = cf.group_action_by_gamma_coord( Otmp.dot(beta0[:, :, ii]), gammatmp) beta[:, :, ii] = beta1n O_hat[:, :, ii] = Otmp qn[:, :, ii] = cf.curve_to_q(beta1n)[0] if np.abs(self.SSE[itr - 1] - self.SSE[itr - 2]) < 1e-15: break else: gamma = gamma_new itr += 1 tau = np.zeros(N) self.alpha = alpha self.nu = nu self.beta0 = beta0 self.betan = beta self.gamma = gamma self.qn = qn self.B = B self.O = O_hat self.b = b[1:-1] self.SSE = self.SSE[0:itr] return
def calc_model(self, B=None, df=20, T=100, max_itr=30, cores=-1, deltaO=.003, deltag=.003): """ This function identifies a multinomial logistic regression model with phase-variability using elastic methods for open curves :param B: optional matrix describing Basis elements :param df: number of degrees of freedom B-spline (default 20) :param T: number of desired samples along curve (default 100) :param max_itr: maximum number of iterations (default 20) :param cores: number of cores for parallel processing (default all) """ n = self.beta.shape[0] N = self.beta.shape[2] time = np.linspace(0, 1, T) m = self.y.max() if n > 500: parallel = True elif T > 100: parallel = True else: parallel = True # Create B-Spline Basis if none provided if B is None: B = bs(time, df=df, degree=4, include_intercept=True) Nb = B.shape[1] q, beta = preproc_open_curve(self.beta, T) qn = q.copy() beta0 = beta.copy() gamma = np.tile(np.linspace(0, 1, T), (N, 1)) gamma = gamma.transpose() O_hat = np.tile(np.eye(n), (N, 1, 1)).T itr = 1 LL = np.zeros(max_itr + 1) while itr <= max_itr: print("Iteration: %d" % itr) Phi = np.ones((N, n * Nb + 1)) for ii in range(0, N): for jj in range(0, n): for kk in range(1, Nb + 1): Phi[ii, jj * Nb + kk] = trapz(qn[jj, :, ii] * B[:, kk - 1], time) # Find alpha and beta using l_bfgs b0 = np.zeros(m * (n * Nb + 1)) out = fmin_l_bfgs_b(mlogit_loss, b0, fprime=mlogit_gradient, args=(Phi, self.Y), pgtol=1e-10, maxiter=200, maxfun=250, factr=1e-30) b = out[0] B0 = b.reshape(n * Nb + 1, m) alpha = B0[0, :] nu = np.zeros((n, T, m)) for i in range(0, m): for j in range(0, n): nu[j, :, i] = B.dot(B0[(j * Nb + 1):((j + 1) * Nb + 1), i]) # compute the logistic loss LL[itr] = mlogit_loss(b, Phi, self.Y) # find gamma gamma_new = np.zeros((T, N)) if parallel: out = Parallel(n_jobs=cores)( delayed(mlogit_warp_grad)(alpha, nu, q[:, :, n], self.Y[n, :], deltaO=deltaO, deltag=deltag) for n in range(N)) for ii in range(0, N): gamma_new[:, ii] = out[ii][0] beta1n = cf.group_action_by_gamma_coord( out[ii][1].dot(beta0[:, :, ii]), out[ii][0]) beta[:, :, ii] = beta1n O_hat[:, :, ii] = out[ii][1] qn[:, :, ii] = cf.curve_to_q(beta[:, :, ii])[0] else: for ii in range(0, N): gammatmp, Otmp = mlogit_warp_grad(alpha, nu, q[:, :, ii], self.Y[ii, :], deltaO=deltaO, deltag=deltag) gamma_new[:, ii] = gammatmp beta1n = cf.group_action_by_gamma_coord( Otmp.dot(beta0[:, :, ii]), gammatmp) beta[:, :, ii] = beta1n O_hat[:, :, ii] = Otmp qn[:, :, ii] = cf.curve_to_q(beta[:, :, ii])[0] if norm(gamma - gamma_new) < 1e-5: break else: gamma = gamma_new.copy() itr += 1 self.alpha = alpha self.nu = nu self.beta0 = beta0 self.betan = beta self.q = q self.qn = qn self.gamma = gamma_new self.O = O_hat self.B = B self.b = b[1:-1] self.Loss = LL[1:itr] self.n_classes = m return
def predict(self, newdata=None): """ This function performs prediction on regression model on new data if available or current stored data in object Usage: obj.predict() obj.predict(newdata) :param newdata: dict containing new data for prediction (needs the keys below, if None predicts on training data) :type newdata: dict :param beta: numpy ndarray of shape (M,N) of M functions with N samples :param y: truth if available """ if newdata != None: beta = newdata['beta'] y = newdata['y'] T = self.q.shape[1] n = beta.shape[2] N = self.q.shape[2] q, beta = preproc_open_curve(beta, T) y_pred = np.zeros(n) for ii in range(0, n): diff = self.q - q[:, :, ii][:, :, np.newaxis] dist = np.zeros(N) for jj in range(0, N): dist[jj] = np.linalg.norm(np.abs(diff[:, :, jj]))**2 beta1 = beta[:, :, ii] beta1 = self.O[:, :, dist.argmin()].dot(beta1) beta1 = cf.group_action_by_gamma_coord( beta1, self.gamma[:, dist.argmin()]) q_tmp = cf.curve_to_q(beta1)[0] y_pred[ii] = self.alpha + cf.innerprod_q2(q_tmp, self.nu) if y is None: y_pred = phi(y_pred) y_labels = np.ones(n) y_labels[y_pred < 0.5] = -1 self.PC = None else: y_pred = phi(y_pred) y_labels = np.ones(n) y_labels[y_pred < 0.5] = -1 TP = sum(y[y_labels == 1] == 1) FP = sum(y[y_labels == -1] == 1) TN = sum(y[y_labels == -1] == -1) FN = sum(y[y_labels == 1] == -1) self.PC = (TP + TN) / float(TP + FP + FN + TN) self.y_pred = y_pred self.y_labels = y_labels else: n = self.q.shape[2] N = self.q.shape[2] y_pred = np.zeros(n) for ii in range(0, n): diff = self.q - self.q[:, :, ii][:, :, np.newaxis] dist = np.zeros(N) for jj in range(0, N): dist[jj] = np.linalg.norm(np.abs(diff[:, :, jj]))**2 beta1 = self.beta0[:, :, ii] beta1 = self.O[:, :, dist.argmin()].dot(beta1) beta1 = cf.group_action_by_gamma_coord( beta1, self.gamma[:, dist.argmin()]) q_tmp = cf.curve_to_q(beta1)[0] y_pred[ii] = self.alpha + cf.innerprod_q2(q_tmp, self.nu) y_pred = phi(y_pred) y_labels = np.ones(n) y_labels[y_pred < 0.5] = -1 TP = sum(self.y[y_labels == 1] == 1) FP = sum(self.y[y_labels == -1] == 1) TN = sum(self.y[y_labels == -1] == -1) FN = sum(self.y[y_labels == 1] == -1) self.PC = (TP + TN) / float(TP + FP + FN + TN) self.y_pred = y_pred self.y_labels = y_labels return
def init_path_rand(beta1, beta_mid, beta2, T=100, k=5): """ Initializes a path in \cal{C}. beta1, beta_mid beta2 are already standardized curves. Creates a path from beta1 to beta_mid to beta2 in shape space, then projects to the closed shape manifold. :param beta1: numpy ndarray of shape (2,M) of M samples (first curve) :param betamid: numpy ndarray of shape (2,M) of M samples (mid curve) :param beta2: numpy ndarray of shape (2,M) of M samples (end curve) :param T: Number of samples of curve (Default = 100) :param k: number of samples along path (Default = 5) :rtype: numpy ndarray :return alpha: a path between two q-functions :return beta: a path between two curves :return O: rotation matrix """ alpha = zeros((2, T, k)) beta = zeros((2, T, k)) q1 = cf.curve_to_q(beta1) q_mid = cf.curve_to_q(beta_mid) # find optimal rotation of q2 beta2, O1, tau1 = cf.find_rotation_and_seed_coord(beta1, beta2) q2 = cf.curve_to_q(beta2) # find the optimal coorespondence gam = cf.optimum_reparam_curve(q2, q1) gamI = uf.invertGamma(gam) # apply optimal reparametrization beta2n = cf.group_action_by_gamma_coord(beta2, gamI) # find optimal rotation of q2 beta2n, O2, tau1 = cf.find_rotation_and_seed_coord(beta1, beta2n) centroid2 = cf.calculatecentroid(beta2n) beta2n = beta2n - tile(centroid2, [T, 1]).T q2n = cf.curve_to_q(beta2n) O = O1.dot(O2) # Initialize a path as a geodesic through q1 --- q_mid --- q2 theta1 = arccos(cf.innerprod_q2(q1, q_mid)) theta2 = arccos(cf.innerprod_q2(q_mid, q2n)) tmp = arange(2, int((k - 1) / 2) + 1) t = zeros(tmp.size) alpha[:, :, 0] = q1 beta[:, :, 0] = beta1 i = 0 for tau in range(2, int((k - 1) / 2) + 1): t[i] = (tau - 1.0) / ((k - 1) / 2.0) qnew = (1 / sin(theta1)) * (sin((1 - t[i]) * theta1) * q1 + sin(t[i] * theta1) * q_mid) alpha[:, :, tau - 1] = cf.project_curve(qnew) x = cf.q_to_curve(alpha[:, :, tau - 1]) a = -1 * cf.calculatecentroid(x) beta[:, :, tau - 1] = x + tile(a, [T, 1]).T i += 1 alpha[:, :, int((k - 1) / 2)] = q_mid beta[:, :, int((k - 1) / 2)] = beta_mid i = 0 for tau in range(int((k - 1) / 2) + 1, k - 1): qnew = (1 / sin(theta2)) * (sin((1 - t[i]) * theta2) * q_mid + sin(t[i] * theta2) * q2n) alpha[:, :, tau] = cf.project_curve(qnew) x = cf.q_to_curve(alpha[:, :, tau]) a = -1 * cf.calculatecentroid(x) beta[:, :, tau] = x + tile(a, [T, 1]).T i += 1 alpha[:, :, k - 1] = q2n beta[:, :, k - 1] = beta2n return (alpha, beta, O)
def geod_sphere(beta1, beta2, k=5): """ This function caluclates the geodecis between open curves beta1 and beta2 with k steps along path :param beta1: numpy ndarray of shape (2,M) of M samples :param beta2: numpy ndarray of shape (2,M) of M samples :param k: number of samples along path (Default = 5) :rtype: numpy ndarray :return dist: geodesic distance :return path: geodesic path :return O: rotation matrix """ lam = 0.0 elastic = 1 rotation = 1 returnpath = 1 n, T = beta1.shape beta1 = cf.resamplecurve(beta1, T) beta2 = cf.resamplecurve(beta2, T) centroid1 = cf.calculatecentroid(beta1) beta1 = beta1 - tile(centroid1, [T, 1]).T centroid2 = cf.calculatecentroid(beta2) beta2 = beta2 - tile(centroid2, [T, 1]).T q1 = cf.curve_to_q(beta1) if rotation: beta2, O1, tau = cf.find_rotation_and_seed_coord(beta1, beta2) q2 = cf.curve_to_q(beta2) else: O1 = eye(2) q2 = cf.curve_to_q(beta2) if elastic: # Find the optimal coorespondence gam = cf.optimum_reparam_curve(q2, q1, lam) gamI = uf.invertGamma(gam) # Applying optimal re-parameterization to the second curve beta2n = cf.group_action_by_gamma_coord(beta2, gamI) q2n = cf.curve_to_q(beta2n) if rotation: beta2n, O2, tau = cf.find_rotation_and_seed_coord(beta1, beta2n) centroid2 = cf.calculatecentroid(beta2n) beta2n = beta2n - tile(centroid2, [T, 1]).T q2n = cf.curve_to_q(beta2n) O = O1.dot(O2) else: q2n = q2 O = O1 # Forming geodesic between the registered curves dist = arccos(cf.innerprod_q2(q1, q2n)) if returnpath: PsiQ = zeros((n, T, k)) PsiX = zeros((n, T, k)) for tau in range(0, k): s = dist * tau / (k - 1.0) PsiQ[:, :, tau] = (sin(dist - s) * q1 + sin(s) * q2n) / sin(dist) PsiX[:, :, tau] = cf.q_to_curve(PsiQ[:, :, tau]) path = PsiQ else: path = 0 return (dist, path, O)
def geod_sphere(beta1, beta2, k=5): """ This function calculates the geodesics between open curves beta1 and beta2 with k steps along path :param beta1: numpy ndarray of shape (2,M) of M samples :param beta2: numpy ndarray of shape (2,M) of M samples :param k: number of samples along path (Default = 5) :rtype: numpy ndarray :return dist: geodesic distance :return path: geodesic path :return O: rotation matrix """ lam = 0.0 elastic = 1 rotation = 1 returnpath = 1 n, T = beta1.shape beta1 = cf.resamplecurve(beta1, T) beta2 = cf.resamplecurve(beta2, T) centroid1 = cf.calculatecentroid(beta1) beta1 = beta1 - tile(centroid1, [T, 1]).T centroid2 = cf.calculatecentroid(beta2) beta2 = beta2 - tile(centroid2, [T, 1]).T q1 = cf.curve_to_q(beta1) if rotation: beta2, O1, tau = cf.find_rotation_and_seed_coord(beta1, beta2) q2 = cf.curve_to_q(beta2) else: O1 = eye(2) q2 = cf.curve_to_q(beta2) if elastic: # Find the optimal coorespondence gam = cf.optimum_reparam_curve(q2, q1, lam) gamI = uf.invertGamma(gam) # Applying optimal re-parameterization to the second curve beta2n = cf.group_action_by_gamma_coord(beta2, gamI) q2n = cf.curve_to_q(beta2n) if rotation: beta2n, O2, tau = cf.find_rotation_and_seed_coord(beta1, beta2n) centroid2 = cf.calculatecentroid(beta2n) beta2n = beta2n - tile(centroid2, [T, 1]).T q2n = cf.curve_to_q(beta2n) O = O1.dot(O2) else: q2n = q2 O = O1 # Forming geodesic between the registered curves dist = arccos(cf.innerprod_q2(q1, q2n)) if returnpath: PsiQ = zeros((n, T, k)) PsiX = zeros((n, T, k)) for tau in range(0, k): s = dist * tau / (k - 1.) PsiQ[:, :, tau] = (sin(dist-s)*q1+sin(s)*q2n)/sin(dist) PsiX[:, :, tau] = cf.q_to_curve(PsiQ[:, :, tau]) path = PsiQ else: path = 0 return(dist, path, O)
def init_path_rand(beta1, beta_mid, beta2, T=100, k=5): r""" Initializes a path in :math:`\cal{C}`. beta1, beta_mid beta2 are already standardized curves. Creates a path from beta1 to beta_mid to beta2 in shape space, then projects to the closed shape manifold. :param beta1: numpy ndarray of shape (2,M) of M samples (first curve) :param betamid: numpy ndarray of shape (2,M) of M samples (mid curve) :param beta2: numpy ndarray of shape (2,M) of M samples (end curve) :param T: Number of samples of curve (Default = 100) :param k: number of samples along path (Default = 5) :rtype: numpy ndarray :return alpha: a path between two q-functions :return beta: a path between two curves :return O: rotation matrix """ alpha = zeros((2, T, k)) beta = zeros((2, T, k)) q1 = cf.curve_to_q(beta1) q_mid = cf.curve_to_q(beta_mid) # find optimal rotation of q2 beta2, O1, tau1 = cf.find_rotation_and_seed_coord(beta1, beta2) q2 = cf.curve_to_q(beta2) # find the optimal coorespondence gam = cf.optimum_reparam_curve(q2, q1) gamI = uf.invertGamma(gam) # apply optimal reparametrization beta2n = cf.group_action_by_gamma_coord(beta2, gamI) # find optimal rotation of q2 beta2n, O2, tau1 = cf.find_rotation_and_seed_coord(beta1, beta2n) centroid2 = cf.calculatecentroid(beta2n) beta2n = beta2n - tile(centroid2, [T, 1]).T q2n = cf.curve_to_q(beta2n) O = O1.dot(O2) # Initialize a path as a geodesic through q1 --- q_mid --- q2 theta1 = arccos(cf.innerprod_q2(q1, q_mid)) theta2 = arccos(cf.innerprod_q2(q_mid, q2n)) tmp = arange(2, int((k-1)/2)+1) t = zeros(tmp.size) alpha[:, :, 0] = q1 beta[:, :, 0] = beta1 i = 0 for tau in range(2, int((k-1)/2)+1): t[i] = (tau-1.)/((k-1)/2.) qnew = (1/sin(theta1))*(sin((1-t[i])*theta1)*q1+sin(t[i]*theta1)*q_mid) alpha[:, :, tau-1] = cf.project_curve(qnew) x = cf.q_to_curve(alpha[:, :, tau-1]) a = -1*cf.calculatecentroid(x) beta[:, :, tau-1] = x + tile(a, [T, 1]).T i += 1 alpha[:, :, int((k-1)/2)] = q_mid beta[:, :, int((k-1)/2)] = beta_mid i = 0 for tau in range(int((k-1)/2)+1, k-1): qnew = (1/sin(theta2))*(sin((1-t[i])*theta2)*q_mid + sin(t[i]*theta2)*q2n) alpha[:, :, tau] = cf.project_curve(qnew) x = cf.q_to_curve(alpha[:, :, tau]) a = -1*cf.calculatecentroid(x) beta[:, :, tau] = x + tile(a, [T, 1]).T i += 1 alpha[:, :, k-1] = q2n beta[:, :, k-1] = beta2n return(alpha, beta, O)
def predict(self, newdata=None): """ This function performs prediction on regression model on new data if available or current stored data in object Usage: obj.predict() obj.predict(newdata) :param newdata: dict containing new data for prediction (needs the keys below, if None predicts on training data) :type newdata: dict :param beta: numpy ndarray of shape (M,N) of M functions with N samples :param y: truth if available """ if newdata != None: beta = newdata['beta'] y = newdata['y'] T = self.q.shape[1] n = beta.shape[2] N = self.q.shape[2] q, beta = preproc_open_curve(beta, T) m = self.n_classes y_pred = np.zeros((n, m)) for ii in range(0, n): diff = self.q - q[:, :, ii][:, :, np.newaxis] dist = np.zeros(N) for jj in range(0, N): dist[jj] = np.linalg.norm(np.abs(diff[:, :, jj]))**2 beta1 = beta[:, :, ii] beta1 = self.O[:, :, dist.argmin()].dot(beta1) beta1 = cf.group_action_by_gamma_coord( beta1, self.gamma[:, dist.argmin()]) q_tmp = cf.curve_to_q(beta1)[0] for jj in range(0, m): y_pred[ii, jj] = self.alpha[jj] + cf.innerprod_q2( q_tmp, self.nu[:, :, jj]) if y is None: y_pred = phi(y_pred.ravel()) y_pred = y_pred.reshape(n, m) y_labels = y_pred.argmax(axis=1) + 1 self.PC = None else: y_pred = phi(y_pred.ravel()) y_pred = y_pred.reshape(n, m) y_labels = y_pred.argmax(axis=1) + 1 PC = np.zeros(m) cls_set = np.arange(1, m + 1) for ii in range(0, m): cls_sub = np.delete(cls_set, ii) TP = sum(y[y_labels == (ii + 1)] == (ii + 1)) FP = sum(y[np.in1d(y_labels, cls_sub)] == (ii + 1)) TN = sum(y[np.in1d(y_labels, cls_sub)] == y_labels[np.in1d( y_labels, cls_sub)]) FN = sum(np.in1d(y[y_labels == (ii + 1)], cls_sub)) PC[ii] = (TP + TN) / float(TP + FP + FN + TN) self.PC = sum(y == y_labels) / float(y_labels.size) self.y_pred = y_pred self.y_labels = y_labels else: n = self.q.shape[2] N = self.q.shape[2] m = self.n_classes y_pred = np.zeros((n, m)) for ii in range(0, n): diff = self.q - self.q[:, :, ii][:, :, np.newaxis] dist = np.zeros(N) for jj in range(0, N): dist[jj] = np.linalg.norm(np.abs(diff[:, :, jj]))**2 beta1 = self.beta0[:, :, ii] beta1 = self.O[:, :, dist.argmin()].dot(beta1) beta1 = cf.group_action_by_gamma_coord( beta1, self.gamma[:, dist.argmin()]) q_tmp = cf.curve_to_q(beta1)[0] for jj in range(0, m): y_pred[ii, jj] = self.alpha[jj] + cf.innerprod_q2( q_tmp, self.nu[:, :, jj]) y_pred = phi(y_pred.ravel()) y_pred = y_pred.reshape(n, m) y_labels = y_pred.argmax(axis=1) + 1 PC = np.zeros(m) cls_set = np.arange(1, m + 1) for ii in range(0, m): cls_sub = np.delete(cls_set, ii) TP = sum(y[y_labels == (ii + 1)] == (ii + 1)) FP = sum(y[np.in1d(y_labels, cls_sub)] == (ii + 1)) TN = sum(y[np.in1d(y_labels, cls_sub)] == y_labels[np.in1d( y_labels, cls_sub)]) FN = sum(np.in1d(y[y_labels == (ii + 1)], cls_sub)) PC[ii] = (TP + TN) / float(TP + FP + FN + TN) self.PC = sum(self.y == y_labels) / float(y_labels.size) self.y_pred = y_pred self.y_labels = y_labels return