|
1 | 1 | #------Eppinga, M.B., Pucko, C.A., Baudena, M., Beckage, B. & Molofsky, J. (2013) A new method to infer vegetation boundary movement from 'snapshot' data. Ecography, 36, 622-635.
|
2 | 2 |
|
3 |
| -version_Eppinga2013Ecography <- function() numeric_version("0.2.3") |
| 3 | +version_Eppinga2013Ecography <- function() numeric_version("0.2.4") |
4 | 4 |
|
5 | 5 |
|
6 | 6 | #---Eppinga et al. 2013: 'Analytical analysis of vegetation boundary movement' (Fig. 1)
|
@@ -63,110 +63,108 @@ calc_Eppinga2013_stats <- function(FR_dist_T17_veg1, FR_dist_mean_T17_veg1, FR_d
|
63 | 63 | res[["FrontsAdvBeyondOptBoundary"]] <- all(FR_dist_mean_T17_veg1 > 0, FR_dist_mean_T17_veg2 < 0)
|
64 | 64 |
|
65 | 65 |
|
66 |
| - if (res[["FrontsAdvBeyondOptBoundary"]]) { |
67 |
| - if (!is.na(seed)) set.seed(seed) |
68 |
| - |
69 |
| - #---Test if vegetation types have advanced comparably |
70 |
| - # i.e., test if difference between front runner distances (FD) of veg1 vs veg2 is 0: eq. 18 |
71 |
| - data_T17 <- cbind(FR_dist_T17_veg1, -FR_dist_T17_veg2) |
72 |
| - delta_T17 <- apply(data_T17, 1, function(x) x[1] - x[2]) |
73 |
| - res[["FD_mean_T17"]] = mean(delta_T17, na.rm = TRUE) |
74 |
| - res[["FD_sd_T17"]] = sd(delta_T17, na.rm = TRUE) |
75 |
| - res[["FD_mean_m"]] = backtransformation17(res[["FD_mean_T17"]]) |
76 |
| - res[["FD_boots_R"]] <- 1e5L # Eppinga et al. 2013: R = 1e5 |
77 |
| - |
78 |
| - bmds <- list(iid = list("boot" = NULL, "ci" = NULL), |
79 |
| - dep = list("boot" = NULL, "ci" = NULL)) |
| 66 | + if (!is.na(seed)) set.seed(seed) |
| 67 | + |
| 68 | + #---Test if vegetation types have advanced comparably |
| 69 | + # i.e., test if difference between front runner distances (FD) of veg1 vs veg2 is 0: eq. 18 |
| 70 | + data_T17 <- cbind(FR_dist_T17_veg1, -FR_dist_T17_veg2) |
| 71 | + delta_T17 <- apply(data_T17, 1, function(x) x[1] - x[2]) |
| 72 | + res[["FD_mean_T17"]] = mean(delta_T17, na.rm = TRUE) |
| 73 | + res[["FD_sd_T17"]] = sd(delta_T17, na.rm = TRUE) |
| 74 | + res[["FD_mean_m"]] = backtransformation17(res[["FD_mean_T17"]]) |
| 75 | + res[["FD_boots_R"]] <- 1e5L # Eppinga et al. 2013: R = 1e5 |
| 76 | + |
| 77 | + bmds <- list(iid = list("boot" = NULL, "ci" = NULL), |
| 78 | + dep = list("boot" = NULL, "ci" = NULL)) |
| 79 | + |
| 80 | + if (requireNamespace("boot", quietly = TRUE)) { |
| 81 | + #- Bootstrap approach assuming independent data (as used by Eppinga et al. 2013) |
| 82 | + bmds[["iid"]][["boot"]] <- boot::boot(data = data_T17, |
| 83 | + statistic = indexed_mean_of_diffs, |
| 84 | + R = res[["FD_boots_R"]], |
| 85 | + sim = "ordinary", stype = "i", |
| 86 | + parallel = "no") |
80 | 87 |
|
81 |
| - if (requireNamespace("boot", quietly = TRUE)) { |
82 |
| - #- Bootstrap approach assuming independent data (as used by Eppinga et al. 2013) |
83 |
| - bmds[["iid"]][["boot"]] <- boot::boot(data = data_T17, |
84 |
| - statistic = indexed_mean_of_diffs, |
85 |
| - R = res[["FD_boots_R"]], |
86 |
| - sim = "ordinary", stype = "i", |
87 |
| - parallel = "no") |
| 88 | + # bias corrected and accelerated bootstrap (BCa) interval |
| 89 | + bmds[["iid"]][["ci"]] <- boot::boot.ci(bmds[["iid"]][["boot"]], |
| 90 | + conf = c(0.95, 0.99, 0.999), type = "bca") |
| 91 | + } else { |
| 92 | + warning("Package 'boot' not installed: 'calc_Eppinga2013_stats' will not estimate iid bootstrap") |
| 93 | + } |
| 94 | + |
| 95 | + if (requireNamespace("boot", quietly = TRUE) && requireNamespace("np", quietly = TRUE)) { |
| 96 | + # Stationary block bootstrap (Politis, D. N., and J. P. Romano. 1994. The Stationary Bootstrap. Journal of the American Statistical Association 89:1303-1313.) |
| 97 | + # with optimal mean block length (Patton, A., D. N. Politis, and H. White. 2009. Correction to "Automatic Block-Length Selection for the Dependent Bootstrap" by D. Politis and H. White (vol 23, pg 53, 2004). Econometric Reviews 28:372-375.) |
| 98 | + |
| 99 | + if (anyNA(delta_T17)) { |
| 100 | + # Multiple imputations of time-series data |
| 101 | + # 'np::b.star' calls acf() and ccf() with default value for 'na.action', i.e., 'na.fail' |
| 102 | + # 'np::b.star' fails if anyNA(data); data contain NAs if a transect row has no cells of a vegetation type |
| 103 | + if (requireNamespace("Amelia", quietly = TRUE)) { |
| 104 | + im_T17 <- cbind(seq_len(nrow(data_T17)), data_T17) |
| 105 | + am_T17 <- Amelia::amelia(im_T17, m = 10, ts = 1, splinetime = 6, |
| 106 | + p2s = 0, parallel = "no") |
88 | 107 |
|
89 |
| - # bias corrected and accelerated bootstrap (BCa) interval |
90 |
| - bmds[["iid"]][["ci"]] <- boot::boot.ci(bmds[["iid"]][["boot"]], |
91 |
| - conf = c(0.95, 0.99, 0.999), type = "bca") |
92 |
| - } else { |
93 |
| - warning("Package 'boot' not installed: 'calc_Eppinga2013_stats' will not estimate iid bootstrap") |
94 |
| - } |
95 |
| - |
96 |
| - if (requireNamespace("boot", quietly = TRUE) && requireNamespace("np", quietly = TRUE)) { |
97 |
| - # Stationary block bootstrap (Politis, D. N., and J. P. Romano. 1994. The Stationary Bootstrap. Journal of the American Statistical Association 89:1303-1313.) |
98 |
| - # with optimal mean block length (Patton, A., D. N. Politis, and H. White. 2009. Correction to "Automatic Block-Length Selection for the Dependent Bootstrap" by D. Politis and H. White (vol 23, pg 53, 2004). Econometric Reviews 28:372-375.) |
99 |
| - |
100 |
| - if (anyNA(delta_T17)) { |
101 |
| - # Multiple imputations of time-series data |
102 |
| - # 'np::b.star' calls acf() and ccf() with default value for 'na.action', i.e., 'na.fail' |
103 |
| - # 'np::b.star' fails if anyNA(data); data contain NAs if a transect row has no cells of a vegetation type |
104 |
| - if (requireNamespace("Amelia", quietly = TRUE)) { |
105 |
| - im_T17 <- cbind(seq_len(nrow(data_T17)), data_T17) |
106 |
| - am_T17 <- Amelia::amelia(im_T17, m = 10, ts = 1, splinetime = 6, |
107 |
| - p2s = 0, parallel = "no") |
108 |
| - |
109 |
| - est_BstarSB <- mean(sapply(am_T17$imputations, function(x) |
110 |
| - np::b.star(apply(x, 1, function(x) x[2] - x[3]), round = TRUE)[, "BstarSB"])) |
111 |
| - } else { |
112 |
| - warning("Package 'Amelia' not installed: 'calc_Eppinga2013_stats' cannot estimate optimal block-length for dependent bootstrap with missing data; poor approximation based on complete-cases used instead") |
113 |
| - est_BstarSB <- np::b.star(delta_T17[complete.cases(delta_T17)], round = TRUE)[, "BstarSB"] |
114 |
| - } |
| 108 | + est_BstarSB <- mean(sapply(am_T17$imputations, function(x) |
| 109 | + np::b.star(apply(x, 1, function(x) x[2] - x[3]), round = TRUE)[, "BstarSB"])) |
115 | 110 | } else {
|
116 |
| - est_BstarSB <- np::b.star(delta_T17, round = TRUE)[, "BstarSB"] |
| 111 | + warning("Package 'Amelia' not installed: 'calc_Eppinga2013_stats' cannot estimate optimal block-length for dependent bootstrap with missing data; poor approximation based on complete-cases used instead") |
| 112 | + est_BstarSB <- np::b.star(delta_T17[complete.cases(delta_T17)], round = TRUE)[, "BstarSB"] |
117 | 113 | }
|
118 |
| - |
119 |
| - res[["FD_depboot_bstar"]] <- min(nrow(data_T17), est_BstarSB) |
120 |
| - bmds[["dep"]][["boot"]] <- boot::tsboot(tseries = data_T17, |
121 |
| - statistic = indexed_mean_of_diffs, |
122 |
| - R = res[["FD_boots_R"]], |
123 |
| - sim = "geom", l = res[["FD_depboot_bstar"]], endcorr = TRUE, n.sim = nrow(data_T17), |
124 |
| - orig.t = TRUE, parallel = "no") |
125 |
| - |
126 |
| - # BCa and studentized CI don't apply for tsboot objects; use instead percentile method |
127 |
| - bmds[["dep"]][["ci"]] <- boot::boot.ci(bmds[["dep"]][["boot"]], |
128 |
| - conf = c(0.95, 0.99, 0.999), type = "perc") |
129 | 114 | } else {
|
130 |
| - warning("Package 'boot' and/or 'np' not installed: 'calc_Eppinga2013_stats' will not estimate dependent bootstrap") |
| 115 | + est_BstarSB <- np::b.star(delta_T17, round = TRUE)[, "BstarSB"] |
131 | 116 | }
|
| 117 | + |
| 118 | + res[["FD_depboot_bstar"]] <- min(nrow(data_T17), est_BstarSB) |
| 119 | + bmds[["dep"]][["boot"]] <- boot::tsboot(tseries = data_T17, |
| 120 | + statistic = indexed_mean_of_diffs, |
| 121 | + R = res[["FD_boots_R"]], |
| 122 | + sim = "geom", l = res[["FD_depboot_bstar"]], endcorr = TRUE, n.sim = nrow(data_T17), |
| 123 | + orig.t = TRUE, parallel = "no") |
| 124 | + |
| 125 | + # BCa and studentized CI don't apply for tsboot objects; use instead percentile method |
| 126 | + bmds[["dep"]][["ci"]] <- boot::boot.ci(bmds[["dep"]][["boot"]], |
| 127 | + conf = c(0.95, 0.99, 0.999), type = "perc") |
| 128 | + } else { |
| 129 | + warning("Package 'boot' and/or 'np' not installed: 'calc_Eppinga2013_stats' will not estimate dependent bootstrap") |
| 130 | + } |
132 | 131 |
|
133 |
| - # Extract bootstrap data |
134 |
| - ptol <- sqrt(.Machine$double.eps) |
135 |
| - ntol <- -sqrt(.Machine$double.neg.eps) |
136 |
| - for (ib in names(bmds)) if (!is.null(bmds[[ib]][["boot"]])) { |
137 |
| - res[[paste0("FD_", ib, "boot_mean")]] = mean(bmds[[ib]][["boot"]]$t, na.rm = TRUE) |
138 |
| - res[[paste0("FD_", ib, "boot_bias")]] <- res[[paste0("FD_", ib, "boot_mean")]] - res[["FD_mean_T17"]] |
139 |
| - res[[paste0("FD_", ib, "boot_se")]] <- as.numeric(sqrt(var(bmds[[ib]][["boot"]]$t, na.rm = TRUE))) |
140 |
| - |
141 |
| - # Test approach 1a: Is 0 contained in ci? |
142 |
| - i_item <- 4 # the i-th item in the list that is returned by boot::boot.ci |
143 |
| - res[[paste0("FD_", ib, "boot_ci_type")]] <- which(names(bmds[[ib]][["ci"]])[i_item] == boot_ci_types) |
144 |
| - conf <- bmds[[ib]][["ci"]][[i_item]] |
145 |
| - pid <- !(as.integer(apply(conf[, 4:5], 1, function(x) sum(x > ptol))) == 1) |
146 |
| - res[[paste0("FD_", ib, "boot_ci0_p")]] <- 1 - if (sum(pid) > 0) max(conf[pid, "conf"]) else 0 # this represents steps of 1, 0.05, 0.01, and 0.001 as upper bound of the p-value |
147 |
| - |
148 |
| - # Test approach 1b: Calculate p-value (for H0: diff = 0); eq. 19 |
149 |
| - # Direct interpretation of eq. 19: sum(Heaviside(abs(bmdiid$t) + abs(bmdiid$t0) - abs(bmdiid$t + bmdiid$t0))) / bmdiid$R |
150 |
| - res[[paste0("FD_", ib, "boot_freq_p")]] <- (if (bmds[[ib]][["boot"]]$t0 > ptol) sum(bmds[[ib]][["boot"]]$t <= ptol) else if (bmds[[ib]][["boot"]]$t0 < ntol) sum(bmds[[ib]][["boot"]]$t >= ntol) else bmds[[ib]][["boot"]]$R) / bmds[[ib]][["boot"]]$R |
151 |
| - } |
| 132 | + # Extract bootstrap data |
| 133 | + ptol <- sqrt(.Machine$double.eps) |
| 134 | + ntol <- -sqrt(.Machine$double.neg.eps) |
| 135 | + for (ib in names(bmds)) if (!is.null(bmds[[ib]][["boot"]])) { |
| 136 | + res[[paste0("FD_", ib, "boot_mean")]] = mean(bmds[[ib]][["boot"]]$t, na.rm = TRUE) |
| 137 | + res[[paste0("FD_", ib, "boot_bias")]] <- res[[paste0("FD_", ib, "boot_mean")]] - res[["FD_mean_T17"]] |
| 138 | + res[[paste0("FD_", ib, "boot_se")]] <- as.numeric(sqrt(var(bmds[[ib]][["boot"]]$t, na.rm = TRUE))) |
| 139 | + |
| 140 | + # Test approach 1a: Is 0 contained in ci? |
| 141 | + i_item <- 4 # the i-th item in the list that is returned by boot::boot.ci |
| 142 | + res[[paste0("FD_", ib, "boot_ci_type")]] <- which(names(bmds[[ib]][["ci"]])[i_item] == boot_ci_types) |
| 143 | + conf <- bmds[[ib]][["ci"]][[i_item]] |
| 144 | + pid <- !(as.integer(apply(conf[, 4:5], 1, function(x) sum(x > ptol))) == 1) |
| 145 | + res[[paste0("FD_", ib, "boot_ci0_p")]] <- 1 - if (sum(pid) > 0) max(conf[pid, "conf"]) else 0 # this represents steps of 1, 0.05, 0.01, and 0.001 as upper bound of the p-value |
| 146 | + |
| 147 | + # Test approach 1b: Calculate p-value (for H0: diff = 0); eq. 19 |
| 148 | + # Direct interpretation of eq. 19: sum(Heaviside(abs(bmdiid$t) + abs(bmdiid$t0) - abs(bmdiid$t + bmdiid$t0))) / bmdiid$R |
| 149 | + res[[paste0("FD_", ib, "boot_freq_p")]] <- (if (bmds[[ib]][["boot"]]$t0 > ptol) sum(bmds[[ib]][["boot"]]$t <= ptol) else if (bmds[[ib]][["boot"]]$t0 < ntol) sum(bmds[[ib]][["boot"]]$t >= ntol) else bmds[[ib]][["boot"]]$R) / bmds[[ib]][["boot"]]$R |
| 150 | + } |
152 | 151 |
|
153 |
| - |
154 |
| - if (requireNamespace("coin", quietly = TRUE)) { |
155 |
| - # Test approach 2: exact Wilcoxon signed rank test (with Pratt correction of zeros) |
156 |
| - wsrt <- coin::wilcoxsign_test(FR_dist_T17_veg1 ~ FR_dist_T17_veg2, distribution = "exact", alternative = "two.sided") |
157 |
| - res[["FD_WSRT_Z"]] <- as.numeric(coin::statistic(wsrt, type = "test")) |
158 |
| - res[["FD_WSRT_p"]] <- coin::pvalue(wsrt) |
159 |
| - res[["FD_WSRT_midp"]] <- coin::midpvalue(wsrt) |
160 |
| - } else { |
161 |
| - warning("Package 'coin' not installed: 'calc_Eppinga2013_stats' will not calculate Wilcoxon signed rank test") |
162 |
| - } |
163 |
| - |
164 |
| - #---Retrospective power: Eppinga et al. 2013: eq. 20 |
165 |
| - tau <- 0.2 #effect size |
166 |
| - n <- sum(complete.cases(data_T17)) |
167 |
| - tcrit <- qt(0.95, df = n) #95% confidence |
168 |
| - res[["FD_retro_power"]] <- 1 - (1/2 * (1 + erf((tcrit - tau * sqrt(n) / res[["FD_sd_T17"]]) / (sqrt(2) * res[["FD_sd_T17"]])))) |
| 152 | + |
| 153 | + if (requireNamespace("coin", quietly = TRUE)) { |
| 154 | + # Test approach 2: exact Wilcoxon signed rank test (with Pratt correction of zeros) |
| 155 | + wsrt <- coin::wilcoxsign_test(FR_dist_T17_veg1 ~ FR_dist_T17_veg2, distribution = "exact", alternative = "two.sided") |
| 156 | + res[["FD_WSRT_Z"]] <- as.numeric(coin::statistic(wsrt, type = "test")) |
| 157 | + res[["FD_WSRT_p"]] <- coin::pvalue(wsrt) |
| 158 | + res[["FD_WSRT_midp"]] <- coin::midpvalue(wsrt) |
| 159 | + } else { |
| 160 | + warning("Package 'coin' not installed: 'calc_Eppinga2013_stats' will not calculate Wilcoxon signed rank test") |
169 | 161 | }
|
| 162 | + |
| 163 | + #---Retrospective power: Eppinga et al. 2013: eq. 20 |
| 164 | + tau <- 0.2 #effect size |
| 165 | + n <- sum(complete.cases(data_T17)) |
| 166 | + tcrit <- qt(0.95, df = n) #95% confidence |
| 167 | + res[["FD_retro_power"]] <- 1 - (1/2 * (1 + erf((tcrit - tau * sqrt(n) / res[["FD_sd_T17"]]) / (sqrt(2) * res[["FD_sd_T17"]])))) |
170 | 168 |
|
171 | 169 | res
|
172 | 170 | }
|
|
0 commit comments