对R数据帧进行快速测试,查看数据帧中一列中的行值是否在另一列中



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

  • 变量
  • FO.可变

以下是数据帧示例的dput输出链接:http://dpaste.com/2SJ6DPX

请告诉我是否有更好的方式来共享这些数据。

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

  • 如果FO.variable在variable内,则为1
  • 如果FO.Variable不在Variable内部,则为0

看起来很简单。。。在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,第四条记录为零,因为"直接/未知"不在"有机搜索,系统电子邮件"中。

如果解决方案能够快速运行,那将是一个额外的好处。apply选项花费了很长很长的时间,也许是因为它们在两列的每个迭代中都循环?

事实证明,这并不像我想象的那么简单。也许是这样,而我只是个傻瓜。无论哪种方式,我都非常感谢任何关于如何最好地处理这一问题的帮助。

我读取数据

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

或者组合(返回逻辑向量似乎比使用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,并将因素强制为特征。

在您的情况下,我会使用一个简单的mapply,正如您正确地说的那样,逐行操作将非常缓慢。此外,(正如Martin所建议的)设置fixed = TRUE并将apriori转换为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
...

这里有一个data.table方法,我认为它在精神上与Martin的方法非常相似:

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. 识别CCD_ 8&variable(保存在dtvars中)和
  2. 查看这些对中的哪一个与CCD_ 11&F0.variable对(在原始表中为dt

最新更新