【问题标题】:R split numeric vector at positionR在位置拆分数字向量
【发布时间】:2013-04-27 19:13:34
【问题描述】:

我想知道在某个索引处将向量一分为二的简单任务:

splitAt <- function(x, pos){
  list(x[1:pos-1], x[pos:length(x)])
}

a <- c(1, 2, 2, 3)

> splitAt(a, 4)
[[1]]
[1] 1 2 2

[[2]]
[1] 3

我的问题:必须有一些现有的功能,但我找不到它? split 有可能吗?如果pos=0pos&gt;length(a),我的幼稚实现也不起作用。

【问题讨论】:

    标签: r vector split


    【解决方案1】:

    一个改进是:

    splitAt <- function(x, pos) unname(split(x, cumsum(seq_along(x) %in% pos)))
    

    现在可以采用位置向量:

    splitAt(a, c(2, 4))
    # [[1]]
    # [1] 1
    # 
    # [[2]]
    # [1] 2 2
    # 
    # [[3]]
    # [1] 3
    

    如果pos &lt;= 0pos &gt;= length(x) 在它返回单个列表项中的整个原始向量的意义上,它的行为确实正确(主观)。如果您希望它出错,请在函数顶部使用 stopifnot

    【讨论】:

    • 谢谢,这对我来说很好用!我仍然很惊讶基础 R 中没有实现 splitAt 函数...
    • 这个函数非常慢,x 很大,可能是因为seq_along(x) 创建了一个很长的向量,然后%in% 必须匹配这个很长的向量。跨度>
    • @Calimo:不,如果你分析它,你会发现大部分时间都花在了缓慢的split 中。你当然可以避免它,但你会在可读性和代码紧凑性方面损失很多。
    【解决方案2】:

    我尝试使用flodel's answer,但在我的情况下使用非常大的x 太慢了(并且必须重复调用该函数)。所以我创建了以下函数,它更快,但也很丑陋并且行为不正常。特别是,它不会检查任何内容,并且至少会为 pos &gt;= length(x)pos &lt;= 0 返回错误的结果(如果您不确定自己的输入并且不太关心速度,您可以自己添加这些检查),也许还有一些其他情况也是如此,所以要小心。

    splitAt2 <- function(x, pos) {
        out <- list()
        pos2 <- c(1, pos, length(x)+1)
        for (i in seq_along(pos2[-1])) {
            out[[i]] <- x[pos2[i]:(pos2[i+1]-1)]
        }
        return(out)
    }
    

    但是,splitAt2 在 x 长度为 106 时运行速度大约快 20 倍:

    library(microbenchmark)
    W <- rnorm(1e6)
    splits <- cumsum(rep(1e5, 9))
    tm <- microbenchmark(
                         splitAt(W, splits),
                         splitAt2(W, splits),
                         times=10)
    tm
    

    【讨论】:

    • 谢谢!同样通过上面的简单示例,splitAt2 表现更好。
    • +1 - 有点漂亮的重写可能是:function(x, pos) {pos &lt;- c(1L, pos, length(x) + 1L); Map(function(x, i, j) x[i:j], list(x), head(pos, -1L), tail(pos, -1L) - 1L)}。随着拆分次数的增加,它似乎也快了一点,不知道为什么。
    • @user1981275 定义“更好”。如果更好 = 更快,我同意,但作为通用功能,稳健性是关键,在这种情况下,flodel 的版本更好。
    • @flodel 确实您的重写速度更快,拆分次数非常多。也无法解释原因。
    【解决方案3】:

    另一个可能比flodel's solution 更快和/或更可读/优雅的替代方案:

    splitAt <- function(x, pos) {
      unname(split(x, findInterval(x, pos)))
    }
    

    【讨论】:

    • 在我的实践中,它不像 flodel 的解决方案那样有效。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-06
    • 1970-01-01
    • 1970-01-01
    • 2019-09-10
    • 2022-12-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多