3
votes

Diviser une colonne de caractères en deux et répéter

Voici les données dont je dispose,

> dataf
  v11_a v11_b v12_a v12_b v13_a v13_b
1    24    31     0     0    31    64
2    31    28     0     0    18    58

Je veux diviser chaque colonne (de la v11 à la v99) en deux colonnes. J'utilise substr pour le diviser comme ceci pour la première colonne,

v11 %>% separate(v11, into = c('v11_a', 'v11_b'), sep = 4)
Error in UseMethod("separate_") : 
  no applicable method for 'separate_' applied to an object of class "character"

Ça a l'air bien sauf la colonne X_data . Je ne veux pas l'avoir dans la sortie. Une meilleure façon de faire ça?

tidyr :: separ ne sera pas applicable car mes données sont de type caractère?

transform(v11, v11_a = substr(v11, 1, 4), v11_b = substr(v11, 5, 8))

    X_data v11_a v11_b
1 00240031  0024  0031
2 00310028  0031  0028

Deuxièmement, comment puis-je répéter le processus pour les colonnes suivantes (par exemple v11 à v99)?

Idéalement, après la division, puis la conversion en type numérique, mes données finales devraient ressembler à ceci,

v11 <- c("00240031", "00310028")
v12 <- c("00000000", "00000000")
v13 <- c("00310064", "00180058")
data <- data.frame(v11, v12, v13)
data <- lapply(data, as.character)
testdata <- as.data.frame(data, stringsAsFactors = F)
testdata[testdata == '0'] <- '000000000'
testdata

Commentaire:

La rapidité avec laquelle vous trouvez des solutions étonnantes est incroyable. Merci à tous.


1 commentaires

3 Réponses :


1
votes

En base R , cela peut être fait en faisant une boucle dans les colonnes, en remplaçant les 0 entre non-zéro par un délimiteur , , lu dans un data.frame ( read.table ), et cbind la liste des ensembles de données

testdata %>%
   summarise_all(funs(list(read.table(text =sub("^(....)", "\\1 ", .),
             header = FALSE)))) %>%
   unnest

Cela peut être également fait avec tidyverse en d'abord rassembler au format 'long', puis faire l'ion séparé , et enfin spread code > retournez-le au format 'large'

library(tidyverse)
gather(testdata) %>%
    separate(value, into = c('a', 'b'), sep=4, convert = TRUE) %>% 
    gather(key1, val, a:b) %>%
    unite(key, key, key1, sep="_") %>% 
    group_by(key) %>% 
    mutate(ind = row_number()) %>% 
    spread(key, val) %>%
    select(-ind)
# A tibble: 2 x 6
#  v11_a v11_b v12_a v12_b v13_a v13_b
#  <int> <int> <int> <int> <int> <int>
#1    24    31     0     0    31    64
#2    31    28     0     0    18    58

Ou une autre option consiste à utiliser summary_all avec read.table code >

lst1 <- lapply(testdata, function(x) {
      x1 <- read.table(text = sub("(?<=[1-9])0+", ",", x, perl = TRUE),
             header = FALSE, sep=",", col.names = c('a', 'b'), fill = TRUE)
      replace(x1, is.na(x1), 0)})
do.call(cbind, lst1)
#   v11.a v11.b v12.a v12.b v13.a v13.b
#1    24    31     0     0    31    64
#2    31    28     0     0    18    58


2 commentaires

Merci beaucoup. Fonctionne comme un charme. Vous êtes, comme d'habitude, l'un des meilleurs sauveteurs ici. Avez-vous des lectures recommandées pour moi afin que je puisse les faire moi-même?


@ome. Heureux de savoir que cela fonctionne. Je suggérerais de pratiquer au moins une heure par jour en plus de la lecture. Je pense que sur YouTube, vous pouvez obtenir beaucoup de vidéos gratuites, mais la pratique est importante



1
votes

Voici une idée utilisant le très pratique pour de telles opérations library(splitstackshape),

library(splitstackshape)

cSplit(setDT(testdata)[, lapply(.SD, function(i) gsub("(.{4})", "\\1 ", i))], names(testdata), sep = ' ')
#   v11_1 v11_2 v12_1 v12_2 v13_1 v13_2
#1:    24    31     0     0    31    64
#2:    31    28     0     0    18    58


1 commentaires

On dirait un paquet très pratique.



1
votes

Certains jouent dans data.table et réutilisent votre logique substr () existante:

library(data.table)
setDT(testdata)
cols <- paste0("v", 11:13)
new_cols <- paste0(rep(cols, 2), rep(c("a", "b"), each = length(cols)))
extra <- function(x) substr(x, 1, 4)
extrb <- function(x) substr(x, 5, 8)
testdata[, (new_cols) := c(lapply(.SD, extra), lapply(.SD, extrb)), .SDcols = cols]

> testdata
        v11      v12      v13 v11a v12a v13a v11b v12b v13b
1: 00240031 00000000 00310064 0024 0000 0031 0031 0000 0064
2: 00310028 00000000 00180058 0031 0000 0018 0028 0000 0058


2 commentaires

J'aime la facilité de débogage de votre code. Une idée comment avoir une sortie sans les trois premières colonnes?


Vous pouvez les supprimer avec testdata [! Cols, with = FALSE] .