NanoSIMS data processing of LANS data in R: how to use calculate

# NanoSIMS data processing of LANS data in R: how to use `calculate`

    library(lans2r)
    library(dplyr)
    library(knitr)

The `?calculate` function is intended to make it easy and efficient to calculate sets of derived quantities (e.g. ratios from `13C` and `12C` data but also from `15N12C` and `14N12C` data). It allows defining exactly how the derived quantities should be calculated but also makes it possible to calculate derived errors and how to go about naming the newly derived quantities. Due to its structure it allows for a large amount of flexibility to cover all possible use cases. This vignettes illustrates some basic examples how this may be used to calculate derived quantities from measured ion currents. The functions `?calculate_ratios`, `?calculate_abundances`, `?calculate_sums` are all pre-implemented examples of using `calculate` to achieve a specific task and work out of the box without additional specifications as illustrated in the main vignette.

#### Test data

For simple illustration, this is a completely contrived artifical data set:

    set.seed(123) # set random seed
    test_data <-
      tibble(
        ROI = rep(1:5, times = 4),
        variable = rep(LETTERS[1:4], each = 5),
        value = rpois(20,lambda = 10),
        sigma = sqrt(value),
        data_type = "raw"
      )
    kable(test_data, d = 2)

ROI | variable | value | sigma | data_type
--- | --- | --- | --- | ---
1 | A | 8 | 2.83 | raw
2 | A | 9 | 3.00 | raw
3 | A | 14 | 3.74 | raw
4 | A | 10 | 3.16 | raw
5 | A | 10 | 3.16 | raw
1 | B | 15 | 3.87 | raw
2 | B | 11 | 3.32 | raw
3 | B | 5 | 2.24 | raw
4 | B | 4 | 2.00 | raw
5 | B | 13 | 3.61 | raw
1 | C | 11 | 3.32 | raw
2 | C | 11 | 3.32 | raw
3 | C | 10 | 3.16 | raw
4 | C | 8 | 2.83 | raw
5 | C | 15 | 3.87 | raw
1 | D | 11 | 3.32 | raw
2 | D | 3 | 1.73 | raw
3 | D | 7 | 2.65 | raw
4 | D | 6 | 2.45 | raw
5 | D | 8 | 2.83 | raw

#### Calculate products and propagate errors

As a first example, using `calculate` to calculate different column products and propagate the error by standard error propagation (assuming no covariance).

    # functions to calculate values and errors and derive names from the names of the variables used
    # note that they all have to take the same parameters (even if they are not used)
    my_value_fun <- function(x, y, x.err, y.err) x*y
    my_error_fun <- function(x, y, x.err, y.err) my_value_fun(x, y, x.err, y.err) * sqrt((x.err/x)^2 + (y.err/y)^2)
    my_name_fun <- function(x, y, x.err, y.err) paste0(deparse(substitute(x)), "*", deparse(substitute(y)))

    derived_data <-
      test_data %>%
      calculate(
        # data type of the derived quantities (can be anything descriptive)
        data_type = "derived",
        # which sets of variables to use for calculations
        c(D, C, `D sigma`, `C sigma`), c(B, A, `B sigma`, `A sigma`), c(B, C, `B sigma`, `C sigma`),
        # the function to make the calculations
        value_fun = my_value_fun, error_fun = my_error_fun, name_fun = my_name_fun)

    ## INFO: 15 'derived' values + errors calculated, 15 added (subset: all)
    ##       values added (stored in 'variable' column): 'B*A' (5x), 'B*C' (5x), 'D*C' (5x)

    kable(derived_data, d = 2)

ROI | variable | value | sigma | data_type
--- | --- | --- | --- | ---
1 | A | 8 | 2.83 | raw
2 | A | 9 | 3.00 | raw
3 | A | 14 | 3.74 | raw
4 | A | 10 | 3.16 | raw
5 | A | 10 | 3.16 | raw
1 | B | 15 | 3.87 | raw
2 | B | 11 | 3.32 | raw
3 | B | 5 | 2.24 | raw
4 | B | 4 | 2.00 | raw
5 | B | 13 | 3.61 | raw
1 | C | 11 | 3.32 | raw
2 | C | 11 | 3.32 | raw
3 | C | 10 | 3.16 | raw
4 | C | 8 | 2.83 | raw
5 | C | 15 | 3.87 | raw
1 | D | 11 | 3.32 | raw
2 | D | 3 | 1.73 | raw
3 | D | 7 | 2.65 | raw
4 | D | 6 | 2.45 | raw
5 | D | 8 | 2.83 | raw
1 | D*C | 121 | 51.59 | derived
2 | D*C | 33 | 21.49 | derived
3 | D*C | 70 | 34.50 | derived
4 | D*C | 48 | 25.92 | derived
5 | D*C | 120 | 52.54 | derived
1 | B*A | 120 | 52.54 | derived
2 | B*A | 99 | 44.50 | derived
3 | B*A | 70 | 36.47 | derived
4 | B*A | 40 | 23.66 | derived
5 | B*A | 130 | 54.68 | derived
1 | B*C | 165 | 65.50 | derived
2 | B*C | 121 | 51.59 | derived
3 | B*C | 50 | 27.39 | derived
4 | B*C | 32 | 19.60 | derived
5 | B*C | 195 | 73.89 | derived

#### Calculate sums of derived quantities

As a second example, build on the derived quantities to generate custom sums (again with standard error propagation):

    my_value_fun <- function(x, y, x.err, y.err) x+y
    my_error_fun <- function(x, y, x.err, y.err) sqrt(x.err^2 + y.err^2)
    my_name_fun <- function(x, y, x.err, y.err) paste0(deparse(substitute(x)), "+", deparse(substitute(y)))

    derived_data2 <-
      derived_data %>%
      calculate(
        # data type of the derived quantities (can be anything descriptive)
        data_type = "derived2",
        # which sets of variables to use for calculations
        c(D, C, `D sigma`, `C sigma`), c(`B*A`, `C`, `B*A sigma`, `C sigma`),
        # the function to make the calculations
        value_fun = my_value_fun, error_fun = my_error_fun, name_fun = my_name_fun)

    ## INFO: 10 'derived2' values + errors calculated, 10 added (subset: all)
    ##       values added (stored in 'variable' column): 'B*A+C' (5x), 'D+C' (5x)

    kable(derived_data2, d = 2)

ROI | variable | value | sigma | data_type
--- | --- | --- | --- | ---
1 | A | 8 | 2.83 | raw
2 | A | 9 | 3.00 | raw
3 | A | 14 | 3.74 | raw
4 | A | 10 | 3.16 | raw
5 | A | 10 | 3.16 | raw
1 | B | 15 | 3.87 | raw
2 | B | 11 | 3.32 | raw
3 | B | 5 | 2.24 | raw
4 | B | 4 | 2.00 | raw
5 | B | 13 | 3.61 | raw
1 | C | 11 | 3.32 | raw
2 | C | 11 | 3.32 | raw
3 | C | 10 | 3.16 | raw
4 | C | 8 | 2.83 | raw
5 | C | 15 | 3.87 | raw
1 | D | 11 | 3.32 | raw
2 | D | 3 | 1.73 | raw
3 | D | 7 | 2.65 | raw
4 | D | 6 | 2.45 | raw
5 | D | 8 | 2.83 | raw
1 | D*C | 121 | 51.59 | derived
2 | D*C | 33 | 21.49 | derived
3 | D*C | 70 | 34.50 | derived
4 | D*C | 48 | 25.92 | derived
5 | D*C | 120 | 52.54 | derived
1 | B*A | 120 | 52.54 | derived
2 | B*A | 99 | 44.50 | derived
3 | B*A | 70 | 36.47 | derived
4 | B*A | 40 | 23.66 | derived
5 | B*A | 130 | 54.68 | derived
1 | B*C | 165 | 65.50 | derived
2 | B*C | 121 | 51.59 | derived
3 | B*C | 50 | 27.39 | derived
4 | B*C | 32 | 19.60 | derived
5 | B*C | 195 | 73.89 | derived
1 | D+C | 22 | 4.69 | derived2
2 | D+C | 14 | 3.74 | derived2
3 | D+C | 17 | 4.12 | derived2
4 | D+C | 14 | 3.74 | derived2
5 | D+C | 23 | 4.80 | derived2
1 | B*A+C | 131 | 52.64 | derived2
2 | B*A+C | 110 | 44.62 | derived2
3 | B*A+C | 80 | 36.61 | derived2
4 | B*A+C | 48 | 23.83 | derived2
5 | B*A+C | 145 | 54.82 | derived2

#### Calculate derived quantity (no error, and direct naming)

To calculate a derived quantity without calculating errors, simply don’t supply an error function. Also illustrated here is a simple way to provide specific variable names and defining the functions in line. Note the use of `sum(x)` as well, this can easily be used for calculating normalized derived quantities.

    derived_data3 <-
      derived_data2 %>%
      calculate(
        # data type of the derived quantities (can be anything descriptive)
        data_type = "special",
        # which sets of variables to use for calculations
        c("my_var", A, B, C),
        # the function to make the calculations
        value_fun = function(name, x, y, z) x+y^2+z^3/sum(x),
        name_fun = function(name, x, y, z) name)

    ## INFO: 5 'special' values + errors calculated, 5 added (subset: all)
    ##       values added (stored in 'variable' column): 'my_var' (5x)

    kable(derived_data3, d = 2)

ROI | variable | value | sigma | data_type
--- | --- | --- | --- | ---
1 | A | 8.00 | 2.83 | raw
2 | A | 9.00 | 3.00 | raw
3 | A | 14.00 | 3.74 | raw
4 | A | 10.00 | 3.16 | raw
5 | A | 10.00 | 3.16 | raw
1 | B | 15.00 | 3.87 | raw
2 | B | 11.00 | 3.32 | raw
3 | B | 5.00 | 2.24 | raw
4 | B | 4.00 | 2.00 | raw
5 | B | 13.00 | 3.61 | raw
1 | C | 11.00 | 3.32 | raw
2 | C | 11.00 | 3.32 | raw
3 | C | 10.00 | 3.16 | raw
4 | C | 8.00 | 2.83 | raw
5 | C | 15.00 | 3.87 | raw
1 | D | 11.00 | 3.32 | raw
2 | D | 3.00 | 1.73 | raw
3 | D | 7.00 | 2.65 | raw
4 | D | 6.00 | 2.45 | raw
5 | D | 8.00 | 2.83 | raw
1 | D*C | 121.00 | 51.59 | derived
2 | D*C | 33.00 | 21.49 | derived
3 | D*C | 70.00 | 34.50 | derived
4 | D*C | 48.00 | 25.92 | derived
5 | D*C | 120.00 | 52.54 | derived
1 | B*A | 120.00 | 52.54 | derived
2 | B*A | 99.00 | 44.50 | derived
3 | B*A | 70.00 | 36.47 | derived
4 | B*A | 40.00 | 23.66 | derived
5 | B*A | 130.00 | 54.68 | derived
1 | B*C | 165.00 | 65.50 | derived
2 | B*C | 121.00 | 51.59 | derived
3 | B*C | 50.00 | 27.39 | derived
4 | B*C | 32.00 | 19.60 | derived
5 | B*C | 195.00 | 73.89 | derived
1 | D+C | 22.00 | 4.69 | derived2
2 | D+C | 14.00 | 3.74 | derived2
3 | D+C | 17.00 | 4.12 | derived2
4 | D+C | 14.00 | 3.74 | derived2
5 | D+C | 23.00 | 4.80 | derived2
1 | B*A+C | 131.00 | 52.64 | derived2
2 | B*A+C | 110.00 | 44.62 | derived2
3 | B*A+C | 80.00 | 36.61 | derived2
4 | B*A+C | 48.00 | 23.83 | derived2
5 | B*A+C | 145.00 | 54.82 | derived2
1 | my_var | 259.10 | NA | special
2 | my_var | 156.10 | NA | special
3 | my_var | 58.61 | NA | special
4 | my_var | 36.04 | NA | special
5 | my_var | 245.18 | NA | special

# Data visualization

This data format makes it easy to have arbitrarily elaborate derived quantities and dynamically include the same calculation for multiple sets of variables. It is also a format that lends itself very well to visualization:

    library(ggplot2)
    derived_data3 %>%
      ggplot() +
      aes(x = ROI, y = value, ymin = value-sigma, ymax = value+sigma, color = variable) +
      geom_errorbar() +
      geom_point() +
      facet_wrap(~data_type, scales = "free")

# Data overview

Lastly, it can be very useful to have data converted to a wide format with all variables and errors next to each other (e.g. for summary tables or export to Excel). This is easily accomplished with lans2r’s `spread_data` function:

    derived_data3 %>% spread_data() %>% kable(d = 2)

ROI | A | B | B*A | B*A+C | B*C | C | D | D*C | D+C | my_var | A sigma | B sigma | B*A sigma | B*A+C sigma | B*C sigma | C sigma | D sigma | D*C sigma | D+C sigma | my_var sigma
--- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | ---
1 | 8 | 15 | 120 | 131 | 165 | 11 | 11 | 121 | 22 | 259.10 | 2.83 | 3.87 | 52.54 | 52.64 | 65.50 | 3.32 | 3.32 | 51.59 | 4.69 | NA
2 | 9 | 11 | 99 | 110 | 121 | 11 | 3 | 33 | 14 | 156.10 | 3.00 | 3.32 | 44.50 | 44.62 | 51.59 | 3.32 | 1.73 | 21.49 | 3.74 | NA
3 | 14 | 5 | 70 | 80 | 50 | 10 | 7 | 70 | 17 | 58.61 | 3.74 | 2.24 | 36.47 | 36.61 | 27.39 | 3.16 | 2.65 | 34.50 | 4.12 | NA
4 | 10 | 4 | 40 | 48 | 32 | 8 | 6 | 48 | 14 | 36.04 | 3.16 | 2.00 | 23.66 | 23.83 | 19.60 | 2.83 | 2.45 | 25.92 | 3.74 | NA
5 | 10 | 13 | 130 | 145 | 195 | 15 | 8 | 120 | 23 | 245.18 | 3.16 | 3.61 | 54.68 | 54.82 | 73.89 | 3.87 | 2.83 | 52.54 | 4.80 | NA

    derived_data3 %>% spread_data(errors = FALSE) %>% kable(d = 2)

ROI | A | B | B*A | B*A+C | B*C | C | D | D*C | D+C | my_var
--- | --- | --- | --- | --- | --- | --- | --- | --- | --- | ---
1 | 8 | 15 | 120 | 131 | 165 | 11 | 11 | 121 | 22 | 259.10
2 | 9 | 11 | 99 | 110 | 121 | 11 | 3 | 33 | 14 | 156.10
3 | 14 | 5 | 70 | 80 | 50 | 10 | 7 | 70 | 17 | 58.61
4 | 10 | 4 | 40 | 48 | 32 | 8 | 6 | 48 | 14 | 36.04
5 | 10 | 13 | 130 | 145 | 195 | 15 | 8 | 120 | 23 | 245.18