Разделять, изменять форму, привязывать многоуровневые данные с использованием tidyverse в одном канале

#r #dplyr #tidyr #purrr

#r #dplyr #tidyr #мурлыканье

Вопрос:

У меня есть единый фрейм данных, в котором собраны объемные данные для нескольких районов и деревень, где зарегистрированы голоса за политические партии. В каждом округе действуют разные партии:

 df_in <- data.frame(
  X1 = c(rep("District1", 3), rep("District2", 3)),
  X2 = c(rep(c("", "Village1", "Village2"), 2)),
  X3 = c("Party1", "30", "11", "Party1", "2", "59"),
  X4 = c("Party2", "55", "42", "Party2", "66", "44"),
  X5 = c("", "", "", "Party3", "32", "13"),
  X6 = c("", "", "", "Party4", "99", "75")
)
  

Я хотел бы получить длинный набор данных о голосах, зарегистрированных для каждой партии в каждой деревне / районе:

 df_out <- data.frame(
  X1 = c(rep("District1", 4), rep("District2", 8)),
  X2 = c("Village1", "Village1", "Village2", "Village2", "Village1", "Village1", "Village1", "Village1", "Village2", "Village2", "Village2", "Village2"),
  X3 = c(
    rep(c("Party1", "Party2"), 2), 
    rep(c("Party1", "Party2", "Party3", "Party4"), 2)
    ),
  X4 = c(30, 55, 11, 42, 2, 66, 32, 99, 59, 44, 13, 75)
)
  

Я хотел бы выполнить переход от ввода к выводу в одном канале. Я работал над чем-то вроде следующей настройки, но пока безуспешно:

 df_out <- df_in %>% 
  split(.$X1) %>% 
  map() %>% 
  gather() %>% 
  bind_rows()
  

Это в правильных строках?

Ответ №1:

Мы также можем сделать это с

 library(dplyr)
library(tidyr)
library(hablar)
df_in %>% 
   # group by 'X1'
   group_by(X1) %>%
   # remove the first row
   slice(-1) %>% 
   # ungroup
   ungroup %>%
   # rename the column names with 'Party'
   rename_at(vars(X3:X6), ~ paste0("Party", 1:4)) %>%   
   # change the type of columns    
   retype %>%
   # gather into long format
   gather(X3, X4, Party1:Party4, na.rm = TRUE) %>%
   # arrange if needed
   arrange(X1, X2)
# A tibble: 12 x 4
#   X1        X2       X3        X4
#   <chr>     <chr>    <chr>  <int>
# 1 District1 Village1 Party1    30
# 2 District1 Village1 Party2    55
# 3 District1 Village2 Party1    11
# 4 District1 Village2 Party2    42
# 5 District2 Village1 Party1     2
# 6 District2 Village1 Party2    66
# 7 District2 Village1 Party3    32
# 8 District2 Village1 Party4    99
# 9 District2 Village2 Party1    59
#10 District2 Village2 Party2    44
#11 District2 Village2 Party3    13
#12 District2 Village2 Party4    75
  

Ответ №2:

  library(tidyverse)
 df_in %>%   
  split(.$X1) %>% 
  map(. %>% gather(key,val,X3:X6) %>% 
        group_by(key) %>% mutate(key1=first(val)) %>% filter(row_number() %in% 2:n() amp; val!="") %>% 
        ungroup() %>% rename(X4=val, X3=key1) %>% select(X1,X2,X3,X4)) %>% 
 bind_rows()
  

Ответ №3:

Еще короче и с правильными именами столбцов.

 library(tidyr)
library(dplyr)
library(magrittr)

df_in %>%
    mutate_all(as.character) %>%  # Or set stringsAsFactors = FALSE
    set_names(c("district", "village", paste0("Party", 1:4))) %>% 
    filter(nchar(village) > 0) %>% 
    gather(party, votes, -district, -village) %>% 
    mutate(votes = as.integer(votes) %>% replace_na(0)) %>% 
    arrange(district, village, party) %>% 
    filter(votes > 0)

##     district  village  party votes
## 1  District1 Village1 Party1    30
## 2  District1 Village1 Party2    55
## 3  District1 Village2 Party1    11
## 4  District1 Village2 Party2    42
## 5  District2 Village1 Party1     2
## 6  District2 Village1 Party2    66
## 7  District2 Village1 Party3    32
## 8  District2 Village1 Party4    99
## 9  District2 Village2 Party1    59
## 10 District2 Village2 Party2    44
## 11 District2 Village2 Party3    13
## 12 District2 Village2 Party4    75