在 R 中分割一个巨大的字符串的有效方法

Efficient way to split a huge string in R

我有一个巨大的字符串 (> 500MB),实际上它是一本完整的书 collection。我在另一个数据框中有一些元信息,例如页码、(不同的)作者和标题。我尝试检测我的巨大字符串中的标题字符串并按标题拆分它。我假设标题是唯一的。

数据如下所示:

mystring <- "Lorem ipsum dolor sit amet, sollicitudin duis maecenas habitasse ultrices aenean tempus"

# a dataframe of meta data, e.g. page numbers and titles
mydf <- data.frame(page = c(1, 2),
                   title = c( "Lorem", "maecenas"))
mydf

  page   title
1    1   Lorem
2    2 vivamus

mygoal <- mydf  # text that comes after the title
mygoal$text <- c("ipsum dolor sit amet, sollicitudin duis", "habitasse ultrices aenean tempus")
mygoal 

  page   title                                    text
1    1   Lorem ipsum dolor sit amet, sollicitudin duis
2    2 vivamus        habitasse ultrices aenean tempus

如何拆分字符串,使两个标题之间的所有内容成为第一个文本,第二个标题之后和第三个标题之前的所有内容成为第二个文本元素 - 以最有效的方式。

我们可以使用strsplit

mygoal$text <- trimws(strsplit(mystring,
      paste(mydf$title, collapse = "|"))[[1]][-1])

-输出

> mygoal
  page    title                                    text
1    1    Lorem ipsum dolor sit amet, sollicitudin duis
2    2 maecenas        habitasse ultrices aenean tempus

如果您想以管道 tidyverse 方式进行操作,您可以尝试使用 stringr::str_extract 和一些正则表达式:

library(dplyr)
library(stringr)
library(glue)

mydf |>  
  mutate(next_title = lead(title, default = "$")) |> 
  mutate(text = str_extract(mystring, glue::glue("(?<={title}\s?)(.*)(?:{next_title})"))) |> 
  select(-next_title)

产量:

page    title                                      text
1    1    Lorem  ipsum dolor sit amet, sollicitudin duis 
2    2 maecenas          habitasse ultrices aenean tempus

如果性能是一个问题,与 data.table 类似的方法是:

library(data.table)
library(stringr)
library(glue)

mydt <- setDT(mydf)

mydt[, next_title :=shift(title, fill = "$", type = "lead")][
  ,text := str_extract(..mystring, glue_data(.SD,"(?<={title}\s?)(.*)(?={next_title})"))][,
    !("next_title")]

导致:

   page    title                                      text
1:    1    Lorem  ipsum dolor sit amet, sollicitudin duis 
2:    2 maecenas          habitasse ultrices aenean tempus

编辑

添加了更好的性能选项:

通常,str_splitstr_split_fixedstr_extract 更快。

str_split 的问题是具有许多备用管道的正则表达式也会减慢该过程,因此另一种解决方案是先用一些固定的字符串替换字符串中的所有标题,然后分裂那些。另一件可以加快拆分速度的事情是使用 str_split_fixed 和 pre-assign 要处理的拆分数。

    # create named character vector for str_replace_all function
split_at <- rep("@@",nrow(mydf))
names(split_at) <- mydf$title
mystring <- str_replace_all(mystring, split_at)

# used fixed in str_split
mydf$text <- str_split(mystring,fixed("@@ "))[[1]][-1]

# Alternative (maybe faster) define number of splits by nrow
mydf$text <- str_split_fixed(mystring,fixed("@@ "), n = nrow(mydf)+1)[,-1]


## using str_split_fixed in data.table
mydt <- setDT(mydf)
mydt[, text := 
       str_split_fixed(mystring,fixed("@@ "), nrow(mydt)+1)[,-1]