在建立分数多项式函数时避免使用eval(parse()) [英] Avoiding eval(parse()) in building fractional polynomial function

查看:103
本文介绍了在建立分数多项式函数时避免使用eval(parse())的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的目标是在R中编写一个函数,该函数接受分数多项式(FP)并返回向量化函数,该函数针对给定的输入数字评估指定的FP. FP定义有两个重要规则:

My goal is to write a function in R that accepts coefficients for a fractional polynomial (FP) and returns a vectorized function which evaluates the specified FP for given input numbers. The FP definition has two important rules:

  • x ^ 0定义为log(x)
  • 幂可以具有多个系数,其中幂p的第二个系数将加法项(x ^ p * log(x))加一个log(x)因子,第三个系数加一个log(x)^因子2(x ^ p * log(x)^ 2),依此类推

我下面的当前解决方案将FP函数构建为字符串,解析该字符串并返回一个计算表达式的函数.我的问题是,是否有更好/更快的方法来避免eval(parse())-可能使用某些substitute()魔术.

My current solution below builds the FP-function as a string, parses the string and returns a function which evaluates the expression. My question is if there is a better/faster way that avoids eval(parse()) - possibly using some substitute() magic.

函数必须处理,该函数具有预先未知的每幂系数数,但在调用时指定. FP的最终评估经常要被调用.

The function must deal with having the number of coefficients per power not known in advance, but specified when being called. The final FP evaluation needs to be fast as it is called very often.

很好,不应该局限于标准幂-2,-1,-0.5、0、0.5、1、2、3.理想情况下,所需的函数将同时执行两个步骤:接受FP系数以及数字向量,并在保持快速的同时返回输入的FP值.

It would be nice not to be limited to the standard powers -2, -1, -0.5, 0, 0.5, 1, 2, 3. Ideally, the desired function would do two steps at once: accept FP-coefficients as well as a vector of numbers and return the FP-values for the input while still being fast.

getFP <- function(p_2, p_1, p_0.5, p0, p0.5, p1, p2, p3, ...) {
    p <- as.list(match.call(expand.dots=TRUE)[-1])         # all args
    names(p) <- sub("^p", "", names(p))     # strip "p" from arg names
    names(p) <- sub("_", "-", names(p))     # replace _ by - in arg names

    ## for one power and the i-th coefficient: build string
    getCoefStr <- function(i, pow, coefs) {
        powBT  <- ifelse(as.numeric(pow), paste0("x^(", pow, ")"), "log(x)")
        logFac <- ifelse(i-1,             paste0("*log(x)^", i-1), "")
        paste0("(", coefs[i], ")*", powBT, logFac)
    }

    onePwrStr <- function(pow, p) { # for one power: build string for all coefs
        coefs  <- eval(p[[pow]])
        pwrStr <- sapply(seq(along=coefs), getCoefStr, pow, coefs)
        paste(pwrStr, collapse=" + ")
    }

    allPwrs <- sapply(names(p), onePwrStr, p)  # for each power: build string
    fpExpr  <- parse(text=paste(allPwrs, collapse=" + "))
    function(x) { eval(fpExpr) }
}

例如-1.5*x^(-1) - 14*log(x) - 13*x^(0.5) + 6*x^0.5*log(x) + 1*x^3,它具有指定的幂(-1、0、0.5、0.5、3),系数为(-1.5,-14,-13、6、1).

An example would be -1.5*x^(-1) - 14*log(x) - 13*x^(0.5) + 6*x^0.5*log(x) + 1*x^3 which has specified powers (-1, 0, 0.5, 0.5, 3) with coefficients (-1.5, -14, -13, 6, 1).

> fp <- getFP(p_1=-1.5, p0=-14, p0.5=c(-13, 6), p3=1)
> fp(1:3)
[1] -13.50000000 -14.95728798   0.01988127

推荐答案

首先,我们创建一个将在序列中生成单个术语的函数

First we create a function that will generate a single term in the sequence

one <- function(p, c = 1, repeated = 1) {
  if (p == 0) {
    lhs <- substitute(c * log(x), list(c = c))
  } else {
    lhs <- substitute(c * x ^ p, list(c = c, p = p))
  }

  if (repeated == 1) return(lhs)
  substitute(lhs * log(x) ^ pow, list(lhs = lhs, pow = repeated - 1))
}
one(0)
# 1 * log(x)
one(2)
# 1 * x^2

one(2, 2)
# 2 * x^2

one(2, r = 2)
# 1 * x ^ 2 * log(x)^1
one(2, r = 3)
# 1 * x ^ 2 * log(x)^2

此处的关键工具是substitute()此处.

接下来,我们编写一个将两个项加在一起的函数.再次使用替代:

Next we write a function that will add together two terms. Again this uses substitute:

add_expr_1 <- function(x, y) {
  substitute(x + y, list(x = x, y = y))
}

add_expr_1(one(0, 1), one(2, 1))

我们可以使用它来创建一个函数,以将任意数量的术语加在一起:

We can use this to make a function to add together any number of terms:

add_expr <- function(x) Reduce(add_expr_1, x)
add_expr(list(one(0, 1), one(1, 1), one(2, 3)))

有了这些片段,最终功能很简单-我们计算出代表的次数,然后对每个powerscoefsreps组合使用Map()调用一次one():

With these piece in place, the final function is simple - we figure out the number of reps, then use Map() to call one() once for each combination of powers, coefs and reps:

fp <- function(powers, coefs) {
  # Determine number of times each power is repeated. This is too
  # clever approach but I think it works
  reps <- ave(powers, powers, FUN = seq_along)

  # Now generate a list of expressions using one
  components <- Map(one, powers, coefs, reps)

  # And combine them together with plus
  add_expr(components)
}

powers <- c(-1, 0, 0.5, 0.5, 3)
coefs <-  c(-1.5, -14, -13, 6, 1)
fp(powers, coefs)
# -1.5 * x^-1 + -14 * log(x) + -13 * x^0.5 + 6 * x^0.5 * log(x)^1 + 
#   1 * x^3

这篇关于在建立分数多项式函数时避免使用eval(parse())的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆