Using Movement Data to Distinguish In-Flight Points of Bald Eagles

Background

  • Data from 57 bald eagles over 4 years

  • Movement variables obtained from bio loggers:

    • KPH -> Instantaneous speed (k/h)
    • Sn -> Horizontal rate (m/s)
    • AGL -> Above ground level (m)
    • abs_angle -> Absolute value of turn angle (radians)
    • VerticalRate -> Vertical rate (m/s)
    • absVR -> Absolute value of vertical rate (m/s)

Research Questions

  1. Can we use the movement variables KPH, Sn, AGL, |Angle|, Vertical rate, and |VR|, to distinguish in-flight from perching points?

  2. Of the in-flight points:

    1. Are there distinct flight behaviors that can be identified?

    2. What are the characteristics of these behaviors?

    3. What are some visual examples of flight segments that demonstrate the different types of in-flight behaviors?

Kmeans for clustering:

  • PAM is slow, can’t control number of clusters in DBSCAN

  • Optimal number of clusters means finding where:

    • the within-cluster distances are small

    • the between-cluster distances are large

  • Methods like WSS or Silhouette:

    • Use an n x n distance matrix

    • 2 Mil x 2 Mil = 4 Tril -> several terabytes

  • Split the data, random sample of 100 measurements per eagle

    • 5.7 k x 5.7 k = 32.5 Mil -> about 200 MB

Sihouette of the Sample Data

Over many samples, 2 or 4 clusters was proven to be optimal

  • Cluster 1:

    • High KPH, Sn, AGL, absVR, and low to mid abs_angle and Vertical Rate

    • The eagle is most likely gliding

  • Cluster 2:

    • Low KPH, Sn, absVR, AGL, higher abs_angle, and moderate VerticalRate

    • The eagle is not flying, it is perched

  • Cluster 3:

    • Moderate KPH, Sn, AGL, and absVR, and lower VerticalRate and abs_angle

    • This means the eagle is flying, but not going that fast, or “flap flying”

  • Cluster 4:

    • High VerticalRate, abs_VR, AGL, and abs_agl, and lower KPH and Sn

    • The eagle is flying upwards.

Appendix

library(tidyverse)
library(factoextra)
library(cluster)
library(ggrepel)

load("eagle_data.Rdata")

# example eagle sample data
set.seed(2)   

eagle_sample <- (eagle_data
                 %>% group_by(Animal_ID)
                 %>% sample_n(size = min(n(), 100))
                 %>% ungroup()
                 )

eagle_sample_data <- (eagle_sample
                      %>% select(KPH, Sn, AGL, abs_angle, VerticalRate, absVR)
                      %>% scale()
)

# find optimal clusters
fviz_nbclust(eagle_sample_data, FUNcluster = kmeans, method = 'silhouette') 

# scale for kmeans
eagle_data_scaled <- (eagle_data
                      %>% select(KPH, Sn, AGL, abs_angle, VerticalRate, absVR)
                      %>% scale()
)

# kmeans
eagles_clustered <- kmeans(eagle_data_scaled, centers = 4, iter.max = 10, nstart = 1)

# add clusters to data
eagle_data_clust <- (eagle_data
                     %>% mutate(cluster = eagles_clustered$cluster))

# select one eagle to visualize
one_eagle_cluster <- (eagle_data_clust
                      %>% filter(Animal_ID == 146)
                      %>% select(cluster)
                      )
# select and scale 
one_eagle <- (eagle_data
              %>% filter(Animal_ID == 146)
              %>% select(KPH, Sn, AGL, abs_angle, VerticalRate, absVR)
              %>% scale()
)

# reduce to 2D
pca_one_eagle <- prcomp(one_eagle)

# 2 PCs and cluster group
plot_one_eagle <- (pca_one_eagle$x[,1:2]
                   %>% data.frame()
                   %>% mutate(cluster = one_eagle_cluster$cluster)
                   )
# plot our one eagle
(fviz_pca(pca_one_eagle, 
         habillage = factor(one_eagle_cluster$cluster),
         label='var',
         repel = TRUE)
 + labs(color = "Cluster", shape = "Cluster")
)

# save the plot for comparisson later
base_plot <- (fviz_pca_ind(pca_one_eagle,
                       habillage = factor(one_eagle_cluster$cluster),
                       label='var',
                       repel = TRUE)
              + labs(color = "Cluster", shape = "Cluster")
)

# make points transparent
base_plot$layers[[1]]$aes_params$alpha <- 0.2
base_plot$layers[[1]]$aes_params$size  <- 0.1
base_plot$layers[[2]]$aes_params$alpha <- 0.2
base_plot$layers[[2]]$aes_params$size  <- 0.1


# obtain one flight segment
one_eagle_time <- (eagle_data_clust
                   %>% filter(Animal_ID == 146)
                   %>% mutate(PC1 = plot_one_eagle$PC1,
                              PC2 = plot_one_eagle$PC2)
                   %>% filter(segment_id == 73356)
)

# plot one flight segment
(base_plot
  + geom_point(data = one_eagle_time, aes(x=PC1, y=PC2, color = factor(cluster)))
  + geom_text_repel(data=one_eagle_time, 
                    aes(x=PC1, y=PC2,label = rownames(one_eagle_time)))
  + xlim(c(-3, 1))
  + ylim(c(-3.307559, 5))
  + ggtitle('Flight Segment 1')
)

# obtain another flight segment
two_eagle_time <- (eagle_data_clust
                   %>% filter(Animal_ID == 146)
                   %>% mutate(PC1 = plot_one_eagle$PC1,
                              PC2 = plot_one_eagle$PC2)
                   %>% filter(segment_id == 79152)
                   %>% slice(195:222)
)

# plot another flight segment
(base_plot
  + geom_point(data = two_eagle_time, aes(x=PC1, y=PC2, color = factor(cluster)))
  + geom_text_repel(data=two_eagle_time, 
                    aes(x=PC1, y=PC2,label = rownames(two_eagle_time)))
  + xlim(c(-3, 5))
  + ylim(c(-2, 2))
  + ggtitle('Flight Segment 2')
)