3
votes

Niveaux de facteur personnalisés dans une chaîne concaténée

J'ai une variable factorielle qui est composée de deux sous-chaînes séparées par un _ , comme string1_string2 . Je veux définir les niveaux de facteur du préfixe ("string1") et du suffixe ("string2") séparément, puis définir un ensemble global de niveaux de facteur pour la chaîne concaténée. De plus, la priorité des niveaux dans la première par rapport à la deuxième sous-chaîne peut varier.


Un petit exemple de ce que je veux réaliser:

[1] DBO_A  PH_A   COND_A DBO_B  PH_B   COND_B DBO_C  PH_C   COND_C
Levels: PH_B COND_B DBO_B PH_A COND_A DBO_A PH_C COND_C DBO_C

Si je ne définissez pas les niveaux de facteurs, ils seront classés par ordre alphabétique. Maintenant, je veux définir les niveaux des chaînes à gauche et à droite du séparateur _ , par exemple

  1. PH COND DBO sur le côté gauche (LHS).
  2. B A C sur le côté droit (RHS).

De plus, je souhaite spécifier quel côté, LHS ou RHS, a priorité sur l'autre. Selon le côté qui a la priorité, l'ordre général des niveaux sera différent:

(1) Si les niveaux sur LHS sont précédents:

[1] DBO_A  PH_A   COND_A DBO_B  PH_B   COND_B DBO_C  PH_C   COND_C
Levels: PH_B PH_A PH_C COND_B COND_A COND_C DBO_B DBO_A DBO_C


0 commentaires

4 Réponses :


-1
votes

Que diriez-vous de quelque chose comme

x <- with(expand.grid(x = c("DBO", "PH", "COND"), y = c("A", "B", "C")),
          factor(paste(x, y, sep = "_"), levels = paste(x, y, sep = "_")))

Vous n'avez pas besoin d'écrire tous les niveaux possibles, juste les niveaux d'un côté et de l'autre.


2 commentaires

Pouvez-vous dire comment non? Compte tenu de la façon dont vous avez utilisé expand.grid, coller sera ordonné d'abord par x et ensuite par y, c'est ainsi que j'ai compris ce dont vous aviez besoin.


L'essentiel n'est pas de savoir comment je produis mon x . Ce sont juste des données reproductibles. Veuillez lire ma sortie attendue.



2
votes

4 commentaires

x dans ma question est une donnée donnée. Ce que je veux, c'est comment utiliser x pour obtenir les deux données attendues.


@DarrenTsai Je ne comprends pas, x est donné, oui, et y ne l'est pas? Vous voulez obtenir y à partir de x ? La fonction dans ma réponse fait ce que vous décrivez dans la question.


Oui, tellement génial !! Merci beaucoup.


Mais comment puis-je obtenir ma deuxième sortie attendue? custom_fct2 (x, b, a) ?



2
votes

Nous pouvons utiliser base R pour ce faire. En utilisant sub supprimer la sous-chaîne dans les niveaux du vecteur, avec match créer un index numérique en vérifiant les valeurs qui sont dans l'ordre personnalisé, réaffecter les niveaux du facteur par ordre en suivant la séquence des niveaux de vecteur en fonction de la correspondance index

f1 <- function(vec, lvls1, lvls2, flag = "former") {
   i1 <- match(sub("_.*", "", levels(vec)), lvls1)
   i2 <- match(sub(".*_", "", levels(vec)), lvls2)

   if(flag == 'former') {
     factor(vec, levels = levels(vec)[seq_along(levels(vec))[order(i1, i2)]])
   } else {
     factor(vec, levels = levels(vec)[seq_along(levels(vec))[order(i2, i1)]])

   }


}

f1(x, c("PH", "COND", "DBO"), c("B", "A", "C"))
#[1] DBO_A  PH_A   COND_A DBO_B  PH_B   COND_B DBO_C  PH_C   COND_C
#Levels: PH_B PH_A PH_C COND_B COND_A COND_C DBO_B DBO_A DBO_C


f1(x, c("PH", "COND", "DBO"), c("B", "A", "C"), flag = "latter")
#[1] DBO_A  PH_A   COND_A DBO_B  PH_B   COND_B DBO_C  PH_C   COND_C
#Levels: PH_B COND_B DBO_B PH_A COND_A DBO_A PH_C COND_C DBO_C

Pour le second cas, inversez simplement l'index dans order

factor(x, levels = levels(x)[seq_along(levels(x))[order(i2, i1)]])

Pour une utilisation répétée, peut être enveloppé dans une fonction

i1 <- match(sub("_.*", "", levels(x)), c("PH", "COND", "DBO"))
i2 <- match(sub(".*_", "", levels(x)), c("B", "A", "C"))
factor(x, levels = levels(x)[seq_along(levels(x))[order(i1, i2)]])


2 commentaires

hé, votre méthode est excellente mais vous modifiez mes données d'origine. Vous pouvez comparer mes données attendues et votre sortie.


@DarrenTsai Désolé, j'ai oublié de vérifier la sortie. Merci d'avoir fait remarquer cela. Fixé



2
votes

À l'aide des fonctions pratiques data.table tstrsplit et setorderv .

Créez un vecteur de noms de colonnes (arbitraires) pour les sous-chaînes ( cols ). Convertissez le vecteur en data.table ( d ). Divisez le vecteur en deux colonnes ( (cols): = tstrsplit (x, split = "_") ). Définissez les niveaux de facteur des sous-chaînes ( factor (V1, levels = l1) ). Triez les données soit par la première sous-chaîne puis la deuxième sous-chaîne, soit par la deuxième puis par la première ( setorderv (d, if (prec == 1) cols else rev (cols)) ). Utilisez la colonne ordonnée 'x' de la table data.table comme niveaux de facteur du vecteur 'x' ( factor (x, levels = d $ x) ).

f2 <- function(x, l1, l2, prec){
  m <- cbind(factor(sub("_.*", "", x), l1), factor(sub(".*_", "", x), l2))
  i <- c(1, 2, 1)[prec:(prec + 1)]
  factor(x, levels = as.character(x)[order(m[ , i[1]], m[ , i[2]])])}

f2(x, l1, l2, prec = 1)
# [1] DBO_A  PH_A   COND_A DBO_B  PH_B   COND_B DBO_C  PH_C   COND_C
# Levels: PH_B PH_A PH_C COND_B COND_A COND_C DBO_B DBO_A DBO_C

f2(x, l1, l2, prec = 2)
# [1] DBO_A  PH_A   COND_A DBO_B  PH_B   COND_B DBO_C  PH_C   COND_C
# Levels: PH_B COND_B DBO_B PH_A COND_A DBO_A PH_C COND_C DBO_C

Une alternative base , dans le même esprit, mais en plaçant des sous-chaînes dans une matrice à la place. Utilisez une expression régulière standard (voir par exemple ici ) pour récupérer des sous-chaînes. Convertir en facteur et définir les niveaux. Créez un index de colonne ( i ). Niveaux d'ordre de 'x' ( as.character (x) [order (m [ i [1]], m [ i [2]])]) ).

library(data.table)

f <- function(x, l1, l2, prec){
  cols <- c("V1", "V2")
  d <- data.table(x)
  d[ , (cols) := tstrsplit(x, split = "_")]
  d[ , `:=`(
    V1 = factor(V1, levels = l1),
    V2 = factor(V2, levels = l2))]
  setorderv(d, if(prec == 1) cols else rev(cols))
  factor(x, levels = d$x)
}

# First substring has precedence
f(x, l1 = c("PH", "COND", "DBO"), l2 = c("B", "A", "C"), prec = 1)
# [1] DBO_A  PH_A   COND_A DBO_B  PH_B   COND_B DBO_C  PH_C   COND_C
# Levels: PH_B PH_A PH_C COND_B COND_A COND_C DBO_B DBO_A DBO_C

# Second substring has precedence
f(x, l1 = c("PH", "COND", "DBO"), l2 = c("B", "A", "C"), prec = 2)
# [1] DBO_A  PH_A   COND_A DBO_B  PH_B   COND_B DBO_C  PH_C   COND_C
# Levels: PH_B COND_B DBO_B PH_A COND_A DBO_A PH_C COND_C DBO_C


2 commentaires

C'est ce dont j'ai besoin. Merci beaucoup pour cette réponse et la révision de ma question.


Je le vois. Cela fonctionne comme je veux. Je suis tellement reconnaissant de votre gentillesse.