Skip to content

Commit db84baf

Browse files
Add bootstrapping to regression routine
1 parent ae3ca9e commit db84baf

File tree

4 files changed

+158
-24
lines changed

4 files changed

+158
-24
lines changed

examples/bootstrap_regression_example.f90

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ program example
77
character, parameter :: tab = achar(9)
88
character, parameter :: nl = new_line('a')
99
integer(int32) :: i
10-
real(real64) :: x(31), y(31), coeffs(4), ymodeled(31), residuals(31)
10+
real(real64) :: x(31), y(31), coeffs(4), ymodeled(31), residuals(31), bias(4)
1111
type(regression_statistics) :: stats(4)
1212
type(bootstrap_regression_statistics) :: bstats(4)
1313

@@ -102,7 +102,7 @@ program example
102102

103103
! Fit the data using the bootstrap approach
104104
call bootstrap_linear_least_squares(3, .true., x, y, coeffs, ymodeled, &
105-
residuals, stats = bstats, nsamples = 5000)
105+
residuals, stats = bstats, nsamples = 100000, bias = bias)
106106

107107
! Display the results
108108
print '(A)', new_line('') // "***** BOOTSTRAP METHOD *****"
@@ -114,12 +114,13 @@ program example
114114

115115
! Illustrate the statistics for each coefficient
116116
do i = 1, size(bstats)
117-
print '(AI0AF6.3AF6.3AF6.3AF6.3AF6.3)', &
117+
print '(AI0AF6.3AF6.3AF6.3AF6.3AF6.3AF6.3)', &
118118
"Coefficient ", i, ":" // nl // &
119119
tab // "Standard Error: ", bstats(i)%standard_error, nl // &
120120
tab // "Upper Confidence Interval: ", bstats(i)%upper_confidence_interval, nl // &
121121
tab // "Lower Confidence Interval: ", bstats(i)%lower_confidence_interval, nl // &
122122
tab // "T-Statistic: ", bstats(i)%t_statistic, nl // &
123-
tab // "P-Value: ", bstats(i)%probability
123+
tab // "P-Value: ", bstats(i)%probability, nl // &
124+
tab // "Bias: ", bias(i)
124125
end do
125126
end program

src/bootstrapping.f90

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
submodule (fstats) bootstrapping
2+
use fstats_errors
3+
use omp_lib
24
implicit none
35

46
! REFERENCES:
@@ -28,13 +30,13 @@ module subroutine bs_linear_least_squares_real64(order, intercept, x, y, &
2830
! Parameters
2931
real(real64), parameter :: zero = 0.0d0
3032
real(real64), parameter :: half = 0.5d0
31-
real(real64), parameter :: one = 1.0d0
33+
real(real64), parameter :: p05 = 5.0d-2
3234

3335
! Local Variables
3436
integer(int32) :: i, j, i1, i2, n, ns, nc, ncoeffs, flag
3537
real(real64) :: eps, alph, ms
36-
real(real64), allocatable, dimension(:,:) :: allcoeffs, allresid, allf, &
37-
ally
38+
real(real64), allocatable, dimension(:) :: fLocal, yLocal, rLocal
39+
real(real64), allocatable, dimension(:,:) :: allcoeffs
3840
type(t_distribution) :: dist
3941
class(errors), pointer :: errmgr
4042
type(errors), target :: deferr
@@ -53,7 +55,7 @@ module subroutine bs_linear_least_squares_real64(order, intercept, x, y, &
5355
if (present(alpha)) then
5456
alph = alpha
5557
else
56-
alph = 0.05d0
58+
alph = p05
5759
end if
5860
n = size(x)
5961
ncoeffs = order + 1
@@ -63,37 +65,54 @@ module subroutine bs_linear_least_squares_real64(order, intercept, x, y, &
6365
if (intercept) nc = nc + 1
6466
dist%dof = real(ns - nc)
6567

66-
! TO DO:
67-
! - Input Checking
68-
! - Verify the size of BIAS & ensure it's the appropriate size along with
69-
! COEFF
70-
7168
! Compute the fit
7269
call linear_least_squares(order, intercept, x, y, coeffs, &
7370
ymod, resid, alpha = alph, err = errmgr)
7471
if (errmgr%has_error_occurred()) return
7572

7673
! Memory Allocations
77-
allocate(allcoeffs(ncoeffs, ns), allresid(n, ns), allf(n, ns), ally(n, ns - 1), &
78-
stat = flag)
74+
allocate(allcoeffs(ncoeffs, ns), source = zero, stat = flag)
7975
if (flag /= 0) then
80-
! TO DO: Memory Error
76+
call report_memory_error(errmgr, "bs_linear_least_squares_real64", flag)
77+
return
8178
end if
8279
allcoeffs(:,1) = coeffs
8380

84-
! Establish the epsilon term - to do, come up with a better resampling approach
81+
! Establish the epsilon term
8582
eps = standard_deviation(y) / sqrt(real(n))
8683
call random_init(.false., .true.)
87-
call random_number(ally)
88-
ally = eps * (ally - half)
8984

90-
! Cycle over each data set
85+
! Cycle over each data set and perform the fit
86+
#ifdef USEOPENMP
87+
!$OMP PARALLEL DO PRIVATE(fLocal, yLocal, rLocal) SHARED(allcoeffs)
88+
do i = 2, ns
89+
! Allocate local arrays on a per-thread basis
90+
if (.not.allocated(fLocal)) allocate(fLocal(n))
91+
if (.not.allocated(yLocal)) allocate(yLocal(n))
92+
if (.not.allocated(rLocal)) allocate(rLocal(n))
93+
94+
! Compute a random data set
95+
call random_number(yLocal)
96+
yLocal = eps * (yLocal - half) + y
97+
98+
! Compute the fit of the perturbed data set
99+
call linear_least_squares(order, intercept, x, yLocal, &
100+
allcoeffs(:,i), fLocal, rLocal, alpha = alph)
101+
end do
102+
!$OMP END PARALLEL DO
103+
#else
104+
! OpenMP is not available - run in a serial manner
105+
allocate(fLocal(n), yLocal(n), rLocal(n))
91106
do i = 2, ns
107+
! Compute a random data set
108+
call random_number(yLocal)
109+
yLocal = eps * (yLocal - half) + y
110+
92111
! Compute the fit of the perturbed data set
93-
ally(:,i-1) = ally(:,i-1) + y
94-
call linear_least_squares(order, intercept, x, ally(:,i-1), &
95-
allcoeffs(:,i), allf(:,i), allresid(:,i), alpha = alph)
112+
call linear_least_squares(order, intercept, x, yLocal, &
113+
allcoeffs(:,i), fLocal, rLocal, alpha = alph)
96114
end do
115+
#endif
97116

98117
! Perform statistics calculations, if needed
99118
if (present(stats)) then
@@ -122,9 +141,14 @@ module subroutine bs_linear_least_squares_real64(order, intercept, x, y, &
122141
! Compute the bias for each parameter, if needed
123142
if (present(bias)) then
124143
! Verify the size of the array
144+
if (size(bias) /= ncoeffs) then
145+
call report_array_size_error(errmgr, &
146+
"bs_linear_least_squares_real64", "bias", ncoeffs, size(bias))
147+
return
148+
end if
125149

126150
! Perform the calculations
127-
do i = 1, nc
151+
do i = 1, ncoeffs
128152
bias(i) = coeffs(i) - mean(allcoeffs(i,:))
129153
end do
130154
end if

tests/fstats_regression_tests.f90

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,5 +222,111 @@ function regression_test_1() result(rst)
222222
end if
223223
end function
224224

225+
! ------------------------------------------------------------------------------
226+
function bootstrap_regression_test_1() result(rst)
227+
! Arguments
228+
logical :: rst
229+
230+
! Variables
231+
integer(int32), parameter :: npts = 31
232+
real(real64), parameter :: tol = 1.0d-6
233+
real(real64), parameter :: c1a(2) = [0.570009521158539d0, 0.486564710761349d0]
234+
real(real64), parameter :: slope2 = 0.7668972622d0
235+
real(real64) :: x(npts), y(npts), ymod(npts), resid(npts), c1(2), c2(2)
236+
237+
! Initialization
238+
rst = .true.
239+
x = [ &
240+
0.0d0, &
241+
0.1d0, &
242+
0.2d0, &
243+
0.3d0, &
244+
0.4d0, &
245+
0.5d0, &
246+
0.6d0, &
247+
0.7d0, &
248+
0.8d0, &
249+
0.9d0, &
250+
1.0d0, &
251+
1.1d0, &
252+
1.2d0, &
253+
1.3d0, &
254+
1.4d0, &
255+
1.5d0, &
256+
1.6d0, &
257+
1.7d0, &
258+
1.8d0, &
259+
1.9d0, &
260+
2.0d0, &
261+
2.1d0, &
262+
2.2d0, &
263+
2.3d0, &
264+
2.4d0, &
265+
2.5d0, &
266+
2.6d0, &
267+
2.7d0, &
268+
2.8d0, &
269+
2.9d0, &
270+
3.0d0 &
271+
]
272+
y = [ &
273+
0.577855138260449d0, &
274+
0.614883095604222d0, &
275+
0.633891127488559d0, &
276+
0.718405829701721d0, &
277+
0.753668502759107d0, &
278+
0.814967857310145d0, &
279+
0.861870996499704d0, &
280+
0.925100533744381d0, &
281+
0.947038018520063d0, &
282+
1.025198043343280d0, &
283+
1.042142354497610d0, &
284+
1.121528566784440d0, &
285+
1.177570314994070d0, &
286+
1.229237567525370d0, &
287+
1.261114062593870d0, &
288+
1.296408162551430d0, &
289+
1.394353657051120d0, &
290+
1.367144391560370d0, &
291+
1.428164431435150d0, &
292+
1.548944935073270d0, &
293+
1.505100149282990d0, &
294+
1.560701023751520d0, &
295+
1.609113012481530d0, &
296+
1.663687366875500d0, &
297+
1.707149545456870d0, &
298+
1.800935947618110d0, &
299+
1.847819988906440d0, &
300+
1.884242821675810d0, &
301+
1.966174239373140d0, &
302+
1.977005266443110d0, &
303+
2.034137257154140d0 &
304+
]
305+
306+
! Fit the model - linear model with intercept
307+
call bootstrap_linear_least_squares(1, .true., x, y, c1, ymod, resid)
308+
309+
if (.not.is_equal(c1(1), c1a(1), tol)) then
310+
rst = .false.
311+
print '(A)', "TEST FAILED: Regression Test 1 - 1"
312+
end if
313+
if (.not.is_equal(c1(2), c1a(2), tol)) then
314+
rst = .false.
315+
print '(A)', "TEST FAILED: Regression Test 1 - 2"
316+
end if
317+
318+
! Fit the model - linear model without intercept
319+
call bootstrap_linear_least_squares(1, .false., x, y, c2, ymod, resid)
320+
321+
if (.not.is_equal(c2(1), 0.0d0)) then
322+
rst = .false.
323+
print '(A)', "TEST FAILED: Regression Test 1 - 3"
324+
end if
325+
if (.not.is_equal(c2(2), slope2)) then
326+
rst = .false.
327+
print '(A)', "TEST FAILED: Regression Test 1 - 4"
328+
end if
329+
end function
330+
225331
! ------------------------------------------------------------------------------
226332
end module

tests/fstats_tests.f90

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ program tests
7979
local = regression_test_1()
8080
if (.not.local) overall = .false.
8181

82+
local = bootstrap_regression_test_1()
83+
if (.not.local) overall = .false.
84+
8285
! Experimental Design
8386
local = get_full_matrix_size_test_1()
8487
if (.not.local) overall = .false.

0 commit comments

Comments
 (0)