J'ai un vecteur de noms
et un autre vecteur v
que je dois faire correspondre avec noms
. Je souhaite recevoir les index des noms
où correspond v
. La correspondance partielle doit être autorisée, mais uniquement lorsque la correspondance partielle est unique.
L'exemple suivant couvre tous les cas pertinents:
match_names3 <- function(v, names) { exact <- match_names1(v, names) assertthat::assert_that(class(exact) != "list") return(exact) }
J'attends les résultats suivants: p >
match_names2 <- function(v, names) { sapply(v, function(i) grep(i, names)) }
Comment puis-je écrire une telle fonction? J'ai pensé à (combinaisons) de qui
et grep
mais je n'ai pas trouvé quelque chose d'utile jusqu'à présent?
Ce que j'ai essayé
( Avant de connaître l'exigence de correspondances partielles .. )
match_names1 <- function(v, names) { sapply(v, function(i) which(i == names)) }
Cela a bien fonctionné pour les exemples v1
, v2
et v5
.
Après avoir obtenu l'exigence de correspondances partielles
match_names(v1, names) # c(1, 2) match_names(v2, names) # c(1, 4) match_names(v3, names) # error match_names(v4, names) # 7 match_names(v5, names) # c(1, 2, 1)
.. qui, bien sûr, ne fonctionne que pour v4
Pour attraper v3
travaillé avec l'extension suivante de match_names1 :
names <- c("a", "b", "c", "ab", "def", "defg", "hij") v1 <- c("a", "b") v2 <- c("a", "ab") v3 <- c("d") v4 <- c("h") v5 <- c("a", "b", "a")
Cela couvre donc v1
, v2
, v3 code> et
v5
, mais pas v4
Merci d'avance pour toute indication.
3 Réponses :
J'ai opté pour des listes car à partir de votre question, on ne sait pas ce qui devrait se passer si une chaîne de v
correspond exactement plus d'une fois dans noms
et la seule façon de conserver toutes les correspondances exactes sont de renvoyer une liste. Si vous n'aimez pas les listes, vous pouvez simplement unlist ()
le résultat.
> match_names(v1, names) [[1]] [1] 1 [[2]] [1] 2 > # c(1, 2) > match_names(v2, names) [[1]] [1] 1 [[2]] [1] 4 > # c(1, 4) > match_names(v3, names) [[1]] integer(0) > # error > match_names(v4, names) [[1]] [1] 7 > # 7 > match_names(v5, names) [[1]] [1] 1 [[2]] [1] 2 [[3]] [1] 1 >
match_names <- function(v, names){ # check exact matches: resList <- lapply(v, function(elt) which(names == elt)) notMatched <- which(lengths(resList) == 0) if (length(notMatched) == 0) return (resList) #partial matching else{ resNotMatched <- lapply(v[notMatched], grep, x = names) matchedOnce <- which(lengths(resNotMatched) == 1) } resList[notMatched[matchedOnce]] <- resNotMatched[matchedOnce] return (resList) }
[[
peut être utilisé pour correspondre partiellement à un nom à la fois:
v1: a b 1 2 v2: a ab 1 4 v3: Error in setNames(seq_along(names), names)[[x, exact = FALSE]] : subscript out of bounds [1] "Error in setNames(seq_along(names), names)[[x, exact = FALSE]] : \n subscript out of bounds\n" attr(,"class") [1] "try-error" attr(,"condition") <simpleError in setNames(seq_along(names), names)[[x, exact = FALSE]]: subscript out of bounds> v4: h 7 v5: a b a 1 2 1
qui donne
f = function(v){ sapply(v, function(x) setNames(seq_along(names), names)[[x, exact=FALSE]]) } # try it on the example vs = list(v1,v2,v3,v4,v5) for (i in seq_along(vs)){ cat("\nv", i, ":\n", sep="") print(try( f(vs[[i]]) )) }
La fonction
match
devrait fonctionner dans tous les cas sauf pour la correspondance partielle v4
.
Pour répondre à une correspondance partielle, vous pouvez définir une fonction comme:
match_names <- function(v, names) { ind <- match(v, names) # If can't find the match then try partial matching if (any(is.na(ind))) { # grepl to find partial matching index ind <- which(grepl(v, names)) # To ensure partial matched value is unique. if (length(ind) > 1) ind <- NA } return(ind) } > match_names(v1, names) [1] 1 2 > match_names(v2, names) [1] 1 4 > match_names(v3, names) [1] NA > match_names(v4, names) [1] 7 > match_names(v5, names) [1] 1 2 1