有效地逐行分隔字符串
Separate string column by row efficiently
我试图根据字符串的分割将字符串列分成两部分。最好用下面的例子来说明。 rowwise
确实有效,但考虑到 data.frame 的大小,我想使用更有效的方法。如何避免使用 rowwise
?
library(dplyr)
library(stringr)
library(tidyr)
#make data
a <- "(1, 10)"
b <- "(10, 20)"
c <- "(20, 30)"
df <- data.frame(size = c(a,b,c))
# Goal is to separate the 'size' column into 'lower' and 'upper' by
# extracting the value contained in the parens and split by a comma.
# Once the column is split into 'upper' and 'lower' I will perform
# additional operations.
# DESIRED RESULT
size lower upper
<fct> <chr> <chr>
1 (1, 10) 1 10
2 (10, 20) 10 20
3 (20, 30) 20 30
# WHAT I HAVE TRIED
> #This works... but too inefficient
> df %>%
+ rowwise() %>%
+ mutate(lower = str_split(size, ",") %>% .[[1]] %>% .[1] %>%
+ str_split("\(") %>% .[[1]] %>% .[2])
size lower
<fct> <chr>
1 (1, 10) 1
2 (10, 20) 10
3 (20, 30) 20
> # I'm not sure why this doesn't work
> df %>%
+ mutate(lower = str_split(size, ",") %>% .[[1]] %>% .[1] %>%
+ str_split("\(") %>% .[[1]] %>% .[2])
size lower
1 (1, 10) 1
2 (10, 20) 1
3 (20, 30) 1
> #Not obivous how to use separate (tidyr)
> df %>%
+ separate(size, sep=",", c("lower", "upper"))
lower upper
1 (1 10)
2 (10 20)
3 (20 30)
对于按行操作,我更喜欢data.table。
试试这个
library(data.table)
library(stringi)
#make data
a <- "(1, 10)"
b <- "(10, 20)"
c <- "(20, 30)"
dt <- data.table(c(a,b,c))
dt[, lower := tstrsplit(V1, ",")[1]]
dt[, lower:= stri_replace_all_regex(lower, '\(', '')]
dt
您没有明确说明您的目标,但您似乎想从字符串中提取第一个数字。使用 stringi::str_extract_first_regex
很容易
library(stringi)
stri_extract_first_regex(df$size, "[0-9]+")
# [1] "1" "10" "20"
所以在你的情况下,
df %>% mutate(lower = as.numeric(stri_extract_first_regex, size, "[0-9]+"))
您可以使用 stri_extract_all_regex
提取所有数字。
根据您的编辑:
df$nums = str_extract_all(df$size, "[0-9]+")
df$lower = as.numeric(sapply(df$nums, `[[`, 1))
df$upper = as.numeric(sapply(df$nums, `[[`, 2))
df
# size nums lower upper
# 1 (1, 10) 1, 10 1 10
# 2 (10, 20) 10, 20 10 20
# 3 (20, 30) 20, 30 20 30
另一种方法是去掉括号和空格,然后单独使用:
df %>%
mutate(just_nums = str_replace_all(size, "[^0-9,]", "")) %>%
separate(just_nums, into = c("lower", "upper"))
# size lower upper
# 1 (1, 10) 1 10
# 2 (10, 20) 10 20
# 3 (20, 30) 20 30
正则表达式模式 "[^0-9,]"
匹配除数字和逗号之外的所有内容。
你快到了。这是我对两种方法的解释,一种与您的类似:
在第一个代码中,我使用了 tidytext 包中的 unnest_tokens,它可以在不同的行上拆分单词,因为您想提取逗号前的第一项(我假设它基于您的示例,尽管你应该提到它)。我已经通过使用过滤器命令选择了第一行基础。
在第二个代码中,我使用了正则表达式(注意你也可以在这里使用str_replace
)。这里我使用 map(因为 str_split 返回的项目是一个列表)来迭代返回的项目并通过 gsub 传递每个项目,它可以替换与反向引用项目匹配的正则表达式。另外为了 select 只有第一项,我在 gsub.
的末尾使用了 [[1]]
library(tidyverse)
library(stringr)
library(tidytext)
df %>%
unnest_tokens(lower,size, token="words",drop=F) %>%
filter(row_number()%%2==T)
df %>%
mutate(lower = map(str_split(df$size, ","), function(x)gsub("\((\w+)","\1",x)[[1]]))
输出:
# size lower
# 1 (1, 10) 1
# 2 (10, 20) 10
# 3 (20, 30) 20
如果您想提取逗号前后的术语,您也可以使用 extract
函数。
tidyr::extract(df, size, c("lower", "upper"), regex= "\((\w+),\s+(\w+)\)")
输出:
# lower upper
# 1 1 10
# 2 10 20
# 3 20 30
一个选项是在从数据中删除 (
和 )
之后使用 tidyr::separate
。
library(tidyverse)
df %>% mutate(size = gsub("\(|)","",size)) %>% # Both ( and ) has been removed.
separate(size, c("Min", "Max"), sep = ",")
# Min Max
# 1 1 10
# 2 10 20
# 3 20 30
我试图根据字符串的分割将字符串列分成两部分。最好用下面的例子来说明。 rowwise
确实有效,但考虑到 data.frame 的大小,我想使用更有效的方法。如何避免使用 rowwise
?
library(dplyr)
library(stringr)
library(tidyr)
#make data
a <- "(1, 10)"
b <- "(10, 20)"
c <- "(20, 30)"
df <- data.frame(size = c(a,b,c))
# Goal is to separate the 'size' column into 'lower' and 'upper' by
# extracting the value contained in the parens and split by a comma.
# Once the column is split into 'upper' and 'lower' I will perform
# additional operations.
# DESIRED RESULT
size lower upper
<fct> <chr> <chr>
1 (1, 10) 1 10
2 (10, 20) 10 20
3 (20, 30) 20 30
# WHAT I HAVE TRIED
> #This works... but too inefficient
> df %>%
+ rowwise() %>%
+ mutate(lower = str_split(size, ",") %>% .[[1]] %>% .[1] %>%
+ str_split("\(") %>% .[[1]] %>% .[2])
size lower
<fct> <chr>
1 (1, 10) 1
2 (10, 20) 10
3 (20, 30) 20
> # I'm not sure why this doesn't work
> df %>%
+ mutate(lower = str_split(size, ",") %>% .[[1]] %>% .[1] %>%
+ str_split("\(") %>% .[[1]] %>% .[2])
size lower
1 (1, 10) 1
2 (10, 20) 1
3 (20, 30) 1
> #Not obivous how to use separate (tidyr)
> df %>%
+ separate(size, sep=",", c("lower", "upper"))
lower upper
1 (1 10)
2 (10 20)
3 (20 30)
对于按行操作,我更喜欢data.table。
试试这个
library(data.table)
library(stringi)
#make data
a <- "(1, 10)"
b <- "(10, 20)"
c <- "(20, 30)"
dt <- data.table(c(a,b,c))
dt[, lower := tstrsplit(V1, ",")[1]]
dt[, lower:= stri_replace_all_regex(lower, '\(', '')]
dt
您没有明确说明您的目标,但您似乎想从字符串中提取第一个数字。使用 stringi::str_extract_first_regex
library(stringi)
stri_extract_first_regex(df$size, "[0-9]+")
# [1] "1" "10" "20"
所以在你的情况下,
df %>% mutate(lower = as.numeric(stri_extract_first_regex, size, "[0-9]+"))
您可以使用 stri_extract_all_regex
提取所有数字。
根据您的编辑:
df$nums = str_extract_all(df$size, "[0-9]+")
df$lower = as.numeric(sapply(df$nums, `[[`, 1))
df$upper = as.numeric(sapply(df$nums, `[[`, 2))
df
# size nums lower upper
# 1 (1, 10) 1, 10 1 10
# 2 (10, 20) 10, 20 10 20
# 3 (20, 30) 20, 30 20 30
另一种方法是去掉括号和空格,然后单独使用:
df %>%
mutate(just_nums = str_replace_all(size, "[^0-9,]", "")) %>%
separate(just_nums, into = c("lower", "upper"))
# size lower upper
# 1 (1, 10) 1 10
# 2 (10, 20) 10 20
# 3 (20, 30) 20 30
正则表达式模式 "[^0-9,]"
匹配除数字和逗号之外的所有内容。
你快到了。这是我对两种方法的解释,一种与您的类似:
在第一个代码中,我使用了 tidytext 包中的 unnest_tokens,它可以在不同的行上拆分单词,因为您想提取逗号前的第一项(我假设它基于您的示例,尽管你应该提到它)。我已经通过使用过滤器命令选择了第一行基础。
在第二个代码中,我使用了正则表达式(注意你也可以在这里使用str_replace
)。这里我使用 map(因为 str_split 返回的项目是一个列表)来迭代返回的项目并通过 gsub 传递每个项目,它可以替换与反向引用项目匹配的正则表达式。另外为了 select 只有第一项,我在 gsub.
library(tidyverse)
library(stringr)
library(tidytext)
df %>%
unnest_tokens(lower,size, token="words",drop=F) %>%
filter(row_number()%%2==T)
df %>%
mutate(lower = map(str_split(df$size, ","), function(x)gsub("\((\w+)","\1",x)[[1]]))
输出:
# size lower
# 1 (1, 10) 1
# 2 (10, 20) 10
# 3 (20, 30) 20
如果您想提取逗号前后的术语,您也可以使用 extract
函数。
tidyr::extract(df, size, c("lower", "upper"), regex= "\((\w+),\s+(\w+)\)")
输出:
# lower upper
# 1 1 10
# 2 10 20
# 3 20 30
一个选项是在从数据中删除 (
和 )
之后使用 tidyr::separate
。
library(tidyverse)
df %>% mutate(size = gsub("\(|)","",size)) %>% # Both ( and ) has been removed.
separate(size, c("Min", "Max"), sep = ",")
# Min Max
# 1 1 10
# 2 10 20
# 3 20 30