Skip to content

Commit f1792bb

Browse files
author
dschlaep
committedApr 24, 2016
version 0.1.0
still a lot of work to do, but code is working and complete for now
1 parent aeac195 commit f1792bb

7 files changed

+216
-178
lines changed
 

‎DESCRIPTION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: ecotoner
22
Title: ecotoner - ecotones / ecological boundaries across environmental gradients
3-
Version: 0.0.0.9051
3+
Version: 0.1.0
44
Authors@R: person("Daniel", "Schlaepfer", email = "daniel.schlaepfer@unibas.ch", role = c("aut", "cre"))
55
Description: ecotoner locates ecotones and extracts geographic and geometric measures of ecotones
66
Depends:

‎R/classes_input.R

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ GridInfo <- setClass("GridInfo",
1818
prototype = list(crs = sp::CRS(NA_character_), res_m = NA_real_, longlat = NA, origin = c(NA_real_, NA_real_), rotation = NA)
1919
)
2020

21-
# TODO(drs): validify that units of slot 'res_m' are correct
21+
# TODO(drs): validate that units of slot 'res_m' are correct
2222

2323
#' An S4-class to represent the information of the input raster grids
2424
#'

‎R/locate_flowpath.R

-1
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,6 @@ calc.Identify_GoodvsBadMigration <- function(patches4, tally, paths, paths_succe
260260
patchIDs_crossedByPaths <- lapply(paths, FUN = function(p) unique(na.omit(raster::extract(patches4, p)))) #entries in order of downstream patches4 crossed
261261
#patches4 that connect directly with the x-border
262262
patchIDs_BadMigration <- tally[tally[, "PatchCoversGoodBorder_TF"] == 1 | (tally[, "SuccessfulFlowpaths_N"] > 0 & tally[, "GoodMigration_N"] > 0 & tally[, "BadMigration_N"] == 0), "PatchID"]
263-
#TODO(drs): shouldn't this be 'patchIDs_GoodMigration'??
264263

265264
if (end_toLeft) {
266265
#Check network of flowpaths (originating from every y-border cell) and decide for each patch whether to be excluded or not

‎R/measure_eppinga.R

+94-96
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#------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.
22

3-
version_Eppinga2013Ecography <- function() numeric_version("0.2.3")
3+
version_Eppinga2013Ecography <- function() numeric_version("0.2.4")
44

55

66
#---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
6363
res[["FrontsAdvBeyondOptBoundary"]] <- all(FR_dist_mean_T17_veg1 > 0, FR_dist_mean_T17_veg2 < 0)
6464

6565

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")
8087

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")
88107

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"]))
115110
} 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"]
117113
}
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")
129114
} 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"]
131116
}
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+
}
132131

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+
}
152151

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")
169161
}
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"]]))))
170168

171169
res
172170
}

‎README.md

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Ecotoner
1+
# Ecotoner -- locates ecotones and extracts geographic and geometric measures of ecotones
22

33
We haven’t really published the code yet nor prepared it for sharing (though through our use of github made it openly accessible), it is actively and gradually being developed by the Schlaepfer lab, and there is no manual - we cannot give you individual support in setting up and running the code except if we agreed on a collaboration or similar agreement.
44

@@ -9,8 +9,11 @@ There is no graphical user interface, help pages and available documentation may
99
If you make use of this model, please cite appropriate references, and we would like to hear about your particular study (especially a copy of any published paper).
1010

1111

12-
Some recent references
13-
*
12+
Some references of the implemented methods
13+
* Danz, N.P., Frelich, L.E., Reich, P.B. & Niemi, G.J. (2012) Do vegetation boundaries display smooth or abrupt spatial transitions along environmental gradients? Evidence from the prairie–forest biome boundary of historic Minnesota, USA. Journal of Vegetation Science, 24, 1129-1140.
14+
* 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.
15+
* Gastner, M., Oborny, B., Zimmermann, D.K. & Pruessner, G. (2009) Transition from connected to fragmented vegetation across an environmental gradient: scaling laws in ecotone geometry. The American Naturalist, 174, E23-E39.
16+
1417

1518

1619

@@ -28,12 +31,12 @@ help(package = "ecotoner") # The index page with some of the functions (which ar
2831
### Using the package
2932
I added the main code which I use to locate and measure my transects (producing the data for later analysis), as demo to the package. I also added data so that it can run as a small contained demo ‘example’ (if the flag do.demo is set to TRUE). You could run this code directly with the demo() function, but this is probably not convenient:
3033
```
31-
demo("BSE-TF_EcotonesAtSlope_v10", package = "ecotoner")
34+
demo("BSE-TF_ecotoner_LocateAndMeasure", package = "ecotoner")
3235
```
3336

3437
Instead, the following command should open my main code in your text editor (at least on unix-alike systems) for easier inspection:
3538
```
36-
system2("open", file.path(system.file("demo", package = "ecotoner"), "BSE-TF_EcotonesAtSlope_v10.R"))
39+
system2("open", file.path(system.file("demo", package = "ecotoner"), "BSE-TF_ecotoner_LocateAndMeasure.R"))
3740
```
3841

3942

‎demo/00Index

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
BSE-TF_EcotonesAtSlope_v10 Code to locate, measure, and analyse big sagebrush -- temperate forest ecotones in the western USA.
1+
BSE-TF_ecotoner_LocateAndMeasure Code to locate and measure big sagebrush--temperate forest ecotones in the western USA.

0 commit comments

Comments
 (0)
Please sign in to comment.