Skip to content

Commit 40374d8

Browse files
Merge pull request #26 from OSIPI/analysis/improvements
Improved plotting and random number generation
2 parents db3f01b + eef8916 commit 40374d8

File tree

5 files changed

+47
-13
lines changed

5 files changed

+47
-13
lines changed

.github/workflows/analysis.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,5 @@ jobs:
6161
f_limited.pdf
6262
Dp_limited.pdf
6363
durations.pdf
64+
curve_plot.pdf
65+
fitted_curves.pdf

conftest.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,20 @@ def save_file(request):
7878
# print(filename)
7979
# filename.unlink(missing_ok=True)
8080
filename = filename.as_posix()
81+
82+
data = data_list(request.config.getoption("--dataFile")) # TODO: clean up this hacky way to get bvalues
83+
[_, bvalues, _] = next(data)
84+
bvalue_string = ["bval_" + str(bvalue) for bvalue in bvalues]
85+
# bvalue_string = ["b_0.0","b_1.0","b_2.0","b_5.0","b_10.0","b_20.0","b_30.0","b_50.0","b_75.0","b_100.0","b_150.0","b_250.0","b_350.0","b_400.0","b_550.0","b_700.0","b_850.0","b_1000.0"]
86+
8187
with open(filename, "w") as csv_file:
8288
writer = csv.writer(csv_file, delimiter=',')
83-
writer.writerow(("Algorithm", "Region", "SNR", "index", "f", "Dp", "D", "f_fitted", "Dp_fitted", "D_fitted"))
89+
writer.writerow(("Algorithm", "Region", "SNR", "index", "f", "Dp", "D", "f_fitted", "Dp_fitted", "D_fitted", *bvalue_string))
90+
yield writer
8491
# writer.writerow(["", datetime.datetime.now()])
85-
return filename
92+
else:
93+
yield None
94+
# return filename
8695

8796
@pytest.fixture(scope="session")
8897
def save_duration_file(request):
@@ -96,8 +105,11 @@ def save_duration_file(request):
96105
with open(filename, "w") as csv_file:
97106
writer = csv.writer(csv_file, delimiter=',')
98107
writer.writerow(("Algorithm", "Region", "SNR", "Duration [us]", "Count"))
108+
yield writer
99109
# writer.writerow(["", datetime.datetime.now()])
100-
return filename
110+
else:
111+
yield None
112+
# return filename
101113

102114
@pytest.fixture(scope="session")
103115
def rtol(request):

tests/IVIMmodels/unit_tests/analyze.r

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ if (runPrediction) {
6565
ivim_decay <- function(f, D, Dp, bvalues) {
6666
return(f*exp(-Dp*bvalues) + (1-f)*exp(-D*bvalues))
6767
}
68+
#TODO: read bvalues from file
6869
bvalues <- c(0.0, 1.0, 2.0, 5.0, 10.0, 20.0, 30.0, 50.0, 75.0, 100.0, 150.0, 250.0, 350.0, 400.0, 550.0, 700.0, 850.0, 1000.0)
6970

7071
generate_curves <- function(data, bvalues, appended_string) {
@@ -82,3 +83,13 @@ data_curves_restricted <- cbind(curves_restricted, signal_fitted=curves_restrict
8283
curve_plot <- ggplot(data_curves_restricted, aes(x=bvalue)) + facet_grid(Region ~ SNR) + geom_line(alpha=0.2, aes(y=signal_fitted, group=interaction(Algorithm, index), color=Algorithm)) + geom_line(aes(y=signal)) + ylim(0, 1) + ylab("Signal (a.u.)")
8384
print(curve_plot)
8485
ggsave("curve_plot.pdf", plot=curve_plot, width = 30, height = 30, units = "cm")
86+
87+
88+
data_points_restricted <- data_restricted[data_restricted$index==0,]
89+
data_points_restricted <- pivot_longer(data_points_restricted, starts_with("bval_"), names_to="bvalue", values_to="fitted_data", names_prefix="bval_", names_transform=list(bvalue = as.numeric))
90+
# data_curves_restricted_idx0 <- data_curves_restricted[data_curves_restricted$index==0,]
91+
data_points_restricted <- cbind(data_curves_restricted[data_curves_restricted$index==0,], fitted_data=data_points_restricted$fitted_data)
92+
# fitted_curves <- ggplot(data_points_restricted, aes(x=bvalue)) + facet_grid(Region ~ SNR) + geom_line(alpha=0.5, aes(y=signal_fitted, group=Algorithm, color=Algorithm)) + geom_point(aes(y=fitted_data, group=Algorithm, color=Algorithm)) + ylim(0, 1) + ylab("Signal (a.u.)")
93+
fitted_curves <- ggplot(data_points_restricted, aes(x=bvalue)) + facet_grid(Region ~ SNR) + geom_line(alpha=0.5, aes(y=signal_fitted, group=Algorithm, color=Algorithm)) + geom_point(aes(y=fitted_data, shape="Data")) + ylim(0, 1) + ylab("Signal (a.u.)") + guides(shape = guide_legend("Fitted Data"))
94+
print(fitted_curves)
95+
ggsave("fitted_curves.pdf", plot=fitted_curves, width = 30, height = 30, units = "cm")

tests/IVIMmodels/unit_tests/test_ivim_synthetic.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,18 @@
1616
@pytest.mark.slow
1717
def test_generated(ivim_algorithm, ivim_data, SNR, rtol, atol, fit_count, rician_noise, save_file, save_duration_file, use_prior):
1818
# assert save_file == "test"
19-
random.seed(42)
19+
rng = np.random.RandomState(42)
20+
# random.seed(42)
2021
S0 = 1
21-
gd = GenerateData()
22+
gd = GenerateData(rng=rng)
2223
name, bvals, data = ivim_data
2324
D = data["D"]
2425
f = data["f"]
2526
Dp = data["Dp"]
2627
fit = OsipiBase(algorithm=ivim_algorithm)
2728
# here is a prior
2829
if use_prior and hasattr(fit, "accepts_priors") and fit.accepts_priors:
29-
prior = [np.random.normal(D, D/3, 10), np.random.normal(f, f/3, 10), np.random.normal(Dp, Dp/3, 10), np.random.normal(1, 1/3, 10)]
30+
prior = [rng.normal(D, D/3, 10), rng.normal(f, f/3, 10), rng.normal(Dp, Dp/3, 10), rng.normal(1, 1/3, 10)]
3031
# prior = [np.repeat(D, 10)+np.random.normal(0,D/3,np.shape(np.repeat(D, 10))), np.repeat(f, 10)+np.random.normal(0,f/3,np.shape(np.repeat(D, 10))), np.repeat(Dp, 10)+np.random.normal(0,Dp/3,np.shape(np.repeat(D, 10))),np.repeat(np.ones_like(Dp), 10)+np.random.normal(0,1/3,np.shape(np.repeat(D, 10)))] # D, f, D*
3132
fit.initialize(prior_in=prior)
3233
time_delta = datetime.timedelta()
@@ -38,11 +39,13 @@ def test_generated(ivim_algorithm, ivim_data, SNR, rtol, atol, fit_count, rician
3839
start_time = datetime.datetime.now()
3940
[f_fit, Dp_fit, D_fit] = fit.osipi_fit(signal, bvals) #, prior_in=prior
4041
time_delta += datetime.datetime.now() - start_time
41-
if save_file:
42-
save_results(save_file, ivim_algorithm, name, SNR, idx, [f, Dp, D], [f_fit, Dp_fit, D_fit])
42+
if save_file is not None:
43+
save_file.writerow([ivim_algorithm, name, SNR, idx, f, Dp, D, f_fit, Dp_fit, D_fit, *signal])
44+
# save_results(save_file, ivim_algorithm, name, SNR, idx, [f, Dp, D], [f_fit, Dp_fit, D_fit])
4345
npt.assert_allclose([f, Dp, D], [f_fit, Dp_fit, D_fit], rtol, atol)
44-
if save_duration_file:
45-
save_duration(save_duration_file, ivim_algorithm, name, SNR, time_delta, fit_count)
46+
if save_duration_file is not None:
47+
save_duration_file.writerow([ivim_algorithm, name, SNR, time_delta/datetime.timedelta(microseconds=1), fit_count])
48+
# save_duration(save_duration_file, ivim_algorithm, name, SNR, time_delta, fit_count)
4649

4750

4851
def save_results(filename, algorithm, name, SNR, idx, truth, fit):

utilities/data_simulation/GenerateData.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,23 @@ class GenerateData:
44
"""
55
Generate exponential and linear data
66
"""
7-
def __init__(self, operator=None):
7+
def __init__(self, operator=None, rng=None):
88
"""
99
Parameters
1010
----------
1111
operator : numpy or torch
1212
Must provide mathematical operators
13+
rng : random number generator
14+
Must provide normal() operator
1315
"""
1416
if operator is None:
1517
self._op = np
1618
else:
1719
self._op = operator
20+
if rng is None:
21+
self._rng = self._op.random
22+
else:
23+
self._rng = rng
1824

1925
def ivim_signal(self, D, Dp, f, S0, bvalues, snr=None, rician_noise=False):
2026
"""
@@ -91,9 +97,9 @@ def add_noise(self, real_signal, snr=None, rician_noise=True, imag_signal=None):
9197
real_noise = self._op.zeros_like(real_signal)
9298
imag_noise = self._op.zeros_like(real_signal)
9399
if snr is not None:
94-
real_noise = self._op.random.normal(0, 1 / snr, real_signal.shape)
100+
real_noise = self._rng.normal(0, 1 / snr, real_signal.shape)
95101
if rician_noise:
96-
noisy_data = self._op.sqrt(self._op.power(real_signal, 2) + self._op.power(imag_signal, 2))
102+
imag_noise = self._rng.normal(0, 1 / snr, imag_signal.shape)
97103
noisy_data = self._op.sqrt(self._op.power(real_signal + real_noise, 2) + self._op.power(imag_signal + imag_noise, 2))
98104
return noisy_data
99105

0 commit comments

Comments
 (0)