如何使用 data.table 迭代修改变量?

How to modify a variable iteratively using data.table?

我希望有人能帮我弄清楚如何在 data.table 中多次修改一个变量,或者找到一种适用于大数据的类似方法。

我有一个包含字符串的数据集(确切地说是地址,但确切的内容并不重要),例如:

library(data.table)
library(stringr)

# example addresses although you can imagine other types of strings here
addr <- data.table(street = c('1 main street', 
                              '99 madison avenue',
                              '340 circle court'))

我有另一个数据集,其中包含一列模式,我想在这些字符串(即 addr 数据集中)中搜索模式,并替换为第二个数据集中另一列中保存的其他字符串。例如:

# example of patterns to search for and what I want to replace them with
abbrev <- data.table(full = c('street', 'avenue', 'circle', 'court'),
                     abbrev = c('st', 'ave', 'cir', 'ct')) 

实际的数据集要大得多:数百万个地址和 300 多个缩写我想检查每个地址。​​

在循环中执行此操作相当简单,但由于大小,我想使用 data.table 并且可能使用 apply 函数来提高此过程的效率.

我正在努力弄清楚如何准确地写这个。我想要如下内容:

# duplicate addresses so we can compare to changes
addr[, orig.street := street]

# function to substitute abbreviations we want
standardize <- function(word, shorter) {
  addr[, street := str_replace_all(street,
                                   paste0(" ", word),
                                   paste0(" ", shorter))]
}

# now run function for all abbreviations we want
addr[, street := mapply(FUN = standardize,
                        word = abbrev$full,
                        shorter = abbrev$abbrev,
                        SIMPLIFY = FALSE, USE.NAMES = FALSE)]

当我在 Rstudio 中 运行 返回错误时,“提供了 4 项要分配给列 'street' 的 3 项。RHS 长度必须为 1(单个值都可以)或完全匹配 LHS 长度。如果您希望 'recycle' RHS,请明确使用 rep() 以使您的代码读者清楚地了解此意图。“

然而它确实给了我想要的东西,尽管有错误:

# it breaks but I do get the desired outcome:
           street       orig.street
1:      1 main st     1 main street
2: 99 madison ave 99 madison avenue
3:     340 cir ct  340 circle court

我觉得一定有一个解决方案我想念,但我还没有想出来。任何帮助将不胜感激。

您可以使用 stri_replace_all_fixed 及其参数 vectorize_all = FALSE 来自库(stringi):

library(data.table)
library(stringi)

addr <- data.table(orig_street = c('1 main street', 
                              '99 madison avenue',
                              '340 circle court'))

abbrev <- data.table(full = c('street', 'avenue', 'circle', 'court'),
                     abbrev = c('st', 'ave', 'cir', 'ct')) 

addr[, street := stri_replace_all_fixed(orig_street, abbrev$full, abbrev$abbrev, vectorize_all = FALSE)]

> addr
         orig_street         street
1:     1 main street      1 main st
2: 99 madison avenue 99 madison ave
3:  340 circle court     340 cir ct

另请参阅 并注意 library(stringr) 导入 library(stringi)

另一种方法是 Reduce 方法:

addr[, street2 := Reduce(function(txt, i) gsub(paste0("\b", abbrev$full[i], "\b"), abbrev$abbrev[i], txt),
                         seq_len(nrow(abbrev)), init = street)][]
#               street        street2
#               <char>         <char>
# 1:     1 main street      1 main st
# 2: 99 madison avenue 99 madison ave
# 3:  340 circle court     340 cir ct

注:

  • 我明确地向 gsub 正则表达式添加了单词边界 (\b),这样我们就不会无意中替换单词的一部分。我认为我们需要这个而不是 fixed=TRUE 因为 gsub("court", "ct", "courteous", fixed = TRUE) returns "cteous".
  • 如果我们尝试 apply 系列(在 abbrev 上),那么我们会看到每个模式的更新值,但不知道(没有额外工作)哪个有变化;此外,如果有可能(一般来说,这里可能没有)多个缩写模式有用,那么我们需要将每个 pattern/replacement 应用于前一个替换的结果 *apply 做不到(那么容易)。
  • 不幸的是,Reduce 不容易遍历帧的行,因此我们遍历行索引 (seq_len(nrow(abbrev)))。

不过,不禁觉得最后一行真的应该是"340 circle ct"。在这种情况下,如果我们假设缩写位于字符串的末尾,我们可以改用它:

addr[, street3 := Reduce(function(txt, i) gsub(paste0("\b", abbrev$full[i], "\s*$"), abbrev$abbrev[i], txt), 
                         seq_len(nrow(abbrev)), init  = street)][]
#               street        street2        street3
#               <char>         <char>         <char>
# 1:     1 main street      1 main st      1 main st
# 2: 99 madison avenue 99 madison ave 99 madison ave
# 3:  340 circle court     340 cir ct  340 circle ct