Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes to --tedpca option #1066

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion tedana/decomposition/pca.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def tedpca(
(stationary Gaussian) process and are ordered from most to least aggressive
(see :footcite:p:`li2007estimating`).
If a float is provided, then it is assumed to represent percentage of variance
explained (0-1) to retain from PCA.
explained (0.0-1.0) to retain from PCA.
If an int is provided, then it is assumed to be the number of components
to select
Default is 'aic'.
Expand Down
22 changes: 18 additions & 4 deletions tedana/tests/test_workflows_parser_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,29 @@ def test_check_tedpca_value():
check_tedpca_value("hello", is_parser=False)

with pytest.raises(argparse.ArgumentTypeError):
check_tedpca_value(1.5, is_parser=True)
check_tedpca_value("1.0", is_parser=True)

with pytest.raises(argparse.ArgumentTypeError):
check_tedpca_value("1.", is_parser=True)

with pytest.raises(ValueError):
check_tedpca_value(1.0, is_parser=False)

with pytest.raises(argparse.ArgumentTypeError):
check_tedpca_value("0.0", is_parser=True)

with pytest.raises(ValueError):
check_tedpca_value(1.5, is_parser=False)
check_tedpca_value(0.0, is_parser=False)

with pytest.raises(ValueError):
check_tedpca_value(-1, is_parser=False)

assert check_tedpca_value(0.95) == 0.95
with pytest.raises(ValueError):
check_tedpca_value(0, is_parser=False)

assert check_tedpca_value(0.95, is_parser=False) == 0.95
assert check_tedpca_value("0.95") == 0.95
assert check_tedpca_value("mdl") == "mdl"
assert check_tedpca_value(52) == 52
assert check_tedpca_value(1, is_parser=False) == 1
assert check_tedpca_value("1") == 1
assert check_tedpca_value(52, is_parser=False) == 52
53 changes: 37 additions & 16 deletions tedana/workflows/parser_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,54 @@
import os.path as op


def check_tedpca_value(string, is_parser=True):
def check_tedpca_value(value, is_parser=True):
"""
Check tedpca argument.

Check if argument is a float in range (0,1), an int greater than 1 or one of a
list of strings.
Check if argument is a float in range (0,1),
a positive integer,
or one of a list of strings.
"""
valid_options = ("mdl", "aic", "kic", "kundu", "kundu-stabilize")
if string in valid_options:
return string
if value in valid_options:
return value

error = argparse.ArgumentTypeError if is_parser else ValueError
dashes = "--" if is_parser else ""

def check_float(floatvalue):
assert isinstance(floatvalue, float)
if not (0.0 < floatvalue < 1.0):
msg = f"Floating-point argument to {dashes}tedpca must be in the range (0.0, 1.0)."
raise error(msg)
return floatvalue

def check_int(intvalue):
assert isinstance(intvalue, int)
if intvalue < 1:
msg = f"Integer argument to {dashes}tedpca must be positive"
raise error(msg)
return intvalue

if isinstance(value, float):
return check_float(value)
if isinstance(value, int):
return check_int(value)

try:
floatarg = float(string)
floatvalue = float(value)
except ValueError:
msg = f"Argument to tedpca must be a number or one of: {', '.join(valid_options)}"
msg = (
f"Argument to {dashes}tedpca must be either a number, "
f"or one of: {', '.join(valid_options)}"
)
raise error(msg)

if floatarg != int(floatarg):
if not (0 < floatarg < 1):
raise error("Float argument to tedpca must be between 0 and 1.")
return floatarg
else:
intarg = int(floatarg)
if floatarg < 1:
raise error("Int argument must be greater than 1")
return intarg
try:
intvalue = int(value)
except ValueError:
return check_float(floatvalue)
return check_int(intvalue)


def is_valid_file(parser, arg):
Expand Down
29 changes: 16 additions & 13 deletions tedana/workflows/tedana.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,17 +148,20 @@ def _get_parser():
dest="tedpca",
type=check_tedpca_value,
help=(
"Method with which to select components in TEDPCA. "
"Method by which to select number of components in TEDPCA. "
"This can be one of the following: "
"String ('mdl', 'kic', 'aic', 'kundu', or 'kundu-stabilize'); "
"floating-point value in the range (0.0, 1.0); "
"positive integer value. "
"PCA decomposition with the mdl, kic and aic options "
"is based on a Moving Average (stationary Gaussian) "
"process and are ordered from most to least aggressive. "
"'kundu' or 'kundu-stabilize' are selection methods that "
"were distributed with MEICA. "
"Users may also provide a float from 0 to 1, "
"in which case components will be selected based on the "
"cumulative variance explained or an integer greater than 1"
"in which case the specificed number of components will be "
"selected."
"are based on a Moving Average (stationary Gaussian) process, "
"and are ordered from most to least aggressive. "
"'kundu' or 'kundu-stabilize' are legacy selection methods "
"that were distributed with MEICA. "
"Floating-point inputs select components based on the "
"cumulative variance explained. "
"Integer inputs select the specificed number of components. "
"Default: 'aic'."
),
default="aic",
)
Expand Down Expand Up @@ -402,8 +405,8 @@ def tedana_workflow(
tedpca : {'mdl', 'aic', 'kic', 'kundu', 'kundu-stabilize', float, int}, optional
Method with which to select components in TEDPCA.
If a float is provided, then it is assumed to represent percentage of variance
explained (0-1) to retain from PCA. If an int is provided, it will output
a fixed number of components defined by the integer between 1 and the
explained (0.0-1.0) to retain from PCA. If an int is provided, it will output
a fixed number of components defined by the integer; must be between 1 and the
number of time points.
Default is 'aic'.
fixed_seed : :obj:`int`, optional
Expand Down Expand Up @@ -509,7 +512,7 @@ def tedana_workflow(
gscontrol = [gscontrol]

# Check value of tedpca *if* it is a predefined string,
# a float on [0, 1] or an int >= 1
# a float in (0.0, 1.0) or an int >= 1
tedpca = check_tedpca_value(tedpca, is_parser=False)

# For z-catted files, make sure it's a list of size 1
Expand Down