【问题标题】:Elegant solution for casting (spreading) multiple columns of character vectors用于转换(传播)多列字符向量的优雅解决方案
【发布时间】:2019-08-06 10:57:16
【问题描述】:

我想将带有联系信息的数据框转换为包含类似信息的城市列表,例如电话号码出现在多列中。

我尝试过同时使用reshape2::dcast()tidyr::spread(),但都没有解决我的问题。我还检查了堆栈溢出的其他帖子,例如

Multiple column spread

尚未找到可行的解决方案。在我看来,这些问题应该相当简单(并且可以通过 spread 或 dcast 解决)。

tmp <- tibble(municipality = c("M1", "M2"), 
       name1 = c("n1", "n2"), name2 = c("n3", "n4"), name3 = c(NA, "n5"), # placeholder names
       phone1 = c("p1", "p2"), phone2 = c("p3", "p4"), phone3 = c(NA, "p5")) # placeholder phone numbers

#solution 1
tmp %>% gather("colname", "value", -municipality) %>% 
  filter(municipality == "M1") %>% #too simplify, should be replaced with group_by(municipality)
  na.omit() %>% mutate(colname = str_replace(colname, "\\d", replacement = "")) %>% 
  spread(., key = "colname", value = "value")

#Solution 2
tmp %>% gather("colname", "value", -municipality) %>% 
  filter(municipality == "M1") %>% # same as above
  na.omit() %>% mutate(colname = str_replace(colname, "\\d", replacement = "")) %>% 
  dcast(municipality + value ~colname)


解决方案 1 导致以下错误: 错误:每行输出必须由唯一的键组合标识。

解决方案 2 产生以下数据框(这是所需的结果,但需要折叠):

  municipality value name phone
1           M1    n1   n1  <NA>
2           M1    n3   n3  <NA>
3           M1    p1 <NA>    p1
4           M1    p3 <NA>    p3

【问题讨论】:

    标签: r tidyr spread dcast


    【解决方案1】:

    你在找吗?

    library(dplyr)
    library(tidyr)
    
    tmp %>%
      gather(key, value, -municipality, na.rm = TRUE) %>%
      mutate(key = gsub("\\d+", "", key)) %>%
      group_by(municipality, key) %>%
      mutate(row = row_number()) %>%
      spread(key, value) %>%
      select(-row)
    
    # municipality name  phone
    # <chr>        <chr> <chr>
    #1 M1           n1    p1   
    #2 M1           n3    p3   
    #3 M2           n2    p2   
    #4 M2           n4    p4   
    #5 M2           n5    p5  
    

    我们可以使用gather 以删除NA 值的长格式数据。从各个列名中删除数字,使它们共享相同的key,创建列group_bymunicipalitykeyspread 数据转换为宽格式。

    【讨论】:

    • 没错!您介意解释一下为什么需要按键分组并变异添加行号吗?
    • @KennethEnevoldsen 因为我们需要的spread 列没有通用标识符。如果我们不这样做,我们会得到一个错误。 tmp %&gt;% gather(key, value, -municipality, na.rm = TRUE) %&gt;% mutate(key = gsub("\\d+", "", key)) %&gt;% spread(key, value)
    【解决方案2】:

    我们可以使用 tidyr 开发版中的 pivot_longer 优雅地做到这一点

    library(dplyr)
    library(tidyr)# 0.8.3.9000
    library(stringr)
    tmp %>%
       rename_at(-1,  ~str_replace(., "(\\d+$)", "_\\1")) %>%
       pivot_longer(cols = -municipality, names_to = c(".value", "group"), 
            names_sep="_", values_drop_na = TRUE) %>%
       select(-group)
    # A tibble: 5 x 3
    #  municipality name  phone
    #  <chr>        <chr> <chr>
    #1 M1           n1    p1   
    #2 M1           n3    p3   
    #3 M2           n2    p2   
    #4 M2           n4    p4   
    #5 M2           n5    p5   
    

    或者另一个选项是melt from data.table

    library(data.table)
    melt(setDT(tmp), measure = patterns("^name", "^phone"), 
       value.name = c("name", "phone"), na.rm = TRUE)[, variable := NULL][]
    #.  municipality name phone
    #1:           M1   n1    p1
    #2:           M2   n2    p2
    #3:           M1   n3    p3
    #4:           M2   n4    p4
    #5:           M2   n5    p5
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-10-12
      • 2023-03-19
      • 2011-02-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-15
      • 1970-01-01
      相关资源
      最近更新 更多