📄 ownomogram.py
字号:
"""
<name>Nomogram</name>
<description>Nomogram viewer for Naive Bayesian, logistic regression or linear SVM classifiers.</description>
<icon>icons/Nomogram.png</icon>
<contact>Martin Mozina (martin.mozina(@at@)fri.uni-lj.si)</contact>
<priority>2500</priority>
"""
#
# Nomogram is a Orange widget for
# for visualization of the knowledge
# obtained with Naive Bayes or logistic regression classifier
#
import math
import orange
import OWGUI
from OWWidget import *
from OWNomogramGraph import *
import orngLR
import orngLR_Jakulin
aproxZero = 0.0001
def getStartingPoint(d, min):
if min<0:
curr_num = Numeric.arange(-min+d, step=d)
curr_num = curr_num[len(curr_num)-1]
curr_num = -curr_num
elif min - d <= 0:
curr_num = 0
else:
curr_num = Numeric.arange(min-d, step=d)
curr_num = curr_num[len(curr_num)-1]
return curr_num
def getRounding(d):
rndFac = math.floor(math.log10(d));
if rndFac<-2:
rndFac = int(-rndFac)
else:
rndFac = 2
return rndFac
def avg(l):
return sum(l)/len(l)
class OWNomogram(OWWidget):
settingsList = ["alignType", "contType", "bubble", "histogram", "histogram_size", "confidence_percent", "sort_type"]
def __init__(self,parent=None, signalManager = None):
OWWidget.__init__(self, parent, signalManager, "Nomogram", 1)
self.setWFlags(Qt.WResizeNoErase | Qt.WRepaintNoErase) #this works like magic.. no flicker during repaint!
self.parent = parent
# self.setWFlags(self.getWFlags()+Qt.WStyle_Maximize)
self.callbackDeposit = [] # deposit for OWGUI callback functions
self.alignType = 0
self.contType = 0
self.yAxis = 0
self.probability = 0
self.showBaseLine = 1
self.table = 0
self.verticalSpacing = 60
self.verticalSpacingContinuous = 100
self.diff_between_ordinal = 30
self.fontSize = 9
self.lineWidth = 1
self.bubble = 1
self.histogram = 1
self.histogram_size = 10
self.data = None
self.cl = None
self.confidence_check = 0
self.confidence_percent = 95
# self.notTargetClassIndex = 1
self.sort_type = 0
self.loadSettings()
self.pointsName = ["Points","Log OR"]
self.totalPointsName = ["Total Points","Log OR Sum"]
self.bnomogram = None
#inputs
self.inputs=[("Classifier", orange.Classifier, self.classifier)]
# GUI definition
self.tabs = QTabWidget(self.controlArea, 'tabWidget')
# GENERAL TAB
GeneralTab = QVGroupBox(self)
self.alignRadio = OWGUI.radioButtonsInBox(GeneralTab, self, 'alignType', ['Left', '0-point'], box='Align',
tooltips=['Attributes in nomogram are left aligned', 'Attributes are not aligned, top scale represents true (normalized) regression coefficient value'],
callback=self.showNomogram)
self.yAxisRadio = OWGUI.radioButtonsInBox(GeneralTab, self, 'yAxis', ['100', 'log OR'], 'yAxis',
tooltips=['values are normalized on a 0-100 point scale','values on top axis show log-linear contribution of attribute to full model'],
callback=self.showNomogram)
self.ContRadio = OWGUI.radioButtonsInBox(GeneralTab, self, 'contType', ['1D', '2D'], 'Continuous',
tooltips=['Continuous attribute are presented on a single scale', 'Two dimensional space is used to present continuous attributes in nomogram.'],
callback=self.showNomogram)
#target combo box
self.TargetClassIndex = 0
self.target = ""
self.targetCombo = OWGUI.comboBox(GeneralTab, self, "target", " Target Class ", tooltip='Select target (prediction) class in the model.', callback = self.setTarget, sendSelectedValue = 1, valueType = str)
#self.yAxisRadio.setDisabled(True)
self.probabilityCheck = OWGUI.checkBox(GeneralTab, self, 'probability','Show prediction', tooltip='', callback = self.setProbability)
#self.probabilityCheck.setDisabled(True)
self.tableCheck = OWGUI.checkBox(GeneralTab, self, 'table','Show table', tooltip='Show table of selected attribute values?')
self.bubbleCheck = OWGUI.checkBox(GeneralTab, self, 'bubble', 'Show details bubble', tooltip='Show details of selected attribute value in a roll-over blob.')
self.tableCheck.setDisabled(True)
self.sortBox = OWGUI.comboBox(GeneralTab, self, "sort_type", box="Sorting", label="Criteria: ", items=["No sorting", "Absolute importance", "Positive influence", "Negative influence"], callback = self.sortNomogram)
self.tabs.insertTab(GeneralTab, "General")
# TREE TAB
NomogramStyleTab = QVGroupBox(self)
self.verticalSpacingLabel = OWGUI.spin(NomogramStyleTab, self, 'verticalSpacing', 15, 100, box = 'Vertical spacing:', tooltip='Define space (pixels) between adjacent attributes.', callback = self.showNomogram)
self.verticalSpacingLabel.setDisabled(True)
self.fontSizeLabel = OWGUI.spin(NomogramStyleTab, self, 'fontSize', 4, 14, box = 'Font size:', tooltip='Font size of nomogram labels.')
self.fontSizeLabel.setDisabled(True)
self.lineWidthLabel = OWGUI.spin(NomogramStyleTab, self, 'lineWidth', 1, 10, box = 'Line width:', tooltip='Define width of lines shown in nomogram.')
self.lineWidthLabel.setDisabled(True)
self.histogramCheck, self.histogramLabel = OWGUI.checkWithSpin(NomogramStyleTab, self, 'Histogram, max. size:', min=1, max=30, checked='histogram', value='histogram_size', step = 1, tooltip='-(TODO)-', checkCallback=self.showNomogram, spinCallback = self.showNomogram)
self.histogramCheck.setChecked(False)
self.histogramCheck.setDisabled(True)
self.histogramLabel.setDisabled(True)
# save button
self.connect(self.graphButton, SIGNAL("clicked()"), self.saveToFileCanvas)
# objects/gui widgets in settings tab for showing and adjusting confidence intervals properties
self.CICheck, self.CILabel = OWGUI.checkWithSpin(NomogramStyleTab, self, 'Confidence Interval (%):', min=1, max=99, step = 1, checked='confidence_check', value='confidence_percent', tooltip='-(TODO)-', checkCallback=self.showNomogram, spinCallback = self.showNomogram)
self.CICheck.setChecked(False)
self.CICheck.setDisabled(True)
self.CILabel.setDisabled(True)
self.showBaseLineCB = OWGUI.checkBox(NomogramStyleTab, self, 'showBaseLine', 'Show Base Line (at 0-point)', callback = self.setBaseLine)
self.tabs.insertTab(NomogramStyleTab, "Settings")
#add a graph widget
self.box=QBoxLayout(self.mainArea, QVBoxLayout.TopToBottom, 0)
self.graph=OWNomogramGraph(self.bnomogram, self.mainArea)
self.graph.setMinimumWidth(200)
self.header = OWNomogramHeader(None, self.mainArea)
self.header.setMinimumHeight(self.verticalSpacing)
self.header.setMaximumHeight(self.verticalSpacing)
self.footer = OWNomogramHeader(None, self.mainArea)
self.footer.setMinimumHeight(self.verticalSpacing*2+10)
self.footer.setMaximumHeight(self.verticalSpacing*2+10)
self.box.addWidget(self.header)
self.box.addWidget(self.graph)
self.box.addWidget(self.footer)
self.resize(700,500)
self.repaint()
self.update()
# mouse pressed flag
self.mousepr = False
# Input channel: the Bayesian classifier
def nbClassifier(self, cl):
# thisd subroutine computes standard error of estimated beta. Note that it is used only for discrete data,
# continuous data have a different computation.
def errOld(e, priorError, key, data):
inf = 0.0
sume = e[0]+e[1]
for d in data:
if d[at]==key:
inf += (e[0]*e[1]/sume/sume)
inf = max(inf, aproxZero)
var = max(1/inf - priorError*priorError, 0)
return (math.sqrt(var))
def err(condDist, att, value, targetClass, priorError, data):
sumE = sum(condDist)
valueE = condDist[targetClass]
distAtt = orange.Distribution(att, data)
inf = distAtt[value]*(valueE/sumE)*(1-valueE/sumE)
inf = max(inf, aproxZero)
var = max(1/inf - priorError*priorError, 0)
return (math.sqrt(var))
classVal = cl.domain.classVar
att = cl.domain.attributes
# calculate prior probability
dist1 = max(aproxZero, 1-cl.distribution[classVal[self.TargetClassIndex]])
dist0 = max(aproxZero, cl.distribution[classVal[self.TargetClassIndex]])
prior = dist0/dist1
if self.data:
sumd = dist1+dist0
priorError = math.sqrt(1/((dist1*dist0/sumd/sumd)*len(self.data)))
else:
priorError = 0
self.bnomogram = BasicNomogram(self, AttValue("Constant", math.log(prior), error = priorError))
if self.data:
stat = orange.DomainBasicAttrStat(self.data)
for at in range(len(att)):
if att[at].varType == orange.VarTypes.Discrete:
if att[at].ordered:
a = AttrLineOrdered(att[at].name, self.bnomogram)
else:
a = AttrLine(att[at].name, self.bnomogram)
for cd in cl.conditionalDistributions[at].keys():
# calculuate thickness
conditional0 = max(cl.conditionalDistributions[at][cd][classVal[self.TargetClassIndex]], aproxZero)
conditional1 = max(1-cl.conditionalDistributions[at][cd][classVal[self.TargetClassIndex]], aproxZero)
beta = math.log(conditional0/conditional1/prior)
if self.data:
#thickness = int(round(4.*float(len(self.data.filter({att[at].name:str(cd)})))/float(len(self.data))))
thickness = float(len(self.data.filter({att[at].name:str(cd)})))/float(len(self.data))
se = err(cl.conditionalDistributions[at][cd], att[at], cd, classVal[self.TargetClassIndex], priorError, self.data) # standar error of beta
else:
thickness = 0
se = 0
a.addAttValue(AttValue(str(cd), beta, lineWidth=thickness, error = se))
else:
a = AttrLineCont(att[at].name, self.bnomogram)
numOfPartitions = 50
if self.data:
maxAtValue = stat[at].max
minAtValue = stat[at].min
else:
maxAtValue = cl.conditionalDistributions[at].keys()[len(cl.conditionalDistributions[at].keys())-1]
minAtValue = cl.conditionalDistributions[at].keys()[0]
d = maxAtValue-minAtValue
d = getDiff(d/numOfPartitions)
# get curr_num = starting point for continuous att. sampling
curr_num = getStartingPoint(d, minAtValue)
rndFac = getRounding(d)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -