Package gmisclib :: Module dictops
[frames] | no frames]

Source Code for Module gmisclib.dictops

  1  """Operations on dictionaries.""" 
  2   
  3   
  4  __version__ = "$Revision: 1.9 $" 
  5   
6 -def filter(d, l):
7 """Passes through items listed in l. 8 @param l: list of keys to preserve 9 @type l: list 10 @param d: dictionary to copy/modify. 11 @type d: mapping 12 @return: a dictionary where all keys not listed in l are removed; 13 all entries listed in l are copied into the (new) 14 output dictionary. 15 @rtype: dict 16 """ 17 # o = {} 18 # for t in l: 19 # o[t] = d[t] 20 # return o 21 return dict( [ (t, d[t]) for t in l ] )
22 23
24 -def remove(d, l):
25 """Removes items listed in l. 26 @param l: list of keys to remove 27 @type l: list 28 @param d: dictionary to copy/modify. 29 @type d: mapping 30 @return: a dictionary where all keys listed in l are removed; 31 all entries not listed in l are copied into the (new) 32 output dictionary. 33 @rtype: dict 34 """ 35 o = d.copy() 36 for t in l: 37 if o.has_key(t): 38 del o[t] 39 return o
40 41
42 -def intersection(*dlist):
43 """@return: a dictionary containing those key=value pairs that are common to all the dictionaries. 44 @rtype: dict 45 @param dlist: dictionaries. 46 @type dlist: list(dict) 47 """ 48 rv = dlist[0].copy() 49 for d in dlist[1:]: 50 dellist = [] 51 for (k, v) in rv.items(): 52 try: 53 tmp = d[k] 54 except KeyError: 55 dellist.append(k) 56 continue 57 if tmp != v: 58 dellist.append(k) 59 for k in dellist: 60 del rv[k] 61 return rv
62 63
64 -def test_intersection():
65 class foo(object): 66 pass
67 d1 = {'a': 'b', 'b': 'x', 'c': None, 'd': foo()} 68 d2 = {'a': 'b', 'b': 'y'} 69 d3 = {'a': 'b', 'c': None} 70 assert intersection(d1, d2) == {'a': 'b'} 71 assert intersection(d1, {}) == {} 72 assert intersection(d1, d3) == {'a': 'b', 'c':None} 73 d4 = {'d': foo()} 74 assert intersection(d1, d4) == {} 75 76
77 -def rev1to1(d):
78 """Reverse a 1-1 mapping so it maps values to keys 79 instead of keys to values. 80 @type d: dict 81 @rtype: dict 82 @return: M such that if m[k]=v, M[v]=k 83 @raise ValueError: if the mapping is many-to-one. 84 """ 85 o = {} 86 for (k, v) in d.items(): 87 if v in o: 88 raise ValueError, 'Dictionary is not 1 to 1 mapping' 89 o[v] = k 90 return o
91 92
93 -def compose(d2, d1):
94 """Compose two mappings (dictionaries) into one. 95 @return: C{d} such that d[k] = d2[d1[k]] 96 @rtype: L{dict} 97 @param d2: second mapping 98 @param d1: first mapping 99 """ 100 o = {} 101 for (k, v) in d1.items(): 102 try: 103 o[k] = d2[d1[k]] 104 except KeyError: 105 pass 106 return o
107 108
109 -class BadFileFormatError(Exception):
110 """This indicates a problem when reading in a dictionary via L{read2c}. 111 """ 112
113 - def __init__(self, *s):
114 Exception.__init__(self, *s)
115 116
117 -def read2c(f):
118 """Read in a dictionary from a two-column file; 119 columns are separated by whitespace. 120 This is not a completely general format, because keys cannot contain 121 whitespace and the values cannot start or end in whitespace. 122 123 The format can contain comments at the top; these are 124 lines that begin with a hash mark (#). Empty lines are 125 ignored. 126 @param f: an open L{file} 127 @return: a dictionary and a list of comments 128 @rtype: tuple(dict(str:str), list(str)) 129 @note: You can get into trouble if you have a key that starts with 130 a hash mark (#). If it happens to be the first key, 131 and if there are no comments or if it directly follows a 132 comment, it will be interpreted as a comment. 133 To avoid this possibility, begin your file with a comment 134 line, then a blank line. 135 """ 136 o = {} 137 comments = [] 138 in_hdr = True 139 n = 0 140 for l in f: 141 n += 1 142 if in_hdr: 143 if l.startswith('#'): 144 comments.append(l[1:].strip()) 145 continue 146 in_hdr = False 147 if l == '\n': 148 continue 149 if not l.endswith('\n'): 150 raise BadFileFormatError("Last line is incomplete: %s" % getattr(f, "name", "???")) 151 try: 152 k, v = l.strip().split(None, 1) 153 except ValueError: 154 raise BadFileFormatError("File %s line %d: too few columns" % 155 (getattr(f, "name", "???"), n) 156 ) 157 if k in o: 158 raise BadFileFormatError("File contains duplicate keys: %s on line %d and earlier" % 159 (repr(k), n) 160 ) 161 o[k] = v 162 return o, comments
163 164
165 -def argmax(d):
166 """For a dictionary of key=value pairs, return the key with the largest value. 167 @note: When there are several equal values, this will return a single, arbitrary choice. 168 @raise ValueError: when the input dictionary is empty. 169 """ 170 kmax = None 171 vmax = None 172 firstpass = True 173 for (k, v) in d.items(): 174 if firstpass or v>vmax: 175 vmax = v 176 firstpass = False 177 kmax = k 178 if firstpass: 179 raise ValueError, "argmax is not defined for an empty dictionary" 180 return kmax
181 182
183 -def add_dol(dict, key, value):
184 """OBSOLETE: replace with class L{dict_of_lists}. 185 Add an entry to a dictionary of lists. 186 A new list is created if necessary; else the value is 187 appended to an existing list. 188 Obsolete: replace with dict_of_lists. 189 """ 190 try: 191 dict[key].append(value) 192 except KeyError: 193 dict[key] = [ value ]
194 195
196 -def add_doc(dict, key, value):
197 """OBSOLETE: replace with class L{dict_of_accums}. 198 Add an entry to a dictionary of counters. 199 A new counter is created if necessary; else the value is 200 added to an existing counter. 201 Obsolete: replace with dict_of_accums. 202 """ 203 try: 204 dict[key] += value 205 except KeyError: 206 dict[key] = value
207 208
209 -class dict_of_lists(dict):
210 """A dictionary of lists.""" 211
212 - def add(self, key, value):
213 """Append value to the list indexed by key.""" 214 try: 215 self[key].append(value) 216 except KeyError: 217 self[key] = [ value ]
218
219 - def addgroup(self, key, values):
220 """Append values to the list indexed by key.""" 221 try: 222 self[key].extend(values) 223 except KeyError: 224 self[key] = values
225
226 - def add_ifdifferent(self, key, value):
227 """Append value to the list indexed by key.""" 228 try: 229 v = self[key] 230 except KeyError: 231 self[key] = [value] 232 else: 233 if value not in v: 234 v.append( value )
235
236 - def copy(self):
237 """This does a shallow copy.""" 238 return dict_of_lists(self)
239 240
241 - def merge(self, other):
242 for (k, v) in other: 243 self.addgroup(k, v)
244 245
246 -class dict_of_sets(dict):
247 """A dictionary of lists."""
248 - def add(self, key, value):
249 """Append value to the set indexed by key.""" 250 try: 251 self[key].add(value) 252 except KeyError: 253 self[key] = set([value])
254
255 - def addgroup(self, key, values):
256 """Append values to the list indexed by key.""" 257 try: 258 self[key].update(values) 259 except KeyError: 260 self[key] = set(values)
261 262
263 - def copy(self):
264 """This does a shallow copy.""" 265 return dict_of_sets(self)
266 267
268 - def merge(self, other):
269 for (k, v) in other: 270 self.addgroup(k, v)
271 272
273 -class dict_of_accums(dict):
274 """A dictionary of accumulators. 275 Note that the copy() function produces a dict_of_accums.""" 276
277 - def add(self, key, value):
278 """Add value to the value indexed by key.""" 279 try: 280 self[key] += value 281 except KeyError: 282 self[key] = value
283 284
285 - def copy(self):
286 return dict_of_accums(self)
287 288
289 - def merge(self, other):
290 for (k, v) in other: 291 self.add(k, v)
292 293
294 -class dict_of_averages(dict):
295 """A dictionary of accumulators. 296 Note that the copy() function produces a dict_of_averages.""" 297
298 - def add(self, key, value, weight=1.0):
299 """Add value to the value indexed by key.""" 300 assert weight >= 0.0 301 if key in self: 302 self[key] += complex(value*weight, weight) 303 else: 304 self[key] = complex(value*weight, weight)
305
306 - def get_avg(self, key):
307 vw = self[key] 308 assert vw.imag > 0.0, "Weight for key=%s is nonpositive(%g)" % (key, vw.imag) 309 return vw.real/vw.imag
310
311 - def get_avgs(self):
312 o = {} 313 for (k, vw) in self.items(): 314 assert vw.imag > 0.0, "Weight for key=%s is nonpositive(%g)" % (k, vw.imag) 315 o[k] = vw.real/vw.imag 316 return o
317
318 - def get_both(self, key):
319 vw = self[key] 320 return (vw.real, vw.imag)
321
322 - def copy(self):
323 return dict_of_averages(self)
324 325
326 - def merge(self, other):
327 for (k, v) in other: 328 self.add(k, v)
329 330
331 -class dict_of_maxes(dict):
332 """A dictionary of maxima. Add a new number and the stored 333 maximum will increase iff the new value is bigger. 334 Note that the copy() function produces a dict_of_accums.""" 335
336 - def add(self, key, value):
337 """Add value to the value indexed by key.""" 338 try: 339 tmp = self[key] 340 if value > tmp: 341 self[key] = value 342 except KeyError: 343 self[key] = value
344 345
346 - def copy(self):
347 return dict_of_maxes(self)
348 349
350 - def merge(self, other):
351 for (k, v) in other: 352 self.add(k, v)
353 354 355
356 -class dict_of_X(dict):
357 """A dictionary of arbitrary things. 358 Note that the copy() function produces a dict_of_X. 359 """ 360
361 - def __init__(self, constructor, incrementer):
362 self.constructor = constructor 363 self.incrementer = incrementer
364
365 - def add(self, key, *value):
366 """Add value to the thing indexed by key.""" 367 try: 368 self.incrementer(self[key], *value) 369 except KeyError: 370 self[key] = self.constructor(*value)
371 372
373 -class list_of_dicts(list):
374 - def __init__(self, *arg):
375 list.__init__(self, arg)
376
377 - def lookup1(self, key, value):
378 for d in self: 379 try: 380 if d[key]==value: 381 return d 382 except KeyError: 383 pass
384 385 386
387 -def test():
388 test_intersection()
389 390 if __name__ == '__main__': 391 test() 392