HLCtools calculates Herd Lying Concordance (HLC) metrics from
individual animal lying-behaviour data.
HLC is a framework for quantifying group-level behavioural cohesion. It uses the distribution of individual lying behaviour within each observation interval to estimate how similarly animals are behaving as a group.
HLCtools is developed by Guilherme Amorim Franchi at Aarhus
University.
The package provides tools for calculating Herd Lying Concordance metrics from individual animal lying-behaviour data. HLC is intended as a flexible framework for quantifying group-level behavioural cohesion from sensor-derived activity records.
If you use HLCtools, please cite the package:
citation("HLCtools")
#> To cite package 'HLCtools' in publications use:
#>
#> Amorim Franchi G (2026). _HLCtools: Calculate Herd Lying Concordance
#> Metrics_. R package version 0.1.0. Developed at Aarhus University.,
#> <https://github.com/guilhermefranchi/HLCtools>.
#>
#> A BibTeX entry for LaTeX users is
#>
#> @Manual{,
#> title = {HLCtools: Calculate Herd Lying Concordance Metrics},
#> author = {Guilherme {Amorim Franchi}},
#> year = {2026},
#> note = {R package version 0.1.0. Developed at Aarhus University.},
#> institution = {Aarhus University},
#> url = {https://github.com/guilhermefranchi/HLCtools},
#> }Traditional threshold-based synchrony metrics classify an interval as synchronous when a fixed proportion of animals are lying, for example 70%. HLC takes a different approach: it calculates group cohesion continuously from between-animal dispersion.
The package currently supports:
HLC_SD: standard-deviation-based HLCHLC_MAD: mean-absolute-deviation-based HLCHLC_IQR: interquartile-range-based HLCHLC_ENT: entropy-based HLC
The package also calculates lying-weighted HLC, which combines group cohesion with the proportion of the interval spent lying.
# Development version
# install.packages("remotes")
# remotes::install_github("your-github-username/HLCtools")data(example_lies)
example_lies
#> group day week time cow lying
#> 1 A 1 1 00:00 cow1 15
#> 2 A 1 1 00:15 cow2 15
#> 3 A 1 1 00:00 cow3 15
#> 4 A 1 1 00:15 cow4 15
#> 5 A 1 1 00:00 cow5 15
#> 6 A 1 1 00:15 cow1 0
#> 7 A 1 1 00:00 cow2 0
#> 8 A 1 1 00:15 cow3 0
#> 9 A 1 1 00:00 cow4 0
#> 10 A 1 1 00:15 cow5 0
#> 11 A 2 1 00:00 cow1 15
#> 12 A 2 1 00:15 cow2 15
#> 13 A 2 1 00:00 cow3 15
#> 14 A 2 1 00:15 cow4 0
#> 15 A 2 1 00:00 cow5 0
#> 16 A 2 1 00:15 cow1 15
#> 17 A 2 1 00:00 cow2 0
#> 18 A 2 1 00:15 cow3 15
#> 19 A 2 1 00:00 cow4 0
#> 20 A 2 1 00:15 cow5 15
#> 21 B 1 1 00:00 cow1 15
#> 22 B 1 1 00:15 cow2 15
#> 23 B 1 1 00:00 cow3 10
#> 24 B 1 1 00:15 cow4 10
#> 25 B 1 1 00:00 cow5 15
#> 26 B 1 1 00:15 cow1 15
#> 27 B 1 1 00:00 cow2 12
#> 28 B 1 1 00:15 cow3 15
#> 29 B 1 1 00:00 cow4 8
#> 30 B 1 1 00:15 cow5 15
#> 31 B 2 1 00:00 cow1 0
#> 32 B 2 1 00:15 cow2 0
#> 33 B 2 1 00:00 cow3 5
#> 34 B 2 1 00:15 cow4 0
#> 35 B 2 1 00:00 cow5 0
#> 36 B 2 1 00:15 cow1 0
#> 37 B 2 1 00:00 cow2 15
#> 38 B 2 1 00:15 cow3 0
#> 39 B 2 1 00:00 cow4 15
#> 40 B 2 1 00:15 cow5 0hlc_intervals <- calculate_hlc(
data = example_lies,
group = group,
animal = cow,
day = day,
period = week,
interval = time,
lying = lying,
interval_min = 15,
methods = c("sd", "mad", "iqr", "entropy"),
sync_thresholds = c(0.6, 0.7, 0.8, 0.9),
add_lying_weighted = TRUE
)
hlc_intervals
#> # A tibble: 8 × 24
#> group day week time n_animals mean_lying lying_prop sd_lying mad_lying
#> <chr> <int> <dbl> <chr> <int> <dbl> <dbl> <dbl> <dbl>
#> 1 A 1 1 00:00 5 9 0.6 7.35 7.2
#> 2 A 1 1 00:15 5 6 0.4 7.35 7.2
#> 3 A 2 1 00:00 5 6 0.4 7.35 7.2
#> 4 A 2 1 00:15 5 12 0.8 6 4.8
#> 5 B 1 1 00:00 5 12 0.8 2.76 2.4
#> 6 B 1 1 00:15 5 14 0.933 2 1.6
#> 7 B 2 1 00:00 5 7 0.467 6.78 6.4
#> 8 B 2 1 00:15 5 0 0 0 0
#> # ℹ 15 more variables: iqr_lying <dbl>, p_lying <dbl>, HLC_SD <dbl>,
#> # HLC_MAD <dbl>, HLC_IQR <dbl>, entropy_raw <dbl>, HLC_ENT <dbl>,
#> # sync60 <int>, sync70 <int>, sync80 <int>, sync90 <int>, HLC_SD_LYING <dbl>,
#> # HLC_MAD_LYING <dbl>, HLC_IQR_LYING <dbl>, HLC_ENT_LYING <dbl>hlc_daily <- summarise_hlc_daily(
data = hlc_intervals,
group = group,
day = day,
period = week,
interval_min = 15
)
hlc_daily
#> # A tibble: 4 × 19
#> group day week mean_HLC_SD mean_HLC_MAD mean_HLC_IQR mean_HLC_ENT
#> <chr> <int> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 A 1 1 0.0202 0.0400 0 0.0290
#> 2 A 2 1 0.110 0.2 0.5 0.154
#> 3 B 1 1 0.683 0.733 0.833 1
#> 4 B 2 1 0.548 0.573 0.5 0.515
#> # ℹ 12 more variables: mean_HLC_SD_LYING <dbl>, mean_HLC_MAD_LYING <dbl>,
#> # mean_HLC_IQR_LYING <dbl>, mean_HLC_ENT_LYING <dbl>, sync60_time_min <dbl>,
#> # sync70_time_min <dbl>, sync80_time_min <dbl>, sync90_time_min <dbl>,
#> # mean_lying_min_interval <dbl>, mean_lying_prop <dbl>, n_intervals <int>,
#> # mean_n_animals <dbl>Different datasets may favour different HLC implementations. HLCtools
therefore provides a descriptive ranking helper.
rank_hlc_methods(
data = hlc_daily,
group = group,
period = week,
lying_prop = mean_lying_prop
)
#> method metric_column n_total n_non_missing pct_missing mean sd
#> 1 HLC_SD mean_HLC_SD 4 4 0 0.3402575 0.3244934
#> 2 HLC_MAD mean_HLC_MAD 4 4 0 0.3866667 0.3214781
#> 3 HLC_ENT mean_HLC_ENT 4 4 0 0.4242837 0.4355433
#> 4 HLC_IQR mean_HLC_IQR 4 4 0 0.4583333 0.3435921
#> median min max n_unique pct_boundary zero_variance
#> 1 0.3289734 0.02020410 0.6828793 4 0 FALSE
#> 2 0.3866667 0.04000000 0.7333333 4 0 FALSE
#> 3 0.3340427 0.02904941 1.0000000 4 25 FALSE
#> 4 0.5000000 0.00000000 0.8333333 3 25 FALSE
#> high_boundary_collapse degeneracy_score detectability_F detectability_p
#> 1 FALSE 0 46.014560 0.02104854
#> 2 FALSE 0 22.222222 0.04217371
#> 3 FALSE 0 7.062389 0.11721597
#> 4 FALSE 0 1.923077 0.29985996
#> delta_AIC temporal_sd outlier_sensitivity outlier_distance_from_1
#> 1 10.7134285 NaN 0.8296094 0.1703906
#> 2 7.9764932 NaN 0.8131274 0.1868726
#> 3 4.0439423 NaN 1.2102363 0.2102363
#> 4 0.6949164 NaN 1.3904983 0.3904983
#> lying_prop_spearman rank_detectability rank_temporal_stability
#> 1 0.4000000 1 NA
#> 2 0.4000000 2 NA
#> 3 0.4000000 3 NA
#> 4 0.6324555 4 NA
#> rank_outlier_robustness rank_boundary rank_missingness rank_degeneracy
#> 1 1 1.5 2.5 2.5
#> 2 2 1.5 2.5 2.5
#> 3 3 3.5 2.5 2.5
#> 4 4 3.5 2.5 2.5
#> n_ranked_criteria weighted_rank_score selected
#> 1 5 1.7 TRUE
#> 2 5 2.1 FALSE
#> 3 5 2.9 FALSE
#> 4 5 3.3 FALSE
#> recommendation_note
#> 1 Selected/ranked under available criteria.
#> 2 Selected/ranked under available criteria.
#> 3 Selected/ranked under available criteria.
#> 4 Selected/ranked under available criteria.The ranking is based on available criteria such as group detectability, temporal stability, outlier robustness, boundary values, and missingness. The result should support transparent method selection, not replace biological interpretation.
hlc_methods()
#> [1] "sd" "mad" "iqr" "entropy"Unweighted HLC describes group-level behavioural cohesion. It can be high when animals are uniformly lying or uniformly standing.
Lying-weighted HLC describes cohesive lying specifically. It is calculated by multiplying interval-level HLC by the mean lying proportion in that interval.
Thus:
- high HLC + high lying-weighted HLC = cohesive collective lying;
- high HLC + low lying-weighted HLC = cohesive behaviour, but not mainly lying;
- low HLC = fragmented group behaviour.
HLCtools computes HLC metrics. It does not automatically decide which
dispersion basis is universally best. The choice of SD, MAD, IQR,
entropy, or another implementation should depend on the dataset,
biological question, group size, sampling interval, and intended
application.