--- title: "External functions with categorical output" output: html_document: toc : true number_sections: true toc_depth: 3 toc_float: true urlcolor: blue vignette: > %\VignetteIndexEntry{External functions with categorical output} \usepackage[utf8]{inputenc} %\VignetteEngine{knitr::rmarkdown} header-includes: - \usepackage{titling} - \pretitle{\begin{center} \includegraphics[]{GGIR-MASTERLOGO-RGB.png}\LARGE\\} - \posttitle{\end{center}} --- GGIR allows users to [embed external functions](https://wadpac.github.io/GGIR/articles/ExternalFunction.html) for processing raw accelerometer data. This functionality can be used when an algorithm that is not part of GGIR needs to be applied to the raw data before summarising the results. Here we describe a specific use case: external functions that classify each epoch into one of a set of categorical types, such as activity types or postures, and the output GGIR generates for them. This functionality is activated by setting `reporttype = "type"` in the `myfun` object. This vignette complements [Embedding external functions in GGIR](https://wadpac.github.io/GGIR/articles/ExternalFunction.html), which describes all elements of `myfun`, how output columns are aggregated to the GGIR epoch size, and how missing values are handled. Those topics are not repeated here. ## Defining an external function An external function used with GGIR is supplied through a `myfun` list. For `reporttype = "type"`, the output of the function is interpreted as categorical information rather than as a numeric signal. For example: ```r classify_activity <- function(data, parameters) { acc = data[, 1] result = ifelse( acc < parameters[1], "rest", ifelse(acc < parameters[2], "walking", "running") ) return(result) } ``` The corresponding `myfun` object can be defined as: ```r myfun = list( FUN = classify_activity, parameters = c(0.05, 0.20), expected_sample_rate = 100, expected_unit = "g", colnames = "activity", minlength = 1, outputres = 5, outputtype = "character", timestamp = FALSE, reporttype = "type" ) ``` For `reporttype = "type"`, the external function returns categorical information rather than a numeric measure that GGIR should average or sum. In this example, it could return values such as: ```text c("rest", "rest", "walking", "walking", "running", "running", "rest") ``` The values returned in the output column become the categorical levels used by GGIR. The elements of `myfun` are described in [Embedding external functions in GGIR](https://wadpac.github.io/GGIR/articles/ExternalFunction.html). For a categorical output, `outputtype` should be `"character"` and `reporttype` should be `"type"`. ## Multiple categorical outputs Multiple categorical output columns are supported, specified as described in [Multiple output columns](https://wadpac.github.io/GGIR/articles/ExternalFunction.html#multiple-output-columns). Each output column is treated as an independent categorical variable. For example, an external function could classify both activity and posture: ```r classify_activity_and_posture <- function(data, parameters) { activity = ifelse( data[, 1] < parameters[1], "rest", ifelse(data[, 1] < parameters[2], "walking", "running") ) posture = ifelse( data[, 2] < parameters[3], "sitting", "standing" ) return(data.frame( activity = activity, posture = posture )) } ``` The corresponding `myfun` object could be defined as: ```r myfun = list( FUN = classify_activity_and_posture, parameters = c(0.05, 0.20, 0.10), expected_sample_rate = 100, expected_unit = "g", colnames = c("activity", "posture"), minlength = 1, outputres = 5, outputtype = c("character", "character"), timestamp = FALSE, reporttype = c("type", "type") ) ``` The two categorical outputs are processed independently. The categorical levels within `activity` are mutually exclusive with each other, and the levels within `posture` are mutually exclusive with each other. However, an epoch can contribute simultaneously to one `activity` level and one `posture` level. For example, an epoch can be classified as `"walking"` for `activity` and `"standing"` for `posture`. This allows several independent categorical dimensions to be obtained from the same external function. ## Classifiers with a higher output resolution If the classifier produces output at a shorter resolution than the GGIR epoch size, the classes need to be aggregated to one class per epoch with `aggfunction`, for example by selecting the most frequent class. See [Aggregation and missing values](https://wadpac.github.io/GGIR/articles/ExternalFunction.html#aggregation-and-missing-values) for a suitable function and for how missing values are handled. Epochs that remain `NA` after aggregation are reported as unclassified, see [Part 2](#part-2) below. ## Additional parameters for `reporttype = "type"` In addition to the elements required to define the external function, several optional parameters can be added to `myfun` to control how the categorical output is summarised. These parameters are `ilevels`, `tbout.dur`, `tbout.criter`, and `tbout.order`. ### `ilevels`: acceleration intensity levels The `ilevels` parameter defines acceleration intensity thresholds used when summarising the categorical output in part 2. For example: ```r myfun$ilevels = c(0, 80, 200) ``` defines three acceleration ranges: ```text 0-80 mg 80-200 mg 200 mg or more ``` These levels are used to obtain type-specific summaries of the time spent at different acceleration intensities. ### `tbout.dur`: bout duration thresholds The `tbout.dur` parameter defines the duration thresholds used to identify bouts within each categorical type. For example: ```r myfun$tbout.dur = c(1, 3, 5, 10) ``` defines the following bout categories: ```text 1-3 minutes 3-5 minutes 5-10 minutes 10 minutes or longer ``` These thresholds determine the bout categories for which type-specific bout summaries are generated. ### `tbout.criter`: bout criterion The `tbout.criter` parameter specifies the criterion used to determine whether a sequence of epochs qualifies as a bout. For example: ```r myfun$tbout.criter = 0.8 ``` can be used to require that at least 80% of the epochs within the bout meet the criterion for the corresponding type. This allows the bout definition to tolerate some epochs that do not belong to the target type while still classifying the surrounding sequence as a bout. ### `tbout.order`: type priority for bout assignment The `tbout.order` parameter specifies the order in which categorical levels are considered when identifying bouts in part 5. This is relevant when more than one categorical level is present within the same output column. Part 5 assigns bouts within a categorical output mutually exclusively, so the order determines which type is considered first when bout periods overlap. For example: ```r myfun$tbout.order = c("running", "walking", "rest") ``` means that GGIR first identifies `running` bouts, then identifies `walking` bouts using the remaining time, and finally identifies `rest` bouts using the time remaining after the previous types have been assigned. Within each type, bout durations are considered from longest to shortest. The spelling and case of classification labels are preserved in the output. Values in `tbout.order` are matched to those labels case-insensitively. The priority is applied independently to each categorical output column. For example, the bout assignment of an `activity` output does not prevent the same epoch from being classified into a bout in an independent `posture` output. ## Running GGIR The parameters can be combined in the same `myfun` object. For example: ```r myfun = list( FUN = classify_activity, parameters = c(0.05, 0.20), expected_sample_rate = 100, expected_unit = "g", colnames = "activity", minlength = 1, outputres = 5, outputtype = "character", timestamp = FALSE, reporttype = "type", ilevels = c(0, 80, 200), tbout.dur = c(1, 3, 5, 10), tbout.criter = 0.8, tbout.order = c("running", "walking", "rest") ) ``` Once `myfun` has been defined, it is supplied to `GGIR()` in the same way as other external functions: ```r library(GGIR) GGIR( datadir = "~/mydata", outputdir = "~/myresults", mode = 1:5, myfun = myfun ) ``` ## Type-specific summaries When `reporttype = "type"`, GGIR uses the categorical levels returned by the external function to generate type-specific summaries. The summaries generated by part 2 and part 5 differ because the two parts process the categorical output in different ways. ### Part 2 Part 2 calculates type-specific summaries within each calendar day. For each categorical type, the output includes the total time assigned to the type and summaries of that time according to the acceleration intensity levels defined by `ilevels`. GGIR also reports two categories for epochs that are not assigned to a type: `unclassified` is valid time for which the external function returned `NA`, while `invalid` is time marked invalid by GGIR's data quality checks. Padding added only to construct complete analysis windows is excluded from both categories. Part 2 also provides bout summaries for each type and bout definition specified by `tbout.dur`. These include: - `totdur`: total duration of the bouts; - `meandur`: mean bout duration; and - `Nbouts`: number of bouts. Bout summaries are calculated independently for each type and bout definition. When multiple `type` output columns are provided, these summaries are generated independently for each output column. The daily and person-level results are written to `part2_daytypesummary.csv` and `part2_typesummary.csv`, respectively. ### Part 5 Part 5 summarises the categorical output separately for the sustained sleep period (`spt`) and the rest of the day (`day`). For each type, the output distinguishes between: - unbouted time; - time occurring within each defined bout category; and - total time assigned to the type. The corresponding metrics include duration (`dur`), mean acceleration (`ACC`), and the number of contiguous blocks or bouts (`Nblocks`/`Nbouts`). For example, for an output column called `activity` with a level called `walking`, variables in the dedicated report follow this structure: ```r activity_dur_spt_walking_unbt_min activity_ACC_spt_walking_unbt_mg activity_Nblocks_spt_walking_unbt ``` Bout-specific variables use the bout categories defined by `tbout.dur`, while total variables describe all time assigned to the type regardless of whether it belongs to a defined bout. The same type-specific metrics are generated for both the `spt` and `day` windows. The exact variables depend on the categorical levels returned by the external function and the bout definitions specified in `myfun`. When multiple categorical output columns are provided, Part 5 generates the corresponding type-specific metrics separately for each output column. The CSV summaries include every categorical output column. The visual PDF report currently displays only the first output column whose `reporttype` is `"type"`; additional categorical outputs are not shown in that plot. For each categorical output column, Part 5 writes cleaned daily and person-level reports named `part5_extfuntype__daysummary__.csv` and `part5_extfuntype__personsummary__.csv`. The corresponding unfiltered daily report is stored under `results/QC` with `daysummary_full` in its filename. ## Part 2 versus part 5 Part 2 and part 5 use different processing strategies for the categorical output. In **part 2**, the recording is divided into calendar days before the type-specific summaries are calculated. In **part 5**, the categorical classification is first obtained over the continuous analysis segment and is then summarised within the relevant day, `spt`, and `day` windows. This difference is particularly relevant for bouts. In part 2, bout summaries are calculated independently within each calendar day, whereas part 5 can identify a bout across a calendar-day boundary when the classification is continuous. In addition, part 5 uses mutually exclusive type assignment for bouts within each categorical output column, with the priority between types determined by `tbout.order`. Therefore, type-specific results from part 2 and part 5 are not expected to be identical; they represent summaries obtained through different processing strategies. ## Related documentation For the general external-function framework, including numeric and event-based outputs, see [Embedding external functions in GGIR](https://wadpac.github.io/GGIR/articles/ExternalFunction.html).