I have a function in R which takes strings which represent commerce discount structures and converts them into numerical multipliers. For example, the string "50/20/15" just means that the original price gets 50% off, then another 20% off, then another 15% off. This will be equivalent to multiplying the original price by (1 - 0.50)*(1 - 0.20)(1 - 0.15), which equals 0.34.
My function is as follows.
disc_str_to_num <- function(string){
numstrings <- strsplit(string,"/")
nums <- mapply(as.numeric, numstrings)
prod(1 - nums/100)
}
which works as desired on single strings.
> disc_str_to_num("50/20/15")
[1] 0.34
Unfortunately this fails for string vectors.
discount_vec <- c("50/10",
"50/10/10",
"50/10/15",
"50/10/20")
> disc_str_to_num(discount_vec)
Error in nums/100 : non-numeric argument to binary operator
I have tried various applications of the Vectorize
function, with no success.
How may I modify my function to work for string vectors? The expected output is a numeric vector of the component-wise values of the string vector.
CodePudding user response:
The prod
should be also within the loop
sapply(strsplit(discount_vec, "/"), \(x) prod(1 - as.numeric(x)/100))
[1] 0.4500 0.4050 0.3825 0.3600
For a single element, mapply
use SIMPLIFY = TRUE
and thus we don't have any issue, however with more than one element, it returns a list
and thus the last step doesn't work
> list(c(1, 3), c(2, 4))/100
Error in list(c(1, 3), c(2, 4))/100 :
non-numeric argument to binary operator
If we use the OP's code, then wrap with another mapply
(or could do this in the same mapply
as showed with sapply
)
> disc_str_to_num <- function(string){
numstrings <- strsplit(string,"/")
nums <- mapply(as.numeric, numstrings)
mapply(\(x) prod(1 - x/100), nums)
}
>
> disc_str_to_num(discount_vec)
[1] 0.4500 0.4050 0.3825 0.3600
Another option would be to read as data.frame
with read.table
, and then use rowProds
from matrixStats
library(matrixStats)
rowProds(as.matrix(1- read.table(text = discount_vec,
header = FALSE, sep = "/", fill = TRUE)/100), na.rm = TRUE)
[1] 0.4500 0.4050 0.3825 0.3600
CodePudding user response:
Use Vectorize like this:
Vectorize(disc_str_to_num)(discount_vec)
## 50/10 50/10/10 50/10/15 50/10/20
## 0.4500 0.4050 0.3825 0.3600
or create a new function name and then invoke it:
disc_str_to_num_vec <- Vectorize(disc_str_to_num)
disc_str_to_num_vec(discount_vec)
## 50/10 50/10/10 50/10/15 50/10/20
## 0.4500 0.4050 0.3825 0.3600
or rewrite the function as shown. Note that strsplit creates a one element list in this function and [[1]] takes the contents.
disc_str_to_num_vec2 <-
Vectorize(function(x) prod(1 - as.numeric(strsplit(x, "/")[[1]]) / 100))
disc_str_to_num_vec2(discount_vec)
## 50/10 50/10/10 50/10/15 50/10/20
## 0.4500 0.4050 0.3825 0.3600