web-dev-qa-db-fra.com

Fractionnement d'une colonne de chaîne de trame de données en plusieurs colonnes différentes

Ce que j'essaie d'accomplir, c'est de diviser une colonne en plusieurs colonnes. Je préférerais que la première colonne contienne "F", la deuxième colonne "US", la troisième "CA6" ou "DL", et la quatrième soit "Z13" ou "U13" etc etc. Mon df entier suit le même schéma de X.XX.XXXX.XXX ou X.XX.XXX.XXX ou X.XX.XX.XXX et je sais que la troisième colonne est l'endroit où se situe mon problème en raison des différentes longueurs. Je n'ai utilisé que substr dans le passé et je pourrais l'utiliser ici avec certaines instructions if, mais j'aimerais apprendre à utiliser le package stringr et POSIX pour ce faire (sauf s'il existe une meilleure option). Merci d'avance.

Voici mon df:

c("F.US.CLE.V13", "F.US.CA6.U13", "F.US.CA6.U13", "F.US.CA6.U13", 
"F.US.CA6.U13", "F.US.CA6.U13", "F.US.CA6.U13", "F.US.CA6.U13", 
"F.US.DL.U13", "F.US.DL.U13", "F.US.DL.U13", "F.US.DL.Z13", "F.US.DL.Z13"
)
27
Tim

Un moyen très direct consiste simplement à utiliser read.table sur votre vecteur de caractères:

> read.table(text = text, sep = ".", colClasses = "character")
   V1 V2  V3  V4
1   F US CLE V13
2   F US CA6 U13
3   F US CA6 U13
4   F US CA6 U13
5   F US CA6 U13
6   F US CA6 U13
7   F US CA6 U13
8   F US CA6 U13
9   F US  DL U13
10  F US  DL U13
11  F US  DL U13
12  F US  DL Z13
13  F US  DL Z13

colClasses doit être spécifié, sinon F est converti en FALSE (ce que je dois corriger dans "splitstackshape", sinon j'aurais recommandé que :))


Mise à jour (> un an plus tard) ...

Alternativement, vous pouvez utiliser ma fonction cSplit , comme ceci:

cSplit(as.data.table(text), "text", ".")
#     text_1 text_2 text_3 text_4
#  1:      F     US    CLE    V13
#  2:      F     US    CA6    U13
#  3:      F     US    CA6    U13
#  4:      F     US    CA6    U13
#  5:      F     US    CA6    U13
#  6:      F     US    CA6    U13
#  7:      F     US    CA6    U13
#  8:      F     US    CA6    U13
#  9:      F     US     DL    U13
# 10:      F     US     DL    U13
# 11:      F     US     DL    U13
# 12:      F     US     DL    Z13
# 13:      F     US     DL    Z13

Ou, separate de "tidyr", comme ceci:

library(dplyr)
library(tidyr)

as.data.frame(text) %>% separate(text, into = paste("V", 1:4, sep = "_"))
#    V_1 V_2 V_3 V_4
# 1    F  US CLE V13
# 2    F  US CA6 U13
# 3    F  US CA6 U13
# 4    F  US CA6 U13
# 5    F  US CA6 U13
# 6    F  US CA6 U13
# 7    F  US CA6 U13
# 8    F  US CA6 U13
# 9    F  US  DL U13
# 10   F  US  DL U13
# 11   F  US  DL U13
# 12   F  US  DL Z13
# 13   F  US  DL Z13
52

C'est ce que vous essayez de faire?

# Our data
text <- c("F.US.CLE.V13", "F.US.CA6.U13", "F.US.CA6.U13", "F.US.CA6.U13", 
"F.US.CA6.U13", "F.US.CA6.U13", "F.US.CA6.U13", "F.US.CA6.U13", 
"F.US.DL.U13", "F.US.DL.U13", "F.US.DL.U13", "F.US.DL.Z13", "F.US.DL.Z13"
)

#  Split into individual elements by the '.' character
#  Remember to escape it, because '.' by itself matches any single character
elems <- unlist( strsplit( text , "\\." ) )

#  We know the dataframe should have 4 columns, so make a matrix
m <- matrix( elems , ncol = 4 , byrow = TRUE )

#  Coerce to data.frame - head() is just to illustrate the top portion
head( as.data.frame( m ) )
#  V1 V2  V3  V4
#1  F US CLE V13
#2  F US CA6 U13
#3  F US CA6 U13
#4  F US CA6 U13
#5  F US CA6 U13
#6  F US CA6 U13
18
Simon O'Hanlon

Le chemin via unlist et matrix semble un peu compliqué, et vous oblige à coder en dur le nombre d'éléments (c'est en fait un très gros no-go. Bien sûr, vous pourriez contourner hard- coder ce numéro et le déterminer au moment de l'exécution)

Je choisirais un itinéraire différent et construirais un bloc de données directement à partir de la liste que strsplit renvoie. Pour moi, c'est conceptuellement plus simple. Il existe essentiellement deux façons de procéder:

  1. as.data.frame - mais comme la liste est exactement à l'envers (nous avons une liste de lignes plutôt qu'une liste de colonnes) nous devons transposer le résultat. Nous effaçons également les rownames car ils sont laids par défaut (mais c'est strictement inutile!):

    `rownames<-`(t(as.data.frame(strsplit(text, '\\.'))), NULL)
    
  2. Vous pouvez également utiliser rbind pour construire un bloc de données à partir de la liste des lignes. Nous utilisons do.call pour appeler rbind avec toutes les lignes comme arguments séparés:

    do.call(rbind, strsplit(text, '\\.'))
    

Les deux façons donnent le même résultat:

     [,1] [,2] [,3]  [,4]
[1,] "F"  "US" "CLE" "V13"
[2,] "F"  "US" "CA6" "U13"
[3,] "F"  "US" "CA6" "U13"
[4,] "F"  "US" "CA6" "U13"
[5,] "F"  "US" "CA6" "U13"
[6,] "F"  "US" "CA6" "U13"
…

De toute évidence, la deuxième façon est beaucoup plus simple que la première.

8
Konrad Rudolph