在R中,扩展一个table,拆分内部区分的行

In R, expand a table, splitting apart rows distinguished internally

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

我正在整理给定制造商产品的 table 价格。这些模型有多个变体,同一模型的每个变体价格相同,因此最初的 table 创建者只列出一次价格,并使用带括号的内部替代方案区分模型。例如。 “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 有关,但在我的例子中,值不是逗号分隔的。

我该如何制作这个 split/expansion?首选 tidyverse 解决方案,但不是必需的。

已更新以反映 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-08-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-08-26 创建

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

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)

我认为它可以根据@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 

这也可以使用 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,但它已被弃用。

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

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")

returns

# 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