0

我试图根据切碎的字符串将字符串列分成两部分。最好用下面的例子来说明。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)
4

4 回答 4

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

另一种方法是去掉括号和空格,然后使用单独的:

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,]"匹配除数字和逗号之外的所有内容。

于 2018-04-19T16:20:12.223 回答
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
于 2018-04-19T18:15:56.563 回答
1

对于按行操作,我更喜欢 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
于 2018-04-19T16:19:18.470 回答
0

你快到了。这是我对两种方法的解释,一种与您的方法相似:

在第一个代码中,我使用了 tidytext 包中的 unnest_tokens,它可以在不同的行上拆分单词,因为您想提取逗号之前的第一项(我假设它基于您的示例,尽管您应该提及它)。我通过使用过滤器命令选择了第一行。

在第二个代码中,我使用了正则表达式(注意你也可以在这里使用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
于 2018-04-19T16:52:14.523 回答