r - 有效转换为R中的向量

标签 r performance benchmarking microbenchmark

谁能帮助我提高此R代码的效率?

我正在尝试编写一个将字符串列表更改为字符串矢量的函数,或者
通常,数字列表是数字的向量,类型元素列表是某种类型的向量。

我希望能够将列表更改为特定类型的矢量(如果它们具有以下属性):


它们是同类型的。列表中的每个元素的类型均为“字符”或“复杂”
上。
列表中的每个元素长度为1。

as_atomic <- local({

    assert_is_valid_elem <- function (elem, mode) {

        if (length(elem) != 1 || !is(elem, mode)) {
            stop("")
        }
        TRUE
    }

    function (coll, mode) {

        if (length(coll) == 0) {
            vector(mode)
        } else {
            # check that the generic vector is composed only
            # of length-one values, and each value has the correct type.

            # uses more memory that 'for', but is presumably faster.
            vapply(coll, assert_is_valid_elem, logical(1), mode = mode)

            as.vector(coll, mode = mode)
        }
    }
})



例如,

as_atomic(list(1, 2, 3), 'numeric')
as.numeric(c(1,2,3))

# this fails (mixed types)
as_atomic( list(1, 'a', 2), 'character' )
# ERROR.

# this fails (non-length one element)
as_atomic( list(1, c(2,3,4), 5), 'numeric' )
# ERROR.

# this fails (cannot convert numbers to strings)
as_atomic( list(1, 2, 3), 'character' )
# ERROR.


上面的代码可以正常工作,但是它非常慢,并且我看不出没有对其进行优化的任何方法
函数的行为。重要的是函数“ as_atomic”的行为方式;我不能切换
到我熟悉的基本函数(例如,取消列表),因为我需要为错误列表抛出错误。

require(microbenchmark)

microbenchmark(
    as_atomic( as.list(1:1000), 'numeric'),
    vapply(1:1000, identity, integer(1)),
    unit = 'ns'
)


在我的(相当快)的计算机上,基准测试的频率约为40Hz,因此此功能几乎总是在我的代码中限制速率。 vapply控制基准具有约1650Hz的频率,这仍然相当慢。

有什么方法可以大大提高该操作的效率?任何建议表示赞赏。

如果需要任何澄清或修改,请在下面留下评论。

编辑:

大家好,

很抱歉收到迟来的答复;我必须先参加考试,然后才能尝试
重新实现这一点。

谢谢大家提供的性能提示。我的性能从可怕的40Hz提高到了
使用普通的R代码更可接受的600hz。

最大的加速来自于使用typeof或mode而不是is;这真的加快了
紧密的内部检查循环。

我可能不得不硬着头皮,用rcpp重写它,以使其真正发挥作用。

最佳答案

此问题有两个部分:


检查输入是否有效
将列表强制为向量


检查有效输入

首先,我会避免使用is(),因为众所周知它很慢。这给出了:

check_valid <- function (elem, mode) {
  if (length(elem) != 1) stop("Must be length 1")
  if (mode(elem) != mode) stop("Not desired type")

  TRUE
}


现在,我们需要弄清楚循环还是应用变体更快。
我们将在所有输入均有效的最坏情况下进行基准测试。

worst <- as.list(0:101)

library(microbenchmark)
options(digits = 3)
microbenchmark(
  `for` = for(i in seq_along(worst)) check_valid(worst[[i]], "numeric"),
  lapply = lapply(worst, check_valid, "numeric"),
  vapply = vapply(worst, check_valid, "numeric", FUN.VALUE = logical(1))
)

## Unit: microseconds
##    expr min  lq median  uq  max neval
##     for 278 293    301 318 1184   100
##  lapply 274 282    291 310 1041   100
##  vapply 273 284    288 298 1062   100


这三种方法基本上是并列的。 lapply()非常轻微
更快,可能是因为它使用了特殊的C技巧

将列表强制转换为矢量

现在让我们看看将列表强制转换为向量的几种方法:

change_mode <- function(x, mode) {
  mode(x) <- mode
  x
}

microbenchmark(
  change_mode = change_mode(worst, "numeric"),
  unlist = unlist(worst),
  as.vector = as.vector(worst, "numeric")
)

## Unit: microseconds
##         expr   min    lq median   uq    max neval
##  change_mode 19.13 20.83  22.36 23.9 167.51   100
##       unlist  2.42  2.75   3.11  3.3  22.58   100
##    as.vector  1.79  2.13   2.37  2.6   8.05   100


因此,看来您已经在使用最快的方法了,
费用以支票为主。

替代方法

另一个想法是,我们可以通过循环来获得更快的速度
在向量上一次,而不是一次检查和一次强制:

as_atomic_for <- function (x, mode) {
  out <- vector(mode, length(x))

  for (i in seq_along(x)) {
    check_valid(x[[i]], mode)
    out[i] <- x[[i]]
  }

  out
}
microbenchmark(
  as_atomic_for(worst, "numeric")
)

## Unit: microseconds
##                             expr min  lq median  uq  max neval
##  as_atomic_for(worst, "numeric") 497 524    557 685 1279   100


那绝对更糟。

总而言之,我认为这建议您是否要执行此功能
更快,您应该尝试对Rcpp中的检查功能进行矢量化处理。

关于r - 有效转换为R中的向量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22515175/

相关文章:

testing - 是否可以动态运行基准测试?

c++ - 我能否将 "force"Cachegrind 分析成一个操作(或行)?

r - 将因子转换为 R 中的日期

r - eulerr 包中的调色板

javascript - 在 Javascript Hashmap 中使用坐标作为键的最快方法

MySQL:VARCHAR 的大小与 PK 一样快,与 INT 一样快吗?

java - 2.5GHz Intel i7 上每秒的增量

r - 从向量中找出所有可能的数字组合以达到给定的总和(无重复)

r - 将单个数据框转换为数据框列表(将列名解析为前缀和后缀)

python - 字节码编译给 Python 代码带来了多少加速?