【问题标题】:Modify complicated character vector automatically自动修改复杂的字符向量
【发布时间】:2017-12-10 14:12:56
【问题描述】:

我有一个复杂的字符向量,其中向量的每个元素都由数字和字母组成。我想简化这个向量,以便将数字和/或字母序列放入范围内。下面是一个例子,输入和输出向量应该是这样的:

# Input vector
input_vec <- c("1,2,3,4,5", "1,2,3,5,6,7,8", "2,3,4,5", "A,B,C", "1,2,3,4,5,A,B,8,9,10,11")

# Here some function should be applied, to create the desired output vector automatically

# Desired output vector
output_vec <- c("1-5", "1-3,5-8", "2-5", "A-C", "1-5,A-B,8-11")

我确信必须有一种方法来构建函数或使用包,以自动化的方式执行此操作,但不幸的是我正在努力寻找解决方案。非常感谢任何帮助!

更新:添加了一个更复杂的向量

input_vec2 <- c("1,2,3,4,5", "1,2,3,5,6,7,8", "2,3,4,5", "A,B,C", "1,2,3,4,5,A,B,8,9,10,11", 
"1", "1,2,3,-4", "lala,3") # This part is new

output_vec2 <- c("1-5", "1-3,5-8", "2-5", "A-C", "1-5,A-B,8-11",
"1", "1-3,-4", "lala,3") # This part is new

【问题讨论】:

  • LETTERS 也可以是A, B, C, K, L, M,这需要变成A-C, K-M吗?
  • 嗨,Sotos,是的,他们可以

标签: r function vector character


【解决方案1】:

这可能还是有点臃肿,但我试图将问题分解为更小的函数。他们来了。首先是一些通用的辅助函数

# Is value numeric?
is_numeric <- function(x) suppressWarnings(!is.na(as.numeric(x)))
# Greate IDs for sequences of values using run-length encoding
rleg <- function(x) {
  r <- rle(x);
  val <- list(group_value = r$values)
  r$values <- seq_along(r$values); 
  val$group_id <- inverse.rle(r)
  val
}

现在还有一些更具体的帮助解决问题

collapse_sequence <- function(x) {
  if (length(x)>1) {
    paste0(x[1],"-", x[length(x)])
  } else {
    x
  }
}

find_runs <- function(x, key=x) {
  nona <- function(x) {x[is.na(x)]<-0; x}
  run <- cumsum(nona(c(1,diff(key)))!=1)
  Map(collapse_sequence, split(x, run))
}

collapse_numeric <- function(x) {
  paste(sapply(find_runs(x, as.numeric(x)), collapse_sequence), collapse=",")
}

collapse_character <- function(x) {
  key <- sapply(x, function(z) ifelse(nchar(z)==1, utf8ToInt(z), NA))
  paste(sapply(find_runs(x, key), collapse_sequence), collapse=",")
}

collapse_runs <- Vectorize(function(x) {
  x <- strsplit(x, ",")[[1]]
  type <- ifelse(is_numeric(x), 1, ifelse(nchar(x)==1, 2, 3))
  group <- rleg(type)
  runs <- Map(function(v, type) {
    if(type==1) {
      collapse_numeric(v)
    } else {
      collapse_character(v)
    }
  },split(x, group$group_id), group$group_value)
  paste(runs, collapse=",")  
})

最后我们用你的输入来测试它

input_vec <- c("1,2,3,4,5", "1,2,3,5,6,7,8", "2,3,4,5", "A,B,C", "1,2,3,4,5,A,B,8,9,10,11")
unname(collapse_runs(input_vec))
# [1] "1-5"          "1-3,5-8"      "2-5"          "A-C"          "1-5,A-B,8-11"
input_vec2 <- c("1,2,3,4,5", "1,2,3,5,6,7,8", "2,3,4,5", "A,B,C", "1,2,3,4,5,A,B,8,9,10,11", "1", 
            "1,2,3,-4", "lala,3")
unname(collapse_runs(input_vec2))
# [1] "1-5"          "1-3,5-8"      "2-5"          "A-C"          "1-5,A-B,8-11"
# [6] "1"            "1-3,-4"       "lala,3"

【讨论】:

  • 非常感谢您的代码@MrFlick!对于我的示例,您的代码工作得非常好,但不幸的是我的向量中有一些单词(参见上面示例向量 2 中的“lala”)。有没有办法调整你的函数,让它们能够处理整个单词?
  • 我已经更新了代码以适应这种情况。但是当你第一次问这个问题时,你真的应该确保你的数据尽可能地代表你真正的问题。回答完问题再改是不礼貌的。
  • 首先非常感谢您的更新。这真的帮助了我很多。另外,我想道歉,我真的不想以任何方式不礼貌。在我的第一个示例中,我试图尽可能好地复制我的问题,但没有意识到额外的情况会导致进一步的问题。我以后一定会避免这种情况。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-03
  • 2016-07-06
  • 1970-01-01
  • 1970-01-01
  • 2018-09-20
  • 1970-01-01
  • 2021-04-30
相关资源
最近更新 更多