R:将多列从长整型重塑

时间:2019-07-12 18:46:08

标签: r data.table reshape tidyr reshape2

使用以下数据:

library(tidyverse)

sample_df <- data.frame(Letter = c("a", "a", "a", "b", "b"),
                        Number = c(1,2,1,3,4),
                        Fruit = c("Apple", "Plum", "Peach", "Pear", "Peach"))


Letter Number Fruit
a      1      Apple
a      2      Plum
a      1      Peach
b      3      Pear
b      4      Peach

我想将一组值从长格式转换为宽格式:

Letter Number_1 Number_2 Fruit_1 Fruit_2 Fruit_3
a      1        2        Apple   Plum    Peach
b      3        4        Pear    Peach   

为此,我尝试使用c("Letter", "Number")c("Letter", "Fruit")为每个唯一组组合创建索引均未成功。首先,是否需要创建该索引,如果需要,应该如何创建?

# Gets Unique Values, but no Index of Unique Combinations
sample_df1 <- sample_df %>%
  group_by(Letter) %>%  
  mutate(Id1 = n_distinct(Letter, Number),
         Id2 = n_distinct(Letter, Fruit))

# Gets Following Error: Column `Id1` must be length 3 (the group size) or one, not 2
sample_df1 <- sample_df %>%
  group_by(Letter) %>%  
  mutate(Id1 = 1:n_distinct(Letter, Number),
         Id2 = 1:n_distinct(Letter, Fruit))

# NOTE: Manually Created the Index Columns to show next problem
sample_df1 <- sample_df %>%
  group_by(Letter) %>%  
  add_column(Id1 = c(1,2,1,1,2),
         Id2 = c(1,2,3,1,2)) 

假设确实需要完成,我手动添加了所需的值,并使用开发性tidyr部分解决了该问题。

# Requires Developmental Tidyr
devtools::install_github("tidyverse/tidyr")  

sample_df1 %>%  
  pivot_wider(names_from = c("Id1", "Id2"), values_from = c("Number", "Fruit")) %>%
  set_names(~ str_replace_all(.,"(\\w+.*)(_\\d)(_\\d)", "\\1\\3"))

#  Letter Number_1 Number_2 Number_3 Fruit_1 Fruit_2 Fruit_3
#<fct>     <dbl>    <dbl>    <dbl> <fct>   <fct>   <fct>  
# a         1        2        1     Apple   Plum    Peach  
# b         3        4       NA     Pear    Peach   NA     

但是,这种方法仍然创建了多余的Number_3列。使用任何tidyrdata.table或任何其他软件包,是否有任何方法可以以期望的格式获取数据而无需复制列?

1 个答案:

答案 0 :(得分:1)

一种选择是将duplicated元素替换为NA的'Letter',然后在重整数据中,删除全部为NA的列

library(data.table)
out <- dcast(setDT(sample_df)[, lapply(.SD, function(x) 
     replace(x, duplicated(x), NA)), Letter], Letter ~ rowid(Letter), 
     value.var = c("Number", "Fruit"))
nm1 <- out[, names(which(!colSums(!is.na(.SD))))]
out[, (nm1) := NULL][]
#   Letter Number_1 Number_2 Fruit_1 Fruit_2 Fruit_3
#1:      a        1        2   Apple    Plum   Peach
#2:      b        3        4    Pear   Peach    <NA>

如果我们要使用tidyverse方法,可以使用类似的选项。请注意,pivot_wider来自tidyrtidyr_0.8.3.9000)的开发版本

library(tidyverse)
sample_df %>% 
     group_by(Letter) %>%
     mutate_at(vars(-group_cols()), ~ replace(., duplicated(.), NA)) %>%
     mutate(rn = row_number()) %>% 
  pivot_wider(
          names_from = rn,
          values_from = c("Number", "Fruit")) %>%
  select_if(~ any(!is.na(.)))
# A tibble: 2 x 6
#  Letter Number_1 Number_2 Fruit_1 Fruit_2 Fruit_3
#  <fct>     <dbl>    <dbl> <fct>   <fct>   <fct>  
#1 a             1        2 Apple   Plum    Peach  
#2 b             3        4 Pear    Peach   <NA>