代码之家  ›  专栏  ›  技术社区  ›  DanY

如何在R中比较一列与一系列不带for循环的相关伪变量

  •  5
  • DanY  · 技术社区  · 6 年前

    我有工作的R代码,但它是不优雅和低效的。我想知道是否有更好的方法:即如何将此过程矢量化和/或减少计算时间?

    library(data.table)
    dt <- data.table(
        visited_a = c(1, 1, 0, 0),
        visited_b = c(1, 0, 0, 0),
        visited_c = c(0, 0, 1, 1),
        purchased = c("b", "b", "c", "a")
    )
    

    我的data.table提供了2017年消费者是否访问商店的虚拟指标。所以 visited_a = 0 意思是她没去商店 a 2017年 visited_b = 1 意思是她确实去过商店 b 2017年。数据还列出了2018年购买的消费者;所有这些消费者都进行了购买。因此,消费者可能(去年)去过或没有去过她(今年)购买的商店。

    我想添加一个变量 purchased_was_visited 为了抓住这个。解决办法是:

    dt$purchased_was_visited <- c(1, 0, 1, 0)
    

    下面是我非常不优雅的代码,它一行一行地循环遍历data.table。一定有更好的办法!

    dt[ , purchased_was_visited := NA]
    for(i in 1:nrow(dt)) {
        brand <- dt[i, purchased]
        col <- paste0("visited_", brand)
        was_it <- dt[i, ..col]
        dt[i, purchased_was_visited := was_it]
    }
    
    5 回复  |  直到 6 年前
        1
  •  2
  •   MKR    6 年前

    一种选择是 get 按每行分组。必须准备列名(基于 purchased )需要评估以检查过去的访问。现在, 得到 如果对每一行求值,函数将提供所需的结果。因此,我们需要对每一行进行分组(例如。 by=1:NROW(dt) ) :

    library(data.table)
    
    dt[,purchased_was_visited := get(paste("visited",purchased,sep="_")), by=1:NROW(dt)]
    
    dt
    #    visited_a visited_b visited_c purchased purchased_was_visited
    # 1:         1         1         0         b                     1
    # 2:         1         0         0         b                     0
    # 3:         0         0         1         c                     1
    # 4:         0         0         1         a                     0
    
        2
  •  4
  •   Frank    6 年前

    我会给你的消费者一个ID列,并将数据存储在两个表中:

    dt[, cid := .I]
    
    # visits
    vDT = melt(dt, id="cid", meas=patterns("visited"), variable.name = "store")[value == 1, !"value"]
    vDT[, store := tstrsplit(store, "_")[[2]]]
    vDT[, year := 2017L]
    
    # choices
    cDT = dt[, .(cid, year = 2018L, store = purchased)]
    

    然后,可以执行联接以将指示符列添加到cDT:

    cDT[, v_before := vDT[.SD, on=.(cid, store, year < year), .N, by=.EACHI]$N]
    
       cid year store v_before
    1:   1 2018     b        1
    2:   2 2018     b        0
    3:   3 2018     c        1
    4:   4 2018     a        0
    
        3
  •  0
  •   YOLO    6 年前

    下面是使用base的另一种方法 apply 命令:

    ## get index of column names which are 1
    vals <- apply(dt[,1:3], 1, function(x) which(x == 1))
    vals <- lapply(vals, function(x) names(x))
    
    # replace the string in column names before underscore
    vals <- lapply(vals, function(x) gsub(pattern = '.*._',replacement = '',x = x))
    
    # create the final column
    dt[, purchased_was_visited := mapply(function(x,y) as.integer(x %in% y), purchased, vals)]
    
    print(dt)
    
       visited_a visited_b visited_c purchased purchased_was_visited
    1:         1         1         0         b                     1
    2:         1         0         0         b                     0
    3:         0         0         1         c                     1
    4:         0         0         1         a                     0
    
        4
  •  0
  •   OzanStats    6 年前

    我不确定它是否优雅,但这里有一个“整洁”和高效的 dplyr 解决方案:

    library(dplyr)
    
    setDF(dt)
    
    dt <- dt %>%
      mutate(
        check_a = if_else(visited_a == 1, if_else(purchased == "a", 1, 0), 0),
        check_b = if_else(visited_b == 1, if_else(purchased == "b", 1, 0), 0),
        check_c = if_else(visited_c == 1, if_else(purchased == "c", 1, 0), 0),
        purchased_was_visited = check_a + check_b + check_c
      ) %>%
      select(-c(5:7))
    
        5
  •  0
  •   IanRiley    6 年前

    这里有另一个想法-使您在“购买”中的值与您为访问的列提供的名称相匹配。这样,就可以使用“purchased”直接查找所需的值。在这里,我已经将“visitea”重命名为“a”,等等。这也可以通过编程来完成,就像您和其他一些人所做的那样,但是也许提前为您的数据和数据表设计以获得可用性是一个更整洁的选择。

    dt <- data.frame(
        a = c(1, 1, 0, 0),
        b = c(1, 0, 0, 0),
        c = c(0, 0, 1, 1),
        purchased = c("b", "b", "c", "a")
        )
    
    dt$purchased_was_visited <- 
         sapply(row.names(dt), function(i) {(dt[i, dt[i, 'purchased']])})
    
    dt
    
    #   a b c purchased purchased_was_visited
    # 1 1 1 0         b                     1
    # 2 1 0 0         b                     0
    # 3 0 0 1         c                     1
    # 4 0 0 1         a                     0
    

    抱歉,我使用的是data.frame而不是data.table,但同样的原则也适用。