1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """Work in progress"""
23
24 import copy
25 import numpy as N
26
27 import Biskit as B
28 import Biskit.tools as T
29
30 from Biskit.ProfileCollection import _ViewSignal, CrossView
31
33 """
34 Access only part of an underlying ProfileCollection from a MultiModel.
35 """
36
37 - def __init__( self, parentProfiles, map2model ):
38 """
39 @param parentProfiles: profiles of the MultiModel
40 @type parentProfiles: ProfileCollection
41 @param map: map positions in this ProfileCollection to positions in the
42 parent profile
43 @type map: N.array of int
44 """
45
46 self.map = map2model
47
48
49 assert isinstance( parentProfiles, B.ProfileCollection )
50 self.pc = parentProfiles
51
52
53 self.default = None
54
55
56 self._viewSignal = _ViewSignal()
57
58 self.initVersion = self.version()
59
61 """Class version.
62 @return: class version number
63 @rtype: str
64 """
65 return 'ProfileMirror $Revision: 1.1 $ on '\
66 + B.ProfileCollection.version(self)
67
68
70 """
71 @return: number of profiles in the collection
72 @rtype: int
73 """
74 return len( self.pc )
75
78
81
83 """
84 Iterate over profile keys::
85 for k in self <==> for k in p.keys()
86
87 @return: list of items
88 @rtype: list
89 """
90 return iter(self.pc)
91
92
94 """
95 Use::
96 getInfo( name ) -> dict with meta infos about profile::
97
98 Guaranteed infos: 'version'->str, 'comment'->str, 'changed'->1|0
99
100 @param name: profile name
101 @type name: str
102
103 @return: dict with infos about profile
104 @rtype: dict
105 @raise ProfileError: if no profile is found with |name|
106 """
107 return self.pc.getInfo( name )
108
109
110 - def get( self, name, default=None ):
111
112 r = self.pc.get( name, default=None )
113
114 if r is None:
115 r = default
116
117 if self.pc[ name, 'isarray']:
118 return N.take( r, self.map )
119
120 return [ r[i] for i in self.map ]
121
122
123 - def setInfo( self, name, **args ):
124 """
125 Add/Override infos about a given profile::
126 e.g. setInfo('relASA', comment='new', params={'bin':'whatif'})
127
128 @raise ProfileError: if no profile is found with |name|
129 """
130 self.pc.setInfo( name, **args )
131
132
133 - def set( self, name, prof, mask=None, default=None, asarray=1,
134 comment=None, **moreInfo ):
135 """
136 Add/override a profile. The two info records 'version', 'changed' and
137 'isarray' are always modified but can be overridden by key=value pairs
138 to this function. If the profile does not yet exist in the parent
139 ProfileCollection, a new profile is created for the whole MultiModel
140 and the values not covered by this ProfileMirror are set to <default>.
141
142 @param name: profile name (i.e. key)
143 @type name: str
144 @param prof: list of values OR None
145 @type prof: [any] OR None
146 @param mask: list 1 x N_items of 0|1, if there are less values than
147 items, provide mask with 0 for missing values,
148 N.sum(mask)==N_items
149 @type mask: [int]
150 @param default: value for items masked and for new parent profile
151 (default: None for lists, 0 for arrays]
152 @type default: any
153 @param asarray: store as list (0), as array (2) or store numbers as
154 array but everything else as list (1) (default: 1)
155 @type asarray: 0|1|2
156 @param comment: goes into info[name]['comment']
157 @type comment: str
158 @param moreInfo: additional key-value pairs for info[name]
159 @type moreInfo: key=value
160
161 @raise ProfileError: if length of prof != length of other profiles
162 @raise ProfileError: if mask is given but N.sum(mask) != len(prof)
163 """
164
165
166 if mask and N.sum(mask) != len(prof):
167 raise ProfileError(
168 "Mask doesn't match profile ( N.sum(mask)!=len(prof) ). " +
169 "%i != %i" % (N.sum(mask), len( prof ) ) )
170
171 r = self.pc.get( name, default=0 )
172
173
174 if not r is 0:
175 if self.pc.get( (name, 'isarray') ):
176 asarray = 2
177 else:
178 asarray = 0
179
180 prof = self.array_or_list( prof, asarray )
181
182
183 if not default and isinstance( prof, N.ndarray ):
184 default = 0
185
186 self.default = default
187
188
189 prof = self.expand( prof, mask, default )
190
191 l = len( self.map )
192 if l and len( prof ) != l:
193 raise ProfileError( "Profile %s has wrong length." % name )
194
195
196
197 if r is 0:
198 r = [ default ] * self.pc.profLength()
199 if isinstance( prof, N.ndarray ):
200 r = N.array( r )
201
202 if isinstance( r, N.ndarray ):
203 N.put( r, self.map, prof )
204 else:
205 r = N.array( r, 'O' )
206 N.put( r, self.map, prof )
207 r = r.tolist()
208
209 self.pc.set( name, r, asarray=asarray, comment=comment, **moreInfo )
210
211
212 - def take( self,indices, *initArgs, **initKw ):
213 """
214 Take from profiles using provided indices::
215 take( indices ) -> FeatureProfiles mapping to subset of parent
216
217 @param indices: list of indices into this profile (not the MultiModel)
218 @type indices: [int]
219
220 @return: new instance that maps to a new subset of positions
221 @rtype: ProfileMirror
222 """
223 rmap = N.take( self.map, indices )
224 return self.__class__( rmap, self.pc )
225
226 - def concat( self, *profiles ):
227 """
228 Concatenate this with the given ProfileMirror(s).::
229 p0.concat( p1 [, p2, ..]) -> single ProfileMirror with the
230 same number of profiles as p0 but with the length of p0+p1+p2..
231
232 Note: it's not yet clear what meaning this will have for ProfileMirrors
233
234 @param profiles: profile(s) to concatenate
235 @type profiles: ProfileCollection(s)
236
237 @return: concatenated profile(s)
238 @rtype: ProfileCollection / subclass
239 """
240 if len( profiles ) == 0:
241 return self
242
243 next = profiles[0]
244
245 assert isinstance( next, self.__class__ ), \
246 'can only concat with other ProfileMirrors'
247
248 rmap = N.concatenate( (self.map, next.map ) )
249
250 r = self.__class__( rmap, self.pc )
251
252 return r.concat( *profiles[1:] )
253
254
256 """
257 Profiles can only be removed by the parent ProfileCollection
258 @raises ProfileError: always
259 """
260 raise B.ProfileError, 'Cannot remove profile %r from a ProfileMirror'\
261 % key
262
264 """
265 Clone profile mirror::
266 clone() -> ProfileMirror (or sub-class)
267
268 @return: profile mirror
269 @rtype: ProfileCollection
270 """
271 return self.__class__( copy.copy( self.map ), self.pc )
272
274 """
275 Profiles can only be cleared by the parent ProfileCollection
276 """
277 raise B.ProfileError, 'Cannot remove profile %r from a ProfileMirror'\
278 % key
279
281 """
282 Length of profile::
283 profLength() -> int; length of first non-None profile or 0
284
285 @return: length of first non-None profile or 0
286 @rtype: int
287 """
288 return len( self.map )
289
290
291
292 if __name__ == '__main__':
293
294 import string
295
296 p = B.ProfileCollection()
297 p['name'] = string.letters
298 p['id'] = range( len(string.letters) )
299
300
301 mirror = ProfileMirror( p, range(0, len(string.letters), 2 ) )
302
303 assert mirror['name'] == list( string.letters[::2] )
304 assert N.all( mirror['id'] == range( 0, p.profLength(), 2 ) )
305
306
307 mirror['m_id'] = range( mirror.profLength() )
308 assert N.all( p['m_id'][::2] == mirror['m_id'] )
309
310 mirror['name'][2] = '#'
311 mirror['name'] = ['#'] * mirror.profLength()
312 assert p['name'][::2] == ['#'] * mirror.profLength()
313 assert p['name'][1::2]== list( string.letters[1::2] )
314