Package pyvision :: Package analysis :: Module Table
[hide private]
[frames] | no frames]

Source Code for Module pyvision.analysis.Table

  1  # PyVision License 
  2  # 
  3  # Copyright (c) 2006-2008 David S. Bolme 
  4  # All rights reserved. 
  5  # 
  6  # Redistribution and use in source and binary forms, with or without 
  7  # modification, are permitted provided that the following conditions 
  8  # are met: 
  9  #  
 10  # 1. Redistributions of source code must retain the above copyright 
 11  # notice, this list of conditions and the following disclaimer. 
 12  #  
 13  # 2. Redistributions in binary form must reproduce the above copyright 
 14  # notice, this list of conditions and the following disclaimer in the 
 15  # documentation and/or other materials provided with the distribution. 
 16  #  
 17  # 3. Neither name of copyright holders nor the names of its contributors 
 18  # may be used to endorse or promote products derived from this software 
 19  # without specific prior written permission. 
 20  #  
 21  #  
 22  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 23  # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 24  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
 25  # A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR 
 26  # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 27  # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 28  # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
 29  # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
 30  # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
 31  # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 32  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 33   
 34  import csv 
 35  import StringIO 
 36  import pyvision as pv 
 37   
38 -def convertVal(val):
39 if val in ("True","False"): 40 return val == "True" 41 try: 42 flt = float(val) 43 except: 44 return val 45 46 if '.' in val: 47 return flt 48 else: 49 return int(round(flt))
50
51 -class Table:
52 ''' 53 Store and manipulate table data 54 ''' 55
56 - def __init__(self,filename=None,default_value=None):
57 self.col_headers = [] 58 self.col_label = None 59 self.row_headers = [] 60 self.row_label = None 61 self.col_format = {} 62 self.hlines = False 63 self.default_value = default_value 64 self.data = {} 65 self.rebuildTable() 66 67 if filename != None: 68 if isinstance(filename,str): 69 f = open(filename,'rb') 70 self.load(f) 71 else: 72 #Assume this is a file pointer 73 self.load(filename)
74
75 - def load(self,f):
76 reader = csv.reader(f) 77 header=None 78 #j = 0 79 for data in reader: 80 if header == None: 81 header = data 82 assert header[0] == "row" 83 continue 84 assert len(header) == len(data) 85 86 for i in range(1,len(header)): 87 row = convertVal(data[0]) 88 col = convertVal(header[i]) 89 val = convertVal(data[i]) 90 #print type(row),row,type(col),col,type(val),val 91 self[row,col] = val
92 93 #j += 1 94 #if j > 300: 95 # break 96
97 - def sortByRowHeader(self,comp_func = cmp):
98 self.row_headers.sort(comp_func)
99 100
101 - def sortByColHeader(self,comp_func = cmp):
102 self.col_headers.sort(comp_func)
103 104
105 - def accumulateData(self,row,col,value):
106 new_value = self.element(row,col) + value 107 self.setData(row,col,new_value)
108 109
110 - def setColumnFormat(self,col,format_str):
111 if format_str == None and self.col_format.has_key(col): 112 # Clear formating for that column 113 del self.col_format[col] 114 else: 115 # Set the formating for that column 116 self.col_format[col] = format_str
117
118 - def __setitem__(self,key,value):
119 return self.setElement(key[0],key[1],value)
120
121 - def setElement(self,row,col,value,accumulate=False):
122 self.setData(row,col,value,accumulate=accumulate)
123
124 - def setData(self,row,col,value,accumulate=False):
125 # Set an entry in the table 126 labels_updated = False 127 128 if col not in self.col_headers: 129 self.col_headers.append(col) 130 labels_updated = True 131 132 if row not in self.row_headers: 133 self.row_headers.append(row) 134 labels_updated = True 135 136 if labels_updated: 137 self.rebuildTable() 138 139 self.data[row][col] = value
140 141
142 - def rebuildTable(self):
143 for row in self.row_headers: 144 if not self.data.has_key(row): self.data[row] = {} 145 for col in self.col_headers: 146 if not self.data[row].has_key(col): self.data[row][col] = self.default_value
147 148
149 - def hasElement(self,row,col):
150 return (self.data.has_key(row) and self.data[row].has_key(col))
151 152
153 - def __getitem__(self,key):
154 return self.element(key[0],key[1])
155
156 - def element(self,row,col):
157 if self.hasElement(row,col): 158 return self.data[row][col] 159 else: 160 return self.default_value
161
162 - def elementAsText(self,row,col):
163 if isinstance(self.col_format,str): 164 return self.col_format%self.element(row,col) 165 if isinstance(self.col_format,dict) and self.col_format.has_key(col): 166 return self.col_format[col]%self.element(row,col) 167 # Todo it would be nice to support callable objects 168 169 # Otherwize just format as a string: 170 return "%s"%(self.element(row,col),)
171
172 - def justifyText(self,text,width,side='l'):
173 assert side in ['c','l','r'] 174 l = len(text) 175 if l > width: 176 return text[:width] 177 else: 178 pad = width - l 179 if side=='c': 180 front = " "*(pad/2) 181 back = " "*(pad - pad/2) 182 return front + text + back 183 elif side=='r': 184 return " "*pad + text 185 else: 186 return text + " "*pad 187 188 return text #TODO:
189
190 - def asHtml(self, print_col_headers = True, print_row_headers = False, equal_cols = False, style='simple'):
191 result = "<TABLE CELLPADDING=6 CELLSPACING=0>\n" 192 result += ' <TR BGCOLOR="#D3C6AD">\n' 193 result += ' ' 194 if print_row_headers: 195 result += '<TD ALIGN=LEFT></TD>' 196 197 for col in self.col_headers: 198 result += '<TH>%s</TH>'%col 199 result += '\n' 200 result += ' </TR>\n' 201 202 i = 0 203 for row in self.row_headers: 204 bgcolor = '#FBEBCE' 205 if i % 2 == 1: 206 bgcolor = '#EFE0C4' 207 result += ' <TR BGCOLOR="%s">\n'%bgcolor 208 result += ' ' 209 if print_row_headers: 210 result += '<TD ALIGN=LEFT>%s</TD>'%(row,) 211 for col in self.col_headers: 212 align = 'LEFT' 213 try: 214 #Check to see if the text looks like a number 215 val = float(self[row,col]) 216 align = 'RIGHT' 217 except: 218 # Default, justify left 219 pass 220 221 val = self.elementAsText(row,col) 222 result += '<TD ALIGN=%s>%s</TD>'%(align,val) 223 result += '\n' 224 result += ' </TR>\n' 225 i += 1 226 result += "</TABLE>\n" 227 return result
228 229 230
231 - def asPlainText(self, print_col_headers = True, print_row_headers = True, equal_cols = False, separator="|"):
232 '''Returns a text string which is a formated table.''' 233 assert len(separator) == 1 234 235 rows = self.row_headers 236 cols = self.col_headers 237 238 col_widths = {} 239 for col in self.col_headers: 240 if print_col_headers: 241 col_widths[col] = len(str(col)) 242 else: 243 col_widths[col] = 0 244 for row in self.row_headers: 245 w = len(self.elementAsText(row,col)) 246 if w > col_widths[col]: 247 col_widths[col] = w 248 if equal_cols: 249 #new_widths = {} 250 max_width = 0 251 for key,value in col_widths.iteritems(): 252 max_width = max(max_width,value) 253 for key in col_widths.keys(): 254 col_widths[key]=max_width 255 256 row_header_width = 0 257 for row in rows: 258 row_header_width = max(row_header_width,len(str(row))) 259 260 261 out = "" 262 263 #Horizontal Rule 264 if print_row_headers: 265 out += "|" + "-"*(row_header_width+2) 266 out += "|" 267 for col in cols: 268 out += "-"*(col_widths[col]+2)+"|" 269 out = out[:-1] 270 out += "|\n" 271 272 if print_col_headers: 273 out += "|" 274 if print_row_headers: 275 out += " "*(row_header_width+2)+"|" 276 for col in cols: 277 text = self.justifyText(str(col),col_widths[col],'l') 278 out += " "+text+" |" 279 out = out[:-1] 280 out += "|\n" 281 282 #Horizontal Rule 283 if print_row_headers: 284 out += "|" + "-"*(row_header_width+2) 285 out += "|" 286 for col in cols: 287 out += "-"*(col_widths[col]+2)+"|" 288 out = out[:-1] 289 out += "|\n" 290 291 for row in rows: 292 out +="|" 293 if print_row_headers: 294 out += " " + self.justifyText(str(row),row_header_width,'l')+" |" 295 for col in cols: 296 text = self.elementAsText(row,col) 297 try: 298 #Check to see if the text looks like a number 299 val = float(text) 300 # Numbers should be justifed right. 301 text = self.justifyText(text,col_widths[col],'r') 302 except: 303 # Default, justify left 304 text = self.justifyText(text,col_widths[col],'l') 305 assert len(text) == col_widths[col] 306 out += " "+text+" "+separator 307 #strip the last separator 308 out = out[:-1] 309 out += "|\n" 310 311 #Horizontal Rule 312 if print_row_headers: 313 out += "|" + "-"*(row_header_width+2) 314 out += "|" 315 for col in cols: 316 out += "-"*(col_widths[col]+2)+"|" 317 out = out[:-1] 318 out += "|\n" 319 320 return out
321 322 323 324 325
326 - def nRows(self):
327 return len(self.row_headers)
328
329 - def nCols(self):
330 return len(self.col_headers)
331 332
333 - def asTex(self):
334 '''Returns a text string which as a table formated for latex'''
335
336 - def asLists(self,headers=True):
337 '''Returns the table data as a list of lists''' 338 rows = self.row_headers 339 cols = self.col_headers 340 341 result = [] 342 343 if headers: 344 tmp = ['row'] 345 for col in cols: 346 tmp.append(col) 347 result.append(tmp) 348 349 for row in rows: 350 tmp = [] 351 if headers: 352 tmp.append(row) 353 for col in cols: 354 tmp.append(self.element(row,col)) 355 result.append(tmp) 356 357 return result
358 359
360 - def head(self,N=10):
361 '''Returns a table from the first N rows.''' 362 rows = self.row_headers 363 cols = self.col_headers 364 365 result = pv.Table() 366 367 for row in rows[:N]: 368 for col in cols: 369 result[row,col] = self[row,col] 370 371 return result
372 373
374 - def tail(self,N=10):
375 '''Returns a table from the last N rows.''' 376 rows = self.row_headers 377 cols = self.col_headers 378 379 result = pv.Table() 380 381 for row in rows[-N:]: 382 for col in cols: 383 result[row,col] = self[row,col] 384 385 return result
386 387
388 - def save(self,filename,headers=True):
389 '''Save the table to CSV''' 390 if isinstance(filename,str): 391 f = open(filename,'wb') 392 else: 393 f = filename # assume file pointer 394 writer = csv.writer(f) 395 writer.writerows(self.asLists(headers=headers))
396
397 - def __str__(self):
398 return self.asPlainText()
399 400 401 import unittest
402 -class _TestTable(unittest.TestCase):
403 - def setUp(self):
404 color = Table(default_value=0) 405 color.accumulateData('red','red',1) 406 color.accumulateData('red','red',1) 407 color.accumulateData('red','red',1) 408 color.accumulateData('blue','blue',1) 409 color.accumulateData('blue','blue',1) 410 color.accumulateData('blue','blue',1) 411 color.accumulateData('blue','blue',1) 412 color.accumulateData('pink','pink',1) 413 color.accumulateData('pink','pink',1) 414 color.accumulateData('pink','pink',1) 415 color.accumulateData('pink','pink',1) 416 color.accumulateData('pink','pink',1) 417 color.accumulateData('pink','red',1) 418 color.accumulateData('pink','red',1) 419 color.accumulateData('blue','red',1) 420 color.accumulateData('blue','red',1) 421 color.accumulateData('red','blue',1) 422 color.accumulateData('green','green',1) 423 color.accumulateData('red','green',1) 424 self.color = color 425 426 # Simulate a face recognition problem with a 427 # probe set of 1000 and a gallery set of 1000 428 # 0.001 FAR and 0.100 FRR 429 sim_face = Table(default_value=0) 430 sim_face.setData('accept','accept',900) 431 sim_face.setData('reject','reject',998001) 432 sim_face.setData('accept','reject',100) 433 sim_face.setData('reject','accept',999) 434 self.sim_face = sim_face
435
436 - def test__str__(self):
437 438 self.color.asPlainText() 439 self.sim_face.asPlainText()
440
441 - def test_asLists(self):
442 self.color.asLists()
443
444 - def test_save(self):
445 expected = 'row,red,blue,pink,green\r\nred,3,1,0,1\r\nblue,2,4,0,0\r\npink,2,0,5,0\r\ngreen,0,0,0,1\r\n' 446 output = StringIO.StringIO() 447 self.color.save(output) 448 self.assertEqual(output.getvalue(),expected) 449 450 expected = '3,1,0,1\r\n2,4,0,0\r\n2,0,5,0\r\n0,0,0,1\r\n' 451 output = StringIO.StringIO() 452 self.color.save(output,headers=False) 453 self.assertEqual(output.getvalue(),expected)
454
455 - def test_verification(self):
456 self.sim_face
457
458 - def test_rowsort(self):
459 tab = Table() 460 461 tab[2,1] = 'b' 462 tab[1,1] = 'a' 463 tab[3,1] = 'c' 464 465 #print tab 466 tab.sortByRowHeader()
467 #print tab 468