将数字向量概括为字符串的函数?

时间:2016-01-06 15:13:22

标签: r

假设我有以下数字向量:

vec = c(1, 2, 3, 5, 7, 8, 9, 10, 11, 12)

我正在寻找一个函数,它将创建一个字符串,以人类的方式总结数字列表,即

"1-3, 5, 7-12"

我怎样才能在R?

中这样做

3 个答案:

答案 0 :(得分:28)

添加其他替代方法,您可以使用deparse方法。例如:

deparse(c(1L, 2L, 3L))
#[1] "1:3"

利用as.character" deparse"给定的"列表"作为输入,我们可以使用:

as.character(split(as.integer(vec), cumsum(c(TRUE, diff(vec) != 1))))
#[1] "1:3"  "5"    "7:12"
toString(gsub(":", "-", .Last.value))
#[1] "1-3, 5, 7-12"

答案 1 :(得分:21)

我假设矢量按照示例中的顺序排序。如果不事先使用vec <- sort(vec)

编辑说明:@DavidArenburg在原始答案中发现了一个错误,其中c(min(x), x)实际应该是c(0, x)。由于我们现在知道我们总是需要首先添加0,因此我们可以省略创建x的第一步并“动态”执行此操作。现在编辑原始答案和其他选项以反映(您可以检查原始帖子的编辑历史记录)。谢谢大卫!

关于调用unname的注释:我使用unname(sapply(...))来确保结果向量未命名,否则将命名为0:(n-1)其中n等于{的长度{1}}。正如@Tensibai在评论中正确指出的那样,如果最终目标是生成由new_vec生成的长度为1的字符向量,则无关紧要,因为toString(new_vec)无论如何都会省略向量名称。

一个选项(可能不是最短的)将是:

toString

结果:

new_vec <- unname(sapply(split(vec, c(0, cumsum(diff(vec) > 1))), function(y) {
  if(length(y) == 1) y else paste0(head(y, 1), "-", tail(y, 1))
}))

感谢@ Zelazny7,可以使用new_vec #[1] "1-3" "5" "7-12" toString(new_vec) #[1] "1-3, 5, 7-12" 函数缩短它:

range

感谢@DavidArenburg,可以使用new_vec <- unname(sapply(split(vec, c(0, cumsum(diff(vec) > 1))), function(y) { paste(unique(range(y)), collapse='-') })) 代替tapply + sapply来进一步缩短:

split

答案 2 :(得分:7)

编辑:我首先通过对矢量进行排序来加快docendo的代码,所以现在它们实际上是平等的。

我还添加了alexis&#39;方法

readable_integers <- function(integers)
{
  integers <- sort(unique(integers))
  group <- cumsum(c(0, diff(integers)) != 1)

  paste0(vapply(split(integers, group),
           function(x){
             if (length(x) == 1) as.character(x)
             else paste0(range(x), collapse = "-")
           },
           character(1)),
           collapse = "; ")
}

library(microbenchmark)
vec = c(1, 2, 3, 5, 7, 8, 9, 10, 11, 12)
microbenchmark(
  docendo = {vec <- sort(vec)
    x <- cumsum(diff(vec) > 1)
   toString(tapply(vec, c(min(x), x), function(y) paste(unique(range(y)), )collapse = "-"))
  },
  Benjamin = readable_integers(vec),
  alexis = {vec <- sort(vec)
            as.character(split(as.integer(vec), cumsum(c(TRUE, diff(vec) != 1))))
            toString(gsub(":", "-", .Last.value))}
)

Unit: microseconds
     expr     min       lq     mean  median       uq     max neval
  docendo 205.273 220.3755 230.3134 228.293 235.4780 467.142   100
 Benjamin 121.991 128.4420 135.5302 133.574 143.3980 161.286   100
   alexis 121.698 128.0030 137.0374 136.507 143.3975 169.790   100

set.seed(pi)
vec = sample(1:1000, 900)

set.seed(pi)
vec = sample(1:1000, 900)

microbenchmark(
  docendo = {vec <- sort(vec)
   x <- cumsum(diff(vec) > 1)
   toString(tapply(sort(vec), c(min(x), x), function(y) paste(unique(range(y)), collapse = "-")))
  },
  Benjamin = readable_integers(vec),
  alexis = {vec <- sort(vec)
            as.character(split(as.integer(vec), cumsum(c(TRUE, diff(vec) != 1))))
            toString(gsub(":", "-", .Last.value))}
)
Unit: microseconds
     expr      min        lq      mean    median        uq      max neval
  docendo 1307.294 1353.7735 1420.3088 1379.7265 1427.8190 2554.473   100
 Benjamin  615.525  626.8155  661.2513  638.8385  665.3765 1676.493   100
   alexis  799.684  808.3355  866.1516  820.0650  833.2615 1974.138   100