1
votes

Faire correspondre les chaînes avec une correspondance partielle autorisée, mais uniquement lorsqu'il y a une correspondance unique

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.


0 commentaires

3 Réponses :


1
votes

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


0 commentaires

1
votes

[[ 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]]) ))
}


0 commentaires

1
votes

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


0 commentaires