【问题标题】:Speedy test on R data frame to see if row values in one column are inside another column in the data frame对 R 数据框进行快速测试,以查看一列中的行值是否在数据框中的另一列内
【发布时间】:2015-06-13 23:34:25
【问题描述】:

我有一个包含 22k 条记录和 6 列的营销数据数据框,其中 2 列是感兴趣的。

  • 变量
  • FO.variable

这是数据帧样本的 dput 输出的链接:http://dpaste.com/2SJ6DPX

如果有更好的方法来分享这些数据,请告诉我。

我要做的就是创建一个额外的二进制保留列,它应该是:

  • 1 如果 FO.variable 在变量内部
  • 0 如果 FO.Variable 不在变量中

似乎是一件简单的事情...在 Excel 中,我只需添加带有“if”公式的另一列,然后将公式粘贴下来。我花了过去几个小时试图得到这个和 R 并且失败了。

这是我尝试过的:

  1. 使用 grepl 进行模式匹配。我以前使用过 grepl ,但这次我试图传递一列而不是字符串。我早期的尝试失败了,因为我试图强制 grepl 和 ifelse 导致 grepl 使用列中的第一个值而不是整个值。

  2. 我的下一个尝试是根据 SO 上的另一篇文章使用 transform 和 grep。我不认为这会给我确切的答案,但我认为它会让我足够接近,让我从那里弄清楚......代码运行了一段时间而不是因为下标无效而出错。

    transform(dd, Keep = FO.variable[sapply(variable, grep, FO.variable)])

  3. 我的下一个尝试是使用 str_detect,但我认为这不是正确的方法,因为我想要行级别的值,而且我认为“any”会使用向量中的任何值?

    kk <- sapply(dd$variable, function(x) any(sapply(dd$FO.variable, str_detect, string = x)))

  4. 编辑:刚刚尝试了一个 for 循环。我更喜欢矢量化方法,但此时我非常绝望。我以前没有使用过 for 循环,因为我避免使用它们并坚持使用其他解决方案。不确定我是否搞砸了语法,这似乎不太正确:

for(i in 1:nrow(dd)){ if(dd[i,4] %in% dd[i,2]) dd$test[i] <- 1 }

正如我所提到的,如果 FO.variable 在变量内部,我的理想输出是带有 1 或 0 的附加列。例如,样本数据中的前三条记录为 1,第四条记录为零,因为“直接/未知”不在“有机搜索,系统电子邮件”中。

如果解决方案可以快速运行,那将是一个好处。应用选项需要很长时间,可能是因为它们在两列的每次迭代中循环?

事实证明这并不像我想象的那么简单。或者也许是这样,而我只是个笨蛋。无论哪种方式,我都感谢有关如何最好地解决此问题的任何帮助。

【问题讨论】:

  • dd$cake = apply(dd, 1, function(x) grepl(x[4], x[2]))
  • 要快速识别某个向量是否在另一个向量中,可以使用mergelike 操作。请参阅我的回答:stackoverflow.com/questions/29356496/…

标签: regex r dataframe data.table apply


【解决方案1】:

我读取数据

df = dget("http://dpaste.com/2SJ6DPX.txt")

然后将“变量”列拆分为各个部分并计算出每个条目的长度

v = strsplit(as.character(df$variable), ",", fixed=TRUE)
len = lengths(v)    ## sapply(v, length) in R-3.1.3

然后我取消了 v 并创建了一个索引,将未列出的 v 映射到它来自的行

uv = unlist(v)
idx = rep(seq_along(v), len)

最后,我找到了 uv 等于 FO.variable 中对应条目的索引

test = (uv == as.character(df$FO.variable)[idx])
df$Keep = FALSE
df$Keep[ idx[test] ] = TRUE

或组合(返回逻辑向量似乎比修改后的data.frame更有用,可以通过dd$Keep = f0(dd)获得)

f0 = function(dd) {
    v = strsplit(as.character(dd$variable), ",", fixed=TRUE)
    len = lengths(v)
    uv = unlist(v)
    idx = rep(seq_along(v), len)

    keep = logical(nrow(dd))
    keep[ idx[uv == as.character(dd$FO.variable)[idx]] ] = TRUE
    keep
}

(使用列是因子这一事实可以更快,但这可能不是故意的?)与(公认的更简单和更容易理解)相比

f1 = function(dd) 
    mapply(grepl, dd$FO.variable, dd$variable, fixed=TRUE)

f1a = function(dd)
    mapply(grepl, as.character(dd$FO.variable), 
           as.character(dd$variable), fixed=TRUE)

f2 = function(dd)
    apply(dd, 1, function(x) grepl(x[4], x[2], fixed=TRUE))

> library(microbenchmark)
> identical(f0(df), f1(df))
[1] TRUE
> identical(f0(df), unname(f2(df)))
[1] TRUE
> microbenchmark(f0(df), f1(df), f1a(df), f2(df))
Unit: microseconds
    expr     min       lq      mean   median       uq     max neval
  f0(df)  57.559  64.6940  70.26804  69.4455  74.1035  98.322   100
  f1(df) 573.302 603.4635 625.32744 624.8670 637.1810 766.183   100
 f1a(df) 138.527 148.5280 156.47055 153.7455 160.3925 246.115   100
  f2(df) 494.447 518.7110 543.41201 539.1655 561.4490 677.704   100

在时序开发过程中添加了两个微妙但重要的附加功能,即在正则表达式中使用 fixed=TRUE,并将因子强制转换为字符。

【讨论】:

  • 我必须承认这是惊人的加速。我已经在更大的数据集上对其进行了测试,但它的性能仍然提高了大约 x20。
  • @DavidArenburg 它可能有助于明确地将因子强制转换为字符向量,mapply(grepl, as.character(dd$F0.variable),... 否则我认为这是为每个 grepl 完成的......
  • 哦,你是对的,这确实可以显着改善结果。
【解决方案2】:

在你的情况下,我会选择一个简单的mapply,正如你所说的那样,按行操作会非常慢。此外,(按照 Martin 的建议)设置 fixed = TRUE 并先验地转换为 character 将显着提高性能。

transform(dd, Keep = mapply(grepl, 
                            as.character(FO.variable), 
                            as.character(variable), 
                            fixed = TRUE))

#    VisitorIDTrue                        variable value      FO.variable FO.value  Keep
# 22      44888657 Direct / Unknown,Organic Search     1 Direct / Unknown        1  TRUE
# 2       44888657   Direct / Unknown,System Email     1 Direct / Unknown        1  TRUE
# 6       44888657             Direct / Unknown,TV     1 Direct / Unknown        1  TRUE
# 10      44888657     Organic Search,System Email     1 Direct / Unknown        1 FALSE
# 18      44888657               Organic Search,TV     1 Direct / Unknown        1 FALSE
# 14      44888657                 System Email,TV     1 Direct / Unknown        1 FALSE
# 24      44888657 Direct / Unknown,Organic Search     1   Organic Search        1  TRUE
# 4       44888657   Direct / Unknown,System Email     1   Organic Search        1 FALSE
...

【讨论】:

    【解决方案3】:

    这是一种我认为在精神上与 Martin 非常相似的 data.table 方法:

    require(data.table)
    
    dt <- data.table(df)
    dt[,`:=`(
        fch = as.character(FO.variable),
        rn  = 1:.N
    )]
    
    dt[,keep:=FALSE]
    dtvars <- dt[,strsplit(as.character(variable),',',fixed=TRUE),by=rn]
    setkey(dt,rn,fch)
    dt[dtvars,keep:=TRUE]
    
    dt[,c("fch","rn"):=NULL]
    

    这个想法是

    1. 识别所有rnvariable对(保存在dtvars中)和
    2. 查看这些对中的哪一个与rnF0.variable 对匹配(在原始表中,dt)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-23
      • 2017-10-25
      • 2020-08-04
      • 2022-01-18
      • 1970-01-01
      相关资源
      最近更新 更多