def jointfPCAd(qn, vec, C, m, mu_psi): (M,N) = qn.shape g = np.vstack((qn, C*vec)) mu_q = qn.mean(axis=1) mu_g = g.mean(axis=1) K = np.cov(g) U, s, V = svd(K) a = np.zeros((N,m)) for i in range(0,N): for j in range(0,m): tmp = (g[:,i]-mu_g) a[i,j] = dot(tmp.T, U[:,j]) qhat = np.tile(mu_q, (N,1)) qhat = qhat.T qhat = qhat + dot(U[0:M,0:m],a.T) vechat = dot(U[M:,0:m], a.T/C) psihat = np.zeros((M-1,N)) gamhat = np.zeros((M-1,N)) for ii in range(0,N): psihat[:,ii] = geo.exp_map(mu_psi,vechat[:,ii]) gam_tmp = cumtrapz(psihat[:,ii]*psihat[:,ii], np.linspace(0,1,M-1), initial=0) gamhat[:,ii] = (gam_tmp - gam_tmp.min()) / (gam_tmp.max() - gam_tmp.min()) U = U[:,0:m] s = s[0:m] return qhat, gamhat, a, U, s, mu_g
def jfpca_sub(mu_psi, vechat): M = mu_psi.shape[0] psihat = geo.exp_map(mu_psi, vechat) gam_tmp = cumtrapz(psihat * psihat, np.linspace(0, 1, M), initial=0) gamhat = (gam_tmp - gam_tmp.min()) / (gam_tmp.max() - gam_tmp.min()) return gamhat
def jointfPCAd(qn, vec, C, m, mu_psi): (M,N) = qn.shape g = np.vstack((qn, C*vec)) mu_q = qn.mean(axis=1) mu_g = g.mean(axis=1) K = np.cov(g) U, s, V = svd(K) a = np.zeros((N,m)) for i in range(0,N): for j in range(0,m): tmp = (g[:,i]-mu_g) a[i,j] = dot(tmp.T, U[:,j]) qhat = np.tile(mu_q, (N,1)) qhat = qhat.T qhat = qhat + dot(U[0:M,0:m],a.T) vechat = dot(U[M:,0:m], a.T/C) psihat = np.zeros((M-1,N)) gamhat = np.zeros((M-1,N)) for ii in range(0,N): psihat[:,ii] = geo.exp_map(mu_psi,vechat[:,ii]) gam_tmp = cumtrapz(psihat[:,ii]*psihat[:,ii], np.linspace(0,1,M-1), initial=0) gamhat[:,ii] = (gam_tmp - gam_tmp.min()) / (gam_tmp.max() - gam_tmp.min()) U = U[:,0:m] s = s[0:m] return qhat, gamhat, a, U, s, mu_g, g, K
def SqrtMean(gam): """ calculates the srsf of warping functions with corresponding shooting vectors :param gam: numpy ndarray of shape (M,N) of M warping functions with N samples :rtype: 2 numpy ndarray and vector :return mu: Karcher mean psi function :return gam_mu: vector of dim N which is the Karcher mean warping function :return psi: numpy ndarray of shape (M,N) of M SRSF of the warping functions :return vec: numpy ndarray of shape (M,N) of M shooting vectors """ (T,n) = gam.shape time = linspace(0,1,T) binsize = mean(diff(time)) psi = zeros((T, n)) for k in range(0, n): psi[:, k] = sqrt(gradient(gam[:, k],binsize)) # Find Direction mnpsi = psi.mean(axis=1) a = mnpsi.repeat(n) d1 = a.reshape(T, n) d = (psi - d1) ** 2 dqq = sqrt(d.sum(axis=0)) min_ind = dqq.argmin() mu = psi[:, min_ind] maxiter = 501 tt = 1 lvm = zeros(maxiter) vec = zeros((T, n)) stp = .3 itr = 0 for i in range(0,n): out, theta = geo.inv_exp_map(mu,psi[:,i]) vec[:,i] = out vbar = vec.mean(axis=1) lvm[itr] = geo.L2norm(vbar) while (lvm[itr] > 0.00000001) and (itr<maxiter): mu = geo.exp_map(mu, stp*vbar) itr += 1 for i in range(0,n): out, theta = geo.inv_exp_map(mu,psi[:,i]) vec[:,i] = out vbar = vec.mean(axis=1) lvm[itr] = geo.L2norm(vbar) gam_mu = cumtrapz(mu*mu, time, initial=0) gam_mu = (gam_mu - gam_mu.min()) / (gam_mu.max() - gam_mu.min()) return mu, gam_mu, psi, vec
def SqrtMedian(gam): """ calculates the median srsf of warping functions with corresponding shooting vectors :param gam: numpy ndarray of shape (M,N) of M warping functions with N samples :rtype: 2 numpy ndarray and vector :return gam_median: Karcher median warping function :return psi_meidan: vector of dim N which is the Karcher median srsf function :return psi: numpy ndarray of shape (M,N) of M SRSF of the warping functions :return vec: numpy ndarray of shape (M,N) of M shooting vectors """ (T, n) = gam.shape time = linspace(0, 1, T) # Initialization psi_median = ones(T) r = 1 stp = 0.3 maxiter = 501 vbar_norm = zeros(maxiter + 1) # compute psi function binsize = mean(diff(time)) psi = zeros((T, n)) v = zeros((T, n)) vtil = zeros((T, n)) d = zeros(n) dtil = zeros(n) for k in range(0, n): psi[:, k] = sqrt(gradient(gam[:, k], binsize)) v[:, k], d[k] = geo.inv_exp_map(psi_median, psi[:, k]) vtil[:, k] = v[:, k] / d[k] dtil[k] = 1 / d[k] vbar = vtil.sum(axis=1) * dtil.sum()**(-1) vbar_norm[r] = geo.L2norm(vbar) # compute phase median by iterative algorithm while (vbar_norm[r] > 0.00000001) and (r < maxiter): psi_median = geo.exp_map(psi_median, stp * vbar) r += 1 for k in range(0, n): v[:, k], tmp = geo.inv_exp_map(psi_median, psi[:, k]) d[k] = arccos(geo.inner_product(psi_median, psi[:, k])) vtil[:, k] = v[:, k] / d[k] dtil[k] = 1 / d[k] vbar = vtil.sum(axis=1) * dtil.sum()**(-1) vbar_norm[r] = geo.L2norm(vbar) vec = v gam_median = cumtrapz(psi_median**2, time, initial=0.0) return gam_median, psi_median, psi, vec
def SqrtMeanInverse(gam): """ finds the inverse of the mean of the set of the diffeomorphisms gamma :param gam: numpy ndarray of shape (M,N) of M warping functions with N samples :rtype: vector :return gamI: inverse of gam """ (T,n) = gam.shape time = linspace(0,1,T) binsize = mean(diff(time)) psi = zeros((T, n)) for k in range(0, n): psi[:, k] = sqrt(gradient(gam[:, k],binsize)) # Find Direction mnpsi = psi.mean(axis=1) a = mnpsi.repeat(n) d1 = a.reshape(T, n) d = (psi - d1) ** 2 dqq = sqrt(d.sum(axis=0)) min_ind = dqq.argmin() mu = psi[:, min_ind] maxiter = 501 tt = 1 lvm = zeros(maxiter) vec = zeros((T, n)) stp = .3 itr = 0 for i in range(0,n): out, theta = geo.inv_exp_map(mu,psi[:,i]) vec[:,i] = out vbar = vec.mean(axis=1) lvm[itr] = geo.L2norm(vbar) while (lvm[itr] > 0.00000001) and (itr<maxiter): mu = geo.exp_map(mu, stp*vbar) itr += 1 for i in range(0,n): out, theta = geo.inv_exp_map(mu,psi[:,i]) vec[:,i] = out vbar = vec.mean(axis=1) lvm[itr] = geo.L2norm(vbar) gam_mu = cumtrapz(mu*mu, time, initial=0) gam_mu = (gam_mu - gam_mu.min()) / (gam_mu.max() - gam_mu.min()) gamI = invertGamma(gam_mu) return gamI
def rgam(N, sigma, num, mu_gam=None): """ Generates random warping functions :param N: length of warping function :param sigma: variance of warping functions :param num: number of warping functions :param mu_gam mean warping function (default identity) :return: gam: numpy ndarray of warping functions """ gam = zeros((N, num)) time = linspace(0, 1, N) binsize = diff(time) binsize = binsize.mean() if mu_gam is None: mu = sqrt(gradient(time,binsize)) else: mu = sqrt(gradient(mu_gam,binsize)) omega = (2 * pi) for k in range(0, num): alpha_i = rn.normal(scale=sigma) v = alpha_i * ones(N) cnt = 1 for l in range(2, 4): alpha_i = rn.normal(scale=sigma) #odd if l % 2 != 0: v = v + alpha_i * sqrt(2) * cos(cnt * omega * time) cnt += 1 #even if l % 2 == 0: v = v + alpha_i * sqrt(2) * sin(cnt * omega * time) psi = geo.exp_map(mu.ravel(),v.ravel()) gam0 = cumtrapz(psi*psi, time, initial=0) gam[:, k] = (gam0 - gam0.min()) / (gam0.max() - gam0.min()) return gam
def jointfPCAd(qn, vec, C, m, mu_psi, parallel, cores): (M, N) = qn.shape g = np.vstack((qn, C * vec)) mu_q = qn.mean(axis=1) mu_g = g.mean(axis=1) K = np.cov(g) U, s, V = svd(K) a = np.zeros((N, m)) for i in range(0, N): for j in range(0, m): tmp = (g[:, i] - mu_g) a[i, j] = np.dot(tmp.T, U[:, j]) qhat = np.tile(mu_q, (N, 1)) qhat = qhat.T qhat = qhat + np.dot(U[0:M, 0:m], a.T) vechat = np.dot(U[M:, 0:m], a.T / C) psihat = np.zeros((M - 1, N)) gamhat = np.zeros((M - 1, N)) if parallel: out = Parallel(n_jobs=cores)(delayed(jfpca_sub)(mu_psi, vechat[:, n]) for n in range(N)) gamhat = np.array(out) gamhat = gamhat.transpose() else: for ii in range(0, N): psihat[:, ii] = geo.exp_map(mu_psi, vechat[:, ii]) gam_tmp = cumtrapz(psihat[:, ii] * psihat[:, ii], np.linspace(0, 1, M - 1), initial=0) gamhat[:, ii] = (gam_tmp - gam_tmp.min()) / (gam_tmp.max() - gam_tmp.min()) U = U[:, 0:m] s = s[0:m] return qhat, gamhat, a, U, s, mu_g, g, K
def joint_gauss_model(self, n=1, no=3): """ This function models the functional data using a joint Gaussian model extracted from the principal components of the srsfs :param n: number of random samples :param no: number of principal components (default = 3) :type n: integer :type no: integer """ # Parameters fn = self.fn time = self.time qn = self.qn gam = self.gam M = time.size # Perform PCA jfpca = fpca.fdajpca(self) jfpca.calc_fpca(no=no) s = jfpca.latent U = jfpca.U C = jfpca.C mu_psi = jfpca.mu_psi # compute mean and covariance mq_new = qn.mean(axis=1) mididx = jfpca.id m_new = np.sign(fn[mididx, :]) * np.sqrt(np.abs(fn[mididx, :])) mqn = np.append(mq_new, m_new.mean()) # generate random samples vals = np.random.multivariate_normal(np.zeros(s.shape), np.diag(s), n) tmp = np.matmul(U, np.transpose(vals)) qhat = np.tile(mqn.T, (n, 1)).T + tmp[0:M + 1, :] tmp = np.matmul(U, np.transpose(vals) / C) vechat = tmp[(M + 1):, :] psihat = np.zeros((M, n)) gamhat = np.zeros((M, n)) for ii in range(n): psihat[:, ii] = geo.exp_map(mu_psi, vechat[:, ii]) gam_tmp = cumtrapz(psihat[:, ii]**2, np.linspace(0, 1, M), initial=0.0) gamhat[:, ii] = (gam_tmp - gam_tmp.min()) / (gam_tmp.max() - gam_tmp.min()) ft = np.zeros((M, n)) fhat = np.zeros((M, n)) for ii in range(n): fhat[:, ii] = uf.cumtrapzmid( time, qhat[0:M, ii] * np.fabs(qhat[0:M, ii]), np.sign(qhat[M, ii]) * (qhat[M, ii] * qhat[M, ii]), mididx) ft[:, ii] = uf.warp_f_gamma(np.linspace(0, 1, M), fhat[:, ii], gamhat[:, ii]) self.rsamps = True self.fs = fhat self.gams = gamhat self.ft = ft self.qs = qhat[0:M, :] return
def jointfPCA(fn, time, qn, q0, gam, no=2, showplot=True): """ This function calculates joint functional principal component analysis on aligned data :param fn: numpy ndarray of shape (M,N) of N aligned functions with M samples :param time: vector of size N describing the sample points :param qn: numpy ndarray of shape (M,N) of N aligned SRSF with M samples :param no: number of components to extract (default = 2) :param showplot: Shows plots of results using matplotlib (default = T) :type showplot: bool :type no: int :rtype: tuple of numpy ndarray :return q_pca: srsf principal directions :return f_pca: functional principal directions :return latent: latent values :return coef: coefficients :return U: eigenvectors """ coef = np.arange(-1., 2.) Nstd = coef.shape[0] # set up for fPCA in q-space mq_new = qn.mean(axis=1) M = time.shape[0] mididx = int(np.round(M / 2)) m_new = np.sign(fn[mididx, :]) * np.sqrt(np.abs(fn[mididx, :])) mqn = np.append(mq_new, m_new.mean()) qn2 = np.vstack((qn, m_new)) # calculate vector space of warping functions mu_psi, gam_mu, psi, vec = uf.SqrtMean(gam) # joint fPCA C = fminbound(find_C,0,1e4,(qn2,vec,q0,no,mu_psi)) qhat, gamhat, a, U, s, mu_g = jointfPCAd(qn2, vec, C, no, mu_psi) # geodesic paths q_pca = np.ndarray(shape=(M, Nstd, no), dtype=float) f_pca = np.ndarray(shape=(M, Nstd, no), dtype=float) for k in range(0, no): for l in range(0, Nstd): qhat = mqn + dot(U[0:(M+1),k],coef[l]*np.sqrt(s[k])) vechat = dot(U[(M+1):,k],(coef[l]*np.sqrt(s[k]))/C) psihat = geo.exp_map(mu_psi,vechat) gamhat = cumtrapz(psihat*psihat,np.linspace(0,1,M),initial=0) gamhat = (gamhat - gamhat.min()) / (gamhat.max() - gamhat.min()) if (sum(vechat)==0): gamhat = np.linspace(0,1,M) fhat = uf.cumtrapzmid(time, qhat[0:M]*np.fabs(qhat[0:M]), np.sign(qhat[M])*(qhat[M]*qhat[M]), mididx) f_pca[:,l,k] = uf.warp_f_gamma(np.linspace(0,1,M), fhat, gamhat) q_pca[:,l,k] = uf.warp_q_gamma(np.linspace(0,1,M), qhat[0:M], gamhat) jfpca_results = collections.namedtuple('jfpca', ['q_pca', 'f_pca', 'latent', 'coef', 'U']) jfpca = jfpca_results(q_pca, f_pca, s, a, U) if showplot: CBcdict = { 'Bl': (0, 0, 0), 'Or': (.9, .6, 0), 'SB': (.35, .7, .9), 'bG': (0, .6, .5), 'Ye': (.95, .9, .25), 'Bu': (0, .45, .7), 'Ve': (.8, .4, 0), 'rP': (.8, .6, .7), } cl = sorted(CBcdict.keys()) fig, ax = plt.subplots(2, no) for k in range(0, no): axt = ax[0, k] for l in range(0, Nstd): axt.plot(time, q_pca[0:M, l, k], color=CBcdict[cl[l]]) axt.set_title('q domain: PD %d' % (k + 1)) axt = ax[1, k] for l in range(0, Nstd): axt.plot(time, f_pca[:, l, k], color=CBcdict[cl[l]]) axt.set_title('f domain: PD %d' % (k + 1)) fig.set_tight_layout(True) cumm_coef = 100 * np.cumsum(s) / sum(s) idx = np.arange(0, s.shape[0]) + 1 plot.f_plot(idx, cumm_coef, "Coefficient Cumulative Percentage") plt.xlabel("Percentage") plt.ylabel("Index") plt.show() return jfpca
def calc_fpca(self, no=3, stds=np.arange(-1., 2.), id=None, parallel=False, cores=-1): """ This function calculates joint functional principal component analysis on aligned data :param no: number of components to extract (default = 3) :param id: point to use for f(0) (default = midpoint) :param stds: number of standard deviations along gedoesic to compute (default = -1,0,1) :param parallel: run in parallel (default = F) :param cores: number of cores for parallel (default = -1 (all)) :type no: int :type id: int :type parallel: bool :type cores: int :rtype: fdajpca object of numpy ndarray :return q_pca: srsf principal directions :return f_pca: functional principal directions :return latent: latent values :return coef: coefficients :return U: eigenvectors """ fn = self.warp_data.fn time = self.warp_data.time qn = self.warp_data.qn q0 = self.warp_data.q0 gam = self.warp_data.gam M = time.shape[0] if id is None: mididx = int(np.round(M / 2)) else: mididx = id Nstd = stds.shape[0] # set up for fPCA in q-space mq_new = qn.mean(axis=1) m_new = np.sign(fn[mididx, :]) * np.sqrt(np.abs(fn[mididx, :])) mqn = np.append(mq_new, m_new.mean()) qn2 = np.vstack((qn, m_new)) # calculate vector space of warping functions mu_psi, gam_mu, psi, vec = uf.SqrtMean(gam, parallel, cores) # joint fPCA C = fminbound(find_C, 0, 1e4, (qn2, vec, q0, no, mu_psi, parallel, cores)) qhat, gamhat, a, U, s, mu_g, g, cov = jointfPCAd( qn2, vec, C, no, mu_psi, parallel, cores) # geodesic paths q_pca = np.ndarray(shape=(M, Nstd, no), dtype=float) f_pca = np.ndarray(shape=(M, Nstd, no), dtype=float) for k in range(0, no): for l in range(0, Nstd): qhat = mqn + np.dot(U[0:(M + 1), k], stds[l] * np.sqrt(s[k])) vechat = np.dot(U[(M + 1):, k], (stds[l] * np.sqrt(s[k])) / C) psihat = geo.exp_map(mu_psi, vechat) gamhat = cumtrapz(psihat * psihat, np.linspace(0, 1, M), initial=0) gamhat = (gamhat - gamhat.min()) / (gamhat.max() - gamhat.min()) if (sum(vechat) == 0): gamhat = np.linspace(0, 1, M) fhat = uf.cumtrapzmid(time, qhat[0:M] * np.fabs(qhat[0:M]), np.sign(qhat[M]) * (qhat[M] * qhat[M]), mididx) f_pca[:, l, k] = uf.warp_f_gamma(np.linspace(0, 1, M), fhat, gamhat) q_pca[:, l, k] = uf.warp_q_gamma(np.linspace(0, 1, M), qhat[0:M], gamhat) self.q_pca = q_pca self.f_pca = f_pca self.latent = s[0:no] self.coef = a self.U = U[:, 0:no] self.mu_psi = mu_psi self.mu_g = mu_g self.id = mididx self.C = C self.time = time self.g = g self.cov = cov self.no = no self.stds = stds return