Skip to content

Commit

Permalink
Merge ff3261a into 278d1e6
Browse files Browse the repository at this point in the history
  • Loading branch information
matthew-brett committed Feb 23, 2021
2 parents 278d1e6 + ff3261a commit 69d2c4f
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 4 deletions.
5 changes: 3 additions & 2 deletions nipy/modalities/fmri/design.py
Expand Up @@ -9,6 +9,7 @@

import numpy as np

from nipy.utils import seq_prod_def
from nipy.algorithms.statistics.utils import combinations
from nipy.algorithms.statistics.formula import formulae
from nipy.algorithms.statistics.formula.formulae import (
Expand Down Expand Up @@ -105,7 +106,7 @@ def _build_formula_contrasts(spec, fields, order):
between factors.
"""
e_factors = [Factor(n, np.unique(spec[n])) for n in fields]
e_formula = np.product(e_factors)
e_formula = seq_prod_def(e_factors)
e_contrasts = {}
# Add contrasts for factors and factor interactions
max_order = min(len(e_factors), order)
Expand All @@ -115,7 +116,7 @@ def _build_formula_contrasts(spec, fields, order):
# Collect factors where there is more than one level
fs = [fc.main_effect for fn, fc in comb if len(fc.levels) > 1]
if len(fs) > 0:
e_contrast = np.product(fs).design(spec)
e_contrast = seq_prod_def(fs).design(spec)
e_contrasts[":".join(names)] = e_contrast
e_contrasts['constant'] = formulae.I.design(spec)
return e_formula, e_contrasts
Expand Down
2 changes: 1 addition & 1 deletion nipy/utils/__init__.py
Expand Up @@ -29,7 +29,7 @@
else:
HAVE_TEMPLATES = True

from .utilities import is_iterable, is_numlike, seq_prod
from .utilities import is_iterable, is_numlike, seq_prod, seq_prod_def

from nipy.testing import Tester
test = Tester().test
Expand Down
13 changes: 12 additions & 1 deletion nipy/utils/tests/test_utilities.py
Expand Up @@ -8,7 +8,7 @@
assert_equal, assert_not_equal)


from ..utilities import is_iterable, is_numlike, seq_prod
from ..utilities import is_iterable, is_numlike, seq_prod, seq_prod_def

def test_is_iterable():
assert_true(is_iterable(()))
Expand Down Expand Up @@ -49,7 +49,18 @@ def test_is_numlike():

def test_seq_prod():
assert_equal(seq_prod(()), 1)
assert_equal(seq_prod((), 2), 2)
assert_equal(seq_prod((1,)), 1)
assert_equal(seq_prod((1, 2)), 2)
assert_equal(seq_prod((1, 2), 2), 4)
assert_equal(seq_prod((1, 2), 2.), 4.)


def test_seq_prod_def():
assert_equal(seq_prod_def(()), 1)
assert_equal(seq_prod_def((), 2), 2)
assert_equal(seq_prod_def((1,)), 1)
assert_equal(seq_prod_def((1, 2)), 2)
# Differs from seq_prod
assert_equal(seq_prod_def((1, 2), 2), 2)
assert_equal(seq_prod_def((1, 2), 2.), 2)
21 changes: 21 additions & 0 deletions nipy/utils/utilities.py
Expand Up @@ -43,3 +43,24 @@ def seq_prod(seq, initial=1):
Result of ``initial * seq[0] * seq[1] .. ``.
"""
return reduce(mul, seq, initial)


def seq_prod_def(seq, default=1):
""" Product of elements in `seq`, with `default` for empty `seq`.
Parameters
----------
seq : sequence
Iterable of values to multiply.
default : object, optional
Value to return in case of empty sequence.
Returns
-------
prod : object
Value of `default` if `seq` is empty, otherwise result of ``seq[0] *
seq[1] .. ``.
"""
if len(seq) == 0:
return default
return reduce(mul, seq)

0 comments on commit 69d2c4f

Please sign in to comment.