BiFuncLib 0.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- BiFuncLib-0.0.2/BiFuncLib/AuxFunc.py +34 -0
- BiFuncLib-0.0.2/BiFuncLib/BiclustResult.py +12 -0
- BiFuncLib-0.0.2/BiFuncLib/BsplineFunc.py +449 -0
- BiFuncLib-0.0.2/BiFuncLib/FDPlot.py +619 -0
- BiFuncLib-0.0.2/BiFuncLib/__init__.py +30 -0
- BiFuncLib-0.0.2/BiFuncLib/bcheatmap.py +269 -0
- BiFuncLib-0.0.2/BiFuncLib/bimax_biclus.py +84 -0
- BiFuncLib-0.0.2/BiFuncLib/cc_bifunc.py +254 -0
- BiFuncLib-0.0.2/BiFuncLib/cc_main_func.py +543 -0
- BiFuncLib-0.0.2/BiFuncLib/cvx_biclus.py +62 -0
- BiFuncLib-0.0.2/BiFuncLib/cvx_main_func.py +642 -0
- BiFuncLib-0.0.2/BiFuncLib/fem_bifunc.py +65 -0
- BiFuncLib-0.0.2/BiFuncLib/fem_main_func.py +281 -0
- BiFuncLib-0.0.2/BiFuncLib/lbm_bifunc.py +62 -0
- BiFuncLib-0.0.2/BiFuncLib/lbm_main_func.py +509 -0
- BiFuncLib-0.0.2/BiFuncLib/local_bifunc.py +42 -0
- BiFuncLib-0.0.2/BiFuncLib/local_main_func.py +434 -0
- BiFuncLib-0.0.2/BiFuncLib/pf_bifunc.py +157 -0
- BiFuncLib-0.0.2/BiFuncLib/pf_main_func.py +227 -0
- BiFuncLib-0.0.2/BiFuncLib/sas_bifunc.py +229 -0
- BiFuncLib-0.0.2/BiFuncLib/sas_main_func.py +604 -0
- BiFuncLib-0.0.2/BiFuncLib/simulation_data.py +470 -0
- BiFuncLib-0.0.2/BiFuncLib/sparse_bifunc.py +13 -0
- BiFuncLib-0.0.2/BiFuncLib/sparse_main_func.py +141 -0
- BiFuncLib-0.0.2/BiFuncLib/ssvd_biclus.py +18 -0
- BiFuncLib-0.0.2/BiFuncLib/ssvd_main_func.py +733 -0
- BiFuncLib-0.0.2/BiFuncLib.egg-info/PKG-INFO +16 -0
- BiFuncLib-0.0.2/BiFuncLib.egg-info/SOURCES.txt +34 -0
- BiFuncLib-0.0.2/BiFuncLib.egg-info/dependency_links.txt +1 -0
- BiFuncLib-0.0.2/BiFuncLib.egg-info/requires.txt +9 -0
- BiFuncLib-0.0.2/BiFuncLib.egg-info/top_level.txt +1 -0
- BiFuncLib-0.0.2/LICENSE.txt +21 -0
- BiFuncLib-0.0.2/PKG-INFO +16 -0
- BiFuncLib-0.0.2/README.md +2 -0
- BiFuncLib-0.0.2/setup.cfg +4 -0
- BiFuncLib-0.0.2/setup.py +34 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import itertools
|
|
4
|
+
from scipy.sparse import lil_matrix
|
|
5
|
+
import matplotlib.pyplot as plt
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AuxFunc:
|
|
9
|
+
def __init__(self, n, m = None, x = None, V = None):
|
|
10
|
+
self.n = n
|
|
11
|
+
self.m = m if m is not None else None
|
|
12
|
+
self.x = np.array(x) if x is not None else None
|
|
13
|
+
self.V = np.array(V) if V is not None else None
|
|
14
|
+
|
|
15
|
+
# Generate break sequence in spline expansion
|
|
16
|
+
def knots_eq(self):
|
|
17
|
+
return np.r_[self.x.min(), np.linspace(self.x.min(), self.x.max(), self.m+2)[1:-1], self.x.max()]
|
|
18
|
+
|
|
19
|
+
# Construct the adjacency matrix
|
|
20
|
+
def create_adjacency(self, plot = True):
|
|
21
|
+
differences = np.linalg.norm(self.V, axis=0)
|
|
22
|
+
connected_ix = np.where(differences == 0)[0]
|
|
23
|
+
index = pd.DataFrame(itertools.combinations(range(self.n), 2))
|
|
24
|
+
i = index.iloc[list(connected_ix),0]
|
|
25
|
+
j = index.iloc[list(connected_ix),1]
|
|
26
|
+
A = lil_matrix((self.n, self.n))
|
|
27
|
+
A[i, j] = 1
|
|
28
|
+
if plot == True:
|
|
29
|
+
plt.figure()
|
|
30
|
+
plt.spy(A, markersize=1)
|
|
31
|
+
plt.show()
|
|
32
|
+
A_array = A.toarray()
|
|
33
|
+
return A_array
|
|
34
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class BiclustResult:
|
|
5
|
+
def __init__(self, params, RowxNumber, NumberxCol, Number, info):
|
|
6
|
+
self.params = params
|
|
7
|
+
self.RowxNumber = RowxNumber
|
|
8
|
+
self.NumberxCol = NumberxCol.T
|
|
9
|
+
self.Number = Number
|
|
10
|
+
self.info = info
|
|
11
|
+
self.cluster_row_sizes = np.sum(RowxNumber, axis=0)
|
|
12
|
+
self.cluster_col_sizes = np.sum(NumberxCol, axis=0)
|
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from GENetLib.fda_func import fd, inprod, eval_basis, int2lfd
|
|
3
|
+
from GENetLib.fda_func import ppbspline, ycheck, wtcheck, fdparcheck
|
|
4
|
+
from scipy.linalg import cholesky, solve, qr, eigh
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# Create a class for b-spline functions
|
|
8
|
+
class BsplineFunc:
|
|
9
|
+
def __init__(self, basisobj, Lfdobj = 2, rng = None, returnMatrix = False):
|
|
10
|
+
self.basisobj = basisobj
|
|
11
|
+
self.Lfdobj = Lfdobj
|
|
12
|
+
self.rng = rng
|
|
13
|
+
self.returnMatrix = returnMatrix
|
|
14
|
+
|
|
15
|
+
# Calculate B-spline penaty matrix
|
|
16
|
+
def penalty_matrix(self, btype = 'spline'):
|
|
17
|
+
if btype == 'spline':
|
|
18
|
+
|
|
19
|
+
# Initialize parameters
|
|
20
|
+
if type(self.Lfdobj) == int:
|
|
21
|
+
Lfdobj = int2lfd(self.Lfdobj)
|
|
22
|
+
else:
|
|
23
|
+
Lfdobj = self.Lfdobj
|
|
24
|
+
nbasis = self.basisobj['nbasis']
|
|
25
|
+
params = self.basisobj['params']
|
|
26
|
+
Rangeval = self.basisobj['rangeval']
|
|
27
|
+
Rng = self.rng if self.rng is not None else Rangeval
|
|
28
|
+
rng = np.array(Rng, dtype=float)
|
|
29
|
+
rangeval = np.array(Rangeval, dtype=float)
|
|
30
|
+
params = np.array(params, dtype=float)
|
|
31
|
+
breaks = np.concatenate(([rangeval[0]], params, [rangeval[1]]))
|
|
32
|
+
nbreaks = len(breaks)
|
|
33
|
+
ninterval = nbreaks - 1
|
|
34
|
+
nderiv = Lfdobj['nderiv']
|
|
35
|
+
norder = nbasis - len(params)
|
|
36
|
+
if nderiv >= norder:
|
|
37
|
+
raise ValueError('nderiv >= norder')
|
|
38
|
+
if nderiv > 0 and nderiv == norder - 1:
|
|
39
|
+
raise ValueError(f"Penalty matrix cannot be evaluated for derivative of order {nderiv} for B-splines of order {norder}")
|
|
40
|
+
bwtlist = Lfdobj['bwtlist']
|
|
41
|
+
isintLfd = True
|
|
42
|
+
if nderiv > 0:
|
|
43
|
+
for ideriv in range(1, nderiv + 1):
|
|
44
|
+
fdj = bwtlist[ideriv - 1]
|
|
45
|
+
if fdj is not None and not np.all(fdj['coefs'] == 0):
|
|
46
|
+
isintLfd = False
|
|
47
|
+
break
|
|
48
|
+
intbreaks = np.concatenate(([rng[0]], params, [rng[1]]))
|
|
49
|
+
index = (intbreaks >= rng[0]) & (intbreaks <= rng[1])
|
|
50
|
+
intbreaks = intbreaks[index]
|
|
51
|
+
uniquebreaks = np.min(np.diff(intbreaks)) > 0
|
|
52
|
+
if isintLfd and rng[0] == rangeval[0] and uniquebreaks and rng[1] == rangeval[1]:
|
|
53
|
+
onesv = np.ones((1, norder))
|
|
54
|
+
knots = np.concatenate(((rangeval[0] * onesv)[0], breaks[1:nbreaks - 1], (rangeval[1] * onesv)[0]))
|
|
55
|
+
polyorder = norder - nderiv
|
|
56
|
+
ndegree = polyorder - 1
|
|
57
|
+
prodorder = 2 * ndegree + 1
|
|
58
|
+
polycoef = np.zeros((ninterval, polyorder, norder))
|
|
59
|
+
indxdown = list(range(norder, nderiv, -1))
|
|
60
|
+
for i in range(nbasis):
|
|
61
|
+
t = knots[i:i + norder + 1]
|
|
62
|
+
ppBlist = ppbspline(t)
|
|
63
|
+
Coeff = ppBlist[0]
|
|
64
|
+
index = ppBlist[1]
|
|
65
|
+
nrowcoef = Coeff.shape[0]
|
|
66
|
+
index = index + i - norder
|
|
67
|
+
CoeffD = np.array(Coeff)[:, :polyorder]
|
|
68
|
+
if nderiv > 0:
|
|
69
|
+
for ideriv in range(1, nderiv + 1):
|
|
70
|
+
fac = np.array(indxdown) - ideriv
|
|
71
|
+
CoeffD = np.outer(np.ones(nrowcoef), fac) * CoeffD
|
|
72
|
+
if i >= norder - 1:
|
|
73
|
+
k = norder - 1
|
|
74
|
+
else:
|
|
75
|
+
k = i
|
|
76
|
+
if i <= norder - 1:
|
|
77
|
+
m = i
|
|
78
|
+
else:
|
|
79
|
+
m = norder - 1
|
|
80
|
+
for j in range(nrowcoef):
|
|
81
|
+
polycoef[i - k + j, :, m - j] = CoeffD[j, :]
|
|
82
|
+
prodmat = np.zeros((nbasis, nbasis))
|
|
83
|
+
convmat = np.zeros((norder, norder, prodorder))
|
|
84
|
+
for k in range(ninterval):
|
|
85
|
+
Coeff = polycoef[k, :, :]
|
|
86
|
+
for i in range(ndegree):
|
|
87
|
+
ind = np.arange(i + 1)
|
|
88
|
+
if len(ind) == 1:
|
|
89
|
+
convmat[:, :, i] = np.outer(Coeff[ind, :], Coeff[i - ind, :])
|
|
90
|
+
convmat[:, :, prodorder - i - 1] = np.outer(Coeff[ndegree - ind, :], Coeff[ndegree - i + ind, :])
|
|
91
|
+
else:
|
|
92
|
+
convmat[:, :, i] = np.dot(Coeff[ind, :].T, Coeff[i - ind, :])
|
|
93
|
+
convmat[:, :, prodorder - i - 1] = np.dot(Coeff[ndegree - ind - 1, :].T, Coeff[ndegree - i + ind, :])
|
|
94
|
+
ind = np.arange(ndegree + 1)
|
|
95
|
+
convmat[:, :, ndegree] = np.dot(Coeff[ind, :].T, Coeff[ndegree - ind, :])
|
|
96
|
+
delta = breaks[k + 1] - breaks[k]
|
|
97
|
+
power = delta
|
|
98
|
+
prodmati = np.zeros((norder, norder))
|
|
99
|
+
for i in range(1, prodorder + 1):
|
|
100
|
+
prodmati += power * convmat[:, :, prodorder - i] / i
|
|
101
|
+
power *= delta
|
|
102
|
+
index = np.arange(k, k + norder)
|
|
103
|
+
prodmat[index[:, None], index] += prodmati
|
|
104
|
+
penaltymat = prodmat
|
|
105
|
+
else:
|
|
106
|
+
if uniquebreaks:
|
|
107
|
+
prodmat = inprod(self.basisobj, self.basisobj, Lfdobj, Lfdobj, rng)
|
|
108
|
+
else:
|
|
109
|
+
rngvec = [rng[0]]
|
|
110
|
+
for i in range(1, nbreaks):
|
|
111
|
+
if breaks[i] == breaks[i - 1]:
|
|
112
|
+
rngvec.append(breaks[i])
|
|
113
|
+
rngvec = list(set(rngvec))
|
|
114
|
+
nrng = len(rngvec)
|
|
115
|
+
if rngvec[-1] < rng[1]:
|
|
116
|
+
rngvec.append(rng[1])
|
|
117
|
+
nrng += 1
|
|
118
|
+
prodmat = np.zeros((nbasis, nbasis))
|
|
119
|
+
for i in range(1, nrng):
|
|
120
|
+
rngi = [rngvec[i - 1] + 1e-10, rngvec[i] - 1e-10]
|
|
121
|
+
prodmati = inprod(self.basisobj, self.basisobj, Lfdobj, Lfdobj, rngi)
|
|
122
|
+
prodmat += prodmati
|
|
123
|
+
penaltymat = prodmat
|
|
124
|
+
return penaltymat
|
|
125
|
+
|
|
126
|
+
elif btype == 'fourier':
|
|
127
|
+
nbasis = self.basisobj['nbasis']
|
|
128
|
+
if nbasis % 2 == 0:
|
|
129
|
+
self.basisobj['nbasis'] = nbasis + 1
|
|
130
|
+
type_ = self.basisobj['btype']
|
|
131
|
+
if type_ != "fourier":
|
|
132
|
+
raise ValueError("Wrong basis type")
|
|
133
|
+
if isinstance(self.Lfdobj, int):
|
|
134
|
+
Lfdobj_ = int2lfd(self.Lfdobj)
|
|
135
|
+
nderiv = Lfdobj_['nderiv']
|
|
136
|
+
penaltymatrix = inprod(self.basisobj, self.basisobj, Lfdobj_, Lfdobj_)
|
|
137
|
+
else:
|
|
138
|
+
nderiv = self.Lfdobj['nderiv']
|
|
139
|
+
penaltymatrix = inprod(self.basisobj, self.basisobj, self.Lfdobj, self.Lfdobj)
|
|
140
|
+
return penaltymatrix
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def smooth_basis(self, argvals, y, wtvec = None, fdnames = None, covariates = None,
|
|
144
|
+
method = "chol", dfscale = 1, returnMatrix = False):
|
|
145
|
+
|
|
146
|
+
# Check and initialize parameters
|
|
147
|
+
fdParobj = self.basisobj
|
|
148
|
+
dimy = y.shape
|
|
149
|
+
n = dimy[0]
|
|
150
|
+
y_check = ycheck(y, n)
|
|
151
|
+
y = y_check['y']
|
|
152
|
+
y0 = y
|
|
153
|
+
nrep = y_check['ncurve']
|
|
154
|
+
nvar = y_check['nvar']
|
|
155
|
+
fdParobj = fdparcheck(fdParobj, nrep)
|
|
156
|
+
fdobj = fdParobj['fd']
|
|
157
|
+
lambda_ = fdParobj['lambda']
|
|
158
|
+
Lfdobj = fdParobj['lfd']
|
|
159
|
+
penmat = fdParobj['penmat']
|
|
160
|
+
if lambda_ < 0:
|
|
161
|
+
print("Value of 'lambda' was negative. 0 used instead.")
|
|
162
|
+
lambda_ = 0
|
|
163
|
+
wtlist = wtcheck(n, wtvec)
|
|
164
|
+
wtvec = wtlist['wtvec']
|
|
165
|
+
onewt = wtlist['onewt']
|
|
166
|
+
matwt = wtlist['matwt']
|
|
167
|
+
nderiv = Lfdobj['nderiv']
|
|
168
|
+
basisobj = fdobj['basis']
|
|
169
|
+
dropind = basisobj['dropind']
|
|
170
|
+
ndropind = len(dropind) if dropind is not None else 0
|
|
171
|
+
nbasis = basisobj['nbasis'] - ndropind
|
|
172
|
+
names = basisobj['names']
|
|
173
|
+
if ndropind > 0:
|
|
174
|
+
names = np.delete(names, dropind)
|
|
175
|
+
if len(dimy) == 2:
|
|
176
|
+
coef = np.zeros((nbasis, dimy[1]))
|
|
177
|
+
ynames = dimy[1]
|
|
178
|
+
vnames = "value"
|
|
179
|
+
elif len(dimy) == 3:
|
|
180
|
+
coef = np.zeros((nbasis, dimy[1], dimy[2]))
|
|
181
|
+
ynames = dimy[1]
|
|
182
|
+
vnames = dimy[2]
|
|
183
|
+
if covariates is not None:
|
|
184
|
+
if not np.issubdtype(covariates.dtype, np.number):
|
|
185
|
+
raise ValueError("Optional argument COVARIATES is not numeric.")
|
|
186
|
+
if covariates.shape[0] != n:
|
|
187
|
+
raise ValueError("Optional argument COVARIATES has incorrect number of rows.")
|
|
188
|
+
q = covariates.shape[1]
|
|
189
|
+
else:
|
|
190
|
+
q = 0
|
|
191
|
+
beta_ = None
|
|
192
|
+
tnames = None
|
|
193
|
+
if y.ndim > 0 and y.shape[0] > 0:
|
|
194
|
+
tnames = np.arange(1, n+1)
|
|
195
|
+
basismat = eval_basis(argvals, basisobj, 0, returnMatrix)
|
|
196
|
+
|
|
197
|
+
# Calculation
|
|
198
|
+
if method == "chol":
|
|
199
|
+
if n > nbasis + q or lambda_ > 0:
|
|
200
|
+
if covariates is not None:
|
|
201
|
+
ind1 = np.arange(n)
|
|
202
|
+
ind2 = np.arange(nbasis, nbasis + q)
|
|
203
|
+
basismat = np.asmatrix(basismat)
|
|
204
|
+
basismat = np.c_[basismat, np.zeros((basismat.shape[0], q))]
|
|
205
|
+
basismat[ind1[:, np.newaxis], ind2] = covariates
|
|
206
|
+
if matwt:
|
|
207
|
+
wtfac = cholesky(wtvec)
|
|
208
|
+
basisw = wtvec @ basismat
|
|
209
|
+
else:
|
|
210
|
+
rtwtvec = np.sqrt(wtvec)
|
|
211
|
+
basisw = (wtvec @ np.ones((1, nbasis + q))) * np.array(basismat)
|
|
212
|
+
Bmat = basisw.T @ basismat
|
|
213
|
+
Bmat0 = Bmat
|
|
214
|
+
if len(dimy) < 3:
|
|
215
|
+
Dmat = basisw.T @ y
|
|
216
|
+
else:
|
|
217
|
+
Dmat = np.zeros((nbasis + q, nrep, nvar))
|
|
218
|
+
for ivar in range(nvar):
|
|
219
|
+
Dmat[:, :, ivar] = basisw.T @ y[:, :, ivar]
|
|
220
|
+
if lambda_ > 0:
|
|
221
|
+
if penmat is None:
|
|
222
|
+
penmat = BsplineFunc(basisobj = basisobj, Lfdobj = Lfdobj).penalty_matrix()
|
|
223
|
+
Bnorm = np.sqrt(np.sum(np.diag(Bmat0.T @ Bmat0)))
|
|
224
|
+
pennorm = np.sqrt(np.sum(penmat * penmat))
|
|
225
|
+
condno = pennorm / Bnorm
|
|
226
|
+
if lambda_ * condno > 1e12:
|
|
227
|
+
lambda_ = 1e12 / condno
|
|
228
|
+
print(f"Warning: lambda reduced to {lambda_} to prevent overflow")
|
|
229
|
+
if covariates is not None:
|
|
230
|
+
penmat = np.block([[penmat, np.zeros((nbasis, q))], [np.zeros((q, nbasis)), np.zeros((q, q))]])
|
|
231
|
+
Bmat = Bmat0 + lambda_ * penmat
|
|
232
|
+
else:
|
|
233
|
+
penmat = None
|
|
234
|
+
Bmat = Bmat0
|
|
235
|
+
Bmat = (Bmat + Bmat.T) / 2
|
|
236
|
+
try:
|
|
237
|
+
Lmat = cholesky(Bmat)
|
|
238
|
+
except np.linalg.LinAlgError:
|
|
239
|
+
Beig = eigh(Bmat)
|
|
240
|
+
BgoodEig = Beig[0] > 0
|
|
241
|
+
Brank = np.sum(BgoodEig)
|
|
242
|
+
if Brank < Bmat.shape[0]:
|
|
243
|
+
print(f"Warning: Matrix of basis function values has rank {Brank} < {Bmat.shape[0]}, ignoring null space")
|
|
244
|
+
goodVec = Beig[1][:, BgoodEig]
|
|
245
|
+
Bmatinv = goodVec @ np.diag(1 / Beig[0][BgoodEig]) @ goodVec.T
|
|
246
|
+
else:
|
|
247
|
+
Lmatinv = solve(Lmat, np.eye(Lmat.shape[0]))
|
|
248
|
+
Bmatinv = Lmatinv @ Lmatinv.T
|
|
249
|
+
|
|
250
|
+
if len(dimy) < 3:
|
|
251
|
+
coef = Bmatinv @ Dmat
|
|
252
|
+
if covariates is not None:
|
|
253
|
+
beta_ = coef[nbasis:, :]
|
|
254
|
+
coef = coef[:nbasis, :]
|
|
255
|
+
else:
|
|
256
|
+
beta_ = None
|
|
257
|
+
else:
|
|
258
|
+
coef = np.zeros((nbasis, nrep, nvar))
|
|
259
|
+
if covariates is not None:
|
|
260
|
+
beta_ = np.zeros((q, nrep, nvar))
|
|
261
|
+
else:
|
|
262
|
+
beta_ = None
|
|
263
|
+
for ivar in range(nvar):
|
|
264
|
+
coefi = Bmatinv @ Dmat[:, :, ivar]
|
|
265
|
+
if covariates is not None:
|
|
266
|
+
beta_[:, :, ivar] = coefi[nbasis:, :]
|
|
267
|
+
coef[:, :, ivar] = coefi[:nbasis, :]
|
|
268
|
+
else:
|
|
269
|
+
coef[:, :, ivar] = coefi
|
|
270
|
+
else:
|
|
271
|
+
if n == nbasis + q:
|
|
272
|
+
if len(dimy) == 2:
|
|
273
|
+
coef = np.linalg.solve(basismat, y)
|
|
274
|
+
else:
|
|
275
|
+
for ivar in range(nvar):
|
|
276
|
+
coef[:, :, ivar] = np.linalg.solve(basismat, y[:, :, ivar])
|
|
277
|
+
penmat = None
|
|
278
|
+
else:
|
|
279
|
+
raise ValueError(f"The number of basis functions = {nbasis + q} exceeds {n} = the number of points to be smoothed.")
|
|
280
|
+
else:
|
|
281
|
+
if n > nbasis or lambda_ > 0:
|
|
282
|
+
if not onewt:
|
|
283
|
+
if matwt:
|
|
284
|
+
wtfac = cholesky(wtvec)
|
|
285
|
+
basismat_aug = wtfac @ basismat
|
|
286
|
+
if len(dimy) < 3:
|
|
287
|
+
y = wtfac @ y
|
|
288
|
+
else:
|
|
289
|
+
for ivar in range(nvar):
|
|
290
|
+
y[:, :, ivar] = wtfac @ y[:, :, ivar]
|
|
291
|
+
else:
|
|
292
|
+
rtwtvec = np.sqrt(wtvec)
|
|
293
|
+
basismat_aug = np.tile(rtwtvec, (nbasis, 1)).T * basismat
|
|
294
|
+
if len(dimy) < 3:
|
|
295
|
+
y = np.tile(rtwtvec, (nrep, 1)).T * y
|
|
296
|
+
else:
|
|
297
|
+
for ivar in range(nvar):
|
|
298
|
+
y[:, :, ivar] = np.tile(rtwtvec, (nrep, 1)).T * y[:, :, ivar]
|
|
299
|
+
else:
|
|
300
|
+
basismat_aug = basismat
|
|
301
|
+
if lambda_ > 0:
|
|
302
|
+
if penmat is None:
|
|
303
|
+
penmat = BsplineFunc(basisobj = basisobj, Lfdobj = Lfdobj).penalty_matrix('fourier')
|
|
304
|
+
eiglist = eigh(penmat)
|
|
305
|
+
Dvec = eiglist[0]
|
|
306
|
+
Vmat = eiglist[1]
|
|
307
|
+
neiglow = nbasis - nderiv
|
|
308
|
+
if Dvec[neiglow] <= 0:
|
|
309
|
+
raise ValueError("Eigenvalue(NBASIS-NDERIV) of penalty matrix is not positive. Check penalty matrix.")
|
|
310
|
+
indeig = np.arange(neiglow)
|
|
311
|
+
penfac = Vmat[:, indeig] @ np.diag(np.sqrt(Dvec[indeig]))
|
|
312
|
+
basismat_aug = np.r_[basismat_aug, np.sqrt(lambda_) * penfac.T]
|
|
313
|
+
if len(dimy) < 3:
|
|
314
|
+
y = np.r_[y, np.zeros((nbasis - nderiv, nrep))]
|
|
315
|
+
else:
|
|
316
|
+
y = np.zeros((y.shape[0] + nbasis - nderiv, y.shape[1], y.shape[2]))
|
|
317
|
+
y[:y.shape[0], :, :] = y0
|
|
318
|
+
ind1 = np.arange(y.shape[0] - (nbasis - nderiv), y.shape[0])
|
|
319
|
+
for ivar in range(nvar):
|
|
320
|
+
y[ind1, :, ivar] = np.zeros((nbasis - nderiv, nrep))
|
|
321
|
+
else:
|
|
322
|
+
penmat = None
|
|
323
|
+
if covariates is not None:
|
|
324
|
+
ind1 = np.arange(n)
|
|
325
|
+
ind2 = np.arange(nbasis, nbasis + q)
|
|
326
|
+
basismat_aug = np.c_[basismat_aug, np.zeros((basismat_aug.shape[0], q))]
|
|
327
|
+
if not onewt:
|
|
328
|
+
if matwt:
|
|
329
|
+
basismat_aug[ind1, ind2] = wtfac @ covariates
|
|
330
|
+
else:
|
|
331
|
+
wtfac = np.tile(rtwtvec, (q, 1)).T
|
|
332
|
+
basismat_aug[ind1, ind2] = wtfac * covariates
|
|
333
|
+
else:
|
|
334
|
+
basismat_aug[ind1, ind2] = covariates
|
|
335
|
+
penmat = np.block([[penmat, np.zeros((nbasis, q))], [np.zeros((q, nbasis)), np.zeros((q, q))]])
|
|
336
|
+
qr_result = qr(basismat_aug)
|
|
337
|
+
if len(dimy) < 3:
|
|
338
|
+
coef = solve(qr_result, y)
|
|
339
|
+
if covariates is not None:
|
|
340
|
+
beta_ = coef[ind2, :]
|
|
341
|
+
coef = coef[:nbasis, :]
|
|
342
|
+
else:
|
|
343
|
+
beta_ = None
|
|
344
|
+
else:
|
|
345
|
+
coef = np.zeros((nbasis, nrep, nvar))
|
|
346
|
+
if covariates is not None:
|
|
347
|
+
beta_ = np.zeros((q, nrep, nvar))
|
|
348
|
+
else:
|
|
349
|
+
beta_ = None
|
|
350
|
+
for ivar in range(nvar):
|
|
351
|
+
coefi = solve(qr_result, y[:, :, ivar])
|
|
352
|
+
if covariates is not None:
|
|
353
|
+
beta_[:, :, ivar] = coefi[ind2, :]
|
|
354
|
+
coef[:, :, ivar] = coefi[:nbasis, :]
|
|
355
|
+
else:
|
|
356
|
+
coef[:, :, ivar] = coefi
|
|
357
|
+
else:
|
|
358
|
+
if n == nbasis + q:
|
|
359
|
+
if len(dimy) == 2:
|
|
360
|
+
coef = np.linalg.solve(basismat, y)
|
|
361
|
+
else:
|
|
362
|
+
for ivar in range(nvar):
|
|
363
|
+
coef[:, :, ivar] = np.linalg.solve(basismat, y[:, :, ivar])
|
|
364
|
+
penmat = None
|
|
365
|
+
else:
|
|
366
|
+
raise ValueError(f"The number of basis functions = {nbasis} exceeds {n} = the number of points to be smoothed.")
|
|
367
|
+
if onewt:
|
|
368
|
+
temp = basismat.T @ basismat
|
|
369
|
+
if lambda_ > 0:
|
|
370
|
+
temp = temp + lambda_ * penmat
|
|
371
|
+
L = cholesky(temp)
|
|
372
|
+
MapFac = solve(L.T, basismat.T)
|
|
373
|
+
y2cMap = solve(L, MapFac)
|
|
374
|
+
else:
|
|
375
|
+
if matwt:
|
|
376
|
+
temp = basismat.T @ (wtvec @ basismat)
|
|
377
|
+
else:
|
|
378
|
+
temp = basismat.T @ (wtvec * basismat)
|
|
379
|
+
|
|
380
|
+
if lambda_ > 0:
|
|
381
|
+
temp = temp + lambda_ * penmat
|
|
382
|
+
L = cholesky((temp + temp.T) / 2)
|
|
383
|
+
MapFac = solve(L.T, basismat.T)
|
|
384
|
+
if matwt:
|
|
385
|
+
y2cMap = solve(L, MapFac @ wtvec)
|
|
386
|
+
else:
|
|
387
|
+
y2cMap = solve(L, MapFac * np.tile(wtvec, (MapFac.shape[0], 1)))
|
|
388
|
+
df_ = np.trace(y2cMap @ basismat)
|
|
389
|
+
|
|
390
|
+
# Calculate SSE
|
|
391
|
+
if len(dimy) < 3:
|
|
392
|
+
yhat = np.array(basismat[:, :nbasis] @ coef)
|
|
393
|
+
SSE = np.sum(np.square(y[:n, :] - yhat))
|
|
394
|
+
if ynames is None:
|
|
395
|
+
ynames = [f"rep_{i+1}" for i in range(nrep)]
|
|
396
|
+
else:
|
|
397
|
+
SSE = 0
|
|
398
|
+
yhat = np.zeros((n, nrep, nvar))
|
|
399
|
+
for ivar in range(nvar):
|
|
400
|
+
yhat[:, :, ivar] = basismat[:, :nbasis] @ coef[:, :, ivar]
|
|
401
|
+
SSE += np.sum(np.square(y[:n, :, ivar] - yhat[:, :, ivar]))
|
|
402
|
+
if ynames is None:
|
|
403
|
+
ynames = [f"rep_{i+1}" for i in range(nrep)]
|
|
404
|
+
if vnames is None:
|
|
405
|
+
vnames = [f"value_{i+1}" for i in range(nvar)]
|
|
406
|
+
|
|
407
|
+
# Calculate GCV
|
|
408
|
+
if df_ < n:
|
|
409
|
+
if len(dimy) < 3:
|
|
410
|
+
gcv = np.zeros(nrep)
|
|
411
|
+
for i in range(nrep):
|
|
412
|
+
SSEi = np.sum(np.square((y[:n, i] - yhat[:, i])))
|
|
413
|
+
gcv[i] = (SSEi / n) / np.square((n - df_) / n)
|
|
414
|
+
else:
|
|
415
|
+
gcv = np.zeros((nrep, nvar))
|
|
416
|
+
for ivar in range(nvar):
|
|
417
|
+
for i in range(nrep):
|
|
418
|
+
SSEi = np.sum(np.square(y[:n, i, ivar] - yhat[:, i, ivar]))
|
|
419
|
+
gcv[i, ivar] = (SSEi / n) / np.square((n - df_) / n)
|
|
420
|
+
gcv = np.array(gcv).tolist()
|
|
421
|
+
else:
|
|
422
|
+
gcv = None
|
|
423
|
+
if fdnames is None:
|
|
424
|
+
fdnames = {
|
|
425
|
+
'time': tnames.tolist() if tnames is not None else [f"time_{i+1}" for i in range(n)],
|
|
426
|
+
'reps': ynames,
|
|
427
|
+
'values': vnames}
|
|
428
|
+
if len(dimy) < 3:
|
|
429
|
+
coef = np.asmatrix(coef)
|
|
430
|
+
fdobj = fd(coef[:nbasis, :], basisobj, fdnames)
|
|
431
|
+
else:
|
|
432
|
+
fdobj = fd(coef[:nbasis, :, :], basisobj, fdnames)
|
|
433
|
+
if penmat is not None and covariates is not None:
|
|
434
|
+
penmat = penmat[:nbasis, :nbasis]
|
|
435
|
+
|
|
436
|
+
# Return results
|
|
437
|
+
smoothlist = {
|
|
438
|
+
'fd': fdobj,
|
|
439
|
+
'df': df_,
|
|
440
|
+
'gcv': gcv,
|
|
441
|
+
'beta': beta_,
|
|
442
|
+
'SSE': SSE,
|
|
443
|
+
'penmat': penmat,
|
|
444
|
+
'y2cMap': y2cMap,
|
|
445
|
+
'argvals': argvals,
|
|
446
|
+
'y': y0
|
|
447
|
+
}
|
|
448
|
+
return smoothlist
|
|
449
|
+
|