Fractionner le dataframe par ligne vide

Nov 28 2020

J'essaie de diviser un dataframe horriblement formaté en une liste de dataframes basée sur les lignes de NA entre les blocs, c'est-à-dire Loc_1, Loc_2, Loc_3. J'ai essayé de diviser les dataframes en R basé sur des lignes vides et de diviser ou de diviser le dataframe en plusieurs dfs en fonction de la ligne vide et du titre d'en-tête sans succès. Je pense que la différence dans mon cas est que je n'ai pas une seule colonne sans valeur NA, car chaque nouveau bloc commence par des NA pour deux lignes dans les deux premières colonnes, et il y a beaucoup de NA dispersés. Des idées? Ceci est mon premier message, alors s'il vous plaît criez si j'ai besoin de publier plus d'informations!

df <- data.frame(
  a = c(NA, NA, "Loc_1", "Loc_1", "Loc_1", NA, NA, NA, "Loc_2", "Loc_2", "Loc_2", NA, NA, NA, "Loc_3", "Loc_3", "Loc_3"),
  b = c(NA, NA, "25:11:2020", "26:11:2020", "27:11:2020", NA, NA, NA, "25:11:2020", "26:11:2020", "27:11:2020",NA, NA, NA, "25:11:2020", "26:11:2020", "27:11:2020"),
  c = c("Var1", "Unit/1", 1:3, NA, "Var3", "Unit/3", NA, 1, 2, NA,"Var1", "Unit/1", 1:3),
  d = c("Var2", "Unit/2", NA, NA, 1, NA, "Var1", "Unit/1", NA, NA, 1, NA, "Var3", "Unit/3", NA, NA, 1)
)
       a          b      c      d
1   <NA>       <NA>   Var1   Var2
2   <NA>       <NA> Unit/1 Unit/2
3  Loc_1 25:11:2020      1   <NA>
4  Loc_1 26:11:2020      2   <NA>
5  Loc_1 27:11:2020      3      1
6   <NA>       <NA>   <NA>   <NA>
7   <NA>       <NA>   Var3   Var1
8   <NA>       <NA> Unit/3 Unit/1
9  Loc_2 25:11:2020   <NA>   <NA>
10 Loc_2 26:11:2020      1   <NA>
11 Loc_2 27:11:2020      2      1
12  <NA>       <NA>   <NA>   <NA>
13  <NA>       <NA>   Var1   Var3
14  <NA>       <NA> Unit/1 Unit/3
15 Loc_3 25:11:2020      1   <NA>
16 Loc_3 26:11:2020      2   <NA>
17 Loc_3 27:11:2020      3      1

Réponses

6 Edo Nov 28 2020 at 00:46

Que diriez-vous de cette solution Base R:

n <- rowSums(is.na(df)) == ncol(df)
cs <- cumsum(n) + 1
s <- split(df[!n, ], cs[!n])

s

#> $`1` #> a b c d #> 1 <NA> <NA> Var1 Var2 #> 2 <NA> <NA> Unit/1 Unit/2 #> 3 Loc_1 25:11:2020 1 <NA> #> 4 Loc_1 26:11:2020 2 <NA> #> 5 Loc_1 27:11:2020 3 1 #> #> $`2`
#>        a          b      c      d
#> 7   <NA>       <NA>   Var3   Var1
#> 8   <NA>       <NA> Unit/3 Unit/1
#> 9  Loc_2 25:11:2020   <NA>   <NA>
#> 10 Loc_2 26:11:2020      1   <NA>
#> 11 Loc_2 27:11:2020      2      1
#> 
#> $`3`
#>        a          b      c      d
#> 13  <NA>       <NA>   Var1   Var3
#> 14  <NA>       <NA> Unit/1 Unit/3
#> 15 Loc_3 25:11:2020      1   <NA>
#> 16 Loc_3 26:11:2020      2   <NA>
#> 17 Loc_3 27:11:2020      3      1

Vous pouvez à nouveau regrouper toutes vos données dans un format long de cette manière avec unpivotr:

library(unpivotr)
library(dplyr)
library(purrr)

map_dfr(s, 
        ~ as_cells(.x) %>% 
         behead("up", "var") %>% 
         behead("up", "uom") %>% 
         behead("left", "loc") %>% 
         behead("left", "date") %>% 
         # filter(!is.na(chr)) %>%  # do you need the NAs?
         mutate(value = as.numeric(chr)) %>% 
         select(var, uom, loc, date, value),
        .id = "df")

#> # A tibble: 18 x 6
#>    df    var   uom    loc   date       value
#>    <chr> <chr> <chr>  <chr> <chr>      <dbl>
#>  1 1     Var1  Unit/1 Loc_1 25:11:2020     1
#>  2 1     Var1  Unit/1 Loc_1 26:11:2020     2
#>  3 1     Var1  Unit/1 Loc_1 27:11:2020     3
#>  4 1     Var2  Unit/2 Loc_1 25:11:2020    NA
#>  5 1     Var2  Unit/2 Loc_1 26:11:2020    NA
#>  6 1     Var2  Unit/2 Loc_1 27:11:2020     1
#>  7 2     Var3  Unit/3 Loc_2 25:11:2020    NA
#>  8 2     Var3  Unit/3 Loc_2 26:11:2020     1
#>  9 2     Var3  Unit/3 Loc_2 27:11:2020     2
#> 10 2     Var1  Unit/1 Loc_2 25:11:2020    NA
#> 11 2     Var1  Unit/1 Loc_2 26:11:2020    NA
#> 12 2     Var1  Unit/1 Loc_2 27:11:2020     1
#> 13 3     Var1  Unit/1 Loc_3 25:11:2020     1
#> 14 3     Var1  Unit/1 Loc_3 26:11:2020     2
#> 15 3     Var1  Unit/1 Loc_3 27:11:2020     3
#> 16 3     Var3  Unit/3 Loc_3 25:11:2020    NA
#> 17 3     Var3  Unit/3 Loc_3 26:11:2020    NA
#> 18 3     Var3  Unit/3 Loc_3 27:11:2020     1

Si vous ne voulez pas une seule dataframe à la fin, utilisez à la mapplace de map_dfret supprimez, .id = "df"

1 27ϕ9 Nov 28 2020 at 00:44

Tu pourrais essayer:

library(dplyr)
library(purrr)

df %>%
  group_split(grp = cumsum(rowSums(is.na(.)) == ncol(.)), .keep = FALSE) %>%
  map_at(.at = -1, tail, -1)

[[1]]
# A tibble: 5 x 4
  a     b          c      d     
  <chr> <chr>      <chr>  <chr> 
1 NA    NA         Var1   Var2  
2 NA    NA         Unit/1 Unit/2
3 Loc_1 25:11:2020 1      NA    
4 Loc_1 26:11:2020 2      NA    
5 Loc_1 27:11:2020 3      1     

[[2]]
# A tibble: 5 x 4
  a     b          c      d     
  <chr> <chr>      <chr>  <chr> 
1 NA    NA         Var3   Var1  
2 NA    NA         Unit/3 Unit/1
3 Loc_2 25:11:2020 NA     NA    
4 Loc_2 26:11:2020 1      NA    
5 Loc_2 27:11:2020 2      1     

[[3]]
# A tibble: 5 x 4
  a     b          c      d     
  <chr> <chr>      <chr>  <chr> 
1 NA    NA         Var1   Var3  
2 NA    NA         Unit/1 Unit/3
3 Loc_3 25:11:2020 1      NA    
4 Loc_3 26:11:2020 2      NA    
5 Loc_3 27:11:2020 3      1    
1 www Nov 28 2020 at 00:55

Pas sûr de votre sortie souhaitée. Voici ma meilleure estimation. J'ai ajouté plus de code en essayant de supprimer les deux premières lignes pour chacune loccar ce ne sont que des noms de colonnes, puis attribuer les nouveaux noms de colonnes en fonction de la première ligne d'origine. Cette étape supplémentaire vous permet de convertir les Varcolonnes en colonnes numériques.

library(tidyverse)

# A helper function to filter rows with any non-NA values
rowAny <- function(x) rowSums(x) > 0

df_list <- df %>%
  # Remove rows with all NA
  filter(rowAny(across(everything(), .fns = function(x) !is.na(x)))) %>%
  # Fill the Loc information
  fill(a, .direction = "up") %>%
  # Split the data frame by a
  split(.$a) %>% # Remove the first two rows and change the column names to the first row (Var1, Var2, Var3, ...) map(function(x){ # Prepare new column names x2 <- x %>% slice(1) %>% t() %>% as.vector() x_names <- c(names(x)[1:2], x2[3:length(x2)]) # Remove the first two rows and assign new column names x3 <- x %>% slice(-1:-2) %>% set_names(x_names) %>% # Change the columns to numeric mutate(across(x2[3:length(x2)], .fns = as.numeric)) return(x3) }) df_list # $Loc_1
#       a          b Var1 Var2
# 1 Loc_1 25:11:2020    1   NA
# 2 Loc_1 26:11:2020    2   NA
# 3 Loc_1 27:11:2020    3    1
# 
# $Loc_2 # a b Var3 Var1 # 1 Loc_2 25:11:2020 NA NA # 2 Loc_2 26:11:2020 1 NA # 3 Loc_2 27:11:2020 2 1 # # $Loc_3
#       a          b Var1 Var3
# 1 Loc_3 25:11:2020    1   NA
# 2 Loc_3 26:11:2020    2   NA
# 3 Loc_3 27:11:2020    3    1