Package rdkit :: Package ML :: Package Cluster :: Module ClusterVis
[hide private]
[frames] | no frames]

Source Code for Module rdkit.ML.Cluster.ClusterVis

  1  # $Id: ClusterVis.py 997 2009-02-25 06:12:43Z glandrum $ 
  2  # 
  3  # Copyright (C) 2001-2006  greg Landrum and Rational Discovery LLC 
  4  # 
  5  #   @@ All Rights Reserved  @@ 
  6  # 
  7  """Cluster tree visualization using Sping 
  8   
  9  """ 
 10   
 11  try: 
 12    from rdkit.sping import pid 
 13    piddle = pid 
 14  except ImportError: 
 15    from rdkit.piddle import piddle 
 16  import ClusterUtils 
 17   
 18  import numpy 
 19   
20 -class VisOpts(object):
21 """ stores visualization options for cluster viewing 22 23 **Instance variables** 24 25 - x/yOffset: amount by which the drawing is offset from the edges of the canvas 26 27 - lineColor: default color for drawing the cluster tree 28 29 - lineWidth: the width of the lines used to draw the tree 30 31 """ 32 xOffset = 20 33 yOffset = 20 34 lineColor = piddle.Color(0,0,0) 35 hideColor = piddle.Color(.8,.8,.8) 36 terminalColors = [piddle.Color(1,0,0),piddle.Color(0,0,1),piddle.Color(1,1,0), 37 piddle.Color(0,.5,.5),piddle.Color(0,.8,0),piddle.Color(.5,.5,.5), 38 piddle.Color(.8,.3,.3),piddle.Color(.3,.3,.8),piddle.Color(.8,.8,.3), 39 piddle.Color(.3,.8,.8)] 40 lineWidth = 2 41 hideWidth = 1.1 42 nodeRad=15 43 nodeColor = piddle.Color(1.,.4,.4) 44 highlightColor = piddle.Color(1.,1.,.4) 45 highlightRad = 10
46
47 -def _scaleMetric(val,power=2,min=1e-4):
48 val = float(val) 49 nval = pow(val,power) 50 if nval < min: 51 return 0.0 52 else: 53 return numpy.log(nval/min)
54
55 -class ClusterRenderer(object):
56 - def __init__(self,canvas,size, 57 ptColors=[],lineWidth=None, 58 showIndices=0, 59 showNodes=1, 60 stopAtCentroids=0, 61 logScale=0, 62 tooClose=-1):
63 self.canvas = canvas 64 self.size = size 65 self.ptColors = ptColors 66 self.lineWidth = lineWidth 67 self.showIndices = showIndices 68 self.showNodes = showNodes 69 self.stopAtCentroids = stopAtCentroids 70 self.logScale = logScale 71 self.tooClose = tooClose
72
73 - def _AssignPointLocations(self,cluster,terminalOffset=4):
74 self.pts = cluster.GetPoints() 75 self.nPts = len(self.pts) 76 self.xSpace = float(self.size[0]-2*VisOpts.xOffset)/float(self.nPts-1) 77 ySize = self.size[1] 78 for i in xrange(self.nPts): 79 pt = self.pts[i] 80 if self.logScale > 0: 81 v = _scaleMetric(pt.GetMetric(), self.logScale) 82 else: 83 v = float(pt.GetMetric()) 84 pt._drawPos = (VisOpts.xOffset+i*self.xSpace, 85 ySize-(v*self.ySpace+VisOpts.yOffset)+terminalOffset)
86
87 - def _AssignClusterLocations(self,cluster):
88 # first get the search order (top down) 89 toDo = [cluster] 90 examine = cluster.GetChildren()[:] 91 while len(examine): 92 node = examine.pop(0) 93 children = node.GetChildren() 94 if len(children): 95 toDo.append(node) 96 for child in children: 97 if not child.IsTerminal(): 98 examine.append(child) 99 # and reverse it (to run from bottom up) 100 toDo.reverse() 101 for node in toDo: 102 if self.logScale > 0: 103 v = _scaleMetric(node.GetMetric(), self.logScale) 104 else: 105 v = float(node.GetMetric()) 106 # average our children's x positions 107 childLocs = [x._drawPos[0] for x in node.GetChildren()] 108 if len(childLocs): 109 xp = sum(childLocs)/float(len(childLocs)) 110 yp = self.size[1] - (v*self.ySpace+VisOpts.yOffset) 111 node._drawPos = (xp,yp)
112
113 - def _DrawToLimit(self,cluster):
114 """ 115 we assume that _drawPos settings have been done already 116 """ 117 if self.lineWidth is None: 118 lineWidth = VisOpts.lineWidth 119 else: 120 lineWidth = self.lineWidth 121 122 examine = [cluster] 123 while len(examine): 124 node = examine.pop(0) 125 xp,yp = node._drawPos 126 children = node.GetChildren() 127 if abs(children[1]._drawPos[0]-children[0]._drawPos[0])>self.tooClose: 128 # draw the horizontal line connecting things 129 drawColor = VisOpts.lineColor 130 self.canvas.drawLine(children[0]._drawPos[0],yp, 131 children[-1]._drawPos[0],yp, 132 drawColor,lineWidth) 133 # and draw the lines down to the children 134 for child in children: 135 if self.ptColors and child.GetData() is not None: 136 drawColor = self.ptColors[child.GetData()] 137 else: 138 drawColor = VisOpts.lineColor 139 cxp,cyp = child._drawPos 140 self.canvas.drawLine(cxp,yp,cxp,cyp,drawColor,lineWidth) 141 if not child.IsTerminal(): 142 examine.append(child) 143 else: 144 if self.showIndices and not self.stopAtCentroids: 145 try: 146 txt = str(child.GetName()) 147 except: 148 txt = str(child.GetIndex()) 149 self.canvas.drawString(txt, 150 cxp-self.canvas.stringWidth(txt)/2, 151 cyp) 152 153 else: 154 # draw a "hidden" line to the bottom 155 self.canvas.drawLine(xp,yp,xp,self.size[1]-VisOpts.yOffset, 156 VisOpts.hideColor,lineWidth)
157 158
159 - def DrawTree(self,cluster,minHeight=2.0):
160 if self.logScale > 0: 161 v = _scaleMetric(cluster.GetMetric(), self.logScale) 162 else: 163 v = float(cluster.GetMetric()) 164 if v <= 0: 165 v = minHeight 166 self.ySpace = float(self.size[1]-2*VisOpts.yOffset)/v 167 168 self._AssignPointLocations(cluster) 169 self._AssignClusterLocations(cluster) 170 if not self.stopAtCentroids: 171 self._DrawToLimit(cluster) 172 else: 173 raise NotImplementedError,'stopAtCentroids drawing not yet implemented'
174 175
176 -def DrawClusterTree(cluster,canvas,size, 177 ptColors=[],lineWidth=None, 178 showIndices=0, 179 showNodes=1, 180 stopAtCentroids=0, 181 logScale=0, 182 tooClose=-1):
183 """ handles the work of drawing a cluster tree on a Sping canvas 184 185 **Arguments** 186 187 - cluster: the cluster tree to be drawn 188 189 - canvas: the Sping canvas on which to draw 190 191 - size: the size of _canvas_ 192 193 - ptColors: if this is specified, the _colors_ will be used to color 194 the terminal nodes of the cluster tree. (color == _pid.Color_) 195 196 - lineWidth: if specified, it will be used for the widths of the lines 197 used to draw the tree 198 199 **Notes** 200 201 - _Canvas_ is neither _save_d nor _flush_ed at the end of this 202 203 - if _ptColors_ is the wrong length for the number of possible terminal 204 node types, this will throw an IndexError 205 206 - terminal node types are determined using their _GetData()_ methods 207 208 """ 209 renderer = ClusterRenderer(canvas,size,ptColors,lineWidth,showIndices,showNodes,stopAtCentroids, 210 logScale,tooClose) 211 renderer.DrawTree(cluster)
212 -def _DrawClusterTree(cluster,canvas,size, 213 ptColors=[],lineWidth=None, 214 showIndices=0, 215 showNodes=1, 216 stopAtCentroids=0, 217 logScale=0, 218 tooClose=-1):
219 """ handles the work of drawing a cluster tree on a Sping canvas 220 221 **Arguments** 222 223 - cluster: the cluster tree to be drawn 224 225 - canvas: the Sping canvas on which to draw 226 227 - size: the size of _canvas_ 228 229 - ptColors: if this is specified, the _colors_ will be used to color 230 the terminal nodes of the cluster tree. (color == _pid.Color_) 231 232 - lineWidth: if specified, it will be used for the widths of the lines 233 used to draw the tree 234 235 **Notes** 236 237 - _Canvas_ is neither _save_d nor _flush_ed at the end of this 238 239 - if _ptColors_ is the wrong length for the number of possible terminal 240 node types, this will throw an IndexError 241 242 - terminal node types are determined using their _GetData()_ methods 243 244 """ 245 if lineWidth is None: 246 lineWidth = VisOpts.lineWidth 247 pts = cluster.GetPoints() 248 nPts = len(pts) 249 if nPts <= 1: return 250 xSpace = float(size[0]-2*VisOpts.xOffset)/float(nPts-1) 251 if logScale > 0: 252 v = _scaleMetric(cluster.GetMetric(), logScale) 253 else: 254 v = float(cluster.GetMetric()) 255 ySpace = float(size[1]-2*VisOpts.yOffset)/v 256 257 for i in xrange(nPts): 258 pt = pts[i] 259 if logScale > 0: 260 v = _scaleMetric(pt.GetMetric(), logScale) 261 else: 262 v = float(pt.GetMetric()) 263 pt._drawPos = (VisOpts.xOffset+i*xSpace, 264 size[1]-(v*ySpace+VisOpts.yOffset)) 265 if not stopAtCentroids or not hasattr(pt,'_isCentroid'): 266 allNodes.remove(pt) 267 268 if not stopAtCentroids: 269 allNodes=ClusterUtils.GetNodeList(cluster) 270 else: 271 allNodes=ClusterUtils.GetNodesDownToCentroids(cluster) 272 273 while len(allNodes): 274 node = allNodes.pop(0) 275 children = node.GetChildren() 276 if len(children): 277 if logScale > 0: 278 v = _scaleMetric(node.GetMetric(), logScale) 279 else: 280 v = float(node.GetMetric()) 281 yp = size[1]-(v*ySpace+VisOpts.yOffset) 282 childLocs = [x._drawPos[0] for x in children] 283 xp = sum(childLocs)/float(len(childLocs)) 284 node._drawPos = (xp,yp) 285 if not stopAtCentroids or node._aboveCentroid > 0: 286 for child in children: 287 if ptColors != [] and child.GetData() is not None: 288 drawColor = ptColors[child.GetData()] 289 else: 290 drawColor = VisOpts.lineColor 291 if showNodes and hasattr(child,'_isCentroid'): 292 canvas.drawLine(child._drawPos[0],child._drawPos[1]-VisOpts.nodeRad/2, 293 child._drawPos[0],node._drawPos[1], 294 drawColor,lineWidth) 295 else: 296 canvas.drawLine(child._drawPos[0],child._drawPos[1], 297 child._drawPos[0],node._drawPos[1], 298 drawColor,lineWidth) 299 canvas.drawLine(children[0]._drawPos[0],node._drawPos[1], 300 children[-1]._drawPos[0],node._drawPos[1], 301 VisOpts.lineColor,lineWidth) 302 else: 303 for child in children: 304 drawColor = VisOpts.hideColor 305 canvas.drawLine(child._drawPos[0],child._drawPos[1], 306 child._drawPos[0],node._drawPos[1], 307 drawColor,VisOpts.hideWidth) 308 canvas.drawLine(children[0]._drawPos[0],node._drawPos[1], 309 children[-1]._drawPos[0],node._drawPos[1], 310 VisOpts.hideColor,VisOpts.hideWidth) 311 312 if showIndices and (not stopAtCentroids or node._aboveCentroid >= 0): 313 txt = str(node.GetIndex()) 314 if hasattr(node,'_isCentroid'): 315 txtColor = piddle.Color(1,.2,.2) 316 else: 317 txtColor = piddle.Color(0,0,0) 318 319 canvas.drawString(txt, 320 node._drawPos[0]-canvas.stringWidth(txt)/2, 321 node._drawPos[1]+canvas.fontHeight()/4, 322 color=txtColor) 323 324 if showNodes and hasattr(node,'_isCentroid'): 325 rad = VisOpts.nodeRad 326 canvas.drawEllipse(node._drawPos[0]-rad/2,node._drawPos[1]-rad/2, 327 node._drawPos[0]+rad/2,node._drawPos[1]+rad/2, 328 piddle.transparent, 329 fillColor=VisOpts.nodeColor) 330 txt = str(node._clustID) 331 canvas.drawString(txt, 332 node._drawPos[0]-canvas.stringWidth(txt)/2, 333 node._drawPos[1]+canvas.fontHeight()/4, 334 color=piddle.Color(0,0,0)) 335 336 if showIndices and not stopAtCentroids: 337 for pt in pts: 338 txt = str(pt.GetIndex()) 339 canvas.drawString(str(pt.GetIndex()), 340 pt._drawPos[0]-canvas.stringWidth(txt)/2, 341 pt._drawPos[1])
342
343 -def ClusterToPDF(cluster,fileName,size=(300,300),ptColors=[],lineWidth=None, 344 showIndices=0,stopAtCentroids=0,logScale=0):
345 """ handles the work of drawing a cluster tree to an PDF file 346 347 **Arguments** 348 349 - cluster: the cluster tree to be drawn 350 351 - fileName: the name of the file to be created 352 353 - size: the size of output canvas 354 355 - ptColors: if this is specified, the _colors_ will be used to color 356 the terminal nodes of the cluster tree. (color == _pid.Color_) 357 358 - lineWidth: if specified, it will be used for the widths of the lines 359 used to draw the tree 360 361 **Notes** 362 363 - if _ptColors_ is the wrong length for the number of possible terminal 364 node types, this will throw an IndexError 365 366 - terminal node types are determined using their _GetData()_ methods 367 368 """ 369 try: 370 from rdkit.sping.PDF import pidPDF 371 except ImportError: 372 from rdkit.piddle import piddlePDF 373 pidPDF = piddlePDF 374 375 canvas = pidPDF.PDFCanvas(size,fileName) 376 if lineWidth is None: 377 lineWidth = VisOpts.lineWidth 378 DrawClusterTree(cluster,canvas,size,ptColors=ptColors,lineWidth=lineWidth, 379 showIndices=showIndices,stopAtCentroids=stopAtCentroids, 380 logScale=logScale) 381 if fileName: 382 canvas.save() 383 return canvas
384
385 -def ClusterToSVG(cluster,fileName,size=(300,300),ptColors=[],lineWidth=None, 386 showIndices=0,stopAtCentroids=0,logScale=0):
387 """ handles the work of drawing a cluster tree to an SVG file 388 389 **Arguments** 390 391 - cluster: the cluster tree to be drawn 392 393 - fileName: the name of the file to be created 394 395 - size: the size of output canvas 396 397 - ptColors: if this is specified, the _colors_ will be used to color 398 the terminal nodes of the cluster tree. (color == _pid.Color_) 399 400 - lineWidth: if specified, it will be used for the widths of the lines 401 used to draw the tree 402 403 **Notes** 404 405 - if _ptColors_ is the wrong length for the number of possible terminal 406 node types, this will throw an IndexError 407 408 - terminal node types are determined using their _GetData()_ methods 409 410 """ 411 try: 412 from rdkit.sping.SVG import pidSVG 413 except ImportError: 414 from rdkit.piddle.piddleSVG import piddleSVG 415 pidSVG = piddleSVG 416 417 canvas = pidSVG.SVGCanvas(size,fileName) 418 419 if lineWidth is None: 420 lineWidth = VisOpts.lineWidth 421 DrawClusterTree(cluster,canvas,size,ptColors=ptColors,lineWidth=lineWidth, 422 showIndices=showIndices,stopAtCentroids=stopAtCentroids, 423 logScale=logScale) 424 if fileName: 425 canvas.save() 426 return canvas
427
428 -def ClusterToImg(cluster,fileName,size=(300,300),ptColors=[],lineWidth=None, 429 showIndices=0,stopAtCentroids=0,logScale=0):
430 """ handles the work of drawing a cluster tree to an image file 431 432 **Arguments** 433 434 - cluster: the cluster tree to be drawn 435 436 - fileName: the name of the file to be created 437 438 - size: the size of output canvas 439 440 - ptColors: if this is specified, the _colors_ will be used to color 441 the terminal nodes of the cluster tree. (color == _pid.Color_) 442 443 - lineWidth: if specified, it will be used for the widths of the lines 444 used to draw the tree 445 446 **Notes** 447 448 - The extension on _fileName_ determines the type of image file created. 449 All formats supported by PIL can be used. 450 451 - if _ptColors_ is the wrong length for the number of possible terminal 452 node types, this will throw an IndexError 453 454 - terminal node types are determined using their _GetData()_ methods 455 456 """ 457 try: 458 from rdkit.sping.PIL import pidPIL 459 except ImportError: 460 from rdkit.piddle import piddlePIL 461 pidPIL = piddlePIL 462 canvas = pidPIL.PILCanvas(size,fileName) 463 if lineWidth is None: 464 lineWidth = VisOpts.lineWidth 465 DrawClusterTree(cluster,canvas,size,ptColors=ptColors,lineWidth=lineWidth, 466 showIndices=showIndices,stopAtCentroids=stopAtCentroids, 467 logScale=logScale) 468 if fileName: 469 canvas.save() 470 return canvas
471