# Creating ADVFQ

# Introduction

This article describes creating an ADVFQ ADaM with Visual Functioning Questionnaire data for ophthalmology endpoints. It is to be used in conjunction with the article on creating a BDS dataset from SDTM. As such, derivations and processes that are not specific to ADVFQ are absent, and the user is invited to consult the aforementioned article for guidance.

Note: All examples assume CDISC SDTM and/or ADaM format as input unless otherwise specified.

The full, open-source VFQ questionnaire can be accessed here.

## Dataset Contents

`{admiralophtha}` suggests to populate ADVFQ solely with VFQ records from the QS SDTM. Any other questionnaire data should be placed in separate datasets (e.g. ADQS).

## Required Packages

The examples of this vignette require the following packages.

    library(dplyr)
    library(admiral)
    library(pharmaversesdtm)
    library(admiraldev)
    library(admiralophtha)

# Programming Workflow

- Initial set up of ADVFQ
- Derive Analysis Value for existing questions (`AVAL`)
- Derive Parameters for recoded items and summary scores
- Derive Analysis Variables (`ANL01FL` `ASEQ`)
- Add ADSL Variables
- Example Script

## Initial set up of ADVFQ

To start, all datasets needed for the creation of the questionnaire dataset should be read into the environment. For the purpose of demonstration we shall use the `{admiral}` QS and ADSL test data. The QS dataset is filtered to the VFQ parameters of interest.

    data("admiral_adsl")
    data("qs_ophtha")
    adsl <- admiral_adsl
    qs <- qs_ophtha

    qs <- qs %>% filter(QSTESTCD %in% c("VFQ1", "VFQ2", "VFQ3", "VFQ4"))

Next, the programmer should create a parameter lookup table which includes `QSTESTCD`, `PARAMCD`, `PARAM`, `PARCAT1` and `PARCAT2` variables. This should include all parameters that will be needed in the final ADVFQ and will be used later to merge parameter information.

    param_lookup <- tibble::tribble(
      ~QSTESTCD, ~PARAMCD, ~PARAM, ~PARCAT1, ~PARCAT2,
      "VFQ1", "VFQ1", "Overall Health", "NEI VFQ-25", "Original Response",
      "VFQ2", "VFQ2", "Eyesight in Both Eyes", "NEI VFQ-25", "Original Response",
      "VFQ3", "VFQ3", "Worry About Eyesight", "NEI VFQ-25", "Original Response",
      "VFQ4", "VFQ4", "Pain in and Around Eyes", "NEI VFQ-25", "Original Response",
      "QR01", "QR01", "Recoded Item - 01", "NEI VFQ-25", "General 01",
      "QR02", "QR02", "Recoded Item - 02", "NEI VFQ-25", "General 01",
      "QR03", "QR03", "Recoded Item - 03", "NEI VFQ-25", "General 02",
      "QR04", "QR04", "Recoded Item - 04", "NEI VFQ-25", "General 02",
      "QSG01", "QSG01", "General Score 01", "NEI VFQ-25", "Averaged Result",
      "QSG02", "QSG02", "General Score 02", "NEI VFQ-25", "Averaged Result",
      "QBCSCORE", "QBCSCORE", "Composite Score", "NEI VFQ-25", "Averaged Result"
    )

Now the ADVFQ dataset can be constructed, merging the filtered QS dataset with ADSL. This is necessary because treatment start date `TRTSDT` is a prerequisite for the derivation of variables such as Analysis Day `ADY` which can be programmed by following the article on creating a BDS dataset from SDTM.

    adsl_vars <- exprs(TRTSDT, TRTEDT, TRT01A, TRT01P)

    advfq <- derive_vars_merged(
      qs,
      dataset_add = adsl,
      new_vars = adsl_vars,
      by_vars = get_admiral_option("subject_keys")
    )

## Derive Analysis Value for Existing Questions

To derive the analysis values we use the function `admiral::derive_vars_merged_lookup()` which merges on `PARAMCD` from the parameter lookup table. This merges on the parameter by `QSTESTCD` and assigns `AVAL` and `AVALC`.

    advfq <- advfq %>%
      ## Add PARAMCD only - add PARAM etc later ----
      derive_vars_merged_lookup(
        dataset_add = param_lookup,
        new_vars = exprs(PARAMCD),
        by_vars = exprs(QSTESTCD)
      ) %>%
      ## Calculate AVAL and AVALC ----
      mutate(
        AVAL = QSSTRESN,
        AVALC = QSORRES
      )

## Derive Parameters for Recoded Items and Summary Scores

Once we have included the initial records from QS, the programmer should next program new records for parameters which recode the original questions. Run this section of code for every question that you need recoding. This gives an example of recoding one question.

    ## QR01 Recoded Item 01
    # set to 100 if [advfq.AVAL] = 1
    # else set to 75 if [advfq.AVAL] = 2
    # else set to 50 if [advfq.AVAL] = 3
    # else set to 25 if [advfq.AVAL] = 4
    # else set to 0 if [advfq.AVAL] = 5
    advfq <- advfq %>%
      derive_summary_records(
        dataset_add = advfq,
        by_vars = c(
          get_admiral_option("subject_keys"),
          exprs(!!!adsl_vars, PARAMCD, VISITNUM, VISIT)
        ),
        filter_add = QSTESTCD == "VFQ1" & !is.na(AVAL),
        set_values_to = exprs(
          AVAL = identity(AVAL),
          PARAMCD = "QR01"
        )
      ) %>%
      mutate(AVAL = ifelse(PARAMCD == "QR01",
        case_when(
          AVAL == 1 ~ 100,
          AVAL == 2 ~ 75,
          AVAL == 3 ~ 50,
          AVAL == 4 ~ 25,
          AVAL >= 5 ~ 0
        ),
        AVAL
      ))

Next, the programmer should create summary records as average of recoded questions using `admiral::derive_summary_records`. This example uses two of the recoded questions to create an average record.

    ## Derive a new record as a summary record  ----
    ## QSG01 General Score 01
    # Average of QR01 and QR02 records
    advfq <- advfq %>%
      derive_summary_records(
        dataset_add = advfq,
        by_vars = c(
          get_admiral_option("subject_keys"),
          exprs(!!!adsl_vars, VISITNUM, VISIT, ADT, ADY)
        ),
        filter_add = PARAMCD %in% c("QR01", "QR02") & !is.na(AVAL),
        set_values_to = exprs(
          AVAL = mean(AVAL),
          PARAMCD = "QSG01"
        )
      )

## Derive Analysis Variables

In most finding ADaMs, an analysis flag is derived to identify the appropriate observation(s) to use for a particular analysis when a subject has multiple observations within a particular timing period.

In this situation, an analysis flag (e.g. `ANLxxFL`) may be used to choose the appropriate record for analysis.

This flag may be derived using the `admiral` function `admiral::derive_var_extreme_flag()`. For this example, we will assume we would like to choose the latest value by our subject keys along with `PARAMCD` and `AVISIT`.

    ## ANL01FL: Flag last result within an AVISIT for post-baseline records ----
    advfq <- advfq %>%
      restrict_derivation(
        derivation = derive_var_extreme_flag,
        args = params(
          new_var = ANL01FL,
          by_vars = c(get_admiral_option("subject_keys"), exprs(PARAMCD, AVISIT)),
          order = exprs(ADT, AVAL),
          mode = "last"
        ),
        filter = !is.na(AVISITN) & ONTRTFL == "Y"
      )

We then derive `ASEQ` using `admiral::derive_var_obs_number()` based on the observation number within the dataset, additionally merge on `PARAM`, `PARCAT1` and `PARCAT2` using the earlier lookup table.

    ## Get ASEQ and PARAM  ----
    advfq <- advfq %>%
      # Calculate ASEQ
      derive_var_obs_number(
        new_var = ASEQ,
        by_vars = get_admiral_option("subject_keys"),
        order = exprs(PARAMCD, ADT, AVISITN, VISITNUM),
        check_type = "error"
      ) %>%
      # Derive PARAM
      derive_vars_merged(dataset_add = select(param_lookup, -QSTESTCD), by_vars = exprs(PARAMCD))

## Add ADSL Variables

Once analysis variables have been programmed, variables from ADSL which are required should be merged on to the dataset using `admiral::derive_vars_merged`.

    # Add all ADSL variables
    advfq <- advfq %>%
      derive_vars_merged(
        dataset_add = select(adsl, !!!negate_vars(adsl_vars)),
        by_vars = get_admiral_option("subject_keys")
      )

## Example Script

ADaM | Sample Code
--- | ---
ADVFQ | ad_advfq.R