Groupings are a big emphasis in sftrack
. Structurally they are built with in the same vein as sfc
and sfg
classes in sf
.
To begin an s_group
is a singular grouping. Its whats stored at the row level. A c_grouping
is a collection of s_groups
and exists at the column level. Groups also have an active_group
argument, which turns on and off certain groups for analysis and plotting purposes.
We start by looking at the structure of a c_grouping
library("sftrack")
data("raccoon", package = "sftrack")
#raccoon <- read.csv(system.file("extdata/raccoon_data.csv", package="sftrack"))
list(id = raccoon$animal_id, month = as.POSIXlt(raccoon$timestamp)$mon + 1)
group_list <-
make_c_grouping(x = group_list, active_group = c("id", "month"))
cg1 <-str(cg1)
## c_grouping object
## List of 445
## $ :List of 2
## ..$ id : chr "TTP-058"
## .. [list output truncated]
## ..- attr(*, "class")= chr "s_group"
## [list output truncated]
## - attr(*, "active_group")= chr [1:2] "id" "month"
## - attr(*, "sort_index")= Factor w/ 4 levels "TTP-041_1","TTP-041_2",..: 3 3 3 3 3 3 3 3 3 3 ...
1]] cg1[[
## $id
## [1] "TTP-058"
##
## $month
## [1] "1"
A grouping contains group related information. The id
of the subject/sensor is the lowest level of grouping for the data. Any additional grouping variables are optional.
A c_grouping is simply a collection of s_group
s. The s_group
is where the grouping data is stored, and can be modified at the row level. The s_groups
main job is to store row level grouping information and maintain consistency of the grouping variables.
An s_group is the grouping variables for a single row of data.
You can make an s_group
object using make_s_group()
, and giving it a list with the group variables named. In this example we have a single sensor named ‘TTP_058’ from a raccoon, and an additional grouping variable of month (entered as its numeric interpretation).
All grouping information is converted and stored as a character in the s_group.
make_s_group(list(id = "TTP_058", month = 4))
singlegroup <-str(singlegroup)
## List of 2
## $ id : chr "TTP_058"
## $ month: chr "4"
## - attr(*, "class")= chr "s_group"
Because s_group
s are simply lists, you can edit individual elements in an s_group
singlegroup
## $id
## [1] "TTP_058"
##
## $month
## [1] "4"
1] <- "CJ15"
singlegroup[$month <- "5"
singlegroupstr(singlegroup)
## List of 2
## $ id : chr "CJ15"
## $ month: chr "5"
## - attr(*, "class")= chr "s_group"
C_groupings are a collection of s_groups with the same grouping names and an ‘active_group’ which is a subset of all the available group names.
Similarly to s_group you can make a c_grouping with make_s_group
. The argument x
takes a list where each element is a vector indicating the named groupings as well as a vector of the active groups.
list(id = rep(1:2, 10), year = rep(2020, 10))
group_list <- make_c_grouping(x = group_list, active_group = c("id", "year"))
cg <-str(cg)
## c_grouping object
## List of 20
## $ :List of 2
## ..$ id : chr "1"
## .. [list output truncated]
## ..- attr(*, "class")= chr "s_group"
## [list output truncated]
## - attr(*, "active_group")= chr [1:2] "id" "year"
## - attr(*, "sort_index")= Factor w/ 2 levels "1_2020","2_2020": 1 2 1 2 1 2 1 2 1 2 ...
You can also make a c_grouping by concatenating multiple s_groups. All s_groups
must have the same names or an error is returned.
make_s_group(list(id = 1, year = 2020))
a <- make_s_group(list(id = 1, year = 2021))
b <- make_s_group(list(id = 2, year = 2020))
c <- c(a, b , c) cg <-
## 1_2020 & 1_2021 & 2_2020 has only one relocation
summary(cg)
## 1_2020 1_2021 2_2020
## 1 1 1
## active_group: id, year
## 0
You’ll notice sftrack warns if any grouping combination only has one relocation. This may be relevant for different kinds of analysis where you need more than one point to consider a movement.
You can also combine c_grouping together with c()
. All names and active_groups must be the same.
c(cg,cg)
cg_combine <-summary(cg_combine)
## 1_2020 1_2021 2_2020
## 2 2 2
## active_group: id, year
## 0
You can also edit groupings like a list, but you must replace it with an s_group object.
1] cg[
## [[1]]
## $id
## [1] "1"
##
## $year
## [1] "2020"
1] <- make_s_group(list(id = 3, year = 2019)) cg[
## 1_2021 & 2_2020 & 3_2019 has only one relocation
1] cg[
## [[1]]
## $id
## [1] "3"
##
## $year
## [1] "2019"
And the group names must match the ones in the c_grouping or an error is returned
# Try to add an s_group with a month field when the original group had year instead
try( cg[1] <- make_s_group(list(id = 3, month = 2019)) )
## Error in check_group_names(ret) : Group names do not match
As c_groupings
are stored as lists, it can be difficult to refer to a single group or combination of groupings. This is where the sort_index can come in handy. The sort_index is a factor of the combined active_group variables for each row where paste(id, name1,name2,…,sep=’_’)). The sort index is remade every time the active group changes, and therefore can be used to subset. You can access the sort index using group_labels()
.
list(id = rep(1:2, 10), year = rep(2020, 10))
group_list <- make_c_grouping(x = group_list, active_group = c("id", "year"))
cg <-group_labels(cg)[1:10]
## [1] 1_2020 2_2020 1_2020 2_2020 1_2020 2_2020 1_2020 2_2020 1_2020 2_2020
## Levels: 1_2020 2_2020
# Subsetting a particular sensor from our raccoon data
data("raccoon", package = "sftrack")
$month <- as.POSIXlt(raccoon$timestamp)$mon + 1
raccoon
$time <- as.POSIXct(raccoon$timestamp, tz = "EST")
raccoon c("longitude","latitude")
coords <- list(id = raccoon$animal_id, month = as.POSIXlt(raccoon$timestamp)$mon+1)
group <- "time"
time <-
as_sftraj(data = raccoon, coords = coords, group = group, time = time)
my_sftraj <-head(my_sftraj[group_labels(my_sftraj) %in% c("TTP-058_1"), ])
## Sftraj with 6 features and 12 fields (3 empty geometries)
## Geometry : "geometry" (XY, crs: NA)
## Timestamp : "time" (POSIXct in UTC)
## Grouping : "sft_group" (*id*, *month*)
## -------------------------------
## animal_id latitude longitude timestamp height hdop vdop fix month
## 1 TTP-058 NA NA 2019-01-19 00:02:30 NA 0.0 0.0 NO 1
## 2 TTP-058 26.06945 -80.27906 2019-01-19 01:02:30 7 6.2 3.2 2D 1
## 3 TTP-058 NA NA 2019-01-19 02:02:30 NA 0.0 0.0 NO 1
## 4 TTP-058 NA NA 2019-01-19 03:02:30 NA 0.0 0.0 NO 1
## 5 TTP-058 26.06769 -80.27431 2019-01-19 04:02:30 858 5.1 3.2 2D 1
## 6 TTP-058 26.06867 -80.27930 2019-01-19 05:02:30 350 1.9 3.2 3D 1
## time sft_group geometry
## 1 2019-01-19 00:02:30 (id: TTP-058, month: 1) POINT EMPTY
## 2 2019-01-19 01:02:30 (id: TTP-058, month: 1) POINT (-80.27906 26.06945)
## 3 2019-01-19 02:02:30 (id: TTP-058, month: 1) POINT EMPTY
## 4 2019-01-19 03:02:30 (id: TTP-058, month: 1) POINT EMPTY
## 5 2019-01-19 04:02:30 (id: TTP-058, month: 1) LINESTRING (-80.27431 26.06...
## 6 2019-01-19 05:02:30 (id: TTP-058, month: 1) LINESTRING (-80.2793 26.068...
You can also subset by entering the group label of the group itself in either the c_grouping or the sftrack/sftraj object:
head(cg["1_2020"])
## [[1]]
## $id
## [1] "1"
##
## $year
## [1] "2020"
##
##
## [[2]]
## $id
## [1] "1"
##
## $year
## [1] "2020"
##
##
## [[3]]
## $id
## [1] "1"
##
## $year
## [1] "2020"
##
##
## [[4]]
## $id
## [1] "1"
##
## $year
## [1] "2020"
##
##
## [[5]]
## $id
## [1] "1"
##
## $year
## [1] "2020"
##
##
## [[6]]
## $id
## [1] "1"
##
## $year
## [1] "2020"
my_sftraj["TTP-058_1", ]
sub <-print(sub, 5, 3)
## Sftraj with 207 features and 12 fields (64 empty geometries)
## Geometry : "geometry" (XY, crs: NA)
## Timestamp : "time" (POSIXct in UTC)
## Grouping : "sft_group" (*id*, *month*)
## -------------------------------
## animal_id latitude longitude ... sft_group
## 1 TTP-058 NA NA ... (id: TTP-058, month: 1)
## 2 TTP-058 26.06945 -80.27906 ... (id: TTP-058, month: 1)
## 3 TTP-058 NA NA ... (id: TTP-058, month: 1)
## 4 TTP-058 NA NA ... (id: TTP-058, month: 1)
## 5 TTP-058 26.06769 -80.27431 ... (id: TTP-058, month: 1)
## geometry time
## 1 POINT EMPTY 2019-01-19 00:02:30
## 2 POINT (-80.27906 26.06945) 2019-01-19 01:02:30
## 3 POINT EMPTY 2019-01-19 02:02:30
## 4 POINT EMPTY 2019-01-19 03:02:30
## 5 LINESTRING (-80.27431 26.06... 2019-01-19 04:02:30
You can refer to the levels of the sort index using group_names
, this returns levels() of the sort index.
group_names(cg)
## [1] "1_2020" "2_2020"
The active_group is a simple yet powerful feature. It dictates how your data is grouped for essentially all calculations. It can also be changed on the fly. You can view and change the active group of a column grouping with active_group()
. Once changed, it recalculates the sort_index and in some cases recalculates the geometries.
Active groups can be changed for any sftrack/straj/c_grouping
# sftrack
active_group(my_sftraj)
## [1] "id" "month"
summary(my_sftraj, stats = TRUE)
## group points NAs begin_time end_time length_m
## 1 TTP-041_1 208 0 2019-01-19 00:02:30 2019-01-31 23:02:30 0.0533572052
## 2 TTP-041_2 15 0 2019-02-01 00:02:30 2019-02-01 23:02:07 0.0001556664
## 3 TTP-058_1 207 0 2019-01-19 00:02:30 2019-01-31 23:02:30 0.1779383524
## 4 TTP-058_2 15 0 2019-02-01 00:02:30 2019-02-01 23:02:30 0.0115635197
# change the active group to id only
active_group(my_sftraj) <- c("id")
active_group(my_sftraj)
## [1] "id"
summary(my_sftraj, stats = TRUE)
## group points NAs begin_time end_time length_m
## 1 TTP-041 223 0 2019-01-19 00:02:30 2019-02-01 23:02:07 0.05351287
## 2 TTP-058 222 0 2019-01-19 00:02:30 2019-02-01 23:02:30 0.18950187
# column groupings work the same way
active_group(cg)
## [1] "id" "year"
active_group(cg) <- "id"
active_group(cg)
## [1] "id"