3 votes

Modifier automatiquement un vecteur de caractères compliqué

J'ai un vecteur de caractères compliqué, dont chaque élément est constitué de chiffres et de lettres. Je veux simplifier ce vecteur, de sorte que les séquences de chiffres et/ou de lettres soient placées dans des plages. Voici un exemple de ce à quoi devraient ressembler les vecteurs d'entrée et de sortie :

# 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")

Je suis sûr qu'il doit y avoir un moyen de construire une fonction ou d'utiliser un paquet, pour faire cela d'une manière automatisée, mais malheureusement j'ai du mal à trouver une solution. Toute aide est la bienvenue !

UPDATE : Ajout d'un vecteur encore plus compliqué

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

1voto

MrFlick Points 34459

C'est peut-être encore un peu lourd, mais j'ai essayé de décomposer le problème en petites fonctions. Les voici. D'abord quelques fonctions d'aide générales

# 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
}

Et maintenant, quelques aides plus spécifiques pour le problème

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=",")  
})

Et enfin, nous le testons avec votre contribution

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"

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X