Skip to content

Commit

Permalink
Added filtering to ProfileProperty.
Browse files Browse the repository at this point in the history
  • Loading branch information
ConnorPigg committed Jun 25, 2019
1 parent 4ab9ec2 commit 6e353cc
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 9 deletions.
6 changes: 2 additions & 4 deletions idpflex/bayes.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ class TabulatedFunctionModel(Model):
Fitting parameters:
- integrated intensity ``amplitude`` :math:`A`
- position of the peak ``center`` :math:`E_0`
- nominal relaxation time ``tau`` :math:`\tau`
- stretching exponent ``beta`` :math:`\beta`
Parameters
----------
Expand All @@ -25,9 +23,9 @@ class TabulatedFunctionModel(Model):
""" # noqa: E501

def __init__(self, prop, interpolator_kind='linear',
prefix='', missing=None, name=None, fill_value='extrapolate',
fill_value='extrapolate', prefix='', missing=None, name=None,
**kwargs):
kwargs.update({'prefix': prefix, 'missing': missing})
kwargs.update({'prefix': prefix, 'missing': missing, 'name': name})
self._interpolator = interp1d(prop.x, prop.y, kind=interpolator_kind,
fill_value=fill_value)
self.prop = prop
Expand Down
31 changes: 30 additions & 1 deletion idpflex/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -1262,7 +1262,6 @@ def feature_weights(self):
if self.e is None or np.allclose(np.zeros(len(self.y)), self.e):
return np.ones(len(self.profile)) / np.sqrt(len(self.profile))
ws = self.profile / self.errors
ws[~np.isfinite(ws)] = self.profile[~np.isfinite(ws)]
return ws / np.linalg.norm(ws)

@property
Expand Down Expand Up @@ -1302,6 +1301,36 @@ def interpolate(self, qvalues, inplace=False):
qvalues=qvalues.copy(), name=self.name,
node=self.node)

def filter(self, mask=None, inplace=False):
"""Filter data with the provided mask, otherwise filter 'bad' data.
Will keep the portion of the profile, qvalues, and errors that align
with true values in the mask. By default, the mask is true when all of
profile, qvalues, and errors are noninfinite and the errors is nonzero.
Parameters
----------
mask: numpy.ndarray of type bool
The mask to apply to the components of the profile.
inplace: bool
If inplace, modify profile data instead of creating new object.
Returns
-------
A new property with the chosen subset of data.
"""
if mask is None:
mask = np.isfinite(self.profile) & np.isfinite(self.qvalues)\
& np.isfinite(self.errors) & (self.errors != 0)
if inplace:
self.profile = self.profile[mask]
self.errors = self.errors[mask]
self.qvalues = self.qvalues[mask]
return self
return self.__class__(name=self.name, profile=self.profile[mask],
node=self.node, qvalues=self.qvalues[mask],
errors=self.errors[mask])


class SansLoaderMixin(object):
r"""Mixin class providing a set of methods to load SANS data into a profile property.""" # noqa: E501
Expand Down
44 changes: 40 additions & 4 deletions tests/test_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ def test_mimic_dict(self):
assert len(propdict2) == 1

def test_feature_vector_domain_weights(self):
x = np.arange(10)
props = {'profile': ps.ProfileProperty(name='profile',
profile=np.arange(10),
qvalues=np.arange(10)*5,
errors=np.arange(10)*.01),
profile=x,
qvalues=x*5,
errors=x*.01 + 0.001),
'scalar': ps.ScalarProperty(name='scalar', x=0, y=1, e=2)}
propdict = ps.PropertyDict(properties=props.values())
assert_array_equal(propdict.feature_vector,
Expand Down Expand Up @@ -297,7 +298,7 @@ def test_instance_decorated_as_node_property(self):
def test_feature_vector_domain_and_weights(self):
v = np.arange(9)
profile_prop = ps.ProfileProperty(name='foo', qvalues=v, profile=10*v,
errors=0.1*v)
errors=0.1*v + 0.001)
assert_array_equal(profile_prop.feature_vector, profile_prop.profile)
assert_array_equal(profile_prop.feature_domain, profile_prop.qvalues)
ws = profile_prop.profile/profile_prop.errors
Expand Down Expand Up @@ -337,6 +338,41 @@ def test_interpolation(self):
assert_array_equal(e, new_sans_prop.errors)
assert_array_equal(x2, new_sans_prop.qvalues)

def test_filter(self):
x1 = np.random.rand(10)
# Gaurantee values outside of the range to test extrapolation
x1[3] = np.nan
y1 = x1**2
e1 = 0.1*y1
mask = np.isfinite(x1) & np.isfinite(y1) & np.isfinite(e1) & (e1 != 0)
prop = ps.ProfileProperty(name='foo', qvalues=x1, profile=y1,
errors=e1)
y2 = y1[mask]
x2 = x1[mask]
e2 = e1[mask]
new_prop = prop.filter(inplace=True)
assert_array_equal(y2, new_prop.profile)
assert_array_equal(e2, new_prop.errors)
assert_array_equal(x2, new_prop.qvalues)
assert new_prop is prop
sans_prop = ps.SansProperty(name='sans_foo', qvalues=x1, profile=y1,
errors=e1, node='SomeNode')
# inplace is False
mask2 = y1 < 0.5
y3 = y1[mask2]
x3 = x1[mask2]
e3 = e1[mask2]
new_sans_prop = sans_prop.filter(mask=mask2)
assert isinstance(new_sans_prop, ps.SansProperty)
assert sans_prop is not new_sans_prop
assert new_sans_prop.node == 'SomeNode'
assert all(new_sans_prop.profile < .5)
assert len(new_sans_prop.profile == new_sans_prop.errors)
assert len(new_sans_prop.profile == new_sans_prop.qvalues)
assert_array_equal(y3, new_sans_prop.profile)
assert_array_equal(e3, new_sans_prop.errors)
assert_array_equal(x3, new_sans_prop.qvalues)


class TestSansProperty(object):

Expand Down

0 comments on commit 6e353cc

Please sign in to comment.