-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathacdc_simplify.R
145 lines (132 loc) · 8.27 KB
/
acdc_simplify.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
######################################
######################################
#### acdc_simplify()
#' @title Simplify the outputs of the AC/DC algorithms
#' @description This function simplifies the output of \code{\link[flapper]{ac}}, \code{\link[flapper]{dc}} and \code{\link[flapper]{acdc}}, by processing information from the 'archive' elements of an \code{\link[flapper]{acdc_archive-class}} object that hold the results of calls to the workhorse routines. This is especially useful if the algorithm(s) have been applied chunk-wise, in which case the results for each chunk are returned in a list. The function aggregates information across chunks to generate a continuous time series of results and a map of the expected proportion of time steps spent in each grid cell.
#' @param archive An \code{\link[flapper]{acdc_archive-class}} object returned by \code{\link[flapper]{ac}}, \code{\link[flapper]{dc}} or \code{\link[flapper]{acdc}}.
#' @param type A character that defines whether the function should be implemented for the outputs of a call to an AC* algorithm (\code{\link[flapper]{ac}} or \code{\link[flapper]{acdc}}), in which case \code{type = "acs"}, or the DC algorithm (\code{\link[flapper]{dc}}), in which case \code{type = "dc"}.
#' @param mask (optional) A spatial mask (e.g., the argument passed to \code{bathy} in \code{\link[flapper]{ac}}, \code{\link[flapper]{dc}} or \code{\link[flapper]{acdc}}) to mask areas (e.g., land) from the overall map. If implemented, cells in masked areas are assigned NAs rather than a score of 0.
#' @param normalise A logical input that defines whether or not to normalise the overall map so that cell scores sum to one. If \code{normalise = FALSE}, the overall map represents the expected number of time steps spent in each grid cell; if \code{normalise = TRUE}, the overall map represents the expected proportion of time steps spent in each grid cell.
#' @param keep_chunks A logical variable that defines whether or not to retain all chunk-specific information.
#' @return The function returns an object of class \code{\link[flapper]{acdc_record-class}}.
#' @details If the \code{\link[flapper]{ac}}, \code{\link[flapper]{dc}} or \code{\link[flapper]{acdc}} function was implemented step-wise, this function simply extracts the necessary information and re-packages it into an \code{\link[flapper]{acdc_record-class}} object. For a chunk-wise implementation, the function (a) computes the map of where the individual could have spent more or less time by aggregating the chunk-specific maps) and (b) simplifies chunk-specific records into a single contiguous time series, with re-defined time stamps from the start to the end of the time series (for AC* algorithm(s)) to return an \code{\link[flapper]{acdc_record-class}} object.
#' @seealso The AC, DC and ACDC algorithms are implemented by \code{\link[flapper]{ac}}, \code{\link[flapper]{dc}} and \code{\link[flapper]{acdc}}. After simplification, \code{\link[flapper]{acdc_plot_trace}}, \code{\link[flapper]{acdc_plot_record}} and \code{\link[flapper]{acdc_animate_record}} can be implemented to visualise time-specific results.
#' @author Edward Lavender
#' @export
#'
acdc_simplify <- function(archive,
type = c("acs", "dc"),
mask = NULL,
normalise = TRUE,
keep_chunks = FALSE) {
#### Checks
if (!(inherits(archive, "acdc_archive") | !inherits(archive, "acdc_record"))) {
stop("Object of class 'acdc_archive' expected.")
}
if (inherits(archive, "acdc_record")) {
message("class(archive) == 'acdc_record': 'archive' returned unchanged.")
return(archive)
}
type <- match.arg(type)
message("acdc_simplify() implemented for type = '", type, "'.")
#### Define container for outputs
out <- list(map = NULL, record = NULL, time = archive$time, args = archive$args, chunks = NULL, simplify = TRUE)
#### Keep chunk-specific information (unchanged), if requested
if (keep_chunks) out$chunks <- archive$archive
#### Simplify extract outputs the algorithm has only been implemented for a single chunk
if (length(archive$archive) == 1) {
out$map <- archive$archive[[1]]$map
out$record <- archive$archive[[1]]$record
#### Otherwise aggregate information across chunks
} else {
#### Get a list of the cumulative maps from each chunk (to be summed below)
maps <- lapply(archive$archive, function(chunk) chunk$map)
#### Process spatial elements so that 'map_cumulative' elements are carried forward (summed) across chunks, if necessary
try_update_spatial <- TRUE
if (!raster::inMemory(maps[[1]])) {
if (!file.exists(maps[[1]]@file@name)) try_update_spatial <- FALSE
}
if (!is.null(archive$args)) {
if (isTRUE(archive$args$save_record_spatial == 0)) try_update_spatial <- FALSE
}
if (try_update_spatial) {
archive$archive <-
lapply(1:length(archive$archive), function(chunk_id) {
# chunk_id <- 2
folder <- archive$archive[[chunk_id]]
if (chunk_id > 1) {
if (chunk_id == 2) {
maps_for_previous_chunks <- maps[[1]]
} else {
maps_for_previous_chunks <- maps[1:(chunk_id - 1)]
maps_for_previous_chunks <- raster::brick(maps_for_previous_chunks)
maps_for_previous_chunks <- raster::calc(maps_for_previous_chunks, sum, na.rm = TRUE)
}
folder$record <-
lapply(folder$record, function(record_elm) {
record_elm$spatial <-
lapply(record_elm$spatial, function(spatial_elm) {
if (rlang::has_name(spatial_elm, "map_cumulative")) {
spatial_elm$map_cumulative <- sum(spatial_elm$map_cumulative, maps_for_previous_chunks, na.rm = TRUE)
}
return(spatial_elm)
})
return(record_elm)
})
}
return(folder)
})
}
#### Simplify records
out$record <- lapply(archive$archive, function(chunk) chunk$record)
#### Process record time stamps, if necessary
if (type == "acs") {
## Define a dataframe to adjust the time stamps recorded for each chunks
# For chunks 2:n_chunks, we will add the time stamps reached by the previous chunk
# ... up to the current chunk
adjust_timestep <- lapply(out$record, function(chunk_record) {
# chunk_record <- out$record[[1]]
dat <- chunk_record[[length(chunk_record)]]$dat
adjustment <- dat[nrow(dat), c("timestep_cumulative", "timestep_detection")]
return(adjustment)
})
adjust_timestep <- do.call(rbind, adjust_timestep)
adjust_timestep$timestep_cumulative <- cumsum(adjust_timestep$timestep_cumulative)
adjust_timestep$timestep_detection <- cumsum(adjust_timestep$timestep_detection)
## Adjust time stamps and add the chunk to the dataframe for each time stamp
out$record <- lapply(1:length(out$record), function(i) {
chunk_record <- out$record[[i]]
if (i == 1) {
adjustment <- data.frame(timestep_cumulative = 0, timestep_detection = 0)
} else {
adjustment <- adjust_timestep[i - 1, ]
}
chunk_record <- lapply(chunk_record, function(t) {
t$dat$timestep_cumulative <- t$dat$timestep_cumulative + adjustment$timestep_cumulative
t$dat$timestep_detection <- t$dat$timestep_detection + adjustment$timestep_detection
t$dat$chunk <- i
return(t)
})
return(chunk_record)
})
}
#### Sum chunk-specific maps across chunks
if (raster::inMemory(maps[[1]]) | file.exists(maps[[1]]@file@name)) {
out$map <- raster::brick(maps)
out$map <- raster::calc(out$map, sum, na.rm = TRUE)
}
#### Flatten record list across chunks
out$record <- purrr::flatten(out$record)
}
#### Mask and normalise the final map
if (!is.null(out$map)) {
if (raster::inMemory(out$map) | file.exists(out$map@file@name)) {
if (!is.null(mask)) out$map <- raster::mask(out$map, mask)
if (normalise) out$map <- out$map / raster::cellStats(out$map, "sum")
}
}
if (is.null(out$map)) warning("out$map could not be processed.", call. = FALSE, immediate. = TRUE)
#### Return outputs
class(out) <- c(class(out), "acdc_record")
return(out)
}