J'ai importé cette feuille Excel sous forme de liste de dataframes. Je souhaite fusionner la liste en une seule trame de données. bind_rows() me permet d'ajouter facilement les dataframes, mais le problème est que j'ai une variable / colonne qui a des noms différents dans chaque dataframe. bind_row() créera par défaut deux colonnes séparées, avec des valeurs vides pour les données des autres blocs de données. Comment puis-je rejoindre ces colonnes?

Exemple de code:

# Sample dataframes
df1 <- tibble(A = c(1,2,3),
              B = c("X","Y","Z"),
              C = c(T,F,F)
                  )
df2 <- tibble(A = c(3,4,5),
              B = c("U","V","W"),
              D = c(T,T,F)
)

# List of dataframes
my_ls <- list(df1, df2)
my_ls
[[1]]
# A tibble: 3 x 3
      A B     C    
  <dbl> <chr> <lgl>
1     1 X     TRUE 
2     2 Y     FALSE
3     3 Z     FALSE

[[2]]
# A tibble: 3 x 3
      A B     D    
  <dbl> <chr> <lgl>
1     3 U     TRUE 
2     4 V     TRUE 
3     5 W     FALSE

# Creating joined dataframe:
my_df <- bind_rows(my_ls)
my_df
# Current outcome: A tibble: 6 x 4
      A B     C     D    
  <dbl> <chr> <lgl> <lgl>
1     1 X     TRUE  NA   
2     2 Y     FALSE NA   
3     3 Z     FALSE NA   
4     3 U     NA    TRUE 
5     4 V     NA    TRUE 
6     5 W     NA    FALSE

Le résultat souhaité:

# Desired outcome: A tibble: 6 x 3
      A B     C         
  <dbl> <chr> <lgl> 
1     1 X     TRUE    
2     2 Y     FALSE    
3     3 Z     FALSE    
4     3 U     TRUE 
5     4 V     TRUE 
6     5 W     FALSE

Actuellement, j'utilise mutate() avec case_when(), où je vérifie quelle colonne n'est pas vide (!is.na()). Cela fonctionne, mais je ne peux pas m'empêcher de penser qu'il doit y avoir un moyen plus simple.

# Example using mutate
my_df <- my_df %>% 
  mutate(
    C = case_when(is.na(C)  & !is.na(D) ~ D,
                  !is.na(C) & is.na(D)  ~ C,
                  # The lines below may be a bit redundant for my purpose, since the dataframes either have the C or D variable.
                  !is.na(C) & !is.na(D) ~ C, # Better would be to return that variable has overlapping information
                  is.na(C)  & is.na(D)  ~ NA
                 )
         ) %>%
        select(-D)
my_df
# A tibble: 6 x 3
      A B     C    
  <dbl> <chr> <lgl>
1     1 X     TRUE 
2     2 Y     FALSE
3     3 Z     FALSE
4     3 U     TRUE 
5     4 V     TRUE 
6     5 W     FALSE
2
Pål Bjartan 16 oct. 2020 à 14:00

3 réponses

Meilleure réponse

Vous pouvez bind_rows, puis sélectionner une valeur non-NA en utilisant coalesce:

library(dplyr)

bind_rows(my_ls) %>% mutate(C = coalesce(C, D)) %>% select(A:C)

#      A  B     C    
#  <dbl> <chr> <lgl>
#1     1 X     TRUE 
#2     2 Y     FALSE
#3     3 Z     FALSE
#4     3 U     TRUE 
#5     4 V     TRUE 
#6     5 W     FALSE
2
Ronak Shah 16 oct. 2020 à 12:17

Suite au commentaire de @KarthikS, vous pouvez renommer vos colonnes avant de les lier. Mon approche utilisant rename_with ne nécessite pas que les colonnes soient dans un ordre spécifique. Pour illustrer cela, j'ai utilisé des exemples de dataframes quelque peu différents:

library(purrr)
library(dplyr)

d1 <- data.frame(A = 1, B = 2, C = 3)
d2 <- data.frame(A = 4, B = 5, D = 6)
d3 <- data.frame(D = 7, A = 8, B = 9)

d <- list(d1, d2, d3)

map(d, ~ rename_with(.x, ~ "C", matches("^D$"))) %>% 
  bind_rows()
#>   A B C
#> 1 1 2 3
#> 2 4 5 6
#> 3 8 9 7

Et maintenant pour votre jeu de données:

d <- list(df1, df2)
map(d, ~ rename_with(.x, ~ "C", matches("^D$"))) %>% 
  bind_rows()
#> # A tibble: 6 x 3
#>       A B     C    
#>   <dbl> <chr> <lgl>
#> 1     1 X     TRUE 
#> 2     2 Y     FALSE
#> 3     3 Z     FALSE
#> 4     3 U     TRUE 
#> 5     4 V     TRUE 
#> 6     5 W     FALSE

Et si nous en ajoutons un supplémentaire avec un ordre différent:

df3 <- tibble(D = c(T,T,F),
              A = c(7,8,9),
              B = c("A","B","C"))

d <- list(df1, df2, df3)
map(d, ~ rename_with(.x, ~ "C", matches("^D$"))) %>% 
  bind_rows()
#> # A tibble: 9 x 3
#>       A B     C    
#>   <dbl> <chr> <lgl>
#> 1     1 X     TRUE 
#> 2     2 Y     FALSE
#> 3     3 Z     FALSE
#> 4     3 U     TRUE 
#> 5     4 V     TRUE 
#> 6     5 W     FALSE
#> 7     7 A     TRUE 
#> 8     8 B     TRUE 
#> 9     9 C     FALSE

Créé le 2020-10-16 par le package reprex (v0.3.0)

1
stefan 16 oct. 2020 à 11:30

Excusez-vous de vous être échappé pour une réponse rapide

expl <- read.table(text= " A B     C     D    
1     1 X     TRUE  NA   
2     2 Y     FALSE NA   
3     3 Z     FALSE NA   
4     3 U     NA    TRUE 
5     4 V     NA    TRUE 
6     5 W     NA    FALSE")

expl$E <- ifelse(is.na(expl$C), expl$D, expl$C)

print(expl)

Ou peut-être

expl[,c("C", "D")] %>% rowMeans(na.rm = TRUE) %>% as.logical()

EDIT: Traduit ce dernier pour ranger:

expl %>% select("C", "D") %>% rowMeans(na.rm = TRUE) %>% as.logical()

MODIFIER après le premier commentaire:

Si vous voulez plus de contrôle, vous devriez probablement écrire les choses que vous voulez faire dans chaque cas dans une fonction similaire à l'exemple suivant:

library(magrittr)

expl <- read.table(text= " A B     C     D    
1     1 X     TRUE  NA   
2     2 Y     FALSE NA   
3     3 Z     FALSE NA   
4     3 U     NA    TRUE 
5     4 V     NA    TRUE 
6     5 W     NA    FALSE
7     7 I     NA    NA
8     9 J     TRUE  TRUE")

myfun <- function(a, b){
  if(is.na(a) & is.na(b)) 
     return(NA)
  if(!is.na(a) & !is.na(b)) {
    warning("too much information, a and b set!")
    return(NaN)
  }
  return(max(a, b, na.rm=TRUE))
}

myfun = Vectorize(myfun)

myfun(expl$C, expl$D) %>% as.logical()
1
Bernhard 16 oct. 2020 à 11:36