bluefoxr / COINr

COINr
https://bluefoxr.github.io/COINr/
Other
25 stars 8 forks source link

A general question on Treat data with customized function #64

Open liuyanguu opened 1 month ago

liuyanguu commented 1 month ago

I'm sorry for the multiple questions. I have a quick question: all my indicators are percentages and I want to apply a simple function like below to all indicators, is it possible to use the "Treat" function for it?

f_custom <- function(x, direction) {
  if (direction == -1) {
    return(1 - x)
  } else {
    return(x) # Identity transformation if direction is 1
  }
}

basically return the same value if direction is 1 and (1 - x) if the direction is -1.

I tried

LCI_imp <- Treat(LCI_imp, dset = "Imputed", 
                     global_specs = list(f_n = "f_custom", f_n_para = "use_iMeta"))

but nothing changes and all values remain the same? Sorry I haven't figured out the right way to do it...

Thank you so much for your help. Much appreciated!

bluefoxr commented 1 month ago

If you're trying to flip the directions of indicators, you probably want to use the Normalise() function, which does that automatically for you based on the directions specified in iMeta. It also lets you pass custom functions to it. You can also do a completely custom operation using the Custom() function if you can't do it another way.

liuyanguu commented 1 month ago

Many thanks for your prompt reply. I have tried Normalise and encounter the same issue. Unfortunately setting global = FALSE is not enough for custom function

library("COINr")
n_f_custom <- function(x, direction) {
  if (direction == -1) {
    return(1 - x)
  } else {
    return(x) # Identity transformation if direction is 1
  }
}
purse <- build_example_purse(up_to = "new_coin")
purse <- Normalise(purse, dset = "Raw", global_specs = list(f_n = "f_custom", f_n_para = "use_iMeta"), global = FALSE)

reports "Error in get_iMeta_norm_paras(coin, func_name = global_specs[["f_n"]]) : Your normalisation function 'f_custom' does not have support for iMeta parameters."

I have also tried Custom, but it seems it cannot use iMeta.

purse <- build_example_purse(up_to = "new_coin")
purse <- Custom(purse, dset = "Raw", f_cust = f_custom, f_cust_para = "use_iMeta")

Update: I managed to get

purse <- Normalise(purse, dset = "Raw", global_specs = list(f_n = "f_custom", f_n_para = "use_iMeta"), global = FALSE)

work by modifying the get_iMeta_norm_paras function, adding the following lines:

required_cols <- switch(
    func_name,
    n_f_custom = c("Direction"),
...)

# add `drop = FALSE` since I only have one argument
      paras <- iMeta[iMeta$iCode == names(l_n)[ii], required_cols, drop = FALSE] # drop = FALSE to keep as data frame

# add to list component
    l$f_n_para <- switch(
      func_name,
      n_f_custom = list(direction = paras$Direction),
...)
bluefoxr commented 1 month ago

@liuyanguu have you looked at the documentation on normalisation at https://bluefoxr.github.io/COINr/articles/normalise.html#coins

As far as I can tell you just want to flip the indicator directions, and this is taken care of by Normalise() without needing to pass your function to it. Normally the reversal of directions is done at the normalisation step, in addition to another scaling operation such as min-max. All of this is built into the package. Also please check the function documentation - if a function doesn't explicitly say it can access iMeta then it can't, these are special cases, again it is written out in the documentation.

liuyanguu commented 1 month ago

Dear @bluefoxr, I have read all the documentation. Indeed, what I am doing is very simple, but I am not only flipping the indicator direction — I am keeping the same value if it is a positive indicator and calculating (1 - x) if it is a negative indicator. And I'd like to use f_n_para = use_iMeta. I see no other way to get Normalise work with a custom function (which pulls information from iMeta) without modifying the get_iMeta_norm_paras function.

purse <- Normalise(purse, dset = "Raw", global_specs = list(f_n = "f_custom", f_n_para = "use_iMeta"), global = FALSE)
bluefoxr commented 1 month ago

I think then maybe the answer is a Custom operation, passing the iMeta data frame "manually" (or just the direction and iCode columns) to the f_cust_para argument.

In Custom.purse() the operation happens like this:

# run global dset through function
iDatas_c <- do.call(f_cust, c(list(x = iDatas), f_cust_para))

So if you make a function like your one above, where it looks up the direction for each column, it should do the trick.