for 循环内部 sapply to return 字符串匹配
for loop inside sapply to return string match
我写这行代码是为了在一个数据框中工作,returns一个不区分大小写的新列与字符串列表的元素匹配。
但是,结果列仅适用于列表的第一个元素,'seed' 在这种情况下,但不适用于其他匹配项。不知道for循环哪里错了
这是您可能想要检查结果的示例数据框。
input.strings <- c('seed', 'fertilizer', 'fertiliser', 'loan', 'interest', 'feed', 'insurance')
polic = data.frame(policy_label=c('seed supply','energy subsidy','fertilizer distribution','loan guarantee','Interest waiver','feed purchase'))
polic$policy_class <- sapply(polic$policy_label, function(x){
for (i in input.strings){
if (grepl(i, tolower(x))){
return(i)
}
else{
return("others")
}
}
})
基础 R 替代方案
这是一种使用 sapply
(并且没有 for
循环)的更快 more-direct 方法,依赖于 grepl
可以在 [=17 上向量化这一事实=]. (它没有在 pattern=
上矢量化,要求长度为 1,这就是我们根本需要 sapply
的原因之一。)
matches <- sapply(input.strings, grepl, x = polic$policy_label)
matches
# seed fertilizer fertiliser loan interest feed insurance
# [1,] TRUE FALSE FALSE FALSE FALSE FALSE FALSE
# [2,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE
# [3,] FALSE TRUE FALSE FALSE FALSE FALSE FALSE
# [4,] FALSE FALSE FALSE TRUE FALSE FALSE FALSE
# [5,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE
# [6,] FALSE FALSE FALSE FALSE FALSE TRUE FALSE
因为我们想要将 "others"
分配给所有没有匹配的东西(并且因为我们将需要至少一个 TRUE
in
matches <- cbind(matches, others = rowSums(matches) == 0)
matches
# seed fertilizer fertiliser loan interest feed insurance others
# [1,] TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
# [2,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE
# [3,] FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
# [4,] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE
# [5,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE
# [6,] FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE
从这里,我们可以找到与真实值关联的名称并将它们分配(可选 ,
-collapsed)到 polic
:
polic$policy_class <- apply(matches, 1, function(z) toString(colnames(matches)[z]))
polic
# policy_label policy_class
# 1 seed supply seed
# 2 energy subsidy others
# 3 fertilizer distribution fertilizer
# 4 loan guarantee loan
# 5 Interest waiver others
# 6 feed purchase feed
仅供参考,我使用 toString
的原因是因为我不想假设永远不会超过一场比赛;也就是说,如果两个 input.strings
由于某种原因匹配一个 policy_label
,则 toString
会将它们组合成一个字符串,例如 "seed, feed"
用于 multi-match 策略。
fuzzyjoin 备选方案
如果您熟悉 merges/joins (and What's the difference between INNER JOIN, LEFT JOIN, RIGHT JOIN and FULL JOIN?),那么这应该看起来很熟悉。如果不是,以这种方式连接数据的概念可以转变为 data-munging/cleaning。
library(fuzzyjoin)
out <- regex_left_join(
polic, data.frame(policy_class = input.strings),
by = c("policy_label" = "policy_class"))
out
# policy_label policy_class
# 1 seed supply seed
# 2 energy subsidy <NA>
# 3 fertilizer distribution fertilizer
# 4 loan guarantee loan
# 5 Interest waiver <NA>
# 6 feed purchase feed
### clean up the NAs for "others"
out$policy_class[is.na(out$policy_class)] <- "others"
与上面的 base-R 变体相比,当多个 input.strings
匹配一个 policy_label
时,这里(还没有!)没有 safe-guard 来处理;当发生这种情况时,具有匹配项的行将被复制,因此您会看到(例如)seed supply
和该行上的所有其他列两次。这可以很容易地通过一些努力来缓解。
我写这行代码是为了在一个数据框中工作,returns一个不区分大小写的新列与字符串列表的元素匹配。
但是,结果列仅适用于列表的第一个元素,'seed' 在这种情况下,但不适用于其他匹配项。不知道for循环哪里错了
这是您可能想要检查结果的示例数据框。
input.strings <- c('seed', 'fertilizer', 'fertiliser', 'loan', 'interest', 'feed', 'insurance')
polic = data.frame(policy_label=c('seed supply','energy subsidy','fertilizer distribution','loan guarantee','Interest waiver','feed purchase'))
polic$policy_class <- sapply(polic$policy_label, function(x){
for (i in input.strings){
if (grepl(i, tolower(x))){
return(i)
}
else{
return("others")
}
}
})
基础 R 替代方案
这是一种使用 sapply
(并且没有 for
循环)的更快 more-direct 方法,依赖于 grepl
可以在 [=17 上向量化这一事实=]. (它没有在 pattern=
上矢量化,要求长度为 1,这就是我们根本需要 sapply
的原因之一。)
matches <- sapply(input.strings, grepl, x = polic$policy_label)
matches
# seed fertilizer fertiliser loan interest feed insurance
# [1,] TRUE FALSE FALSE FALSE FALSE FALSE FALSE
# [2,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE
# [3,] FALSE TRUE FALSE FALSE FALSE FALSE FALSE
# [4,] FALSE FALSE FALSE TRUE FALSE FALSE FALSE
# [5,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE
# [6,] FALSE FALSE FALSE FALSE FALSE TRUE FALSE
因为我们想要将 "others"
分配给所有没有匹配的东西(并且因为我们将需要至少一个 TRUE
in
matches <- cbind(matches, others = rowSums(matches) == 0)
matches
# seed fertilizer fertiliser loan interest feed insurance others
# [1,] TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
# [2,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE
# [3,] FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
# [4,] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE
# [5,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE
# [6,] FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE
从这里,我们可以找到与真实值关联的名称并将它们分配(可选 ,
-collapsed)到 polic
:
polic$policy_class <- apply(matches, 1, function(z) toString(colnames(matches)[z]))
polic
# policy_label policy_class
# 1 seed supply seed
# 2 energy subsidy others
# 3 fertilizer distribution fertilizer
# 4 loan guarantee loan
# 5 Interest waiver others
# 6 feed purchase feed
仅供参考,我使用 toString
的原因是因为我不想假设永远不会超过一场比赛;也就是说,如果两个 input.strings
由于某种原因匹配一个 policy_label
,则 toString
会将它们组合成一个字符串,例如 "seed, feed"
用于 multi-match 策略。
fuzzyjoin 备选方案
如果您熟悉 merges/joins (and What's the difference between INNER JOIN, LEFT JOIN, RIGHT JOIN and FULL JOIN?),那么这应该看起来很熟悉。如果不是,以这种方式连接数据的概念可以转变为 data-munging/cleaning。
library(fuzzyjoin)
out <- regex_left_join(
polic, data.frame(policy_class = input.strings),
by = c("policy_label" = "policy_class"))
out
# policy_label policy_class
# 1 seed supply seed
# 2 energy subsidy <NA>
# 3 fertilizer distribution fertilizer
# 4 loan guarantee loan
# 5 Interest waiver <NA>
# 6 feed purchase feed
### clean up the NAs for "others"
out$policy_class[is.na(out$policy_class)] <- "others"
与上面的 base-R 变体相比,当多个 input.strings
匹配一个 policy_label
时,这里(还没有!)没有 safe-guard 来处理;当发生这种情况时,具有匹配项的行将被复制,因此您会看到(例如)seed supply
和该行上的所有其他列两次。这可以很容易地通过一些努力来缓解。