Skip to contents

The goal of SummarizedActigraphy is to provide functions for reading Actigraphy data and turn it into SummarizedExperiments.

Installation

You can install SummarizedActigraphy from GitHub with:

# install.packages("remotes")
remotes::install_github("muschellij2/SummarizedActigraphy")

Reading in some data

library(SummarizedActigraphy)
url = paste0("https://github.com/THLfi/read.gt3x/files/",
             "3522749/GT3X%2B.01.day.gt3x.zip")
destfile = tempfile(fileext = ".zip")
dl = utils::download.file(url, destfile = destfile)
gt3x_file = utils::unzip(destfile, exdir = tempdir())
gt3x_file = gt3x_file[!grepl("__MACOSX", gt3x_file)]
path = gt3x_file
result = summarize_actigraphy(path)
#> Running Daily Actigraphy
#> Getting the First Day
#> Summarizing Data
result
#> # A tsibble: 1,440 x 13 [1m]
#>    time   AI_mean AI_median SD_mean SD_median MAD_mean MAD_median MEDAD_mean
#>    <time>   <dbl>     <dbl>   <dbl>     <dbl>    <dbl>      <dbl>      <dbl>
#>  1 00'00"  0.168     0.168  0.00613   0.00613  0.00394    0.00394    0.00291
#>  2 01'00"  0.0501    0.0501 0.00514   0.00514  0.00393    0.00393    0.00276
#>  3 02'00"  0         0      0         0        0          0          0      
#>  4 03'00"  0         0      0         0        0          0          0      
#>  5 04'00"  0         0      0         0        0          0          0      
#>  6 05'00"  0         0      0         0        0          0          0      
#>  7 06'00"  0.0802    0.0802 0.00375   0.00375  0.00308    0.00308    0.00216
#>  8 07'00"  0.0271    0.0271 0.00298   0.00298  0.00206    0.00206    0.00124
#>  9 08'00"  0.0865    0.0865 0.00411   0.00411  0.00355    0.00355    0.00452
#> 10 09'00"  0.0543    0.0543 0.00230   0.00230  0.00197    0.00197    0.00142
#> # ℹ 1,430 more rows
#> # ℹ 5 more variables: MEDAD_median <dbl>, ENMO_t_mean <dbl>,
#> #   ENMO_t_median <dbl>, mean_r_mean <dbl>, mean_r_median <dbl>

Reading in GT3X files

In GT3X files, you must be aware of Idle Sleep Mode, which saves power on the device, but the device essentially stops recording. In most cases, you still want a “full” time series without missing elements. To read in gt3x files, we use read.gt3x::read.gt3x. By default read.gt3x::read.gt3x, that data isn’t in the output data set. If you use the imputeZeros = TRUE argument, then they are there, but all 0s, which doesn’t make sense (no gravity even!?).

In Actigraph’s ActiLife, the raw output data repeats the same value before the device went into sleep mode. To mimic this behavior, the SummarizedActigraphy::fix_zeros function sets the values in the rows with all 0 to be NA (just for X/Y/Z, not time), and then uses zoo::na.locf for last observation carried forward (LOCF). Aside: you could also use tidyr::fill(direction = "down") for the tidyverse folks.

In SummarizedActigraphy::read_actigraphy, we use read.gt3x::read.gt3x(asDataFrame = TRUE, imputeZeroes = TRUE) as the default.

See below as an example.

data = SummarizedActigraphy::read_actigraphy(path)
#> Input is a .gt3x file, unzipping to a temporary location first...
#> Unzipping gt3x data to /var/folders/1s/wrtqcpxn685_zk570bnx9_rr0000gr/T//RtmpquRevB
#> 1/1
#> Unzipping /var/folders/1s/wrtqcpxn685_zk570bnx9_rr0000gr/T//RtmpquRevB/GT3X+ (01 day).gt3x
#>  === info.txt, activity.bin, lux.bin extracted to /var/folders/1s/wrtqcpxn685_zk570bnx9_rr0000gr/T//RtmpquRevB/GT3X+(01day)
#> GT3X information
#>  $ Serial Number     :"NEO1DXXXXXXXX"
#>  $ Device Type       :"GT3XPlus"
#>  $ Firmware          :"2.5.0"
#>  $ Battery Voltage   :"4.22"
#>  $ Sample Rate       :30
#>  $ Start Date        : POSIXct, format: "2012-06-27 10:54:00"
#>  $ Stop Date         : POSIXct, format: "2012-06-28 11:54:00"
#>  $ Download Date     : POSIXct, format: "2012-06-28 16:25:52"
#>  $ Board Revision    :"4"
#>  $ Unexpected Resets :"0"
#>  $ Sex               :"Male"
#>  $ Height            :"172.72"
#>  $ Mass              :"69.8532249799612"
#>  $ Age               :"43"
#>  $ Race              :"White / Caucasian"
#>  $ Limb              :"Ankle"
#>  $ Side              :"Left"
#>  $ Dominance         :"Non-Dominant"
#>  $ DateOfBirth       :"621132192000000000"
#>  $ Subject Name      :"GT3XPlus"
#>  $ Serial Prefix     :"NEO"
#>  $ Last Sample Time  : 'POSIXct' num(0) 
#>  - attr(*, "tzone")= chr "GMT"
#>  $ Acceleration Scale:341
#>  $ Acceleration Min  :"-6.0"
#>  $ Acceleration Max  :"6.0"
#> Parsing GT3X data via CPP.. expected sample size: 2700000
#> Using NHANES-GT3X format - older format
#> Sample size: 2700000
#> Scaling...
#> Done (in 0.675724983215332 seconds)
df = data$data
df = tibble::as_tibble(df)
all_zero = df$X == 0 & df$Y == 0 & df$Z == 0
df[all_zero, ] %>% 
  dplyr::mutate(time = lubridate::floor_date(time, "1 sec")) %>% 
  dplyr::distinct()
#> # A tibble: 3,341 × 4
#>    time                    X     Y     Z
#>    <dttm>              <dbl> <dbl> <dbl>
#>  1 2012-06-27 10:54:00     0     0     0
#>  2 2012-06-27 10:54:01     0     0     0
#>  3 2012-06-27 10:54:02     0     0     0
#>  4 2012-06-27 10:54:03     0     0     0
#>  5 2012-06-27 10:54:04     0     0     0
#>  6 2012-06-27 11:27:40     0     0     0
#>  7 2012-06-27 11:27:41     0     0     0
#>  8 2012-06-27 11:27:42     0     0     0
#>  9 2012-06-27 11:27:43     0     0     0
#> 10 2012-06-27 11:27:44     0     0     0
#> # ℹ 3,331 more rows
df = SummarizedActigraphy::fix_zeros(df)
df[all_zero, ] %>% 
  dplyr::mutate(time = lubridate::floor_date(time, "1 sec")) %>% 
  dplyr::distinct()
#> # A tibble: 3,341 × 4
#>    time                     X      Y     Z
#>    <dttm>               <dbl>  <dbl> <dbl>
#>  1 2012-06-27 10:54:00  0      0      0   
#>  2 2012-06-27 10:54:01  0      0      0   
#>  3 2012-06-27 10:54:02  0      0      0   
#>  4 2012-06-27 10:54:03  0      0      0   
#>  5 2012-06-27 10:54:04  0      0      0   
#>  6 2012-06-27 11:27:40 -0.123 -0.029 -1.04
#>  7 2012-06-27 11:27:41 -0.123 -0.029 -1.04
#>  8 2012-06-27 11:27:42 -0.123 -0.029 -1.04
#>  9 2012-06-27 11:27:43 -0.123 -0.029 -1.04
#> 10 2012-06-27 11:27:44 -0.123 -0.029 -1.04
#> # ℹ 3,331 more rows

Note well, the fix_zeros does not do anything with data that is all zero in the beginning of a time series (as there is no observation to carry forward). This behavior should mimic ActiLife to our knowledge and that data is likely to be discarded regardless.

Converting to wide 1440 format

hms_times = structure(seq(0, 86340, by = 60), class = c("hms", "difftime"),
                      units = "secs")
hms_times = tibble::tibble(time = hms_times)
measure = "AI_mean"
tmeasure = c("time", measure)
x = result[, tmeasure, drop = FALSE]
x = tibble::as_tibble(x)
x = dplyr::left_join(hms_times, x)
#> Joining with `by = join_by(time)`
head(x)
#> # A tibble: 6 × 2
#>   time   AI_mean
#>   <time>   <dbl>
#> 1 00'00"  0.168 
#> 2 01'00"  0.0501
#> 3 02'00"  0     
#> 4 03'00"  0     
#> 5 04'00"  0     
#> 6 05'00"  0
x1440 = tidyr::spread(x, time, value = measure)
x1440
#> # A tibble: 1 × 1,440
#>   `00:00:00` `00:01:00` `00:02:00` `00:03:00` `00:04:00` `00:05:00` `00:06:00`
#>        <dbl>      <dbl>      <dbl>      <dbl>      <dbl>      <dbl>      <dbl>
#> 1      0.168     0.0501          0          0          0          0     0.0802
#> # ℹ 1,433 more variables: `00:07:00` <dbl>, `00:08:00` <dbl>, `00:09:00` <dbl>,
#> #   `00:10:00` <dbl>, `00:11:00` <dbl>, `00:12:00` <dbl>, `00:13:00` <dbl>,
#> #   `00:14:00` <dbl>, `00:15:00` <dbl>, `00:16:00` <dbl>, `00:17:00` <dbl>,
#> #   `00:18:00` <dbl>, `00:19:00` <dbl>, `00:20:00` <dbl>, `00:21:00` <dbl>,
#> #   `00:22:00` <dbl>, `00:23:00` <dbl>, `00:24:00` <dbl>, `00:25:00` <dbl>,
#> #   `00:26:00` <dbl>, `00:27:00` <dbl>, `00:28:00` <dbl>, `00:29:00` <dbl>,
#> #   `00:30:00` <dbl>, `00:31:00` <dbl>, `00:32:00` <dbl>, `00:33:00` <dbl>, …