Help on Variogram Interpretation

47 views
Skip to first unread message

annika

unread,
Oct 28, 2025, 9:47:28 AMOct 28
to ctmm R user group

Hello Chris,

I’m currently working on estimating home ranges of adult kestrels using the ctmm package, and this is my first time using ctmm. I have cleaned my data, converted the cleaned dataset to a telemetry object, and performed variogram calculation and model selection. I am only working with data from the breeding season, so migration should not be an issue.

However, my main problem is interpreting the variogram plots. I am unsure if these patterns indicate range residency or not, and I would greatly appreciate guidance on whether I can reliably continue with model fitting and home range estimation based on these plots.

Thank you very much!

Best regards, Annika


Below is my R workflow for reference:

data <- movebank_download_study(
  study_id = 3066197013,
  sensor_type_id = "sigfox-geolocation",
  timestamp_start = as.POSIXct("2024-03-20 00:00:00", tz = "UTC"),
  timestamp_end   = as.POSIXct("2024-09-22 23:59:59", tz = "UTC"),
  individual_local_identifier = c("Kestrel_BP")
)

#================================================#
### 2. Data Preparation & Cleaning
#================================================#

# Check track structure
mt_is_track_id_cleaved(data)
data <- dplyr::arrange(data, mt_track_id(data))

mt_is_time_ordered(data)
data <- dplyr::arrange(data, mt_track_id(data), mt_time(data))

# Check for duplicates and empty coordinates
mt_has_unique_location_time_records(data)
data <- data[!duplicated(mt_time(data)), 

mt_has_no_empty_points(data)
data <- dplyr::filter(data, !sf::st_is_empty(data))

# Filter locations: only >2 connected antennas
data_duplicates <- data %>%
  filter(str_count(sigfox_duplicates, "\\|") >= 2)

# Extract coordinates
if ("geometry" %in% colnames(data_duplicates)) {
  coords <- st_coordinates(data_duplicates)
  data_duplicates$longitude <- coords[, 1]
  data_duplicates$latitude  <- coords[, 2]
  data_duplicates$geometry  <- NULL
}

# Filter by link quality (lqu > 1)
data_good_lqu <- data_duplicates %>%
  filter(sigfox_link_quality > 1) %>%
  as.data.frame()

#=========================================#
### 3. Convert to Telemetry Object
#=========================================#

# Use cleaned CSV path or saved dataframe
data <- as.telemetry("Kestrel_BP_cleaned.csv")

projection(data) <- median(data)

plot(data)

compass()  # North = up

#===================================#
### 4. Outlier Detection & Removal
#===================================#

OUT <- outlie(data, main = "Map Outliers Kestrel_BP")
plot(OUT, main = "Outliers Kestrel_BP")

# Remove speed outliers
MAX_SPEED <- 50
GOOD <- OUT$speed < MAX_SPEED
data_clean <- data[GOOD, ]
OUT_clean <- outlie(data_clean, main = "Speed Outliers Removed Kestrel_BP")
plot(OUT_clean)

# Remove distance outliers
MAX_DIST <- 15000
GOODd <- OUT_clean$distance < MAX_DIST
data_clean <- data_clean[GOODd, ]
OUT_clean <- outlie(data_clean, main = "Distance Outliers Removed Kestrel_BP")
plot(OUT_clean, main = "Outliers removed Kestrel_BP")

#================================================#
### 5. Variogram & Model Fitting
#================================================#

SVF <- variogram(data_clean)
plot(SVF, main = "Variogram Kestrel_BP", xlim = c(0, max(SVF$lag)))

dt.plot(data_clean)

GUESS <- ctmm.guess(data_clean, interactive = FALSE)
FITS  <- ctmm.select(data_clean, GUESS, trace = 3, verbose = TRUE, cores = -1)
summary(FITS)
summary(FITS[[1]])

plot(SVF, FITS[[1]], main = "Best-fit Variogram Kestrel_FO", xlim = c(0, max(SVF$lag)))

#================================================#
### 6. Weighted AKDE (Home Range)
#================================================#

wAKDE <- akde(data_clean, FITS[[1]], weights = TRUE)
plot(data_clean, wAKDE, lwd = 2, main = "wAKDE Kestrel_AP")
summary(wAKDE)


Sampling_intervalls.png

Variogram.png

Variogram_Kestrel_BP.png

Best_Fit.png

Christen Fleming

unread,
Nov 14, 2025, 2:51:37 PM (13 days ago) Nov 14
to ctmm R user group
Hi Annika,

In terms of the range residency condition, those all look good.

I'm assuming the oscillations in the data are daily and are either from CPF behavior or daily duty cycling in the data, etc..

Best,
Chris
Reply all
Reply to author
Forward
0 new messages