Help on Variogram Interpretation

15 views
Skip to first unread message

annika

unread,
Oct 28, 2025, 9:47:28 AM (7 days ago) Oct 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

Reply all
Reply to author
Forward
0 new messages