# Non-wear detection from ActiGraph data

The `actigraph.sleepr` package implements two non-wear detection algorithms: Troiano (Troiano et al. 2008) and Choi (Choi et al. 2011). For illustration let’s use one day of sample data recorded by a GT3X+ monitor. See the sleep vignette for details on reading AGD files and collapsing epochs.

    file_10s <- system.file("extdata", "ActiSleepPlus-RawData-Day01.agd",
      package = "actigraph.sleepr"
    )
    agdb_60s <- read_agd(file_10s) %>%
      collapse_epochs(60) %>%
      mutate(date = lubridate::as_date(timestamp)) %>%
      select(date, timestamp, starts_with("axis")) %>%
      group_by(date)
    agdb_60s
    #> # A tibble: 1,440 × 5
    #> # Groups:   date [2]
    #>    date       timestamp           axis1 axis2 axis3
    #>    <date>     <dttm>              <int> <int> <int>
    #>  1 2012-04-04 2012-04-04 13:29:00   600  1025   891
    #>  2 2012-04-04 2012-04-04 13:30:00    88   367    74
    #>  3 2012-04-04 2012-04-04 13:31:00   493   646   622
    #>  4 2012-04-04 2012-04-04 13:32:00   358   284   174
    #>  5 2012-04-04 2012-04-04 13:33:00   290   341    77
    #>  6 2012-04-04 2012-04-04 13:34:00    57   166    23
    #>  7 2012-04-04 2012-04-04 13:35:00   158   240    40
    #>  8 2012-04-04 2012-04-04 13:36:00    68   214   102
    #>  9 2012-04-04 2012-04-04 13:37:00  1500  1562  1252
    #> 10 2012-04-04 2012-04-04 13:38:00   165   296    59
    #> # ℹ 1,430 more rows

Long stretches that consist almost entirely of zero counts (zero epochs) suggest that the device wasn’t worn at all and therefore should be excluded from downstream analysis.

### Non-wear detection with the Troiano algorithm

The Troiano algorithm for detecting periods of non-wear formalizes a technique used to analyze the 2003-2004 NHANES data; the original SAS source code can be found at https://riskfactor.cancer.gov/tools/nhanes_pam/. The method has some flexibility as a non-wear period can contain a few nonzero epochs of artifactual movement (spikes).

`activity_threshold`
  Highest activity level to be considered “zero”; an epoch with activity exceeding the threshold is considered a “spike”. The default threshold is 0.
`min_period_len`
  Minimum number of consecutive zero epoch to start a non-wear period. The default is 60.
`max_nonzero_count`
  Epochs with activity greater than `max_nonzero_count` are labeled “zero”. The default is `Inf`.
`spike_tolerance`
  Also known as artifactual movement interval. At most `spike_tolerance` “nonzero” epochs can occur in sequence during a non-wear period without interrupting it. The default is 2.
`spike_stoplevel`
  An activity spike that exceeds `spike_stoplevel` counts ends a non-wear period, even if the spike tolerance has not been reached. The default is 100.
`use_magnitude`
  If true, the magnitude of the vector (axis1, axis2, axis3) is used to measure activity; otherwise the axis1 value is used. The default is FALSE.
`endat_nnz_seq`
  If true, a non-wear period ends with a run of nonzero epochs that is longer than `spike_tolerance`. This corresponds to the option “Require consecutive epochs outside of the activity threshold” in ActiLife’s Wear Time Validation menu. The default is TRUE.

The Troiano algorithm specifies that a non-wear period starts with `min_length` consecutive epochs/minutes of zero activity and ends with more than `spike_tolerance` epochs/minutes of nonzero activity.

    periods_nonwear <- agdb_60s %>% apply_troiano(min_period_len = 45)
    periods_nonwear
    #> # A tibble: 2 × 4
    #> # Groups:   date [1]
    #>   date       period_start        period_end          length
    #>   <date>     <dttm>              <dttm>               <int>
    #> 1 2012-04-05 2012-04-05 02:56:00 2012-04-05 03:59:00     63
    #> 2 2012-04-05 2012-04-05 04:54:00 2012-04-05 05:45:00     51

The parameter settings of the Troiano algorithm are stored as attributes.

    tail(attributes(periods_nonwear), 8)
    #> $nonwear_algorithm
    #> [1] "Troiano"
    #>
    #> $min_period_len
    #> [1] 45
    #>
    #> $max_nonzero_count
    #> [1] Inf
    #>
    #> $spike_tolerance
    #> [1] 2
    #>
    #> $spike_stoplevel
    #> [1] 100
    #>
    #> $activity_threshold
    #> [1] 0
    #>
    #> $endat_nnz_seq
    #> [1] TRUE
    #>
    #> $use_magnitude
    #> [1] FALSE

Once non-wear periods are detected, we can further screen those intervals. For example, we can ignore non-wear periods that are too short.

    periods_nonwear %>% filter(length >= 60)
    #> # A tibble: 1 × 4
    #> # Groups:   date [1]
    #>   date       period_start        period_end          length
    #>   <date>     <dttm>              <dttm>               <int>
    #> 1 2012-04-05 2012-04-05 02:56:00 2012-04-05 03:59:00     63

Or we can flag 24-hour day as invalid if the device was worn only for a short period of time.

    # Find the periods during which the device was worn
    periods_wear <- complement_periods(
      periods_nonwear, agdb_60s,
      period_start, period_end
    )
    periods_wear
    #> # A tibble: 4 × 4
    #> # Groups:   date [2]
    #>   date       period_start        period_end          length
    #>   <date>     <dttm>              <dttm>               <int>
    #> 1 2012-04-04 2012-04-04 13:29:00 2012-04-04 23:59:00    631
    #> 2 2012-04-05 2012-04-05 00:00:00 2012-04-05 02:55:00    176
    #> 3 2012-04-05 2012-04-05 04:00:00 2012-04-05 04:53:00     54
    #> 4 2012-04-05 2012-04-05 05:46:00 2012-04-05 13:28:00    463

    # Label each epoch with the period_id of the wear period it falls in
    # or with NA if the epoch falls outside a wear period
    agdb_wear <- combine_epochs_periods(
      agdb_60s, periods_wear,
      period_start, period_end
    )
    agdb_wear
    #> # A tibble: 1,440 × 6
    #> # Groups:   date [2]
    #>    date       timestamp           axis1 axis2 axis3 period_id
    #>    <date>     <dttm>              <int> <int> <int>     <int>
    #>  1 2012-04-04 2012-04-04 13:29:00   600  1025   891         1
    #>  2 2012-04-04 2012-04-04 13:30:00    88   367    74         1
    #>  3 2012-04-04 2012-04-04 13:31:00   493   646   622         1
    #>  4 2012-04-04 2012-04-04 13:32:00   358   284   174         1
    #>  5 2012-04-04 2012-04-04 13:33:00   290   341    77         1
    #>  6 2012-04-04 2012-04-04 13:34:00    57   166    23         1
    #>  7 2012-04-04 2012-04-04 13:35:00   158   240    40         1
    #>  8 2012-04-04 2012-04-04 13:36:00    68   214   102         1
    #>  9 2012-04-04 2012-04-04 13:37:00  1500  1562  1252         1
    #> 10 2012-04-04 2012-04-04 13:38:00   165   296    59         1
    #> # ℹ 1,430 more rows

    # Once we add the wear/nonwear information, it is straightforward to
    # compute summary statistics or join with external minute-by-minute data
    agdb_wear %>%
      group_by(period_id, .add = TRUE) %>%
      summarise(
        time_worn = n(),
        ave_counts = mean(axis1),
        .groups = "drop_last"
      )
    #> # A tibble: 5 × 4
    #> # Groups:   date [2]
    #>   date       period_id time_worn ave_counts
    #>   <date>         <int>     <int>      <dbl>
    #> 1 2012-04-04         1       631    1446.
    #> 2 2012-04-05         1       176      75.9
    #> 3 2012-04-05         2        54      31.5
    #> 4 2012-04-05         3       463    1208.
    #> 5 2012-04-05        NA       116       8.42

### Non-wear detection with the Choi algorithm

The Choi algorithm extends the Troiano algorithm by requiring that short spikes of artifactual movement during a non-wear period are preceded and followed by consecutive zero epochs.

`min_period_len`
  Minimum number of consecutive zero epoch to start a non-wear period. The default is 90.
`min_window_len`
  The minimum number of consecutive “zero” epochs immediately preceding and following a spike of artifactual movement. The default is 30.
`spike_tolerance`
  Also known as artifactual movement interval. At most `spike_tolerance` “nonzero” epochs can occur in sequence during a non-wear period without interrupting it. The default is 2.
`use_magnitude`
  If true, the magnitude of the vector (axis1, axis2, axis3) is used to measure activity; otherwise the axis1 value is used. The default is FALSE.

    periods_nonwear <- agdb_60s %>% apply_choi(
      min_period_len = 45,
      min_window_len = 10
    )
    periods_nonwear
    #> # A tibble: 1 × 4
    #> # Groups:   date [1]
    #>   date       period_start        period_end          length
    #>   <date>     <dttm>              <dttm>               <int>
    #> 1 2012-04-05 2012-04-05 03:03:00 2012-04-05 03:59:00     56

Again, the parameter settings are saved as attributes.

    tail(attributes(periods_nonwear), 5)
    #> $nonwear_algorithm
    #> [1] "Choi"
    #>
    #> $min_period_len
    #> [1] 45
    #>
    #> $min_window_len
    #> [1] 10
    #>
    #> $spike_tolerance
    #> [1] 2
    #>
    #> $use_magnitude
    #> [1] FALSE

### References

Troiano, Richard P, David Berrigan, Kevin W Dodd, Louise C Mâsse, Timothy Tilert, and Margaret McDowell. 2008. “Physical Activity in the United States Measured by Accelerometer.” Medicine & Science in Sports & Exercise 40 (1): 181–88.

Choi, Leena, Zhouwen Liu, Charles E. Matthews, and Maciej S. Buchowski. 2011. “Validation of Accelerometer Wear and Nonwear Time Classification Algorithm.” Medicine & Science in Sports & Exercise 43 (2): 357–64.