#
source:
orange/Orange/OrangeWidgets/VisualizeQt/OWMDSQt.py
@
11474:df0622184ee6

Revision 11474:df0622184ee6, 31.6 KB checked in by markotoplak, 12 months ago (diff) |
---|

Rev | Line | |
---|---|---|

[9052] | 1 | """ |

2 | <name>MDS Qt</name> | |

3 | <description>Multi dimensional scaling (Qt)</description> | |

[11474] | 4 | <icon>icons/MDS.svg</icon> |

[9052] | 5 | <contact>Ales Erjavec (ales.erjavec(@at@)fri.uni-lj.si)</contact> |

6 | <priority>2500</priority> | |

7 | """ | |

8 | ||

9 | from OWWidget import * | |

10 | import Orange | |

11 | import orange | |

12 | import orngMDS | |

13 | import OWGUI | |

14 | import numpy, sys, math, time, os | |

15 | import OWColorPalette | |

16 | import OWToolbars | |

17 | #from OWGraph import * | |

18 | #from PyQt4.Qwt5 import * | |

19 | ||

[11474] | 20 | from plot.owplot import OWPlot, OWCurve |

21 | from plot.owpoint import OWPoint | |

[9052] | 22 | |

23 | from random import random | |

24 | ||

25 | ||

26 | class OWMDSQt(OWWidget): | |

27 | settingsList=["graph.PointSize", "graph.proportionGraphed", "graph.ColorAttr", "graph.SizeAttr", | |

28 | "graph.ShapeAttr", "graph.NameAttr", "graph.ShowStress", "graph.NumStressLines", | |

29 | "graph.ShowName", "graph.differentWidths", "graph.stressByTransparency", "graph.useAntialiasing" | |

30 | "StressFunc", "applyLSMT", "toolbarSelection", "autoSendSelection", "selectionOptions", "computeStress", | |

31 | "RefreshMode"] | |

32 | contextHandlers={"":DomainContextHandler("", [ContextField("graph.ColorAttr", DomainContextHandler.Optional), | |

33 | ContextField("graph.SizeAttr", DomainContextHandler.Optional), | |

34 | ContextField("graph.ShapeAttr", DomainContextHandler.Optional), | |

35 | ContextField("graph.NameAttr", DomainContextHandler.Optional), | |

36 | ContextField("graph.ShowName", DomainContextHandler.Optional)])} | |

[9066] | 37 | |

[9052] | 38 | def __init__(self, parent=None, signalManager=None, name="Multi Dimensional Scaling"): |

39 | OWWidget.__init__(self, parent, signalManager, name, wantGraph=True) | |

[9546] | 40 | self.inputs=[("Distances", orange.SymMatrix, self.cmatrix), ("Data Subset", ExampleTable, self.cselected)] |

41 | self.outputs=[("Data", ExampleTable)] | |

[9052] | 42 | |

43 | self.StressFunc=3 | |

[9066] | 44 | self.minStressDelta=5e-5 |

[9052] | 45 | self.maxIterations=5000 |

46 | self.maxImprovment=10 | |

47 | self.autoSendSelection=0 | |

48 | self.toolbarSelection=0 | |

49 | self.selectionOptions=0 | |

50 | self.computeStress=1 | |

51 | self.ReDraw=1 | |

52 | self.NumIter=1 | |

53 | self.RefreshMode=0 | |

54 | self.applyLSMT = 0 | |

55 | ||

56 | self.stressFunc=[("Kruskal stress", orngMDS.KruskalStress), | |

57 | ("Sammon stress", orngMDS.SammonStress), | |

58 | ("Signed Sammon stress", orngMDS.SgnSammonStress), | |

59 | ("Signed relative stress", orngMDS.SgnRelStress)] | |

60 | ||

61 | self.graph=MDSPlot(self.mainArea) | |

62 | self.mainArea.layout().addWidget(self.graph) | |

63 | ||

64 | self.loadSettings() | |

65 | ||

66 | tabs=OWGUI.tabWidget(self.controlArea) | |

67 | ||

68 | mds=OWGUI.createTabPage(tabs, "MDS") | |

69 | graph=OWGUI.createTabPage(tabs, "Graph") | |

70 | ||

71 | ##MDS Tab | |

72 | init=OWGUI.widgetBox(mds, "Initialization") | |

73 | OWGUI.button(init, self, "Randomize", self.randomize) | |

74 | OWGUI.button(init, self, "Jitter", self.jitter) | |

75 | OWGUI.button(init, self, "Torgerson", self.torgerson) | |

76 | opt=OWGUI.widgetBox(mds, "Optimization") | |

77 | ||

78 | self.startButton=OWGUI.button(opt, self, "Optimize", self.testStart) | |

79 | OWGUI.button(opt, self, "Single Step", self.smacofStep) | |

80 | box = OWGUI.widgetBox(opt, "Stress Function") | |

81 | OWGUI.comboBox(box, self, "StressFunc", items=[a[0] for a in self.stressFunc], callback=self.updateStress) | |

82 | OWGUI.radioButtonsInBox(opt, self, "RefreshMode", ["Every step", "Every 10 steps", "Every 100 steps"], "Refresh During Optimization", callback=lambda :1) | |

83 | ||

84 | self.stopping=OWGUI.widgetBox(opt, "Stopping Conditions") | |

[9066] | 85 | OWGUI.hSlider(OWGUI.widgetBox(self.stopping, "Min. stress change", flat=True), |

86 | self, "minStressDelta", minValue=5e-5, maxValue=1e-2, step=5e-5, | |

87 | labelFormat="%.5f", intOnly=0) | |

88 | OWGUI.hSlider(OWGUI.widgetBox(self.stopping, "Max. number of steps", flat=True), | |

89 | self, "maxIterations", minValue=10, maxValue=5000, step=10, | |

90 | labelFormat="%i") | |

[9052] | 91 | |

92 | ##Graph Tab | |

93 | OWGUI.hSlider(graph, self, "graph.PointSize", box="Point Size", minValue=1, maxValue=20, callback=self.graph.updateData) | |

94 | self.colorCombo=OWGUI.comboBox(graph, self, "graph.ColorAttr", box="Color", callback=self.graph.updateData) | |

95 | self.sizeCombo=OWGUI.comboBox(graph, self, "graph.SizeAttr", box="Size", callback=self.graph.updateData) | |

96 | self.shapeCombo=OWGUI.comboBox(graph, self, "graph.ShapeAttr", box="Shape", callback=self.graph.updateData) | |

97 | self.nameCombo=OWGUI.comboBox(graph, self, "graph.NameAttr", box="Label", callback=self.graph.updateData) | |

98 | ||

99 | box = OWGUI.widgetBox(graph, "Distances & Stress") | |

100 | OWGUI.checkBox(box, self, "graph.ShowStress", "Show similar pairs", callback = self.graph.updateLinesRepaint) | |

101 | b2 = OWGUI.widgetBox(box) | |

102 | OWGUI.widgetLabel(b2, "Proportion of connected pairs") | |

103 | OWGUI.separator(b2, height=3) | |

104 | sl = OWGUI.hSlider(b2, self, "graph.proportionGraphed", minValue=0, maxValue=20, callback=self.graph.updateLinesRepaint, tooltip="Proportion of connected pairs (Maximum of 1000 lines will be drawn") | |

105 | OWGUI.checkBox(box, self, "graph.differentWidths", "Show distance by line width", callback = self.graph.updateLinesRepaint) | |

[9066] | 106 | OWGUI.checkBox(box, self, "graph.stressByTransparency", "Show stress by transparency", callback = self.graph.updateData) |

[9052] | 107 | OWGUI.checkBox(box, self, "graph.stressBySize", "Show stress by symbol size", callback = self.updateStressBySize) |

108 | self.updateStressBySize(True) | |

109 | ||

110 | OWGUI.checkBox(graph, self, "graph.antialias_points", label="Use antialiasing", box="Antialiasing", tooltip="Use antialiasing for beter quality graphics", callback=self.graph.updateData) | |

111 | ||

112 | self.zoomToolbar=OWToolbars.ZoomSelectToolbar(self, graph, self.graph, self.autoSendSelection) | |

113 | self.connect(self.zoomToolbar.buttonSendSelections, SIGNAL("clicked()"), self.sendSelections) | |

114 | self.graph.autoSendSelectionCallback = lambda :self.autoSendSelection and self.sendSelections() | |

115 | ||

116 | OWGUI.checkBox(graph, self, "autoSendSelection", "Auto send selected") | |

117 | OWGUI.radioButtonsInBox(graph, self, "selectionOptions", ["Don't append", "Append coordinates", "Append coordinates as meta"], box="Append coordinates", callback=self.sendIf) | |

118 | ||

119 | mds.setSizePolicy(QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)) | |

120 | graph.setSizePolicy(QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)) | |

121 | self.controlArea.setMinimumWidth(250) | |

[9066] | 122 | |

[9052] | 123 | OWGUI.rubber(mds) |

[9066] | 124 | OWGUI.rubber(self.controlArea) |

125 | ||

[9052] | 126 | infoBox=OWGUI.widgetBox(mds, "Info") |

127 | self.infoA=OWGUI.widgetLabel(infoBox, "Avg. stress:") | |

128 | self.infoB=OWGUI.widgetLabel(infoBox, "Num. steps") | |

129 | # OWGUI.button(self.controlArea, self, "Save", self.graph.saveToFile, debuggingEnabled = 0) | |

130 | self.connect(self.graphButton, SIGNAL("clicked()"), self.graph.saveToFile) | |

131 | self.resize(900,630) | |

132 | ||

133 | self.done=True | |

134 | self.data=None | |

135 | self.selectedInputExamples=[] | |

136 | self.selectedInput=[] | |

137 | ||

138 | def cmatrix(self, matrix=None): | |

139 | self.closeContext() | |

140 | self.origMatrix=matrix | |

141 | self.data=data=None | |

142 | if matrix: | |

143 | self.data=data=getattr(matrix, "items") | |

144 | matrix.matrixType = orange.SymMatrix.Symmetric | |

145 | ||

146 | self.graph.ColorAttr=0 | |

147 | self.graph.SizeAttr=0 | |

148 | self.graph.ShapeAttr=0 | |

149 | self.graph.NameAttr=0 | |

150 | self.graph.closestPairs = None | |

151 | if isinstance(data, orange.ExampleTable): | |

152 | self.setExampleTable(data) | |

153 | elif isinstance(data, list): | |

154 | self.setList(data) | |

155 | elif isinstance(data, orange.VarList): | |

156 | self.setVarList(data) | |

157 | if matrix: | |

158 | self.mds=orngMDS.MDS(matrix) | |

159 | self.mds.points=numpy.random.random(size=[self.mds.n, self.mds.dim]) | |

160 | self.mds.getStress() | |

161 | self.stress=self.getAvgStress(self.stressFunc[self.StressFunc][1]) | |

162 | if data and type(data) == orange.ExampleTable: | |

163 | self.openContext("",self.data) | |

164 | self.graph.setData(self.mds, self.colors, self.sizes, self.shapes, self.names, self.selectedInput) | |

165 | else: | |

166 | self.graph.clear() | |

167 | ||

168 | def cselected(self, selected=[]): | |

[9066] | 169 | self.selectedInputExamples=selected or [] |

[9052] | 170 | if self.data and type(self.data)==orange.ExampleTable: |

171 | self.setExampleTable(self.data) | |

172 | self.graph.setData(self.mds, self.colors, self.sizes, self.shapes, self.names, self.selectedInput) | |

173 | ||

174 | def setExampleTable(self, data): | |

175 | self.colorCombo.clear() | |

176 | self.sizeCombo.clear() | |

177 | self.shapeCombo.clear() | |

178 | self.nameCombo.clear() | |

179 | attributes=[attr for attr in data.domain.variables+data.domain.getmetas().values() or [] ] | |

180 | discAttributes=filter(lambda a: a.varType==orange.VarTypes.Discrete, attributes) | |

181 | contAttributes=filter(lambda a: a.varType==orange.VarTypes.Continuous, attributes) | |

182 | attrName=[attr.name for attr in attributes] | |

183 | for name in ["Same color"]+attrName: | |

184 | self.colorCombo.addItem(name) | |

185 | for name in ["Same size"]+map(lambda a:a.name, contAttributes): | |

186 | self.sizeCombo.addItem(name) | |

187 | for name in ["Same shape"]+map(lambda a: a.name, discAttributes): | |

188 | self.shapeCombo.addItem(name) | |

189 | for name in ["No name"]+attrName: | |

190 | self.nameCombo.addItem(name) | |

191 | ||

192 | # if data.domain.classVar: | |

193 | # if data.domain.classVar.varType == orange.VarTypes.Discrete: | |

194 | # self.graph.ColorAttr = len(data.domain.variables) # index 0 is Same color! | |

195 | # elif data.domain.classVar.varType == orange.VarTypes.Continuous: | |

196 | # self.graph.SizeAttr = len(data.domain.variables) # index 0 is Same color! | |

197 | try: | |

198 | self.graph.NameAttr = 1 + [name.lower() for name in attrName].index("name") | |

199 | except: | |

200 | pass | |

201 | ||

202 | self.attributes=attributes | |

203 | self.discAttributes=discAttributes | |

204 | self.contAttributes=contAttributes | |

205 | ||

206 | self.colors=[[Qt.black]*(len(attributes)+1) for i in range(len(data))] | |

207 | self.shapes=[[OWPoint.Ellipse]*(len(discAttributes)+1) for i in range(len(data))] | |

[9066] | 208 | self.sizes=[[1.0]*(len(contAttributes)+1) for i in range(len(data))] |

[9052] | 209 | self.names=[[""]*(len(attributes)+1) for i in range(len(data))] |

210 | try: | |

211 | selectedInput=self.selectedInputExamples.select(data.domain) | |

[9066] | 212 | except Exception: |

[9052] | 213 | selectedInput=[] |

[9066] | 214 | self.selectedInput=map(lambda d: selectedInput and (d in selectedInput), data) |

[9052] | 215 | contI=discI=attrI=1 |

216 | def check(ex, a): | |

217 | try: | |

218 | ex[a] | |

219 | except: | |

220 | return False | |

221 | return not ex[a].isSpecial() | |

222 | ||

223 | for j, attr in enumerate(attributes): | |

224 | if attr.varType==orange.VarTypes.Discrete: | |

225 | c=OWColorPalette.ColorPaletteHSV(len(attr.values)) | |

226 | for i in range(len(data)): | |

227 | self.colors[i][attrI]= check(data[i],attr) and c[int(data[i][attr])] or Qt.black | |

228 | ## self.shapes[i][discI]= data[i][attr].isSpecial() and self.graph.shapeList[0] or self.graph.shapeList[int(data[i][attr])%len(self.graph.shapeList)] | |

229 | self.shapes[i][discI]= check(data[i],attr) and self.graph.shapeList[int(data[i][attr])%len(self.graph.shapeList)] or self.graph.shapeList[0] | |

230 | self.names[i][attrI]= check(data[i],attr) and " "+str(data[i][attr]) or "" | |

231 | #self.sizes[i][contI]=5 | |

232 | attrI+=1 | |

233 | discI+=1 | |

234 | elif attr.varType==orange.VarTypes.Continuous: | |

235 | c=OWColorPalette.ColorPaletteBW(-1) | |

236 | #val=[e[attr] for e in data if not e[attr].isSpecial()] | |

237 | val=[e[attr] for e in data if check(e, attr)] | |

238 | minVal=min(val or [0]) | |

239 | maxVal=max(val or [1]) | |

[9066] | 240 | span = max(maxVal - minVal, 1e-6) |

[9052] | 241 | for i in range(len(data)): |

[9066] | 242 | self.colors[i][attrI]=check(data[i],attr) and c.getColor((data[i][attr] - minVal)/span) or Qt.black |

[9052] | 243 | #self.shapes[i][discI]=self.graph.shapeList[0] |

244 | self.names[i][attrI]=check(data[i],attr) and " "+str(data[i][attr]) or "" | |

[9066] | 245 | self.sizes[i][contI]=check(data[i],attr) and (float(self.data[i][attr]) - minVal) / span or 1.0 |

[9052] | 246 | contI+=1 |

247 | attrI+=1 | |

248 | else: | |

249 | for i in range(len(data)): | |

250 | self.colors[i][attrI]=Qt.black | |

251 | #self.shapes[i][j+1]=self.graph.shapeList[0] | |

252 | self.names[i][attrI]= check(data[i],attr) and " "+str(data[i][attr]) or "" | |

253 | #self.sizes[i][j+1]=5 | |

254 | attrI+=1 | |

255 | if data and data.domain.classVar: | |

256 | if data.domain.classVar.varType == orange.VarTypes.Discrete: | |

257 | self.graph.ColorAttr = len(self.colors[0]) - 1 # index 0 is Same color! | |

258 | elif data.domain.classVar.varType == orange.VarTypes.Continuous: | |

259 | self.graph.SizeAttr = len(self.sizes[0]) - 1 # index 0 is Same color! | |

260 | ||

261 | def setList(self, data): | |

262 | self.colorCombo.clear() | |

263 | self.sizeCombo.clear() | |

264 | self.shapeCombo.clear() | |

265 | self.nameCombo.clear() | |

266 | for name in ["Same color", "strain"]: | |

267 | self.colorCombo.addItem(name) | |

268 | for name in ["No name", "name", "strain"]: | |

269 | self.nameCombo.addItem(name) | |

270 | ||

271 | self.colors=[[Qt.black]*3 for i in range(len(data))] | |

272 | self.shapes=[[OWPoint.Ellipse] for i in range(len(data))] | |

[9066] | 273 | self.sizes=[[1.0] for i in range(len(data))] |

[9052] | 274 | self.selectedInput=[False]*len(data) |

275 | ||

276 | if type(data[0]) in [str, unicode]: | |

277 | self.names = [("", di, "", "") for di in data] | |

278 | else: | |

279 | self.names=[[""]*4 for i in range(len(data))] | |

280 | try: | |

281 | strains=list(set([d.strain for d in data])) | |

282 | c=OWColorPalette.ColorPaletteHSV(len(strains)) | |

283 | for i, d in enumerate(data): | |

284 | self.colors[i][1]=c[strains.index(d.strain)] | |

285 | self.names[i][1]=" "+d.name | |

286 | self.names[i][2]=" "+d.strain | |

287 | except Exception, val: | |

288 | print val | |

289 | ||

290 | def setVarList(self, data): | |

291 | self.colorCombo.clear() | |

292 | self.sizeCombo.clear() | |

293 | self.shapeCombo.clear() | |

294 | self.nameCombo.clear() | |

295 | for name in ["Same color", "Variable"]: | |

296 | self.colorCombo.addItem(name) | |

297 | for name in ["No name", "Var name"]: | |

298 | self.nameCombo.addItem(name) | |

299 | self.colors=[[Qt.black]*3 for i in range(len(data))] | |

300 | self.shapes=[[OWPoint.Ellipse] for i in range(len(data))] | |

[9066] | 301 | self.sizes=[[1.0] for i in range(len(data))] |

[9052] | 302 | self.names=[[""]*4 for i in range(len(data))] |

303 | self.selectedInput=[False]*len(data) | |

304 | try: | |

305 | c=OWColorPalette.ColorPaletteHSV(len(data)) | |

306 | for i, d in enumerate(data): | |

307 | self.colors[i][1]=c[i] | |

308 | self.names[i][1]=" " +str(d.name) | |

309 | except Exception, val: | |

310 | print val | |

311 | ||

312 | def updateStressBySize(self, noRepaint = False): | |

313 | self.sizeCombo.setDisabled(self.graph.stressBySize) | |

314 | if not noRepaint: | |

[9066] | 315 | self.graph.updateData() |

[9052] | 316 | |

317 | def smacofStep(self): | |

318 | if not getattr(self, "mds", None): | |

319 | return | |

320 | for i in range(self.NumIter): | |

321 | self.mds.SMACOFstep() | |

322 | if self.computeStress: | |

323 | self.mds.getStress(self.stressFunc[self.StressFunc][1]) | |

324 | self.stress=self.getAvgStress(self.stressFunc[self.StressFunc][1]) | |

325 | #st=time.clock() | |

326 | if self.ReDraw: | |

327 | self.graph.updateData() | |

328 | #print "Update:", time.clock()-st | |

329 | ||

330 | ## I (Janez) disabled LSMT because it is implemented as it never should be: | |

331 | # orngMDS.LSMT transforms the distance matrix itself (indeed there is | |

332 | # the original stored, too), and from that point on there is no way the | |

333 | # user can "untransform" it, except for resending the signal | |

334 | # Since the basic problem is in bad design of orngMDS, I removed the option | |

335 | # from the widget. If somebody has time to fix orngMDS first, he's welcome. | |

336 | def LSMT(self): | |

337 | if not getattr(self, "mds", None): | |

338 | return | |

339 | self.mds.LSMT() | |

340 | if self.computeStress: | |

341 | self.mds.getStress(self.stressFunc[self.StressFunc][1]) | |

342 | self.stress=self.getAvgStress(self.stressFunc[self.StressFunc][1]) | |

343 | if self.ReDraw: | |

344 | self.graph.updateData() | |

345 | ||

346 | def torgerson(self): | |

347 | if not getattr(self, "mds", None): | |

348 | return | |

349 | self.mds.Torgerson() | |

350 | if self.computeStress: | |

351 | self.mds.getStress(self.stressFunc[self.StressFunc][1]) | |

352 | self.stress=self.getAvgStress(self.stressFunc[self.StressFunc][1]) | |

353 | self.graph.updateData() | |

354 | ||

355 | def randomize(self): | |

356 | if not getattr(self, "mds", None): | |

357 | return | |

358 | self.mds.points = numpy.random.random(size=[self.mds.n,2]) | |

359 | if self.computeStress: | |

360 | self.mds.getStress(self.stressFunc[self.StressFunc][1]) | |

361 | self.stress=self.getAvgStress(self.stressFunc[self.StressFunc][1]) | |

362 | self.graph.updateData() | |

363 | ||

364 | def jitter(self): | |

365 | if not getattr(self, "mds", None): | |

366 | return | |

[9066] | 367 | mi = numpy.min(self.mds.points, axis=0) |

368 | ma = numpy.max(self.mds.points, axis=0) | |

369 | st = 0.05 * (ma - mi) | |

[9052] | 370 | for i in range(self.mds.n): |

371 | for j in range(2): | |

[9066] | 372 | self.mds.points[i][j] += st[j]*(random() - 0.5) |

[9052] | 373 | if self.computeStress: |

374 | self.mds.getStress(self.stressFunc[self.StressFunc][1]) | |

375 | self.stress=self.getAvgStress(self.stressFunc[self.StressFunc][1]) | |

376 | self.graph.updateData() | |

377 | ||

378 | def start(self): | |

379 | if not getattr(self, "mds", None): | |

380 | return | |

381 | if self.done==False: | |

382 | self.done=True | |

383 | return | |

384 | self.done=False | |

385 | self.startButton.setText("Stop") | |

386 | numIter=0 | |

387 | self.progressBarInit() | |

388 | pcur=0 | |

389 | startStress=oldStress=stress=self.getAvgStress(self.stressFunc[self.StressFunc][1]) | |

390 | startTime=time.clock() | |

391 | hist=[stress]*3 | |

392 | while not self.done and numIter<self.maxIterations: | |

393 | for i in range(self.NumIter): | |

394 | self.mds.SMACOFstep() | |

395 | qApp.processEvents() | |

396 | if self.computeStress: | |

397 | self.mds.getStress(self.stressFunc[self.StressFunc][1]) | |

398 | self.stress=stress=self.getAvgStress(self.stressFunc[self.StressFunc][1]) | |

399 | hist.pop(0) | |

400 | hist.append(abs(oldStress-stress)) | |

401 | numIter+=1 | |

402 | self.infoB.setText("Num. steps: %i" % numIter) | |

403 | qApp.processEvents() | |

404 | if self.ReDraw: | |

405 | self.graph.updateData() | |

406 | qApp.processEvents() | |

407 | if self.computeStress and abs(sum(hist)/3)<abs(self.minStressDelta*oldStress): | |

408 | break | |

409 | ## Update progress bar | |

410 | p1=abs(self.minStressDelta*oldStress)/max(sum(hist)/3, 1e-6)*100 | |

411 | if p1>100: p1=0 | |

412 | pcur=min(max([p1, float(numIter)/self.maxIterations*100, pcur]),99) | |

413 | self.progressBarSet(int(pcur)) | |

414 | ||

415 | oldStress=stress | |

416 | self.startButton.setText("Optimize") | |

417 | self.progressBarFinished() | |

418 | #if not self.ReDraw: | |

419 | self.graph.updateData() | |

420 | self.done=True | |

421 | #print "time %i " % (time.clock()-startTime) | |

422 | ||

423 | def testStart(self): | |

424 | if not getattr(self, "mds", None): | |

425 | return | |

426 | if self.done==False: | |

427 | self.done=True | |

428 | return | |

429 | self.done=False | |

430 | self.startButton.setText("Stop Optimization") | |

431 | self.stopping.setDisabled(1) | |

432 | self.progressBarInit() | |

433 | self.iterNum=0 | |

434 | self.mds.progress_callback=self.callback | |

435 | self.mds.mds.optimize(self.maxIterations, self.stressFunc[self.StressFunc][1], self.minStressDelta) | |

436 | if self.iterNum%(math.pow(10,self.RefreshMode)): | |

437 | self.graph.updateData() | |

438 | self.startButton.setText("Optimize") | |

439 | self.stopping.setDisabled(0) | |

440 | self.progressBarFinished() | |

441 | self.done=True | |

442 | ||

443 | def callback(self, a,b=None): | |

444 | if not self.iterNum%(math.pow(10,self.RefreshMode)): | |

445 | self.graph.updateData() | |

446 | self.iterNum+=1 | |

447 | self.infoB.setText("Num. steps: %i" % self.iterNum) | |

448 | self.infoA.setText("Avg. Stress: %f" % self.mds.avgStress) | |

449 | self.progressBarSet(int(a*100)) | |

450 | qApp.processEvents() | |

451 | if self.done: | |

452 | return 0 | |

453 | else: | |

454 | return 1 | |

455 | ||

456 | ||

457 | def getAvgStress(self, stressf=orngMDS.SgnRelStress): | |

458 | return self.mds.avgStress | |

459 | """ | |

460 | self.mds.getDistance() | |

461 | total=0.0 | |

462 | total=sum([abs(a[0]) for a in self.mds.arr]) | |

463 | self.infoA.setText("Avg. stress: %.7f" % (total/(self.mds.n*self.mds.n))) | |

464 | return total/(self.mds.n*self.mds.n) | |

465 | """ | |

466 | ||

467 | def sendIf(self, i=-1): | |

468 | if self.autoSendSelection: | |

469 | self.sendSelections() | |

470 | ||

471 | def sendSelections(self): | |

472 | if not getattr(self, "mds", None): | |

473 | return | |

474 | points = self.graph.main_curve.points() | |

475 | selectedInd = [i for i, p in enumerate(points) if p.is_selected()] | |

476 | if type(self.data)==orange.ExampleTable: | |

477 | self.sendExampleTable(selectedInd) | |

478 | elif type(self.data)==list: | |

479 | self.sendList(selectedInd) | |

480 | ||

481 | def sendExampleTable(self, selectedInd): | |

482 | if self.selectionOptions==0: | |

[9546] | 483 | self.send("Data", orange.ExampleTable(self.data.getitems(selectedInd))) |

[9052] | 484 | else: |

485 | xAttr=orange.FloatVariable("X") | |

486 | yAttr=orange.FloatVariable("Y") | |

487 | if self.selectionOptions==1: | |

488 | domain=orange.Domain([xAttr, yAttr]+[v for v in self.data.domain.variables]) | |

489 | domain.addmetas(self.data.domain.getmetas()) | |

490 | else: | |

491 | domain=orange.Domain(self.data.domain) | |

492 | domain.addmeta(orange.newmetaid(), xAttr) | |

493 | domain.addmeta(orange.newmetaid(), yAttr) | |

494 | selection=orange.ExampleTable(domain) | |

495 | selection.extend(self.data.getitems(selectedInd)) | |

496 | for i in range(len(selectedInd)): | |

497 | selection[i][xAttr]=self.mds.points[selectedInd[i]][0] | |

498 | selection[i][yAttr]=self.mds.points[selectedInd[i]][1] | |

[9546] | 499 | self.send("Data", selection) |

[9052] | 500 | |

501 | def sendList(self, selectedInd): | |

502 | if self.data and type(self.data[0]) == str: | |

503 | xAttr=orange.FloatVariable("X") | |

504 | yAttr=orange.FloatVariable("Y") | |

505 | nameAttr= orange.StringVariable("name") | |

506 | if self.selectionOptions == 1: | |

507 | domain = orange.Domain([xAttr, yAttr, nameAttr]) | |

508 | selection = orange.ExampleTable(domain) | |

509 | for i in range(len(selectedInd)): | |

510 | selection.append(list(self.mds.points[selectedInd[i]]) + [self.data[i]]) | |

511 | else: | |

512 | domain = orange.Domain([nameAttr]) | |

513 | if self.selectionOptions: | |

514 | domain.addmeta(orange.newmetaid(), xAttr) | |

515 | domain.addmeta(orange.newmetaid(), yAttr) | |

516 | selection = orange.ExampleTable(domain) | |

517 | for i in range(len(selectedInd)): | |

518 | selection.append([self.data[i]]) | |

519 | if self.selectionOptions: | |

520 | selection[i][xAttr]=self.mds.points[selectedInd[i]][0] | |

521 | selection[i][yAttr]=self.mds.points[selectedInd[i]][1] | |

[9546] | 522 | self.send("Data", selection) |

[9052] | 523 | return |

524 | ||

525 | if not selectedInd: | |

526 | self.send("Structured Data Files", None) | |

527 | else: | |

528 | datasets=[self.data[i] for i in selectedInd] | |

529 | names=list(set([d.dirname for d in datasets])) | |

530 | data=[(name, [d for d in filter(lambda a:a.strain==name, datasets)]) for name in names] | |

531 | self.send("Structured Data Files",data) | |

532 | ||

533 | def updateStress(self): | |

534 | if not getattr(self, "mds", None): | |

535 | return | |

536 | self.mds.getStress(self.stressFunc[self.StressFunc][1]) | |

537 | self.graph.replot() | |

538 | ||

539 | def sendReport(self): | |

540 | self.reportSettings("Optimization", | |

541 | [("Stress function", self.stressFunc[self.StressFunc][0]), | |

542 | ("Minimal stress change", self.minStressDelta), | |

543 | ("Maximal number of steps", self.maxIterations)]) | |

544 | if self.graph.ColorAttr or self.graph.stressBySize or self.graph.SizeAttr or self.graph.ShapeAttr or self.graph.NameAttr or self.graph.ShowStress: | |

545 | self.reportSettings("Visual settings", | |

546 | [self.graph.ColorAttr and ("Point color", self.colorCombo.currentText()), | |

547 | self.graph.stressBySize and ("Point size", "<stress>") | |

548 | or self.graph.SizeAttr and ("Point size", self.sizeCombo.currentText()), | |

549 | self.graph.ShapeAttr and ("Point shape", self.shapeCombo.currentText()), | |

550 | self.graph.NameAttr and ("Labels", self.nameCombo.currentText()), | |

551 | self.graph.ShowStress and ("Proportion of connected pairs", self.graph.proportionGraphed)]) | |

552 | self.reportSection("Chart") | |

553 | self.reportImage(self.graph.saveToFileDirect) | |

554 | ||

555 | ||

556 | class MDSPlot(OWPlot): | |

557 | def __init__(self, parent=None, name=None): | |

558 | OWPlot.__init__(self, parent, name) | |

559 | self.use_animations = False | |

560 | self.animate_points = False | |

561 | self.antialias_points = True | |

562 | self.data = None | |

563 | self.mds = None | |

564 | self.PointSize = 5 | |

565 | self.ColorAttr = 0 | |

566 | self.SizeAttr = 0 | |

567 | self.ShapeAttr = 0 | |

568 | self.NameAttr = 0 | |

569 | self.ShowStress = False | |

570 | self.differentWidths = True | |

571 | self.stressByTransparency = True | |

572 | self.stressBySize = False | |

573 | self.NumStressLines = 10 | |

574 | self.proportionGraphed = 20 | |

575 | self.ShowName = True | |

576 | #self.curveKeys=[] | |

577 | self.pointKeys = [] | |

578 | self.points = [] | |

579 | self.lines = [] | |

580 | self.lineKeys = [] | |

581 | self.distanceLineCurves = [] | |

582 | self.colors = [] | |

583 | self.sizes = [] | |

584 | self.closestPairs = None | |

585 | self.shapeList = [OWPoint.Ellipse, | |

586 | OWPoint.Rect, | |

587 | OWPoint.Diamond, | |

588 | OWPoint.Triangle, | |

589 | OWPoint.DTriangle , | |

590 | OWPoint.UTriangle, | |

591 | OWPoint.LTriangle, | |

592 | OWPoint.RTriangle, | |

593 | OWPoint.Cross, | |

594 | OWPoint.XCross ] | |

595 | ||

596 | def setData(self, mds, colors, sizes, shapes, names, showFilled): | |

597 | self.mds = mds | |

598 | self.colors = colors | |

599 | self.sizes = sizes | |

600 | self.shapes = shapes | |

601 | self.names = names | |

[9066] | 602 | self.showFilled = showFilled |

[9052] | 603 | self.updateData() |

604 | ||

605 | def updateData(self): | |

606 | self.clear() | |

607 | self.distanceLineCurves = [] | |

608 | if self.ShowStress: | |

609 | self.updateDistanceLines() | |

610 | self.setPoints() | |

611 | self.replot() | |

612 | ||

613 | def updateDistanceLines(self): | |

614 | if not self.mds: | |

615 | return | |

616 | N = len(self.mds.points) | |

617 | np = min(int(N*(N-1)/2. * self.proportionGraphed/100.), 1000) # draw maximum of 1000 closest pairs | |

618 | needlines = int(math.ceil((1 + math.sqrt(1+8*np)) / 2)) | |

619 | ||

620 | if self.closestPairs is None or len(self.closestPairs) < np: | |

621 | import heapq | |

622 | m = self.mds.originalDistances | |

623 | self.closestPairs = sorted(heapq.nsmallest(np, ((m[i, j], i, j) for i in range(m.dim) for j in range(i)))) | |

624 | ||

625 | for c in self.distanceLineCurves: | |

626 | try: | |

627 | c.detach() | |

628 | except RuntimeError, ex: #underlying C/C++ object has been deleted | |

629 | pass | |

630 | self.distanceLineCurves = [] | |

631 | ||

632 | hdist = self.closestPairs[:np] | |

633 | if not hdist: | |

634 | return | |

635 | ||

636 | black = QColor(192,192,192) | |

637 | if self.differentWidths: | |

638 | mindist = hdist[0][0] | |

639 | maxdist = hdist[-1][0] | |

640 | else: | |

641 | mindist = maxdist = 0 | |

642 | if maxdist != mindist: | |

643 | k = 3 / (maxdist - mindist)**2 | |

644 | for dist, i, j in hdist: | |

645 | pti, ptj = self.mds.points[i], self.mds.points[j] | |

646 | c = self.add_curve("n_lines", black, black, 2, style=OWCurve.Lines, xData=[pti[0],ptj[0]], yData=[pti[1],ptj[1]], lineWidth = max(1, (maxdist - dist)**2 * k)) | |

647 | c.set_in_background(True) | |

648 | self.distanceLineCurves.append(c) | |

649 | else: | |

650 | for dist, i, j in hdist: | |

651 | pti, ptj = self.mds.points[i], self.mds.points[j] | |

652 | c = self.add_curve("n_lines", black, black, 2, OWCurve.Lines, xData=[pti[0],ptj[0]], yData=[pti[1],ptj[1]], lineWidth = 2) | |

653 | c.set_in_background(True) | |

654 | self.distanceLineCurves.append(c) | |

655 | ||

656 | ||

657 | def updateLinesRepaint(self): | |

658 | if self.mds: | |

659 | if self.ShowStress: | |

660 | self.updateDistanceLines() | |

661 | else: | |

662 | for c in self.distanceLineCurves: | |

663 | try: | |

664 | c.detach() | |

665 | except RuntimeError, ex: #underlying C/C++ object has been deleted | |

666 | pass | |

667 | self.distanceLineCurves = [] | |

668 | self.replot() | |

669 | ||

670 | def setPoints(self): | |

671 | if not self.mds: | |

672 | return | |

673 | x_data = [p[0] for p in self.mds.points] | |

674 | y_data = [p[1] for p in self.mds.points] | |

[9066] | 675 | |

676 | if self.stressBySize or self.stressByTransparency: | |

677 | stresses = map(sum, self.mds.stress) | |

678 | ||

679 | mins, maxs = min(stresses), max(stresses) | |

680 | stress_scale = 1. / max(1e-7, maxs - mins) | |

681 | # stress_scale = 1. / max(1e-7, maxs - mins) | |

682 | ||

683 | if self.ColorAttr != 0: | |

684 | colors = [c[self.ColorAttr] for c in self.colors] | |

685 | else: | |

686 | colors = [QColor(Qt.black) for _ in self.colors] #QColor(Qt.black) | |

687 | ||

688 | if self.stressByTransparency: | |

689 | for c, s in zip(colors, stresses): | |

690 | c.setAlpha(math.floor((1.0 - ((s - mins) * stress_scale)) * 255)) | |

691 | ||

692 | if self.stressBySize: | |

693 | sizes = [(s - mins) * stress_scale * self.PointSize for s in stresses] | |

694 | elif self.SizeAttr != 0: | |

695 | sizes = [s[self.SizeAttr] * self.PointSize for s in self.sizes] | |

696 | else: | |

697 | sizes = [self.PointSize] | |

698 | ||

[9052] | 699 | if self.ShapeAttr != 0: |

700 | shapes = [s[self.ShapeAttr] for s in self.shapes] | |

701 | else: | |

702 | shapes = [self.shapeList[0]] | |

703 | ||

704 | if self.NameAttr != 0: | |

705 | labels = [n[self.NameAttr] for n in self.names] | |

706 | else: | |

707 | labels = [] | |

708 | ||

[9066] | 709 | self.set_main_curve_data(x_data, y_data, colors, labels, sizes, shapes, self.showFilled) |

[9052] | 710 | |

711 | def sendData(self, *args): | |

712 | pass | |

713 | ||

714 | ||

715 | if __name__=="__main__": | |

716 | app=QApplication(sys.argv) | |

717 | w=OWMDSQt() | |

718 | w.show() | |

719 | data=orange.ExampleTable("../../doc/datasets/iris.tab") | |

720 | ## data = orange.ExampleTable(r"E:\Development\Orange Datasets\UCI\iris.tab") | |

721 | ## data=orange.ExampleTable("/home/ales/src/MDSjakulin/eu_nations.txt") | |

722 | matrix = orange.SymMatrix(len(data)) | |

723 | dist = orange.ExamplesDistanceConstructor_Euclidean(data) | |

724 | matrix = orange.SymMatrix(len(data)) | |

725 | matrix.setattr('items', data) | |

726 | for i in range(len(data)): | |

727 | for j in range(i+1): | |

728 | matrix[i, j] = dist(data[i], data[j]) | |

729 | ||

730 | w.cmatrix(matrix) | |

[9066] | 731 | w.cselected(orange.ExampleTable(data[:50])) |

[9052] | 732 | app.exec_() |

[9066] | 733 | w.saveSettings() |

[9052] | 734 |

**Note:**See TracBrowser for help on using the repository browser.