Commit 4c72d37f authored by Paul McCarthy's avatar Paul McCarthy 🚵
Browse files

Merge branch 'rel/3.5.3' into 'v3.5'

Rel/3.5.3

See merge request fsl/fslpy!282
parents 548ec054 3255464e
Pipeline #7792 passed with stages
in 1 minute and 47 seconds
...@@ -2,6 +2,17 @@ This document contains the ``fslpy`` release history in reverse chronological ...@@ -2,6 +2,17 @@ This document contains the ``fslpy`` release history in reverse chronological
order. order.
3.5.3 (Tuesday 9th February 2021)
---------------------------------
Fixed
^^^^^
* Fixed a bug in :func:`.featanalysis.loadClusterResults` (!281).
3.5.2 (Friday 29th January 2021) 3.5.2 (Friday 29th January 2021)
--------------------------------- ---------------------------------
...@@ -12,8 +23,8 @@ Fixed ...@@ -12,8 +23,8 @@ Fixed
* Adjusted the :func:`.dicom.scanDir` function so that it will set a * Adjusted the :func:`.dicom.scanDir` function so that it will set a
default value for ``SeriesDescription`` if it is not present in the default value for ``SeriesDescription`` if it is not present in the
``dcm2niix`` ``json`` output. ``dcm2niix`` ``json`` output (!279).
* Fixed some issues with API documentation generation. * Fixed some issues with API documentation generation (!279).
3.5.1 (Thursday 21st January 2021) 3.5.1 (Thursday 21st January 2021)
......
...@@ -373,7 +373,7 @@ def loadClusterResults(featdir, settings, contrast): ...@@ -373,7 +373,7 @@ def loadClusterResults(featdir, settings, contrast):
if not op.exists(clusterFile): if not op.exists(clusterFile):
return None return None
# In higher levle analysis run in some standard # In higher level analysis run in some standard
# space, the cluster coordinates are in standard # space, the cluster coordinates are in standard
# space. We transform them to voxel coordinates. # space. We transform them to voxel coordinates.
# later on. # later on.
...@@ -480,13 +480,13 @@ def loadClusterResults(featdir, settings, contrast): ...@@ -480,13 +480,13 @@ def loadClusterResults(featdir, settings, contrast):
zcog = [c.zcogx, c.zcogy, c.zcogz] zcog = [c.zcogx, c.zcogy, c.zcogz]
copemax = [c.copemaxx, c.copemaxy, c.copemaxz] copemax = [c.copemaxx, c.copemaxy, c.copemaxz]
zmax = affine.transform([zmax], coordXform)[0].round() zmax = affine.transform([zmax], coordXform)[0]
zcog = affine.transform([zcog], coordXform)[0].round() zcog = affine.transform([zcog], coordXform)[0]
copemax = affine.transform([copemax], coordXform)[0].round() copemax = affine.transform([copemax], coordXform)[0]
c.zmaxx, c.zmaxy, c.zmaxz = zmax c.zmaxx, c.zmaxy, c.zmaxz = zmax
c.zcogx, c.zcogy, c.zcogz = zcog c.zcogx, c.zcogy, c.zcogz = zcog
c.copemax, c.copemaxy, c.copemaxz = copemax c.copemaxx, c.copemaxy, c.copemaxz = copemax
return clusters return clusters
......
...@@ -47,7 +47,7 @@ import re ...@@ -47,7 +47,7 @@ import re
import string import string
__version__ = '3.5.2' __version__ = '3.5.3'
"""Current version number, as a string. """ """Current version number, as a string. """
......
...@@ -59,7 +59,7 @@ def test_isFEATDir(): ...@@ -59,7 +59,7 @@ def test_isFEATDir():
# it's not a feat directory # it's not a feat directory
assert not featanalysis.isFEATDir('nonexistent.feat') assert not featanalysis.isFEATDir('nonexistent.feat')
# If any of the above files are not # If any of the above files are not
# present, it is not a FEAT directory # present, it is not a FEAT directory
perms = it.chain(it.combinations(paths, 1), perms = it.chain(it.combinations(paths, 1),
it.combinations(paths, 2), it.combinations(paths, 2),
...@@ -69,7 +69,7 @@ def test_isFEATDir(): ...@@ -69,7 +69,7 @@ def test_isFEATDir():
assert not featanalysis.isFEATDir( assert not featanalysis.isFEATDir(
op.join(testdir, 'analysis.feat')) op.join(testdir, 'analysis.feat'))
def test_hasStats(): def test_hasStats():
with tests.testdir(['analysis.feat/stats/zstat1.nii.gz']) as testdir: with tests.testdir(['analysis.feat/stats/zstat1.nii.gz']) as testdir:
...@@ -78,7 +78,7 @@ def test_hasStats(): ...@@ -78,7 +78,7 @@ def test_hasStats():
with tests.testdir(['analysis.feat/stats/zstat1.txt']) as testdir: with tests.testdir(['analysis.feat/stats/zstat1.txt']) as testdir:
featdir = op.join(testdir, 'analysis.feat') featdir = op.join(testdir, 'analysis.feat')
assert not featanalysis.hasStats(featdir) assert not featanalysis.hasStats(featdir)
def test_hasMelodicDir(): def test_hasMelodicDir():
...@@ -94,7 +94,7 @@ def test_getAnalysisDir(): ...@@ -94,7 +94,7 @@ def test_getAnalysisDir():
'analysis.feat/design.fsf', 'analysis.feat/design.fsf',
'analysis.feat/design.mat', 'analysis.feat/design.mat',
'analysis.feat/design.con'] 'analysis.feat/design.con']
testpaths = ['analysis.feat/filtered_func_data.nii.gz', testpaths = ['analysis.feat/filtered_func_data.nii.gz',
'analysis.feat/stats/zstat1.nii.gz', 'analysis.feat/stats/zstat1.nii.gz',
'analysis.feat/logs/feat4_post', 'analysis.feat/logs/feat4_post',
...@@ -106,7 +106,7 @@ def test_getAnalysisDir(): ...@@ -106,7 +106,7 @@ def test_getAnalysisDir():
t = op.join(testdir, t) t = op.join(testdir, t)
assert featanalysis.getAnalysisDir(t) == expected assert featanalysis.getAnalysisDir(t) == expected
def test_getTopLevelAnalysisDir(): def test_getTopLevelAnalysisDir():
testcases = [ testcases = [
('analysis.feat/filtered_func_data.ica/melodic_IC.nii.gz', 'analysis.feat'), ('analysis.feat/filtered_func_data.ica/melodic_IC.nii.gz', 'analysis.feat'),
...@@ -127,7 +127,7 @@ def test_getReportFile(): ...@@ -127,7 +127,7 @@ def test_getReportFile():
for paths, expected in testcases: for paths, expected in testcases:
with tests.testdir(paths) as testdir: with tests.testdir(paths) as testdir:
featdir = op.join(testdir, 'analysis.feat') featdir = op.join(testdir, 'analysis.feat')
if expected: if expected:
...@@ -145,7 +145,7 @@ def test_loadContrasts(): ...@@ -145,7 +145,7 @@ def test_loadContrasts():
/ContrastName1 c1 /ContrastName1 c1
/ContrastName2 c2 /ContrastName2 c2
/ContrastName3 c3 /ContrastName3 c3
/NumContrasts 3 /NumContrasts 3
/Matrix /Matrix
1 0 0 1 0 0
0 1 0 0 1 0
...@@ -188,9 +188,9 @@ def test_loadContrasts(): ...@@ -188,9 +188,9 @@ def test_loadContrasts():
0 1 1 0 1 1
""", """,
] ]
with pytest.raises(Exception): with pytest.raises(Exception):
featanalysis.loadContrasts('no file') featanalysis.loadContrasts('no file')
with tests.testdir() as testdir: with tests.testdir() as testdir:
featdir = op.join(testdir, 'analysis.feat') featdir = op.join(testdir, 'analysis.feat')
...@@ -275,7 +275,7 @@ def test_isFirstLevelAnalysis(): ...@@ -275,7 +275,7 @@ def test_isFirstLevelAnalysis():
'2ndlevel_1.gfeat', '2ndlevel_2.gfeat'] '2ndlevel_1.gfeat', '2ndlevel_2.gfeat']
for featdir in featdirs: for featdir in featdirs:
expected = featdir.startswith('1') expected = featdir.startswith('1')
featdir = op.join(datadir, featdir) featdir = op.join(datadir, featdir)
settings = featanalysis.loadSettings(featdir) settings = featanalysis.loadSettings(featdir)
...@@ -301,14 +301,15 @@ def test_loadClusterResults(): ...@@ -301,14 +301,15 @@ def test_loadClusterResults():
with tests.testdir() as testdir: with tests.testdir() as testdir:
# For higher level analyses, the # work from a copy of the test data directory
# loadClusterResults function peeks
# at the FEAT input data file
# header, so we have to generate it.
newfeatdir = op.join(testdir, 'analysis.feat') newfeatdir = op.join(testdir, 'analysis.feat')
shutil.copytree(op.join(datadir, featdir), newfeatdir) shutil.copytree(op.join(datadir, featdir), newfeatdir)
featdir = newfeatdir featdir = newfeatdir
# For higher level analyses, the
# loadClusterResults function peeks
# at the FEAT input data file
# header, so we have to generate it.
if not firstlevel: if not firstlevel:
datafile = op.join(featdir, 'filtered_func_data.nii.gz') datafile = op.join(featdir, 'filtered_func_data.nii.gz')
data = np.random.randint(1, 10, (91, 109, 91)) data = np.random.randint(1, 10, (91, 109, 91))
...@@ -333,6 +334,35 @@ def test_loadClusterResults(): ...@@ -333,6 +334,35 @@ def test_loadClusterResults():
assert featanalysis.loadClusterResults( assert featanalysis.loadClusterResults(
featdir, settings, 0) is None featdir, settings, 0) is None
# The above loop just checks that the number of
# clusters loaded for each analysis was correct.
# Below we check that the cluster data was loaded
# correctly, just for one analysis
featdir = op.join(datadir, '1stlevel_1.feat')
settings = featanalysis.loadSettings(featdir)
cluster = featanalysis.loadClusterResults(featdir, settings, 0)[0]
expected = {
'index' : 1,
'nvoxels' : 296,
'p' : 1.79e-27,
'logp' : 26.7,
'zmax' : 6.03,
'zmaxx' : 34,
'zmaxy' : 10,
'zmaxz' : 1,
'zcogx' : 31.4,
'zcogy' : 12.3,
'zcogz' : 1.72,
'copemax' : 612,
'copemaxx' : 34,
'copemaxy' : 10,
'copemaxz' : 1,
'copemean' : 143
}
for k, v in expected.items():
assert np.isclose(v, getattr(cluster, k))
def test_getDataFile(): def test_getDataFile():
paths = ['analysis.feat/filtered_func_data.nii.gz', paths = ['analysis.feat/filtered_func_data.nii.gz',
...@@ -393,9 +423,9 @@ def test_getResidualFile(): ...@@ -393,9 +423,9 @@ def test_getResidualFile():
assert featanalysis.getResidualFile(featdir) == expect assert featanalysis.getResidualFile(featdir) == expect
else: else:
with pytest.raises(fslpath.PathError): with pytest.raises(fslpath.PathError):
featanalysis.getResidualFile(featdir) featanalysis.getResidualFile(featdir)
def test_getPEFile(): def test_getPEFile():
testcases = [ testcases = [
(['analysis.feat/stats/pe1.nii.gz', (['analysis.feat/stats/pe1.nii.gz',
...@@ -417,7 +447,7 @@ def test_getPEFile(): ...@@ -417,7 +447,7 @@ def test_getPEFile():
assert featanalysis.getPEFile(featdir, pei) == expect assert featanalysis.getPEFile(featdir, pei) == expect
else: else:
with pytest.raises(fslpath.PathError): with pytest.raises(fslpath.PathError):
featanalysis.getPEFile(featdir, pei) featanalysis.getPEFile(featdir, pei)
def test_getCOPEFile(): def test_getCOPEFile():
...@@ -441,8 +471,8 @@ def test_getCOPEFile(): ...@@ -441,8 +471,8 @@ def test_getCOPEFile():
assert featanalysis.getCOPEFile(featdir, ci) == expect assert featanalysis.getCOPEFile(featdir, ci) == expect
else: else:
with pytest.raises(fslpath.PathError): with pytest.raises(fslpath.PathError):
featanalysis.getCOPEFile(featdir, ci) featanalysis.getCOPEFile(featdir, ci)
def test_getZStatFile(): def test_getZStatFile():
testcases = [ testcases = [
...@@ -465,8 +495,8 @@ def test_getZStatFile(): ...@@ -465,8 +495,8 @@ def test_getZStatFile():
assert featanalysis.getZStatFile(featdir, zi) == expect assert featanalysis.getZStatFile(featdir, zi) == expect
else: else:
with pytest.raises(fslpath.PathError): with pytest.raises(fslpath.PathError):
featanalysis.getZStatFile(featdir, zi) featanalysis.getZStatFile(featdir, zi)
def test_getClusterMaskFile(): def test_getClusterMaskFile():
testcases = [ testcases = [
...@@ -489,4 +519,4 @@ def test_getClusterMaskFile(): ...@@ -489,4 +519,4 @@ def test_getClusterMaskFile():
assert featanalysis.getClusterMaskFile(featdir, ci) == expect assert featanalysis.getClusterMaskFile(featdir, ci) == expect
else: else:
with pytest.raises(fslpath.PathError): with pytest.raises(fslpath.PathError):
featanalysis.getClusterMaskFile(featdir, ci) featanalysis.getClusterMaskFile(featdir, ci)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment