The 2019/2020 Australian bushfires had a devastating impact on the natural landscape, threatening our native biodiversity. More than ever, decision makers need curated, open access biodiversity data to help respond effectively to future bushfires.
Our team at the Atlas of Living Australia (ALA) has been working with Invertebrates Australia and CSIRO National Research Collections team to collate biodiversity datasets that can be used for off-the-shelf bushfire assessments. The two datasets contain data on Australian taxonomic groups that are often overlooked and severely affected during bushfires: invertebrates (insects, molluscs, spiders) and vascular plants.
We are thrilled to announce that these datasets are available from CSIRO’s data access portal!
This post expands on our last post about using convex and alpha hulls to visualise distributions of data-deficient species.
Here, we show you how to compute a new form of spatial polygon — a concave hull — and use it to represent a species’ range and to calculate the overlap with fire-burned areas (range overlap). Unlike convex hulls, concave hulls have the added flexibility to adjust their tightness to the data (or concavity). This flexibility allows more accurate estimation of species ranges, making it a useful approach to rapidly assess how natural disasters like bush fires affected biodiversity.
Download data
First we will load the R packages we need:
# install.packages("pacman")
pacman::p_load(tidyverse, here, rmapshaper, sf, ggpointdensity, viridis, ozmaps, concaveman, cowplot, patchwork)Next, we will go to the Data Access Portal to download the invertebrate and vascular plant datasets.
Click on the Files tab (under the main title), then the Download button (in the top right corner), and select Download all files as Zip archive. Save this zip file in a local folder inside your current R project and be sure to unzip it.
Now we can read the curated datasets into R:
# Invertebrates data
inverts <- read_csv(here("your_directory_name", "invertebrate.data.03.2023.csv"))
# Vascular plants data
vplants <- read_csv(here("your_directory_name", "vascularplant.data.03.2023.csv")) |>
rename(latitude = latitude_used, # Rename coordinate variables for consistency
longitude = longitude_used)Overview of data
Both datasets are based on studies that investigated the impact of the Black Summer bushfires and are designed to support future modelling and impact assessments. The invertebrate dataset spans across Australia, whereas the vascular plant dataset is restricted to South-Eastern Australia and contains only species where more than 50% of their range was affected by the 2019/2020 fires.
Summary of Invertebrate and Vascular Plant Data
| Taxonomic Group | Classes | Families | Species | Records |
|---|---|---|---|---|
| Invertebrates | 46 | 2,044 | 44,146 | 300,987 |
| Vascular Plants | 7 | 76 | 896 | 41,572 |
A total of 44,146 invertebrate species and 896 species of vascular plants are represented in these datasets. Below, we’ve attempted to show the geographic range of these data. So much data makes it challenging to visualise all data points and concave hulls at once. As such, we created the concave hull maps (left) below by randomly selecting one invertebrate species from each class and one plant species from each family.
Code
# Identify species that have more than 4 observations
more_than_4_obs <- inverts |>
group_by(scientific_name) |>
summarise(n_obs = n()) |>
filter(n_obs > 4) |>
pull(scientific_name)
# Subset species with more than 4 observations and appear on mainland Australia + Tasmania
inverts_subset <- inverts |>
filter(scientific_name %in% more_than_4_obs) |>
filter(latitude < -10, latitude >= -45,
longitude >= 113, longitude <= 155) |>
select(scientific_name:family, longitude, latitude)
# Nest occurrence data
inverts_nest <- inverts_subset |>
nest(coords = c(longitude, latitude))
# Subset a random species from each class
set.seed(123) # Set seed so we all get the same results
subset <- inverts_nest |>
group_by(class) |>
slice_sample(n = 1)
# Convert coordinates into sf object and compute concave hulls as list columns.
subset_concave <- subset |>
mutate(points_sf = map(.x = coords,
~ st_as_sf(.x, coords = c("longitude", "latitude"),
crs = 4326)),
concave_sf = map(points_sf,
~ concaveman(.x)))
# Unnest the concave hull list column
subset_concave <- subset_concave |>
select(scientific_name:family, concave_sf) |>
unnest(cols = c(concave_sf)) |>
ungroup() |>
st_sf(crs = 4326)
# Retrieve Australia polygon
aus <- st_transform(ozmap_country, 4326)
# Plotting spatial distributions
inverts_concave <- ggplot() +
geom_sf(data = aus, colour = "black", fill = NA) +
geom_sf(data = subset_concave, fill = "#609966", alpha = 0.2, lwd = 0) +
coord_sf(xlim = c(110, 155)) +
theme_void()
# Create plot showing overlapping points
inverts_points_map <- ggplot() +
geom_pointdensity(data = inverts_subset,
mapping = aes(x = longitude,
y = latitude)) +
geom_sf(data = aus, colour = "white", fill = NA) +
scale_color_viridis(option = "E", begin = 0.1) +
coord_sf(xlim = c(110, 155)) +
guides(alpha = "none",
colour = guide_colorbar(title = "Number of \noverlapping points")) +
theme_void() +
theme(legend.position = "bottom",
legend.margin = margin(0, 0, 0, 0),
legend.box.margin = margin(0, 0, 0, 0),
legend.justification = "left"
)
inverts_concave + inverts_points_map + plot_annotation(title = "Invertebrate Dataset") Code
# Identify species that have more than 4 observations
more_than_4_obs_plants <- vplants |>
group_by(scientific_name) |>
summarise(n_obs = n()) |>
filter(n_obs > 4) |>
pull(scientific_name)
# Subset species with more than 4 observations and appear on mainland Australia + Tasmaina
vplant_subset <- vplants |>
filter(scientific_name %in% more_than_4_obs_plants) |>
filter(latitude < -10, latitude >= -45,
longitude >= 113, longitude <= 155) |>
select(species, class:genus, longitude, latitude)
# Nest occurrence data
vplant_nest <- vplant_subset |>
nest(coords = c(longitude, latitude))
# Subset a random species from each family
set.seed(123) # Set seed so we all get the same results
plant_subset <- vplant_nest |>
group_by(family) |>
slice_sample(n = 1)
# Compute concave hulls
pl_subset_concave <- plant_subset |>
mutate(points_sf = map(.x = coords,
~ st_as_sf(.x, coords = c("longitude", "latitude"),
crs = 4326)),
concave_sf = map(points_sf,
~ concaveman(.x)))
# Unnest the data
pl_subset_concave <- pl_subset_concave |>
select(species:family, concave_sf) |>
unnest(cols = c(concave_sf)) |>
st_as_sf(crs = 4326)
# Plotting spatial distributions
plant_concave <- ggplot() +
geom_sf(data = aus, colour = "black", fill = NA) +
geom_sf(data = pl_subset_concave, fill = "#609966", colour = NA, alpha = 0.15, lwd = 0) +
coord_sf(xlim = c(140, 158),
ylim = c(-23, -43)) +
theme_void()
# Create plot showing overlapping points
plants_points_map <- ggplot() +
geom_pointdensity(data = vplant_subset,
mapping = aes(x = longitude,
y = latitude)) +
geom_sf(data = aus, colour = "black", fill = NA) +
scale_color_viridis(option = "E", begin = 0.1) +
coord_sf(xlim = c(140, 158),
ylim = c(-23, -43)) +
guides(alpha = "none",
colour = guide_colorbar(title = "Number of \noverlapping points")) +
theme_void() +
theme(legend.position = "bottom",
legend.margin = margin(0, 0, 0, 0),
legend.box.margin = margin(0, 0, 0, 0),
legend.justification = "left"
)
plant_concave + plants_points_map + plot_annotation(title = "Vascular Plant Dataset")Pre-cleaning
Let’s use these datasets to calculate concave hulls and range overlaps with burned regions. One benefit of using these curated datasets is that they do not contain any duplicates or missing values. This makes data cleaning an easier job!
However, there are still a few steps we need to do before computing concave hulls:
Remove data-deficient species
First, we need to filter out any data-deficient species with fewer than 4 data points because concave hulls are best estimated with at least 4 data points. To do this, we’ll calculate the number of observations for each species, then identify which ones have more than 4 records. Using this list of species, we can extract their data.
more_than_4_obs <- inverts |>
group_by(scientific_name) |>
summarise(n_obs = n()) |>
filter(n_obs > 4) |>
pull(scientific_name)
more_than_4_obs |> head()[1] "Aaaaba fossicollis" "Aaaaba nodosus" "Aades cultratus"
[4] "Aades griseatus" "Aaroniella rawlingsi" "Abantiades latipennis"
inverts_subset <- inverts |>
filter(scientific_name %in% more_than_4_obs)Restrict data to mainland Australia and Tasmania
The invertebrate dataset includes records on offshore islands which can drastically skew the shape of a species’ concave hull. For the purpose of this post, we will filter these out and only use records that occur on mainland Australia and Tasmania.
subset_mainland <- inverts_subset |>
filter(latitude < -10, latitude >= -45,
longitude >= 113, longitude <= 155) |>
select(scientific_name:family, longitude, latitude)List columns and nesting occurrence data
For the majority of calculations in this post, we will be making use of list columns, a very useful data structure for iterative analyses. You can think of a list column as mini data frames nested within a column by a grouping variable.
In this case we are nesting the coordinate data by species, which will make operations at the species level more efficient.
inverts_nest <- subset_mainland |>
nest(coords = c(longitude, latitude))
inverts_nest |>
print(n = 6)# A tibble: 16,347 × 4
scientific_name class family coords
<chr> <chr> <chr> <list>
1 Aaaaba fossicollis Insecta Buprestidae <tibble [12 × 2]>
2 Aaaaba nodosus Insecta Buprestidae <tibble [16 × 2]>
3 Aades cultratus Insecta Curculionidae <tibble [20 × 2]>
4 Aades griseatus Insecta Curculionidae <tibble [5 × 2]>
5 Aaroniella rawlingsi Insecta Philotarsidae <tibble [35 × 2]>
6 Abantiades latipennis Insecta Hepialidae <tibble [10 × 2]>
# ℹ 16,341 more rows
You can inspect elements in the list column like this:
inverts_nest |>
pluck("coords", 1) |> # 1 refers to the first element of the list column
print(n = 6)# A tibble: 12 × 2
longitude latitude
<dbl> <dbl>
1 153. -28.4
2 151. -33.8
3 153. -31.0
4 146. -37.8
5 153. -30.3
6 151. -33.8
# ℹ 6 more rows
The biggest change with working with list columns is that you have to iterate across each element. To do this, we will use various functions from the {purrr} package for the next calculation steps.
Species range overlap with fire-burned areas
Get fire layer
Shapefiles for the 2019-2020 fire season are available through the National Indicative Aggregated Fire Extent Dataset from the Department of Climate Change, Energy, the Environment and Water.
Click on Download Data (near the top of the page), then click on NIAFED_v20200623.zip to download the zip file. Save the zip file in your project folder and unzip to retrieve the shapefiles.
Now we can read the shapefile into R and set the projection to EPSG:4326. To speed up the computation of concave hulls and overlaps, we will remove elevation values and simplify the edges of the fire POLYGON.
fire <- st_read(here("your_directory_name", "NIAFED_20190701_20200622_v20200623.shp")) |>
st_transform(crs = 4326) |>
st_zm() |> # Remove Z or M values
ms_simplify() # Simplify edges of the fire layerChoose taxonomic group
While it is possible to calculate range overlap with burned areas for all species in the dataset, it can take a lot of memory and processing time. Instead, we will demonstrate our workflow with — the bee family (Apidae) — as a working example.
# Filter invertebrate data to Apidae
bees <- inverts_nest |>
filter(family == "Apidae") Compute concave hull
In the next steps, we will work through the coordinate data for each species iteratively using map.
We will transform each species’ coordinates into an sf object using st_as_sf(), then compute the concave hulls with the concaveman() function. You can adjust the tightness of the hull boundary around a set of points using the concavity argument - the smaller the value, the tighter the hull. We’ve wrapped mutate() around these steps so the output will become variables in our tibble.
bees_concave <- bees |>
mutate(points_sf = map(.x = coords,
~ st_as_sf(.x, coords = c("longitude", "latitude"), # Set as sf object
crs = 4326) |>
rename(points = geometry)), # Rename geometry variable to something intuitive
concave_sf = map(points_sf,
~ concaveman(.x, concavity = 2) |> # Compute concave hulls
rename(concave = polygons)) # Rename geometry variable to something intuitive
)
bees_concave |> print(n = 6)# A tibble: 54 × 6
scientific_name class family coords points_sf concave_sf
<chr> <chr> <chr> <list> <list> <list>
1 Amegilla (Asaropoda) bombiformis Insecta Apidae <tibble> <sf> <sf>
2 Amegilla (Asaropoda) calva Insecta Apidae <tibble> <sf> <sf>
3 Amegilla (Asaropoda) dawsoni Insecta Apidae <tibble> <sf> <sf>
4 Amegilla (Asaropoda) paracalva Insecta Apidae <tibble> <sf> <sf>
5 Amegilla (Asaropoda) rhodoscymna Insecta Apidae <tibble> <sf> <sf>
6 Amegilla (Notomegilla) aeruginosa Insecta Apidae <tibble> <sf> <sf>
# ℹ 48 more rows
Compute range overlap and descriptive statistics
To compute range overlaps, we need to set our geometry calculations to assume the Earth is flat and not spherical by setting sf_use_s2(FALSE). This may be a limitation of the method but it still gives us a good approximation.
# Disable spherical geometry
sf_use_s2(FALSE) Using st_intersection(), we can identify the overlap between each species’ concave hull with fire-burned areas. We can then use st_area() to calculate the area (m2) of overlap and convert it into a percentage of each species’ original range so that all species are comparable.
Using possibly() with our map() functions allows the calculations to return NA for species that did not overlap with burned areas. Once calculations are complete, we will un-nest the variables: overlap_area and percent_overlap, so they appear as regular columns in our tibble.
# Calculate range overlap
bees_overlap <- bees_concave |>
mutate(
overlap_sf = map(concave_sf,
possibly(~ st_intersection(fire, .x) |> select(-Id) |> rename(overlap = geometry))), # Identify overlap
overlap_area = map(overlap_sf,
possibly( ~ st_area(.x))), # Calculate area
percent_overlap = map2(.x = overlap_area,
.y = concave_sf,
possibly( ~ (.x / st_area(.y)) * 100))) |> # Calculate percentage
unnest(cols = c(overlap_area, percent_overlap)) # Unnest the area and percentage columns
bees_overlap |> print(n = 6)# A tibble: 23 × 9
scientific_name class family coords points_sf concave_sf overlap_sf
<chr> <chr> <chr> <list> <list> <list> <list>
1 Amegilla (Asaropoda) bo… Inse… Apidae <tibble> <sf> <sf> <sf>
2 Amegilla (Asaropoda) da… Inse… Apidae <tibble> <sf> <sf> <sf>
3 Amegilla (Asaropoda) pa… Inse… Apidae <tibble> <sf> <sf> <sf>
4 Amegilla (Zonamegilla) … Inse… Apidae <tibble> <sf> <sf> <sf>
5 Austroplebeia cassiae Inse… Apidae <tibble> <sf> <sf> <sf>
6 Braunsapis clarissima Inse… Apidae <tibble> <sf> <sf> <sf>
# ℹ 17 more rows
# ℹ 2 more variables: overlap_area [m^2], percent_overlap [1]
Rank species by fire impact
Next, we will take the top 3 species with the highest percentage range overlap with fire-burned areas (percent_overlap) for our data visualisation. The top 3 species include a stingless bee, a reed bee, and a carpenter bee.
top3 <- bees_overlap |>
slice_max(order_by = percent_overlap,
n = 3)
top3# A tibble: 3 × 9
scientific_name class family coords points_sf concave_sf overlap_sf
<chr> <chr> <chr> <list> <list> <list> <list>
1 Austroplebeia cassiae Inse… Apidae <tibble> <sf> <sf> <sf>
2 Exoneura (Exoneura) hac… Inse… Apidae <tibble> <sf> <sf> <sf>
3 Xylocopa (Koptortosoma)… Inse… Apidae <tibble> <sf> <sf> <sf>
# ℹ 2 more variables: overlap_area [m^2], percent_overlap [1]
Make map
We will now select the variables we need and un-nest the relevant ones (points_sf, concave_sf, overlap_sf) for plotting - this gives us everything we need to create our maps!
bee_map_data <- top3 |>
select(scientific_name, points_sf, concave_sf, overlap_sf) |>
unnest(cols = c(points_sf, concave_sf, overlap_sf))
bee_map_data |> print(n = 6)# A tibble: 31 × 4
scientific_name points concave
<chr> <POINT [°]> <POLYGON [°]>
1 Austroplebeia cassiae (143.45 -14.08) ((143.27 -14.08, 143.28 -14.05, 142.92 …
2 Austroplebeia cassiae (143.27 -14.08) ((143.27 -14.08, 143.28 -14.05, 142.92 …
3 Austroplebeia cassiae (143.32 -14.1) ((143.27 -14.08, 143.28 -14.05, 142.92 …
4 Austroplebeia cassiae (145.13 -15.67) ((143.27 -14.08, 143.28 -14.05, 142.92 …
5 Austroplebeia cassiae (143.28 -14.05) ((143.27 -14.08, 143.28 -14.05, 142.92 …
6 Austroplebeia cassiae (142.92 -13.42) ((143.27 -14.08, 143.28 -14.05, 142.92 …
# ℹ 25 more rows
# ℹ 1 more variable: overlap <MULTIPOLYGON [°]>
Create the base map
Let’s create our base map with the outline of Australia and the fire-burned area. You can see a majority of burnt areas are located in Northern Australia and the South-East coast.
# Retrieve Australia polygon
aus <- st_transform(ozmap_country, 4326)
base_map <- ggplot() +
geom_sf(data = aus, colour = "black", fill = "white") +
geom_sf(data = fire, fill = "#FEC3A6", colour = "#FEC3A6") +
theme_void()Add species range overlap
Now we can add the range overlap of our three bee species. We use geometry within aes to specify which variable from bee_map_data we want to plot.
main_map <- base_map +
geom_sf(data = bee_map_data,
aes(geometry = concave,
colour = scientific_name,
fill = scientific_name),
size = 1.5, alpha = 0.1) +
geom_sf(data = bee_map_data,
aes(geometry = overlap),
colour = "#FF925C", fill = "#FF925C") + # To highlight area of overlap
geom_sf(data = bee_map_data,
aes(geometry = points,
colour = scientific_name),
size = 1) +
scale_colour_manual(values = c("#023E50", "#7B8A6A", "#3C908E")) +
scale_fill_manual(values = c("#023E50", "#7B8A6A", "#3C908E")) +
guides(colour = guide_legend(override.aes = list(alpha = 1))) +
coord_sf(xlim = c(128, 158)) +
theme(legend.title= element_blank(),
legend.position = "bottom") Create inset map
The navy blue hull in the top right corner of Australia is very small (Austroplebeia cassiae), so we will make an enlarged inset map so we can see it clearer.
inset <- main_map +
coord_sf(
xlim = c(142.8 , 145.3),
ylim = c(-15.9, -13.25),
expand = FALSE
) +
theme_void() +
theme(legend.position = "none",
panel.border = element_rect(colour = "black", fill = NA, linewidth = 0.2))We’ll also draw a box around the area of interest in the enlarged map.
main_bbox <- main_map +
geom_rect(aes(xmin = 142.8, xmax = 145.3,
ymin = -15.9, ymax = -13.25),
colour = "black",
fill = NA,
lwd = 0.2) Arrange map components
Finally, we can arrange our base map and inset together for our final map!
This map shows the three bee species with the highest percentage overlap with fire-burned areas. Two of these bee species are located in Northern Australia and one is located in South-Eastern Australia.
combined_map <- ggdraw(main_bbox) +
draw_plot(inset,
x = 0.52, y = 0.63,
width =0.45, height = 0.3)
combined_mapBonus: Vascular plants
We repeated the same workflow with the vascular plant dataset and created a map of range overlap with burned areas for the genus Daviesia.
Commonly known as bitterpeas, Daviesia comprises plants pollinated by reed bees (Exoneura), which are featured in the bee map above.
Left: Daviesa buxifolia (Betty and Don Wood CC BY 3.0), Middle: Daviesa nova-anglica (Janeteveh CC BY-NC 4.0)), Right: Daviesa suaveolens (Crisp, M.D. CC BY 3.0))
Code
# Extract candidate genus
daviesia <- vplant_nest |>
filter(genus == "Daviesia")
# Compute concave hulls
daviesia_concave <- daviesia |>
mutate(points_sf = map(.x = coords,
~ st_as_sf(.x, coords = c("longitude", "latitude"),
crs = 4326)),
concave_sf = map(points_sf,
~ concaveman(.x)))
# Compute range overlap and descriptive statistics and select
daviesia_overlap <- daviesia_concave |>
mutate(overlap_sf = map(concave_sf,
possibly(~ st_intersection(fire, .x) |> select(-Id))),
overlap_area = map(overlap_sf,
possibly(~ st_area(.x))),
percent_overlap = map2(.x = overlap_area,
.y = concave_sf,
possibly(~ (.x / st_area(.y))*100))) |>
unnest(cols = c(overlap_area, percent_overlap))
## Prepare for plotting and rename variables
daviesia_map_data <- daviesia_overlap |>
select(species, overlap_area, percent_overlap, points_sf, concave_sf, overlap_sf) |>
unnest() |>
rename(points = geometry,
concave = polygons,
overlap = geometry1)
## Create main map reusing base_map from above
daviesia_main_map <- base_map +
geom_sf(data = daviesia_map_data,
aes(geometry = concave,
colour = species,
fill = species),
size = 1.5, alpha = 0.005) +
geom_sf(data = daviesia_map_data,
aes(geometry = overlap),
colour = "#FF925C", fill = "#FF925C") +
geom_sf(data = daviesia_map_data,
aes(geometry = points,
colour = species),
size = 0.9) +
scale_colour_manual(values = c("#023E50", "#7B8A6A", "#3C908E" )) +
scale_fill_manual(values = c("#023E50", "#7B8A6A", "#3C908E")) +
guides(colour = guide_legend(override.aes = list(alpha = 1))) +
coord_sf(xlim = c(140, 158),
ylim = c(-23, -43)) +
theme(legend.title= element_blank(),
legend.position = "bottom")
# Inset 1
daviesia_inset_1 <- daviesia_main_map +
coord_sf(
xlim = c(145.5 , 150.3),
ylim = c(-35, -37.95),
expand = FALSE
) +
theme_void() +
theme(legend.position = "none",
panel.border = element_rect(colour = "black", fill = NA, linewidth = 0.3))
# Inset 2
daviesia_inset_2 <- daviesia_main_map +
coord_sf(
xlim = c(151.55 , 152.75),
ylim = c(-28.6, -31.25),
expand = FALSE
) +
theme_void() +
theme(legend.position = "none",
panel.border = element_rect(colour = "black", fill = NA, linewidth = 0.3))
# Drawing the inset boxes on main map
daviesia_bbox <- daviesia_main_map +
geom_rect(aes(xmin = 145.5, xmax = 150.3, # Inset 1
ymin = -35, ymax = -37.95),
colour = "black",
fill = NA, linewidth = 0.2) +
geom_rect(aes(xmin = 151.55, xmax = 152.75, # Inset 2
ymin = -28.6, ymax = -31.25),
colour = "black",
fill = NA, linewidth = 0.2)
# Daviesia plot with insets
daviesia_combined <- ggdraw(daviesia_bbox) +
draw_plot(daviesia_inset_1, x = 0.59, y = 0.15,
width = 0.42, height = 0.30) +
draw_plot(daviesia_inset_2, x = 0.52, y = 0.52,
width = 0.5, height = 0.4)
daviesia_combinedFinal thoughts
In natural catastrophes, decision makers have limited time to act. They need ready-to-go data and workflows to assess and manage possible consequences of the catastrophe and any proposed ways to mitigate it. Here, we used curated datasets of Australian invertebrates and vascular plants to illustrate how concave hulls can represent estimate species range and estimate range overlap with natural disasters. We hope our work can aid future assessments of vulnerable species and help prioritise conservation efforts.
Expand for session info
─ Session info ───────────────────────────────────────────────────────────────
setting value
version R version 4.3.2 (2023-10-31 ucrt)
os Windows 10 x64 (build 19045)
system x86_64, mingw32
ui RTerm
language (EN)
collate English_Australia.utf8
ctype English_Australia.utf8
tz Australia/Sydney
date 2024-02-12
pandoc 3.1.1 @ C:/Program Files/RStudio/resources/app/bin/quarto/bin/tools/ (via rmarkdown)
─ Packages ───────────────────────────────────────────────────────────────────
package * version date (UTC) lib source
concaveman * 1.1.0 2020-05-11 [1] CRAN (R 4.3.2)
cowplot * 1.1.1 2020-12-30 [1] CRAN (R 4.3.2)
dplyr * 1.1.4 2023-11-17 [1] CRAN (R 4.3.2)
forcats * 1.0.0 2023-01-29 [1] CRAN (R 4.3.2)
ggplot2 * 3.4.4 2023-10-12 [1] CRAN (R 4.3.1)
ggpointdensity * 0.1.0 2019-08-28 [1] CRAN (R 4.3.2)
gt * 0.10.0 2023-10-07 [1] CRAN (R 4.3.2)
here * 1.0.1 2020-12-13 [1] CRAN (R 4.3.2)
htmltools * 0.5.7 2023-11-03 [1] CRAN (R 4.3.2)
lubridate * 1.9.3 2023-09-27 [1] CRAN (R 4.3.2)
ozmaps * 0.4.5 2021-08-03 [1] CRAN (R 4.3.2)
patchwork * 1.1.3 2023-08-14 [1] CRAN (R 4.3.1)
purrr * 1.0.2 2023-08-10 [1] CRAN (R 4.3.2)
readr * 2.1.4 2023-02-10 [1] CRAN (R 4.3.2)
rmapshaper * 0.5.0 2023-04-11 [1] CRAN (R 4.3.2)
sessioninfo * 1.2.2 2021-12-06 [1] CRAN (R 4.3.2)
sf * 1.0-14 2023-07-11 [1] CRAN (R 4.3.2)
stringr * 1.5.1 2023-11-14 [1] CRAN (R 4.3.2)
tibble * 3.2.1 2023-03-20 [1] CRAN (R 4.3.2)
tidyr * 1.3.0 2023-01-24 [1] CRAN (R 4.3.2)
tidyverse * 2.0.0 2023-02-22 [1] CRAN (R 4.3.2)
viridis * 0.6.4 2023-07-22 [1] CRAN (R 4.3.2)
viridisLite * 0.4.2 2023-05-02 [1] CRAN (R 4.3.1)
[1] C:/Users/KEL329/R-packages
[2] C:/Users/KEL329/AppData/Local/Programs/R/R-4.3.2/library
──────────────────────────────────────────────────────────────────────────────