빈 행으로 데이터 프레임 분할

Nov 28 2020

나는 Loc_1, Loc_2, Loc_3과 같은 블록 사이의 NA 행을 기반으로 끔찍하게 형식이 지정된 데이터 프레임을 데이터 프레임 목록으로 분할하려고합니다. 나는 시도했습니다 빈 행을 기반으로 R에 분할 dataframes을 하고 빈 행과 헤더 제목에 따라 여러 DFS에 분할 또는 분할 dataframe 행운과 함께. 내 경우의 차이점은 각각의 새로운 청크가 처음 두 열의 두 행에 대한 NA로 시작하고 전체에 흩어져있는 많은 NA가 있기 때문에 NA 값이없는 단일 열이 없다는 것입니다. 어떤 아이디어? 이것은 나의 첫 번째 게시물이므로 더 많은 정보를 게시하려면 외쳐주세요!

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

답변

6 Edo Nov 28 2020 at 00:46

이 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

다음과 같이 긴 형식으로 모든 데이터를 깔끔하게 다시 설정할 수 있습니다 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

끝에 하나의 고유 한 데이터 프레임을 원하지 않는 경우 map대신 사용 map_dfr하고 제거하십시오., .id = "df"

1 27ϕ9 Nov 28 2020 at 00:44

시도해 볼 수 있습니다.

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

원하는 출력이 확실하지 않습니다. 여기에 내 최선의 추측이 있습니다. loc열 이름 일 뿐이므로 각각에 대해 처음 두 행을 제거하려는 코드를 더 추가 한 다음 원래 첫 번째 행을 기반으로 새 열 이름을 할당합니다. 이 추가 단계를 통해 Var열을 숫자 로 변환 할 수 있습니다 .

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