Skip to content

Commit a450cd7

Browse files
authored
Merge pull request #186 from Luiskitsu/funcy
test/func: created a test and function for MorphFuncy
2 parents b37383e + b2c46cb commit a450cd7

File tree

3 files changed

+165
-0
lines changed

3 files changed

+165
-0
lines changed

news/morphfuncy.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
**Added:**
2+
3+
* General morph function that applies a user-supplied Python function to the y-coordinates of morph data
4+
5+
**Changed:**
6+
7+
* <news item>
8+
9+
**Deprecated:**
10+
11+
* <news item>
12+
13+
**Removed:**
14+
15+
* <news item>
16+
17+
**Fixed:**
18+
19+
* <news item>
20+
21+
**Security:**
22+
23+
* <news item>

src/diffpy/morph/morphs/morphfuncy.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
from diffpy.morph.morphs.morph import LABEL_GR, LABEL_RA, Morph
2+
3+
4+
class MorphFuncy(Morph):
5+
"""Apply the user-supplied Python function to the y-coordinates of the
6+
morph data"""
7+
8+
# Define input output types
9+
summary = "Apply a Python function to the y-axis data"
10+
xinlabel = LABEL_RA
11+
yinlabel = LABEL_GR
12+
xoutlabel = LABEL_RA
13+
youtlabel = LABEL_GR
14+
15+
def morph(self, x_morph, y_morph, x_target, y_target):
16+
"""General morph function that applies a user-supplied function to the
17+
y-coordinates of morph data to make it align with a target.
18+
19+
Configuration Variables
20+
-----------------------
21+
function: callable
22+
The user-supplied function that applies a transformation to the
23+
y-coordinates of the data.
24+
25+
parameters: dict
26+
A dictionary of parameters to pass to the function.
27+
These parameters are unpacked using **kwargs.
28+
29+
Returns
30+
-------
31+
A tuple (x_morph_out, y_morph_out, x_target_out, y_target_out)
32+
where the target values remain the same and the morph data is
33+
transformed according to the user-specified function and parameters
34+
The morphed data is returned on the same grid as the unmorphed data
35+
36+
Example
37+
-------
38+
Import the funcy morph function:
39+
>>> from diffpy.morph.morphs.morphfuncy import MorphFuncy
40+
41+
Define or import the user-supplied transformation function:
42+
>>> def sine_function(x, y, amplitude, frequency):
43+
>>> return amplitude * np.sin(frequency * x) * y
44+
45+
Provide initial guess for parameters:
46+
>>> parameters = {'amplitude': 2, 'frequency': 2}
47+
48+
Run the funcy morph given input morph array (x_morph, y_morph)
49+
and target array (x_target, y_target):
50+
>>> morph = MorphFuncy()
51+
>>> morph.function = sine_function
52+
>>> morph.parameters = parameters
53+
>>> x_morph_out, y_morph_out, x_target_out, y_target_out = morph.morph(
54+
... x_morph, y_morph, x_target, y_target)
55+
56+
To access parameters from the morph instance:
57+
>>> x_morph_in = morph.x_morph_in
58+
>>> y_morph_in = morph.y_morph_in
59+
>>> x_target_in = morph.x_target_in
60+
>>> y_target_in = morph.y_target_in
61+
>>> parameters_out = morph.parameters
62+
"""
63+
Morph.morph(self, x_morph, y_morph, x_target, y_target)
64+
65+
self.y_morph_out = self.function(
66+
self.x_morph_in, self.y_morph_in, **self.parameters
67+
)
68+
return self.xyallout

tests/test_morphfuncy.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import numpy as np
2+
import pytest
3+
4+
from diffpy.morph.morphs.morphfuncy import MorphFuncy
5+
6+
7+
def sine_function(x, y, amplitude, frequency):
8+
return amplitude * np.sin(frequency * x) * y
9+
10+
11+
def exponential_decay_function(x, y, amplitude, decay_rate):
12+
return amplitude * np.exp(-decay_rate * x) * y
13+
14+
15+
def gaussian_function(x, y, amplitude, mean, sigma):
16+
return amplitude * np.exp(-((x - mean) ** 2) / (2 * sigma**2)) * y
17+
18+
19+
def polynomial_function(x, y, a, b, c):
20+
return (a * x**2 + b * x + c) * y
21+
22+
23+
def logarithmic_function(x, y, scale):
24+
return scale * np.log(1 + x) * y
25+
26+
27+
@pytest.mark.parametrize(
28+
"function, parameters, expected_function",
29+
[
30+
(
31+
sine_function,
32+
{"amplitude": 2, "frequency": 5},
33+
lambda x, y: 2 * np.sin(5 * x) * y,
34+
),
35+
(
36+
exponential_decay_function,
37+
{"amplitude": 5, "decay_rate": 0.1},
38+
lambda x, y: 5 * np.exp(-0.1 * x) * y,
39+
),
40+
(
41+
gaussian_function,
42+
{"amplitude": 1, "mean": 5, "sigma": 1},
43+
lambda x, y: np.exp(-((x - 5) ** 2) / (2 * 1**2)) * y,
44+
),
45+
(
46+
polynomial_function,
47+
{"a": 1, "b": 2, "c": 0},
48+
lambda x, y: (x**2 + 2 * x) * y,
49+
),
50+
(
51+
logarithmic_function,
52+
{"scale": 0.5},
53+
lambda x, y: 0.5 * np.log(1 + x) * y,
54+
),
55+
],
56+
)
57+
def test_funcy(function, parameters, expected_function):
58+
x_morph = np.linspace(0, 10, 101)
59+
y_morph = np.sin(x_morph)
60+
x_target = x_morph.copy()
61+
y_target = y_morph.copy()
62+
x_morph_expected = x_morph
63+
y_morph_expected = expected_function(x_morph, y_morph)
64+
morph = MorphFuncy()
65+
morph.function = function
66+
morph.parameters = parameters
67+
x_morph_actual, y_morph_actual, x_target_actual, y_target_actual = (
68+
morph.morph(x_morph, y_morph, x_target, y_target)
69+
)
70+
71+
assert np.allclose(y_morph_actual, y_morph_expected)
72+
assert np.allclose(x_morph_actual, x_morph_expected)
73+
assert np.allclose(x_target_actual, x_target)
74+
assert np.allclose(y_target_actual, y_target)

0 commit comments

Comments
 (0)