Datenrahmen durch leere Zeile teilen
Ich versuche, einen schrecklich formatierten Datenrahmen in eine Liste von Datenrahmen aufzuteilen, basierend auf den Zeilen der NAs zwischen den Blöcken, dh Loc_1, Loc_2, Loc_3. Ich habe versucht , in R Splitting Datenrahmen basierend auf leere Zeilen und Dividieren oder Split - Datenrahmen in mehrere dfs basierend auf leere Zeile und Header - Titel ohne Glück. Ich denke, der Unterschied in meinem Fall ist, dass ich keine einzige Spalte ohne NA-Wert habe, da jeder neue Block mit NAs für zwei Zeilen in den ersten beiden Spalten beginnt und es eine Menge NAs gibt, die überall verstreut sind. Irgendwelche Ideen? Dies ist mein erster Beitrag, also bitte schreien Sie, wenn ich weitere Informationen veröffentlichen muss!
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
Antworten
Wie wäre es mit dieser Base R-Lösung:
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
Auf diese Weise können Sie alle Ihre Daten in einem langen Format wieder sauber zusammenstellen 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
Wenn Sie am Ende keinen eindeutigen Datenrahmen möchten, verwenden Sie map
anstelle von map_dfr
und entfernen Sie ihn, .id = "df"
Du könntest es versuchen:
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
Nicht sicher, Ihre gewünschte Ausgabe. Hier ist meine beste Vermutung. Ich habe mehr Code hinzugefügt, um die ersten beiden Zeilen für jede zu entfernen, loc
da dies nur Spaltennamen sind, und dann die neuen Spaltennamen basierend auf der ursprünglichen ersten Zeile zuzuweisen. Mit diesem zusätzlichen Schritt können Sie die Var
Spalten in numerische konvertieren .
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