Créer une fonction pour changer un caractère

Postez ici vos questions, réponses, commentaires ou suggestions - Les sujets seront ultérieurement répartis dans les archives par les modérateurs

Modérateur : Groupe des modérateurs

Mathieu Lazarus
Messages : 3
Enregistré le : 27 Avr 2018, 15:22

Créer une fonction pour changer un caractère

Messagepar Mathieu Lazarus » 15 Oct 2018, 10:04

Bonjour,

J'essaie de construire une fonction pour corriger des fautes de frappes éventuelles dans des données enregistrés par un opérateur (je me doute qu'il doit y avoir un package pour ça, si c'est le cas, je suis toute ouïe).
Je suis confronté à un problème que je ne comprends pas et je n'arrive pas à trouver la solution sur des forums. Ce problème doit venir de mon incompréhension de la fonction "function()".

Lorsque je prends un code simple pour modifier des variables de classe character en d'autres:

Code : Tout sélectionner

Test=c(rep("A",3),rep("B",3))
Test
Test[which(Test %in% "B")] <- "A"
Test


Pas de soucis, les "B" deviennent des "A".

Par contre, dès que j'insère ça dans une fonction:

Code : Tout sélectionner

Test=c(rep("A",3),rep("B",3))

Testouille <- function(Word,Word_List){
  print(Word_List)
  Word_List[which(Word_List %in% "B")] <- Word
  print(Word_List)   
}

Testouille("A",Test)
Test


Les instructions "print()" me permettent de confirmer que la modification a bien eu lieue sur l'objet "Word_list" mais lorsque j'exécute ma fonction la modification de l'objet "Test" n'est pas gardée en mémoire (je pensais que si l'objet est défini avant la fonction dans l'environnement global il est toujours modifié dans l'environnement global... J'ai également essayé "<<-").
Si quelqu'un peut m'exposer l'étendue de mon ignorance, j'en serais ravi.

Merci d'avance.
Mathieu.

Serge Rapenne
Messages : 1426
Enregistré le : 20 Aoû 2007, 15:17
Contact :

Re: Créer une fonction pour changer un caractère

Messagepar Serge Rapenne » 15 Oct 2018, 11:43

Bonjour,

Ce comportement est parfaitement normal et logique. Pour que l'objet Test soit modifié, il faut le changer explicitement :

Code : Tout sélectionner

Testouille("A",Test)
ne fait que lancer la fonction avec les paramètres voulus, rien n'indique que c'est Test qui doit prendre les valeurs de sortie de la fonction. Tu pourrais très bien ne pas vouloir changer les données d'entrée.
Dans ton cas il faudrait faire :

Code : Tout sélectionner

Test<-Testouille("A",Test)

Informations complémentaires :
Les variables ne sont connus que dans leur portée c-a-d qu'une variable déclarée dans une fonction n'est connu qu'à l’intérieur de cette fonction.
Tu aurais très bien pu déclarer ta fonction comme ça

Code : Tout sélectionner

Testouille <- function(Word,Test){
  Test[which(Test %in% "B")] <- Word 
  Test
}
Ce qui n'aurait rien changé au contenu de Test dans le programme principal. et tu aurais pu appeller la fonction comme ça:

Code : Tout sélectionner

Test<-Testouille("A",Test=Test)


Serge

Jean-Emmanuel Longueville
Messages : 310
Enregistré le : 23 Fév 2011, 08:03

Re: Créer une fonction pour changer un caractère

Messagepar Jean-Emmanuel Longueville » 15 Oct 2018, 12:19

Connaissez vous la fonction gsub ?
Qui semble permettre de faire ce que vous voulez voir plus.
Jean-Emmanuel
Ingénieur d'étude LNEC

Mathieu Lazarus
Messages : 3
Enregistré le : 27 Avr 2018, 15:22

Re: Créer une fonction pour changer un caractère

Messagepar Mathieu Lazarus » 15 Oct 2018, 16:05

Bonjour Serge,

En effet... Merci beaucoup pour cette leçon, ça m'évitera de perdre du temps à l'avenir.

Connaissez vous la fonction gsub ?
Qui semble permettre de faire ce que vous voulez voir plus.


Je ne la connais pas et elle semble faire ce que je cherche à faire, mais pas dans la totalité...
Pour détailler je vous met les trois lignes de code que j'avais écris ( vous le trouverez peut-être très saugrenu, mais je n'ai découvert l’existence des expressions régulières depuis seulement une journée).

Code : Tout sélectionner

# Liste de nom "tel qu'il m'a été donné par l'opérateur"
 Factor_list <- c("MiChEL","Michel","Michel","Mechel", "MICHEL","Michel","Houellebeck","HOUELEBECQ","Gertrude","GERTRUDE")
 
# Nom des facteurs (il faut forcément les connaitre à priori ici)
 Factor_names<- c("Michel", "Houellebecq", "Gertrude")

# Définition d'une boucle qui permet de créer les diverses possibilités de nom pour une faute de frappe
library(stringr)
Correct_names <- function(Word,Factor_List) {
  for (k in 1:nchar(Word)) {
    X <- Word
    str_sub(X, k, k) <- "*"
    if (k == 1) {
      RE_Names <- X
    } else{
      RE_Names <- paste0(RE_Names, "|", X)
    }
  }
  misspelled_names <- grep(RE_Names,Factor_List,value = T,ignore.case = T)
  Factor_List[which(Factor_List %in% misspelled_names)] <- Word
  # Si je ne me trompe pas, je peux remplacer cette ligne par
  # gsub(misspelled_names, Word)
  Factor_List
}

 for (i in Factor_names){
  Factor_list <- Correct_names(i,Factor_list)
  Factor_list
}


ça fonctionne mais c'est pas très beau...
Dans mon cas, générer les possibles fautes de frappe est utile car j'ai plus de 10 niveaux sur certains facteurs.

Encore merci.
Mathieu.

Jean-Emmanuel Longueville
Messages : 310
Enregistré le : 23 Fév 2011, 08:03

Re: Créer une fonction pour changer un caractère

Messagepar Jean-Emmanuel Longueville » 16 Oct 2018, 11:06

Si j'ai bien compris votre soucis il y a deux types de faute de frappe à réglé les majuscules d'un coté et les remplacements de caractère de l'autre.
Pour les majuscules je peux vous proposer la fonction suivante :

Code : Tout sélectionner

#### Nettoyage des majuscules ####
#' @title str_sentence_case
#' @author Jean-Emmanuel Longueville
#' @description This function remove all upper case except the first of a string.
#' @param string
#' @example :
#' \code{
#' string <- "Une Phrase Ecrite N'importe COMMENT EN TERME DE MAJUSCULE"
#' str_sentence_case(string)
####


str_sentence_case <- function( string ) {
  # adapted from https://stackoverflow.com/questions/18509527/first-letter-to-upper-case
  # behave as expected, even for short string (i.e. "" or "a")
  substr(string, 1, 1) <- toupper(substr(string, 1, 1))
  substr(string, 2, nchar(string)) <- tolower(substr(string, 2, nchar(string)))
  return(string)
}

Cependant ce code ne corrigera pas les lettres proches mais différentes. Il vous faudrait une sorte de correcteur orthographique?
Jean-Emmanuel
Ingénieur d'étude LNEC

François Bonnot
Messages : 537
Enregistré le : 10 Nov 2004, 15:19
Contact :

Re: Créer une fonction pour changer un caractère

Messagepar François Bonnot » 16 Oct 2018, 11:31

Bonjour,
Je propose la méthode suivante basée sur un score égal au nombre de lettres communes bien placées.
Elle est rudimentaire et ne prend pas en compte les insertions/délétions placées près du début de la chaine.

Code : Tout sélectionner

score <- function(word1,word2) {
    if (nchar(word2)>nchar(word1)) word2 <- substr(word2,1,nchar(word1))
    else word1 <- substr(word1,1,nchar(word2))
    sum(strsplit(tolower(word2),'')[[1]]==strsplit(tolower(word1),'')[[1]])
}

replace.word <- function(dico,word) {
    s <- sapply(dico,score,word)
    if (max(s) < nchar(word)/2) return(word) ## score insuffisant
    dico[which(s==max(s))[1]]
}

replace.list <- function(list.words,dico) {
    sapply(list.words,function(x) replace.word(dico,x))
}

replace.list(Factor_list,Factor_names)
François

Mathieu Lazarus
Messages : 3
Enregistré le : 27 Avr 2018, 15:22

Re: Créer une fonction pour changer un caractère

Messagepar Mathieu Lazarus » 22 Oct 2018, 12:47

Bonjour,

Pour la question correcteur orthographique, en effet c'est l'idée. Pour faire simple, j'ai des fiches de production correspondant à un produit. Dans chacune de ces fiches j'ai un facteur qui regroupe plusieurs produits. Le problème c'est qu'il existe toujours des fautes de frappes. J'ai fais une fonction à base de sapply/list.files/read_exel pour construire mon data_frame, mais ça ne corrige pas les fautes dans les facteurs. Très honnêtement, c'est possible de corriger les facteurs manuellement mais avec une fonction, si je suis de nouveau face à ce problème, j'aurais une réponse.

D’où la suggestions de Mr Bonnot que je trouve très intéressante. Je vais la tester sur plusieurs exemples et je vous dirais à l'occasion ce que j'en pense.

Dans tous les cas c'est super, merci beaucoup!

A+,
Mathieu.


Retourner vers « Questions en cours »

Qui est en ligne

Utilisateurs parcourant ce forum : Google [Bot] et 1 invité