What connections are for
Many movement datasets need to record relationships between the values of an identity or temporal variable. The most common case is a skeleton — edges between keypoints — but the same shape works for a social network (edges between individuals) or a session graph.
aniframe stores this as a single metadata field, connections, plus a small family of functions for managing it. This article covers that field and those functions.
For the rest of the metadata see Metadata on an aniframe; for the data-column structure see The aniframe data structure.
Storage shape
connections is a named list keyed by the variable the relationships describe. Each entry is a 2-column from/to tibble. The order of from / to is preserved as supplied, so downstream consumers can treat the table as either directed (parent → child kinematic chains) or undirected (skeleton edges).
fish <- example_aniframe(n_keypoints = 7) |>
set_connections(
list(
c("head", "neck"),
c("neck", "shoulder_right"),
c("neck", "shoulder_left"),
c("shoulder_right", "abdomen"),
c("shoulder_left", "abdomen")
)
)
get_connections(fish, "keypoint")
#> # A tibble: 5 × 2
#> from to
#> <chr> <chr>
#> 1 head neck
#> 2 neck shoulder_right
#> 3 neck shoulder_left
#> 4 shoulder_right abdomen
#> 5 shoulder_left abdomenThe four functions
set_connections(data, connections, variable = "keypoint") # replace
get_connections(data, variable = NULL) # full list, or one entry
add_connections(data, from, to, variable = "keypoint") # append (vectorised)
remove_connections(data, from, to, variable = "keypoint") # exact match
set_connections()
Replaces the connection table for a single variable. Three input shapes are accepted — pick whichever is most natural:
# Implicit by position: element[1] is `from`, element[2] is `to`
set_connections(data, list(c("head", "neck"), c("neck", "shoulder_right")))
# Named within each pair — use when direction should be obvious in the call
set_connections(data, list(c(from = "head", to = "neck")))
# 2-column data.frame — useful when connections come from another tabular source
set_connections(
data,
data.frame(
from = c("head", "neck"),
to = c("neck", "shoulder_right")
)
)
get_connections()
Returns the full named list when variable is NULL, or a single tibble when a variable name is given.
get_connections(fish, "keypoint")
#> # A tibble: 5 × 2
#> from to
#> <chr> <chr>
#> 1 head neck
#> 2 neck shoulder_right
#> 3 neck shoulder_left
#> 4 shoulder_right abdomen
#> 5 shoulder_left abdomen
add_connections() / remove_connections()
These mirror each other: both take from and to as either single strings or vectors of equal length, so multiple edges go in or out in one call.
fish <- add_connections(
fish,
from = c("abdomen", "abdomen"),
to = c("hip_right", "hip_left")
)
nrow(get_connections(fish, "keypoint"))
#> [1] 7
# Same shape, in reverse — remove three pairs at once:
fish <- remove_connections(
fish,
from = c("abdomen", "abdomen", "head"),
to = c("hip_right", "hip_left", "neck")
)
nrow(get_connections(fish, "keypoint"))
#> [1] 4remove_connections() matches exactly on from/to. Direction matters: removing (a, b) won’t strip (b, a). If you stored an edge in one direction but want it gone regardless of orientation, include both pairs in the call (or invoke twice with swapped arguments).
Connections on other variables
connections is keyed by any identity or temporal variable, not just keypoints. A social-network study, for example, might record edges between individuals:
social <- example_aniframe(n_individuals = 3) |>
set_connections(
list(c("1", "2"), c("2", "3")),
variable = "individual"
)
get_connections(social)
#> $individual
#> # A tibble: 2 × 2
#> from to
#> <chr> <chr>
#> 1 1 2
#> 2 2 3The variable argument must be a column listed in variables_what or variables_when — set_connections() errors otherwise to catch typos.
Catching typos
When a from/to value isn’t found in the corresponding column, set_connections() and add_connections() emit a warning but keep the connection — the value may legitimately be missing in this particular recording while being valid elsewhere.
example_aniframe(n_keypoints = 5) |>
set_connections(list(c("head", "necc"))) # typo
#> Warning: Some connection endpoints are not present in the "keypoint" column: "necc".
#> ℹ Keeping them in case the value is recorded in another file or video.
#> # Individuals: 1, 2, 3
#> # Keypoints: head, neck, shoulder_right, shoulder_left, abdomen
#> # Sessions: 1
#> # Trials: 1
#> individual keypoint session trial time x y confidence
#> <int> <fct> <int> <int> <int> <dbl> <dbl> <dbl>
#> 1 1 head 1 1 1 -1.04 -2.02 0.743
#> 2 1 head 1 1 2 -0.113 -0.539 0.665
#> 3 1 head 1 1 3 1.54 -1.35 0.684
#> 4 1 head 1 1 4 -0.132 -0.749 0.818
#> 5 1 head 1 1 5 -0.619 -0.0597 0.837
#> 6 1 head 1 1 6 0.665 0.486 0.751
#> 7 1 head 1 1 7 -0.0302 1.04 0.727
#> 8 1 head 1 1 8 -0.835 -1.13 0.526
#> 9 1 head 1 1 9 0.297 -0.122 0.571
#> 10 1 head 1 1 10 -0.0500 -0.970 0.756
#> # ℹ 740 more rows