To use abmR, you must first install it from Github using devtools and load the library:
# devtools: install_github("bgoch5/abmR")
# If install gives errors, try running the following:
# Sys.setenv(R_REMOTES_NO_ERRORS_FROM_WARNINGS="true")
library(abmR,quietly=TRUE,warn.conflicts=FALSE)
While this package is still in development, it will be updated frequently, so please be sure to re-install frequently. Installing abmR will also automaticaly install its dependencies, if you don’t already have them installed. These include raster, sp, rgdal, table1, googledrive, swfscMisc, geosphere, kableExtra, and gtsummary, and ggplot.
The modeling functions discussed in section 2 require two input objects to work, which we will discuss here.
Example data that will be used in this Vignette and other package documentation can be downloaded using get_ex_data(). This function will download to your hard drive (to a location specified by your current working directory) the below NDVI raster files, totaling approximately 620 MB. The first time you use this function, you will be directed to your browser and required to sign in to your Google account to connect to the Tidyverse API. If you use the function a second time, you may simply follow the prompts and enter a number corresponding to the previous accounts listed.
# get_ex_data()
Once you have the data downloaded, you must read it into R using commands like the following.
You may instead use your own environmental raster data - NDVI or otherwise - and read it into R in a similar way. Please refer to the file “Aggregating Environmental Rasters.R” on the abmR Github page for an example on how you might create a final rasterstack from a group of NOAA NDVI .nc files.
ndvi_raster_EU=stack("C:/Users/BGOCHANOUR/Documents/GitHub/move-model/Data/2013 NDVI/NDVI_2013_Europe")
ndvi_raster_EU_composite=raster("C:/Users/BGOCHANOUR/Documents/GitHub/move-model/Data/2013 NDVI/NDVI_2013_Europe_composite")
We can now quickly examine our raster data to see if it read in correctly.
The first raster is a 27-layer stack (of which we will plot the first four layers), and the second is a composite raster formed by taking the mean of all layers.
plot(ndvi_raster_EU[[1:4]]) # First four layers of 27 layer RasterStack
plot(ndvi_raster_EU_composite)
This object will be constructed with the function as.species, and contains information on origin longitude (x) and latitude (y) as well as two morphological parameters, morphpar1 and morphpar2 for your agent. x and y are the only required parameters. However, if you choose to specify one of morphpar1 through morphpar2sign, you must also specify all other parameters. See ?as.species for more details.
EU.pop = as.species(x=10,
y=50,
morphpar1=24,
morphpar1mean=16,
morphpar1sd=5,
morphpar1sign="Pos",
morphpar2=12,
morphpar2mean=9,
morphpar2sd=3,
morphpar2sign="Neg")
abmR has two functions for modeling animal movement: moveSIM and energySIM. Both moveSIM and energySIM will output two things: (1) a dataframe of results ($results) and (2) a summary table of input parameters used ($run_params). For more details on how each function works, please see the documentation by using ?moveSIM or ?energySIM.
moveSIMFirst, we will look at moveSIM, which runs basic Brownian/Ornstein-Uhlenbeck agent-based models for multiple replicates. Here, mortality occurs when an agent crosses fail_thresh on n_failures+ 1 consecutive timesteps. For more function details, please consult the documentation: ?moveSIM.
For each timestep, agents can have status “Alive”, “Stopped”, or “Died”. All agents start alive and may stop if, on a particular timestep, there are no non-NA raster values in the search region. This often occurs when agents are searching over an ocean or a large lake, for example. Once an agent stops, they remain stopped for the rest of the run. Similarly, once an agent dies, they retain this status for all subsequent timesteps. All timesteps with agent status “Stopped” or “Died” will have lat/lon=NA, so as to not affect subsequent analyses.
set.seed(1)
EU.move=moveSIM(replicates = 50,
days=27,
env_rast=ndvi_raster_EU,
search_radius=250,
sigma=0.1,
dest_x=999,
dest_y=999,
mot_x=0.7,
mot_y=0.7,
modeled_species=EU.pop,
optimum = 0.7,
direction="S",
write_results=F,
single_rast=F,
mortality = T,
n_failures = 3,
fail_thresh = 0.50)
#> [1] "Agent died"
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Agent died"
#> [1] "Number of agents processed: 5"
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Agent died"
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Number of agents processed: 10"
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Number of agents processed: 15"
#> [1] "Agent died"
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Number of agents processed: 20"
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Agent died"
#> [1] "Agent died"
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Number of agents processed: 25"
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Agent died"
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Number of agents processed: 30"
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Agent died"
#> [1] "Number of agents processed: 35"
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Number of agents processed: 40"
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Number of agents processed: 45"
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Agent died"
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Number of agents processed: 50"
We can simply view our results in the console by typing NA.move or EU.move. However, we have created the function tidy_results() to provide cleaner output. Simply supply your moveSIM() or energySIM() output and specify type=“results” or type=“run_params”, depending on which portion of your output you’d like to display.
Let’s examine our moveSIM() output. Here, we view just the first 10 rows.
tidy_results(EU.move,
type="results",
nrows=10)
| agent_id | day | lon | lat | curr_status | distance | |
|---|---|---|---|---|---|---|
| 1 | Agent_01 | 0 | 10.000 | 50.000 | Alive | NA |
| 2 | Agent_01 | 1 | 8.625 | 48.375 | Alive | 206.705 |
| 3 | Agent_01 | 2 | 8.375 | 45.875 | Alive | 278.942 |
| 4 | Agent_01 | 3 | 9.375 | 45.125 | Alive | 114.271 |
| 5 | Agent_01 | 4 | 8.275 | 44.275 | Alive | 128.562 |
| 6 | Agent_01 | 5 | 8.075 | 44.175 | Alive | 19.454 |
| 7 | Agent_01 | 6 | 4.875 | 43.775 | Alive | 260.174 |
| 8 | Agent_01 | 7 | 1.975 | 42.775 | Alive | 260.049 |
| 9 | Agent_01 | 8 | -0.025 | 42.675 | Alive | 163.929 |
| 10 | Agent_01 | 9 | 2.025 | 42.575 | Alive | 168.278 |
tidy_results(EU.move,
type="run_params")
| Param value | |
|---|---|
| replicates | 50 |
| days | 27 |
| env_raster | env_raster |
| search_radius | 250 |
| sigma | 0.1 |
| dest_x | 999 |
| dest_y | 999 |
| mot_x | 0.7 |
| mot_y | 0.7 |
| modeled_species | EU.pop |
| optimum | 0.7 |
| n_failures | 3 |
| direction | S |
| mortality | TRUE |
| write_results | FALSE |
| single_rast | FALSE |
| missing_pct | 63.9285714285714 |
| mortality_pct | 18 |
energySIMNext, we turn our attention to energySIM(), the more advanced function that simulates agent energy stores. Agents begin with energy=init_energy, and die when they reach energy=0. energy_adj mediates the energy gain or loss for each timestep. The 11 elements of this vector represent energy gain/loss for environmental values in the optimal range (1st element) and within 10, 20, …, 80, 90, and 90+ percent deviation (11th element) of the optimal range. We recommend using the default values for energy_adj unless you have a reason for changing them.
For each timestep, agents can have status “Alive”, “Stopped”, or “Died”. All agents start alive and may stop if, on a particular timestep, there are no non-NA raster values in the search region. This often occurs when agents are searching over an ocean or a large lake, for example. Once an agent stops, they remain stopped for the rest of the run. Similarly, once an agent dies, they retain this status for all subsequent timesteps. All timesteps with agent status “Stopped” or “Died” will have lat/lon=NA, so as to not affect subsequent analyses.
EU.energy = energySIM(replicates = 50,
days=27,
env_rast=ndvi_raster_EU,
search_radius=250,
sigma=0.1,
dest_x=999,
dest_y=999,
mot_x=0.7,
mot_y=0.7,
modeled_species=EU.pop,
optimum_lo = 0.65,
optimum_hi=0.75,
init_energy=100,
direction="S",
write_results=F,
single_rast=F,
mortality = T)
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Number of agents processed: 5"
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Number of agents processed: 10"
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Number of agents processed: 15"
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Number of agents processed: 20"
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Agent died"
#> [1] "Number of agents processed: 25"
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Number of agents processed: 30"
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Number of agents processed: 35"
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Agent died"
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Number of agents processed: 40"
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Number of agents processed: 45"
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Can't find any non-NA cells. Searching over lake or ocean. Agent stopped."
#> [1] "Number of agents processed: 50"
The tidy_results function will also work on energySIM() output. Here, we view just the first 10 rows of our results.
tidy_results(EU.energy,
type="results",
nrows=10)
| agent_id | day | lon | lat | curr_status | energy | delta_energy | distance | |
|---|---|---|---|---|---|---|---|---|
| 1 | Agent_01 | 0 | 10.000 | 50.000 | Alive | 100 | NA | NA |
| 2 | Agent_01 | 1 | 8.875 | 48.125 | Alive | 85 | -15 | 224.268 |
| 3 | Agent_01 | 2 | 8.225 | 45.875 | Alive | 65 | -20 | 255.281 |
| 4 | Agent_01 | 3 | 8.125 | 44.625 | Alive | 90 | 25 | 139.370 |
| 5 | Agent_01 | 4 | 10.775 | 44.225 | Alive | 70 | -20 | 215.321 |
| 6 | Agent_01 | 5 | 11.425 | 44.075 | Alive | 90 | 20 | 54.537 |
| 7 | Agent_01 | 6 | 11.625 | 43.725 | Alive | 100 | 10 | 42.135 |
| 8 | Agent_01 | 7 | NA | NA | Stopped | NA | NA | NA |
| 9 | Agent_01 | 8 | NA | NA | Stopped | NA | NA | NA |
| 10 | Agent_01 | 9 | NA | NA | Stopped | NA | NA | NA |
tidy_results(EU.energy,
type="run_params")
| Param value | |
|---|---|
| replicates | 50 |
| days | 27 |
| env_raster | env_raster |
| search_radius | 250 |
| sigma | 0.1 |
| dest_x | 999 |
| dest_y | 999 |
| mot_x | 0.7 |
| mot_y | 0.7 |
| modeled_species | EU.pop |
| optimum_lo | 0.65 |
| optimum_hi | 0.75 |
| init_energy | 100 |
| direction | S |
| mortality | TRUE |
| write_results | FALSE |
| single_rast | FALSE |
| missing_pct | 63.1428571428571 |
| mortality_pct | 4 |
The output from moveSIM and energySIM are simple dataframes, so one may easily visualize their results using custom codes. However, we have provided moveVIZ and energyVIZ, respectively, to provide pre-prepared solutions for visualizing and summarizing your movement simulation results.
moveVIZ(EU.move,
type="plot",
title="moveSIM European results")
energyVIZ(EU.energy,
type="plot",
title="EnergySIM European results")
moveVIZ(EU.move,
type="summary_table")
| Characteristic | N = 1,4001 |
|---|---|
| lon | 8.4 (3.2, 10.0) |
| Unknown | 895 |
| lat | 44.63 (43.38, 45.98) |
| Unknown | 895 |
| curr_status | |
| Alive | 505 (36%) |
| Died | 132 (9.4%) |
| Stopped | 763 (55%) |
| distance | 204 (113, 241) |
| Unknown | 945 |
|
1
Median (IQR); n (%)
|
|
energyVIZ(EU.energy,
type="summary_table")
| Characteristic | N = 1,4001 |
|---|---|
| lon | 8.3 (4.9, 10.0) |
| Unknown | 884 |
| lat | 44.60 (43.41, 46.03) |
| Unknown | 884 |
| curr_status | |
| Alive | 514 (37%) |
| Died | 31 (2.2%) |
| Stopped | 855 (61%) |
| energy | 85 (75, 100) |
| Unknown | 884 |
| delta_energy | 0 (-15, 10) |
| Unknown | 934 |
| distance | 155 (87, 223) |
| Unknown | 934 |
|
1
Median (IQR); n (%)
|
|
The next two types of output are available only for energySIM() output.
energyVIZ(EU.energy,
type="strat_table")
| -25 (N=5) |
-20 (N=71) |
-15 (N=61) |
-10 (N=41) |
-5 (N=24) |
0 (N=89) |
5 (N=46) |
10 (N=44) |
15 (N=38) |
20 (N=30) |
25 (N=17) |
Overall (N=1400) |
|
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| energy | ||||||||||||
| Mean (SD) | 63.0 (16.8) | 68.1 (13.2) | 71.1 (18.9) | 65.1 (27.1) | 77.3 (21.7) | 93.1 (16.1) | 83.5 (18.6) | 87.7 (15.3) | 84.9 (16.3) | 84.7 (12.7) | 90.0 (8.66) | 82.0 (19.9) |
| Median [Min, Max] | 75.0 [40.0, 75.0] | 65.0 [10.0, 80.0] | 80.0 [10.0, 85.0] | 75.0 [0, 90.0] | 82.5 [10.0, 95.0] | 100 [20.0, 100] | 85.0 [15.0, 100] | 90.0 [25.0, 100] | 85.0 [25.0, 100] | 85.0 [45.0, 100] | 90.0 [70.0, 100] | 85.0 [0, 100] |
| Missing | 0 (0%) | 0 (0%) | 0 (0%) | 0 (0%) | 0 (0%) | 0 (0%) | 0 (0%) | 0 (0%) | 0 (0%) | 0 (0%) | 0 (0%) | 884 (63.1%) |
| day | ||||||||||||
| Mean (SD) | 13.0 (3.24) | 2.52 (2.44) | 4.75 (6.22) | 9.27 (6.38) | 10.5 (7.02) | 9.35 (5.07) | 9.59 (6.96) | 6.98 (5.35) | 7.21 (4.85) | 4.30 (1.49) | 4.47 (2.21) | 13.5 (8.08) |
| Median [Min, Max] | 13.0 [8.00, 17.0] | 2.00 [1.00, 14.0] | 2.00 [1.00, 27.0] | 8.00 [1.00, 23.0] | 9.00 [2.00, 27.0] | 8.00 [3.00, 25.0] | 6.50 [3.00, 26.0] | 5.00 [3.00, 26.0] | 5.00 [3.00, 23.0] | 4.00 [3.00, 8.00] | 3.00 [3.00, 10.0] | 13.5 [0, 27.0] |
| distance | ||||||||||||
| Mean (SD) | 171 (118) | 215 (47.8) | 193 (72.4) | 144 (86.2) | 147 (85.3) | 156 (85.2) | 137 (67.2) | 116 (65.1) | 113 (51.3) | 97.9 (56.0) | 93.4 (44.6) | 153 (79.0) |
| Median [Min, Max] | 166 [38.5, 355] | 227 [9.96, 258] | 218 [16.7, 357] | 148 [16.7, 311] | 163 [5.57, 266] | 152 [11.8, 346] | 146 [27.4, 265] | 91.6 [24.0, 275] | 118 [29.8, 267] | 86.9 [16.3, 238] | 93.6 [6.84, 166] | 155 [5.57, 357] |
| Missing | 0 (0%) | 0 (0%) | 0 (0%) | 0 (0%) | 0 (0%) | 0 (0%) | 0 (0%) | 0 (0%) | 0 (0%) | 0 (0%) | 0 (0%) | 934 (66.7%) |
energyVIZ(EU.energy,
type="gradient")
#> [inverse distance weighted interpolation]
Need help? Have suggestions to make abmR better? If so, please open an issue or pull request on Github (https://github.com/bgoch5/abmr), or drop me an email at ben.gochanour@ou.edu.