我试图根据切断字符串将字符串列分成两部分。以下示例最佳说明。 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)
答案 0 :(得分:1)
对于rowwise操作,我更喜欢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
答案 1 :(得分:1)
您没有明确说明您的目标,但似乎您想要从字符串中提取第一个数字。使用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
另一种方法是摆脱parens和whitespace然后单独使用:
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,]"
匹配除数字和逗号之外的所有内容。
答案 2 :(得分:1)
选项是从数据中删除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
答案 3 :(得分:0)
你快到了。以下是我对两种方法的解释,一种类似于你的方法:
在第一个代码中,我使用了tidytext包中的unnest_tokens,它可以在不同的行上拆分单词,因为你想在逗号之前提取第一个项目(我假设它是基于你的例子,尽管你应该提到它)。我通过使用filter命令选择了第一行。
在第二个代码中,我使用了正则表达式(请注意,您也可以使用此处str_replace
)。这里我使用map(因为str_split返回的项是一个列表)来迭代返回的项并通过gsub传递每个,这可以替换与后引用项匹配的正则表达式。另外,为了只选择第一项,我在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