Skip to content
Snippets Groups Projects
Commit 118dbbc8 authored by Paul McCarthy's avatar Paul McCarthy
Browse files

Ability to plot 'reduced' data (with respect to a specific PE/COPE),

and residuals.
parent a7fcf7e2
No related branches found
No related tags found
No related merge requests found
......@@ -154,6 +154,7 @@ class FEATImage(fslimage.Image):
self.__contrasts = cons
self.__settings = settings
self.__residuals = None
self.__pes = [None] * self.numEVs()
self.__copes = [None] * self.numContrasts()
......@@ -183,24 +184,37 @@ class FEATImage(fslimage.Image):
return [list(c) for c in self.__contrasts]
def __getPEFile(self, prefix, ev):
prefix = op.join(self.__featDir, 'stats', '{}{}'.format(
prefix, ev + 1))
def __getStatsFile(self, prefix, ev=None):
if ev is not None: prefix = '{}{}'.format(prefix, ev + 1)
prefix = op.join(self.__featDir, 'stats', prefix)
return glob.glob('{}.*'.format(prefix))[0]
def getPE(self, ev):
if self.__pes[ev] is None:
pefile = self.__getPEFile('pe', ev)
pefile = self.__getStatsFile('pe', ev)
self.__pes[ev] = nib.load(pefile).get_data()
return self.__pes[ev]
def getResiduals(self):
if self.__residuals is None:
resfile = self.__getStatsFile('res4d')
self.__residuals = nib.load(resfile).get_data()
return self.__residuals
def getCOPE(self, num):
if self.__copes[num] is None:
copefile = self.__getPEFile('cope', num)
copefile = self.__getStatsFile('cope', num)
self.__copes[num] = nib.load(copefile).get_data()
return self.__copes[num]
......@@ -229,6 +243,15 @@ class FEATImage(fslimage.Image):
return modelfit + data.mean()
def reducedData(self, xyz, contrast, fullmodel=False):
x, y, z = xyz
residuals = self.getResiduals()[x, y, z, :]
modelfit = self.fit(contrast, xyz, fullmodel)
return residuals + modelfit
# def getThresholdedZStats(self):
# pass
......
......@@ -290,6 +290,8 @@ properties = TypeDict({
'FEATTimeSeries.plotFullModelFit' : 'Plot full model fit',
'FEATTimeSeries.plotPEFits' : 'Plot PE{} fit',
'FEATTimeSeries.plotCOPEFits' : 'Plot COPE{} fit ({})',
'FEATTimeSeries.plotResiduals' : 'Show residuals',
'FEATTimeSeries.reduceAgainst' : 'Reduce data against',
'OrthoEditProfile.selectionSize' : 'Selection size',
'OrthoEditProfile.selectionIs3D' : '3D selection',
......
......@@ -198,12 +198,25 @@ class TimeSeriesControlPanel(fslpanel.FSLViewPanel):
display.name))
full = props.makeWidget( self.__widgets, ts, 'plotFullModelFit')
res = props.makeWidget( self.__widgets, ts, 'plotResiduals')
pes = props.makeListWidgets(self.__widgets, ts, 'plotPEFits')
copes = props.makeListWidgets(self.__widgets, ts, 'plotCOPEFits')
reduce = props.makeWidget( self.__widgets, ts, 'reduceAgainst')
self.__widgets.AddWidget(
full,
displayName=strings.properties[ts, 'plotFullModelFit'],
groupName='currentFEATSettings')
self.__widgets.AddWidget(
res,
displayName=strings.properties[ts, 'plotResiduals'],
groupName='currentFEATSettings')
self.__widgets.AddWidget(
reduce,
displayName=strings.properties[ts, 'reduceAgainst'],
groupName='currentFEATSettings')
for i, pe in enumerate(pes):
self.__widgets.AddWidget(
......
......@@ -56,9 +56,16 @@ class TimeSeries(plotpanel.DataSeries):
return True
def getData(self):
ydata = np.array( self.data, dtype=np.float32)
xdata = np.arange(len(ydata), dtype=np.float32)
def getData(self, xdata=None, ydata=None):
"""
:arg xdata:
:arg ydata: Used by subclasses in case they have already done some
processing on the data.
"""
if xdata is None: xdata = np.arange(len(self.data), dtype=np.float32)
if ydata is None: ydata = np.array( self.data, dtype=np.float32)
if self.tsPanel.usePixdim:
xdata *= self.overlay.pixdim[3]
......@@ -77,19 +84,28 @@ class FEATTimeSeries(TimeSeries):
plotFullModelFit = props.Boolean(default=False)
plotResiduals = props.Boolean(default=False)
plotPEFits = props.List(props.Boolean(default=False))
plotCOPEFits = props.List(props.Boolean(default=False))
# TODO 'None', or any PE/COPE
reduceDataAgainst = props.Choice()
reduceAgainst = props.Choice()
def __init__(self, *args, **kwargs):
TimeSeries.__init__(self, *args, **kwargs)
self.name = '{}_{}'.format(type(self).__name__, id(self))
numEVs = self.overlay.numEVs()
numCOPEs = self.overlay.numContrasts()
numEVs = self.overlay.numEVs()
numCOPEs = self.overlay.numContrasts()
copeNames = self.overlay.contrastNames()
reduceOpts = ['none'] + \
['PE{}'.format(i + 1) for i in range(numEVs)]
for i in range(numCOPEs):
name = 'COPE{} ({})'.format(i + 1, copeNames[i])
reduceOpts.append(name)
self.getProp('reduceAgainst').setChoices(reduceOpts, instance=self)
for i in range(numEVs):
self.plotPEFits.append(False)
......@@ -98,12 +114,17 @@ class FEATTimeSeries(TimeSeries):
self.plotCOPEFits.append(False)
self.__fullModelTs = None
self.__resTs = None
self.__peTs = [None] * numEVs
self.__copeTs = [None] * numCOPEs
self.addListener('plotFullModelFit',
self.name,
self.__plotFullModelFitChanged)
self.addListener('plotResiduals',
self.name,
self.__plotResidualsChanged)
for i, plotPEFit in enumerate(
self.plotPEFits.getPropertyValueList()):
......@@ -121,6 +142,28 @@ class FEATTimeSeries(TimeSeries):
plotCOPEFit.addListener(self.name, onChange)
def getData(self):
reduce = self.reduceAgainst
if reduce == 'none':
data = None
else:
idx = int(reduce.split()[0][-1]) - 1
numEVs = self.overlay.numEVs()
if reduce.startswith('PE'):
contrast = [0] * numEVs
contrast[idx] = 1
else:
contrast = self.overlay.contrasts()[idx]
data = self.overlay.reducedData(self.coords, contrast, False)
return TimeSeries.getData(self, ydata=data)
def __copy__(self):
copy = type(self)(self.tsPanel, self.overlay, self.coords)
......@@ -148,6 +191,9 @@ class FEATTimeSeries(TimeSeries):
if self.plotFullModelFit:
modelts.append(self.__fullModelTs)
if self.plotResiduals:
modelts.append(self.__resTs)
for i in range(self.overlay.numEVs()):
if self.plotPEFits[i]:
modelts.append(self.__peTs[i])
......@@ -158,6 +204,25 @@ class FEATTimeSeries(TimeSeries):
return modelts
def __plotResidualsChanged(self, *a):
if not self.plotResiduals:
self.__resTs = None
return
rts = FEATResidualTimeSeries(
self.tsPanel,
self.overlay,
self.coords)
rts.colour = (0.8, 0.4, 0)
rts.alpha = self.alpha
rts.label = self.label
rts.lineWidth = self.lineWidth
rts.lineStyle = self.lineStyle
self.__resTs = rts
def __plotCOPEFitChanged(self, copenum):
if not self.plotCOPEFits[copenum]:
......@@ -237,6 +302,14 @@ class FEATTimeSeries(TimeSeries):
return True
class FEATResidualTimeSeries(TimeSeries):
def getData(self):
x, y, z = self.coords
data = self.overlay.getResiduals()[x, y, z, :]
return TimeSeries.getData(self, ydata=data)
class FEATModelFitTimeSeries(TimeSeries):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment