【问题标题】:In R, expand a table, splitting apart rows distinguished internally在 R 中,展开一个表,将内部区分的行分开
【发布时间】:2021-08-26 13:05:58
【问题描述】:

[编辑:反应很好;谢谢你。不幸的是,我过度简化了我的代表。我的实际数据有时在括号内有破折号。此外,如前所述,有时有两个以上的变体。我正在相应地编辑我的代表。]

我正在整理给定制造商的产品价格表。模型有变体,同一模型的每个变体价格相同,因此原始表格创建者仅列出了一次价格,并使用内部括号替代方案来区分模型。例如。 “1000-a”和“1000-b”是同一型号的变体,在价目表上标为“1000-a(b)”。我想在自己的行中列出每个模型。

代表:

prices <- data.frame(
  model = c("1000-a(-b)", "2000-a(b)", "150-L(R)", "TX-a(-b)-J", "350-LX(-S)(-AB)"),
  price = seq(1999,5999,1000)
)
> prices
            model price
1      1000-a(-b)  1999
2       2000-a(b)  2999
3        150-L(R)  3999
4      TX-a(-b)-J  4999
5 350-LX(-S)(-AB)  5999

有时差异是单个字符,有时是多个。有时它们位于型号的末尾,有时位于中间。 我想要产生这个的代码:

> desired_prices
    model price
1  1000-a  1999
2  1000-b  1999
3  2000-a  2999
4  2000-b  2999
5   150-L  3999
6   150-R  3999
7  TX-a-J  4999
8  TX-b-J  4999
9  350-LX  5999
10  350-S  5999
11 350-AB  5999

这似乎与问题 Split comma-separated strings in a column into separate rows 有关,但在我的情况下,值不是逗号分隔的。

我如何制作这种拆分/扩展? tidyverse 解决方案是首选但不是必需的。

【问题讨论】:

  • 括号中是否只有一个替代方案,还是可以有多个?喜欢150-lx(sn)(av)(px)
  • @AllanCameron,是的,有一些倍数,尽管数量很少,我愿意单独处理。
  • 您正在将TXa(-b)-J 转换为TX-a-JTX-b-J。这对吗?很难识别一个模式...应该是TX-a(-b)-J
  • @MartinGal 是的,谢谢。 (reprex 现已修复)

标签: r data-munging


【解决方案1】:

已更新以反映 OP 修订的数据框。 关键正则表达式步骤:

  1. 分隔所有变体,即括号和内容
  2. 清洁参考模型
  3. 每个附加模型 ID 的单独行
  4. 从其他模型 ID 中删除括号和破折号
  5. 将参考 ID 替换为附加模型 ID
library(dplyr)
library(tidyr)
library(stringr)

  prices %>% 
  mutate(var = str_extract_all(model, "\\([\\-A-z]{1,3}\\)"),
         mod_1 = str_remove_all(model, "\\(([\\-A-z]{1,3})\\)")) %>% 
  unnest(var) %>% 
  mutate(var = str_remove_all(var, "[(\\-)]"),
         mod_2 = str_replace(mod_1, "(?<=\\-)[A-z]{1,3}", var)) %>% 
  select(price, mod_1, mod_2) %>% 
  pivot_longer(-price) %>% 
  select(-name) %>% 
  distinct()

#> # A tibble: 11 x 2
#>    price value 
#>    <dbl> <chr> 
#>  1  1999 1000-a
#>  2  1999 1000-b
#>  3  2999 2000-a
#>  4  2999 2000-b
#>  5  3999 150-L 
#>  6  3999 150-R 
#>  7  4999 TX-a-J
#>  8  4999 TX-b-J
#>  9  5999 350-LX
#> 10  5999 350-S 
#> 11  5999 350-AB

reprex package (v2.0.0) 于 2021 年 8 月 26 日创建

数据

prices <- data.frame(
  model = c("1000-a(-b)", "2000-a(b)", "150-L(R)", "TX-a(-b)-J", "350-LX(-S)(-AB)"),
  price = seq(1999,5999,1000))

来自 OP 的初始数据集:

library(dplyr)
library(tidyr)
library(stringr)


prices <- data.frame(
  model = c("1000-a(b)", "2000-a(b)", "150-lx(sn)", "350-lx(sn)", "TXa(b)-J"),
  price = seq(1999,5999,1000)
)


prices1 <- 
  prices %>% 
  mutate(model1 = str_remove(model, "\\(([a-z]{1,2})\\)"),
         model2 = str_remove(model, "[a-z]{1,2}(?=\\()"),
         model2 = str_replace_all(model2, "[()]", "")) %>% 
  select(-model)%>% 
  pivot_longer(-price, values_to = "model") %>% 
  select(-name)


prices1       
#> # A tibble: 10 x 2
#>    price model 
#>    <dbl> <chr> 
#>  1  1999 1000-a
#>  2  1999 1000-b
#>  3  2999 2000-a
#>  4  2999 2000-b
#>  5  3999 150-lx
#>  6  3999 150-sn
#>  7  4999 350-lx
#>  8  4999 350-sn
#>  9  5999 TXa-J 
#> 10  5999 TXb-J

reprex package (v2.0.0) 于 2021 年 8 月 26 日创建

【讨论】:

  • 更新以反映修改后的数据框。
【解决方案2】:

这是对彼得答案的快速修改,以防变体前面的字母不是小写。这只是增加了一个步骤来删除括号内长度相等的字母。

prices %>%
  mutate(
    model1 = str_remove_all(model, '\\s*\\([^)]*\\)' ), #remove everything between bracket
    size = str_extract(prices$model, '(?<=\\().+?(?=\\))') %>% nchar(),
    model2 = str_remove_all(model, sprintf("[A-Za-z]{%s}(?=\\()|\\(|\\)", size))
  ) %>% 
  select(-model, -size)%>% 
  pivot_longer(-price, values_to = "model") %>% 
  select(-name)

【讨论】:

    【解决方案3】:

    我认为可以根据@Peter 和@Quixotic22 所写的内容进行一些简化。这假设变体只包含字母:

    df <- prices %>%
        mutate(
            model1 = str_replace( model, "(.+)\\((\\w+)\\)", "\\1"),
            length = nchar(sub("(.+)\\((\\w+)\\)(.+)?", "\\2", model)),
            model2 = str_replace(
                model, 
                paste("(.+)\\w{", length, "}\\((\\w+)\\)", sep=""), 
                "\\1\\2"
                )
            ) %>%
        select(-model, -length) %>%
        pivot_longer(-price, values_to = "model") %>%
        select(-name)
    df
    # A tibble: 10 x 2
       price model 
       <dbl> <chr> 
     1  1999 1000-a
     2  1999 1000-b
     3  2999 2000-a
     4  2999 2000-b
     5  3999 150-lx
     6  3999 150-sn
     7  4999 350-lx
     8  4999 350-sn
     9  5999 TXa-J 
    10  5999 TXb-J 
    

    【讨论】:

      【解决方案4】:

      这也可以使用stringr 包如下完成:

      prices %>%
      mutate(model_x = str_replace_all(model,"\\([^()]+\\)","")) %>%
      mutate(extract = str_extract(model,"\\([^()]+\\)")) %>% 
      mutate(model_y = str_remove_all(
                                       str_remove_all(model, extract),"[()]")) %>% 
      select(-model, -extract)%>% 
      pivot_longer(-price, values_to = "model") %>% 
      select(-name)
      

      第一行删除括号之间的任何内容的提取,即 (),如提取列中所示。

      第二行只是重复显示已提取的内容。

      第三行连同括号一起从原始字段中删除。

      最后我们使用 pivot_longer 整理数据。您也可以使用gather,但它已被弃用。

      【讨论】:

        【解决方案5】:

        我不知道这是否适用于所有情况:

        library(dplyr)
        library(tidyr)
        library(stringr)
        
        prices %>% 
          mutate(mod_1 = str_extract_all(model, "((?<=-)\\w*?(?=\\(-?\\w*?\\))|(?<=\\(-?)\\w*?(?=\\)))")) %>% 
          unnest(mod_1) %>%
          mutate(model = str_replace(model, "(?<=-)\\w*?\\(-?\\w*\\)", mod_1), .keep = "unused")
        

        返回

        # A tibble: 11 x 2
           model       price
           <chr>       <dbl>
         1 1000-a       1999
         2 1000-b       1999
         3 2000-a       2999
         4 2000-b       2999
         5 150-L        3999
         6 150-R        3999
         7 TX-a-J       4999
         8 TX-b-J       4999
         9 350-LX(-AB)  5999
        10 350-S(-AB)   5999
        11 350-AB(-AB)  5999
        

        【讨论】:

          猜你喜欢
          • 2020-08-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-03-15
          • 1970-01-01
          • 1970-01-01
          • 2015-01-01
          • 1970-01-01
          相关资源
          最近更新 更多