在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 修改后的数据框架。
关键正则表达式步骤:
- 分隔所有变体,即括号和内容
- 清理参考模型
- 每个额外的模型 ID 单独一行
- 从其他模型 ID 中删除括号和破折号
- 将参考 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
[编辑:反应很好;谢谢你。不幸的是,我过度简化了我的代表。我的实际数据有时在括号内有破折号。另外,如前所述,有时会有两个以上的变体。我正在相应地编辑我的代表。]
我正在整理给定制造商产品的 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 修改后的数据框架。 关键正则表达式步骤:
- 分隔所有变体,即括号和内容
- 清理参考模型
- 每个额外的模型 ID 单独一行
- 从其他模型 ID 中删除括号和破折号
- 将参考 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