Skip to content

Commit 7a35a2c

Browse files
authored
[ENH] Update nimare spec (#675)
* update script and output * update script and output for config * give reasonable defaults for different algorithms (remove SCALE) * remove unwanted classes
1 parent f99ffd5 commit 7a35a2c

File tree

5 files changed

+348
-89
lines changed

5 files changed

+348
-89
lines changed

compose/neurosynth-frontend/scripts/parse_nimare_docs.py

Lines changed: 127 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
import re
55

66

7-
from numpydoc.docscrape import ClassDoc
7+
from numpydoc.docscrape import ClassDoc, FunctionDoc
88
import nimare.meta.cbma as nicoords
99
import nimare.meta.ibma as niimgs
1010
import nimare.meta.kernel as nikern
1111
import nimare.correct as crrct
1212
import nimare
1313

1414
PARAM_OPTIONAL_REGEX = re.compile(
15-
r"(?:\:obj\:`)?(?P<type>.*?)`?(?:, optional|\(optional\))?$"
15+
r"(?:\:obj\:`)?(?P<type>\{.*\}|.*?)(?:`)?(?:(?:, optional|\(optional\))|(?:, default=(?P<default>.*)))?$"
1616
)
1717

1818
NIMARE_CORRECTORS = [
@@ -25,7 +25,14 @@
2525
NIMARE_IMAGE_ALGORITHMS = [
2626
cls
2727
for cls in inspect.getmembers(niimgs, inspect.isclass)
28-
if cls[0] not in ["NiftiMasker", "MetaEstimator"]
28+
if cls[0] not in [
29+
"NiftiMasker",
30+
"MetaEstimator",
31+
"Memory",
32+
"Estimator",
33+
"IBMAEstimator",
34+
"Memory"
35+
]
2936
]
3037

3138

@@ -38,13 +45,21 @@
3845
"SCALE": "ALEKernel",
3946
}
4047

48+
BLACKLIST_ALGORITHMS = [
49+
"SCALE"
50+
]
51+
4152
BLACKLIST_PARAMS = [
4253
"n_cores",
4354
"memory_limit",
4455
"null_distributions_",
4556
"inputs_",
4657
"masker",
4758
"kernel_transformer",
59+
"memory",
60+
"memory_level",
61+
"result",
62+
"self",
4863
]
4964

5065
config = {
@@ -61,26 +76,112 @@ def _derive_type(type_name):
6176
type_name, _ = spl[0], spl[1:]
6277
optional_type = PARAM_OPTIONAL_REGEX.match(type_name)
6378
if optional_type:
64-
return optional_type.group("type")
65-
return type_name
79+
return optional_type.group("type"), optional_type.group("default")
80+
return type_name, None
81+
82+
83+
def _derive_default(class_signature, param):
84+
default = getattr(
85+
cls_signature.parameters.get(param.name), "default", None
86+
)
87+
if isinstance(default, tuple):
88+
default = default[0]
89+
90+
if default is inspect._empty:
91+
default = None
92+
93+
# try to parse default from docstring
94+
if default is None:
95+
dtype, default = _derive_type(param.type)
96+
if default is not None:
97+
if dtype == "int":
98+
default = int(default)
99+
elif dtype == "float":
100+
default = float(default)
101+
elif dtype == "bool":
102+
if default.lower() == "true":
103+
default = True
104+
elif default.lower() == "false":
105+
default = False
106+
elif dtype == "str":
107+
default = str(default)
108+
elif dtype == "list":
109+
default = list(default)
110+
elif dtype == "tuple":
111+
default = tuple(default)
112+
elif dtype == "dict":
113+
default = dict(default)
114+
elif dtype == "set":
115+
default = set(default)
116+
elif dtype == "NoneType":
117+
default = None
118+
else:
119+
raise ValueError(f"Unknown type: {dtype}")
120+
if isinstance(default, tuple):
121+
default = default[0]
122+
return default
123+
124+
125+
def _check_fwe(cls):
126+
# Check if the method exists
127+
has_method = hasattr(cls, 'correct_fwe_montecarlo')
128+
if has_method:
129+
# Get the method
130+
method = getattr(cls, 'correct_fwe_montecarlo')
131+
132+
# Get the source code of the method
133+
source_code = inspect.getsource(method)
134+
135+
# Check if the source code contains 'NotImplementedError'
136+
fwe_enabled = 'NotImplementedError' not in source_code
137+
else:
138+
fwe_enabled = False
139+
140+
if fwe_enabled:
141+
# Get the signature of the method
142+
method_signature = inspect.signature(method)
143+
144+
# get the function docstring
145+
mdocs = FunctionDoc(method)
146+
147+
# Get the default parameters of the method
148+
method_default_parameters = {
149+
param.name: {
150+
"description": " ".join(param.desc),
151+
"type": _derive_type(param.type)[0] or None,
152+
"default": _derive_default(method_signature, param),
153+
}
154+
for param in mdocs._parsed_data["Parameters"]
155+
if param.name not in BLACKLIST_PARAMS
156+
},
157+
158+
if isinstance(method_default_parameters, tuple):
159+
method_default_parameters = method_default_parameters[0]
160+
161+
return True, method_default_parameters
162+
else:
163+
return False, None
66164

67165

68166
for algo, cls in NIMARE_COORDINATE_ALGORITHMS:
167+
if algo in BLACKLIST_ALGORITHMS:
168+
continue
69169
docs = ClassDoc(cls)
70170
cls_signature = inspect.signature(cls)
171+
71172
config["CBMA"][algo] = {
72173
"summary": " ".join(docs._parsed_data["Summary"]),
73174
"parameters": {
74175
param.name: {
75176
"description": " ".join(param.desc),
76-
"type": _derive_type(param.type) or None,
77-
"default": getattr(
78-
cls_signature.parameters.get(param.name), "default", None
79-
),
177+
"type": _derive_type(param.type)[0] or None,
178+
"default": _derive_default(cls_signature, param),
80179
}
81180
for param in docs._parsed_data["Parameters"]
82181
if param.name not in BLACKLIST_PARAMS
83182
},
183+
"FWE_enabled": _check_fwe(cls)[0],
184+
"FWE_parameters": _check_fwe(cls)[1],
84185
}
85186

86187
kern_cls = getattr(nikern, DEFAULT_KERNELS[algo])
@@ -91,10 +192,8 @@ def _derive_type(type_name):
91192
"kernel__"
92193
+ param.name: {
93194
"description": " ".join(param.desc),
94-
"type": _derive_type(param.type),
95-
"default": getattr(
96-
kern_cls_signature.parameters.get(param.name), "default", None
97-
),
195+
"type": _derive_type(param.type)[0],
196+
"default": _derive_default(kern_cls_signature, param),
98197
}
99198
for param in kern_docs._parsed_data["Parameters"]
100199
if param.name not in BLACKLIST_PARAMS
@@ -109,10 +208,8 @@ def _derive_type(type_name):
109208
"parameters": {
110209
param.name: {
111210
"description": " ".join(param.desc),
112-
"type": _derive_type(param.type) or None,
113-
"default": getattr(
114-
cls_signature.parameters.get(param.name), "default", None
115-
),
211+
"type": _derive_type(param.type)[0] or None,
212+
"default": _derive_default(cls_signature, param),
116213
}
117214
for param in docs._parsed_data["Parameters"]
118215
if param.name not in BLACKLIST_PARAMS
@@ -121,27 +218,36 @@ def _derive_type(type_name):
121218

122219

123220
for algo, cls in NIMARE_IMAGE_ALGORITHMS:
221+
if algo in BLACKLIST_ALGORITHMS:
222+
continue
124223
docs = ClassDoc(cls)
125224
cls_signature = inspect.signature(cls)
126225
config["IBMA"][algo] = {
127226
"summary": " ".join(docs._parsed_data["Summary"]),
128227
"parameters": {
129228
param.name: {
130229
"description": " ".join(param.desc),
131-
"type": _derive_type(param.type) or None,
132-
"default": getattr(
133-
cls_signature.parameters.get(param.name), "default", None
134-
),
230+
"type": _derive_type(param.type)[0] or None,
231+
"default": _derive_default(cls_signature, param),
135232
}
136233
for param in docs._parsed_data["Parameters"]
137234
if param.name not in BLACKLIST_PARAMS
138235
},
236+
"FWE_enabled": _check_fwe(cls)[0],
237+
"FWE_parameters": _check_fwe(cls)[1],
139238
}
140239

240+
# SET MANUAL DEFAULTS (Hacks!)
241+
# for some reason treating this as a set causes errors :(
242+
config["CORRECTOR"]["FWECorrector"]["parameters"]["method"]["type"] = "str"
243+
# since we don't have sample size, setting another reasonable default
244+
config["CBMA"]["ALE"]["parameters"]["kernel__fwhm"]["default"] = 8
245+
config["CBMA"]["ALESubtraction"]["parameters"]["kernel__fwhm"]["default"] = 8
141246

142247
# save config file
143248
fname = (
144249
Path(__file__).parent.parent
250+
/ "NiMARE"
145251
/ "src"
146252
/ "assets"
147253
/ "config"

0 commit comments

Comments
 (0)