J'ai un aspect de data
comme ceci:
bind_rows(d1, d2, d3) %>% as_tibble() # A tibble: 6 x 4 A B C D <dbl> <dbl> <dbl> <dbl> 1 1 2 3 4 2 1 2 3 4 3 1 2 3 4 4 10 20 30 40 5 10 20 30 40 6 NA 200 300 400
C'était une mauvaise liaison par lignes et je voulais diviser les data
en 3 sous-données ( d1
, d2
et d3
) comme ceci:
REMARQUE: Dans ma situation réelle, d1
, d2
et d3
ont des nrow()
. J'ai mis nrow(d1) = 3
, nrow(d2) = 2
et nrow(d3) = 1
juste pour simplifier la question dans cet exemple.
d1 <- data.frame(A = rep(1,3), B = rep(2,3), C = rep(3,3), D = rep(4,3)) d2 <- data.frame(A = rep(10,2), B = rep(20,2), C = rep(30,2), D = rep(40,2)) d3 <- data.frame( B = 200, C = 300, D = 400) > d1 A B C D 1 1 2 3 4 2 1 2 3 4 3 1 2 3 4 > d2 A B C D 1 10 20 30 40 2 10 20 30 40 > d3 B C D 1 200 300 400
Et puis je pourrais les lier correctement en utilisant bind_rows
de dplyr
data <- structure(list(A = c("1", "1", "1", "A", "10", "10", "B", "200"), B = c("2", "2", "2", "B", "20", "20", "C", "300"), C = c("3","3", "3", "C", "30", "30", "D", "400"), D = c("4", "4", "4", "D", "40", "40", NA, NA)), row.names = c(NA, -8L), class = c("tbl_df","tbl", "data.frame")) data > data # A tibble: 8 x 4 A B C D <chr> <chr> <chr> <chr> 1 1 2 3 4 2 1 2 3 4 3 1 2 3 4 4 A B C D 5 10 20 30 40 6 10 20 30 40 7 B C D NA 8 200 300 400 NA
Le problème est que je suis troublé par la façon d'obtenir la d1
, d2
et d3
de data
.
Toute aide sera grandement appréciée!
3 Réponses :
Voici une solution tidyverse
.
process_df
prend un process_df
données et définit les noms de colonne et supprime la première ligne.
cars %>% add_row(speed = 1, dist = 2, .before = TRUE)
Ajoutez une ligne d'en-tête contenant uniquement les noms de colonne.
Utilisez rowwise()
et c_across()
pour obtenir les valeurs de toutes les colonnes par ligne. Utilisez cette option pour identifier les lignes qui sont des lignes d'en-tête.
group_map
appliquera une fonction sur chaque groupe et bind_rows
combinera les résultats.
cars %>% add_row(!!!new_row, .before = TRUE) # (Works)
!!!
dans new_row
set_names(names(.))
crée un vecteur nommé qui représente la ligne que nous voulons ajouter. Cependant, add_row
n'accepte pas un vecteur nommé - il veut que les valeurs soient spécifiées comme arguments.
Voici un exemple simplifié.
cars %>% add_row(new_row, .before = TRUE) # (Error)
add_row
n'accepte pas un vecteur nommé, donc cela ne fonctionne pas.
new_row <- c(speed = 1, dist = 2)
!!!
décompressera le vecteur comme arguments de la fonction.
data %>% add_row(!!!set_names(names(.)), .before = 1) %>% rowwise() %>% mutate( group = all(is.na(c_across()) | c_across() %in% names(.)) ) %>% ungroup() %>% mutate(group = cumsum(group)) %>% group_by(group) %>% group_map(process_df) %>% bind_rows() #> # A tibble: 6 x 4 #> A B C D #> <chr> <chr> <chr> <chr> #> 1 1 2 3 4 #> 2 1 2 3 4 #> 3 1 2 3 4 #> 4 10 20 30 40 #> 5 10 20 30 40 #> 6 NA 200 300 400
!!!
ci-dessus aboutit essentiellement à ceci:
process_df <- function(df, ...) { df %>% set_names(slice(., 1)) %>% select(which(!is.na(names(.)))) %>% slice(-1) }
Désolé, je n'ai pas clarifié la question. Votre réponse fonctionne lorsque d1
, d2
et d3
ont le même nrow
, mais ma situation réelle est que d1
, d2
et d3
ont un nrow
différent et j'ai mis à jour la question.
@zhiweili, j'ai mis à jour ma réponse pour gérer les nouveaux exemples de données.
@ Paul, merci. Cela m'aide vraiment, mais il y a une petite question que je ne comprends pas bien la ligne de code commençant par add_row(....)
. Je sais que ce code de ligne ajoute une nouvelle ligne en haut des data
et je peux remplacer ce code de ligne par rbind(names(data), data)
qui pourrait faire la même chose. Mais je veux toujours savoir la théorie comment cela fonctionne dans votre code, en particulier le !!!
(Je google et dit: !!!
est généralement utilisé pour évaluer une liste d'expressions, mais je ne comprends pas )
Voir ma réponse mise à jour pour l'explication.
Est-ce que ça marche:
data # A tibble: 5 x 4 A B C D <chr> <chr> <chr> <chr> 1 1 2 3 4 2 A B C D 3 10 20 30 40 4 B C D NA 5 200 300 400 NA data <- rbind(LETTERS[1:4],data) data # A tibble: 6 x 4 A B C D <chr> <chr> <chr> <chr> 1 A B C D 2 1 2 3 4 3 A B C D 4 10 20 30 40 5 B C D NA 6 200 300 400 NA split(data, rep(1:ceiling(nrow(data)/2), each = 2)) $`1` # A tibble: 2 x 4 A B C D <chr> <chr> <chr> <chr> 1 A B C D 2 1 2 3 4 $`2` # A tibble: 2 x 4 A B C D <chr> <chr> <chr> <chr> 1 A B C D 2 10 20 30 40 $`3` # A tibble: 2 x 4 A B C D <chr> <chr> <chr> <chr> 1 B C D NA 2 200 300 400 NA
Désolé, je n'ai pas clarifié la question. Votre réponse fonctionne lorsque d1
, d2
et d3
ont le même nrow
, mais ma situation réelle est que d1
, d2
et d3
ont un nrow
différent et j'ai mis à jour la question.
Solution de base R:
df <- structure(list(A = c("1", "1", "1", "A", "10", "10", "B", "200"), B = c("2", "2", "2", "B", "20", "20", "C", "300"), C = c("3","3", "3", "C", "30", "30", "D", "400"), D = c("4", "4", "4", "D", "40", "40", NA, NA)), row.names = c(NA, -8L), class = c("tbl_df","tbl", "data.frame"))
Y compris l'envoi de data.frames séparés vers l'environnement mondial:
df <- structure(list(A = c("1", "A", "10", "B", "200"), B = c("2", "B", "20", "C", "300"), C = c("3", "C", "30", "D", "400"), D = c("4","D", "40", NA, NA)), row.names = c(NA, -5L), class = c("tbl_df", "tbl", "data.frame"))
Solution Tidyverse:
df %>% rbind(names(.), .) %>% t() %>% data.frame() %>% type.convert() %>% split.default(cumsum(!sapply(., is.integer))) %>% Map(function(x){ y <- setNames(t(x[,-1, drop = FALSE]), x[,1]) data.frame(y[,!is.na(colSums(y)), drop = FALSE])}, .) %>% set_names(str_c('d', names(.))) %>% list2env(., .GlobalEnv)
Notez la solution ajustée pour refléter la modification de la question:
rdf <- type.convert(data.frame(t(rbind(names(df), df)))) dflist <- Map(function(x) { y <- setNames(t(x[, -1, drop = FALSE]), x[, 1]) y[, !is.na(colSums(y))] }, split.default(rdf, cumsum(!sapply(rdf, is.integer)))) list2env(setNames(dflist, paste0('d', names(dflist))), .GlobalEnv)
Nouvelle solution incluant push to Global Env:
rdf <- type.convert(data.frame(t(rbind(names(df), df)))) Map(function(x){ y <- setNames(t(x[,-1, drop = FALSE]), x[,1]); y[,!is.na(colSums(y))] }, split.default(rdf, cumsum(!sapply(rdf, is.integer))))
Solution Tidyverse ajustée:
library(tidyverse) df %>% rbind(names(df), .) %>% split(cumsum(seq_len(nrow(.)) %% 2)) %>% Map(function(x){setNames(x[2,], x[1,])[,complete.cases(t(x))]}, .) %>% set_names(str_c('d', names(.))) %>% list2env(., .GlobalEnv)
Les données:
list2env(setNames(Map(function(x){setNames(data.frame(t(x[,2, drop = FALSE])), x[,1])[,!is.na(x[,1])]}, split.default(cbind(X0 = names(df), data.frame(t(df))), c(0, seq_len(nrow(df)) %/% 2))), paste0('d', seq_len(ceiling(nrow(df) / 2)))), .GlobalEnv)
Données mises à jour:
Map(function(x){setNames(data.frame(t(x[,2, drop = FALSE])), x[,1])[,!is.na(x[,1])]}, split.default(cbind(X0 = names(df), data.frame(t(df))), c(0, seq_len(nrow(df)) %/% 2)))
Désolé, je n'ai pas clarifié la question. Votre réponse fonctionne lorsque d1
, d2
et d3
ont le même nrow
, mais ma situation réelle est que d1
, d2
et d3
ont un nrow
différent et j'ai mis à jour la question.
Si ma réponse répond à votre question initiale, veuillez la voter.