1

For lab data, measurements are normally provided with detection/reporting limits and confidence intervals. For example, I might have a measurement of Magnesium concentration in water where the minimum reporting value is 5 and I have received two measurements, the first is 10 and the second is "<5" (ie. below reporting value). As an end user, there are times that you want "<5" to be treated as "5", sometimes treated as "0", sometimes treated as "2.5".

How I am approaching this problem is by constructing an S3 class with an attribute LRL (lower reporting limit). What I would like to be able to have the user do is the following:

a <- set_measurement("<5", LRL = 5)
b <- set_measurement(8, LRL = 5)
set_conservatism(1) # sets a global variable called "conservatism_coefficient" to 1
a
# 5 [LRL: 5]
c <- b + a
# 13 [LRL: 5]
set_conservatism(0.5)
a
# 2.5 [LRL: 5]
b + a
# 10.5 [LRL: 5]
c
# 13 [LRL: 5]

What I'm imagining is that the value of "a' is somehow set to "LRL*conservatism_co-efficient" rather than a number. Then when some other function tries to access the value, the value is dynamically computed based on the current conservatism_co-efficient.

Is this possible, and/or am I just going about this completely the wrong way?

vorpal
  • 268
  • 4
  • 17
  • First, this is a very interesting idea. If it doesn't exist already, I hope you continue along this path. It sounds like you're talking about physical chemistry, but this could also be applied to medicine. Second, I think this is feasible. Unfortunately, I think it will entail making methods for a lot of different functions. Check out [this section of Advanced R](http://adv-r.had.co.nz/S3.html) to get an idea of what you're dealing with. – Ian Campbell Apr 30 '20 at 02:50
  • @IanCampbell that is exactly what I was trying to avoid having to do. I was hoping there might be some way that I could calculate the value before it was passed off to other functions, rather than requiring a re-write of basically every generic function. Oh well, maybe that's why nobody else appears to have done it yet. – vorpal Apr 30 '20 at 05:21

1 Answers1

1

Do not be scared to try overloading the generic functions you need. You could achieve what you want by just modifying the print function and the group of arithmetic operations Ops:

set_conservatism = function(factor) {
    # Set global CONSERVATISM
    CONSERVATISM <<- factor
}

set_measurement = function(value, lrl=5) {
    # Create a new measurement
    v_ = "measurement"  # Dummy identifier

    # Set attributes of a measurement
    measurement = structure(v_, "value"=value, "lrl"=lrl)
    # Set class attribute of measurement
    class(measurement) = "measurement"
    measurement
}

update_measurement = function(x) {
    # Return value of measurement based on CONSERVATISM
    if (attr(x, "value") < attr(x, "lrl")) {
        attr(x, "lrl") * CONSERVATISM
    } else {
        attr(x, "value")
    }
}

print.measurement = function(x, ...) {
    # UserMethod for printing a measurement
    update_measurement(x)
}

Ops.measurement = function(e1, e2) {
    # UserMethod for arithmetic operations with measurements
    e1 = update_measurement(e1)
    e2 = update_measurement(e2)
    NextMethod(.Generic)
}

a = set_measurement(0)  # Any value smaller than lrl will do
b = set_measurement(8)

set_conservatism(1)

a + b
>>> 13

set_conservatism(0.5)

a + b
>>> 10.5

(Side note from a Python programmer: Things like this are very easily achieved with properties in Python and by overriding magic methods)

Jan Joswig
  • 693
  • 5
  • 20