Fractionner le dataframe par ligne vide
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
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 map
place de map_dfr
et supprimez, .id = "df"
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
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 loc
car 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 Var
colonnes 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