Pisahkan kerangka data dengan baris kosong

Nov 28 2020

Saya mencoba untuk membagi dataframe yang diformat mengerikan menjadi daftar dataframe berdasarkan baris NA di antara blok yaitu Loc_1, Loc_2, Loc_3. Saya sudah mencoba Memisahkan kerangka data di R berdasarkan baris kosong dan Membagi atau membagi kerangka data menjadi beberapa df berdasarkan baris kosong dan judul tajuk tanpa hasil. Saya pikir perbedaan dalam kasus saya adalah saya tidak mendapatkan satu col pun tanpa nilai NA, karena setiap potongan baru dimulai dengan NA untuk dua baris di dua kolom pertama, dan ada banyak NAs yang tersebar di seluruh. Ada ide? Ini adalah posting pertama saya, jadi tolong teriak jika saya perlu memposting info lebih lanjut!

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

Jawaban

6 Edo Nov 28 2020 at 00:46

Bagaimana dengan solusi Base R ini:

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

Anda dapat mengatur kembali semua data Anda dengan rapi dalam format panjang dengan cara ini dengan 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

Jika Anda tidak menginginkan satu kerangka data unik di akhir, gunakan mapsebagai ganti map_dfrdan hapus, .id = "df"

1 27ϕ9 Nov 28 2020 at 00:44

Kamu bisa mencoba:

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

Tidak yakin dengan hasil yang Anda inginkan. Ini tebakan terbaik saya. Saya menambahkan lebih banyak kode mencoba menghapus dua baris pertama untuk masing-masing lockarena itu hanya nama kolom, dan kemudian menetapkan nama kolom baru berdasarkan baris pertama yang asli. Langkah tambahan ini memungkinkan Anda untuk mengubah Varkolom menjadi numerik.

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