带有if / else函数的mutate()

时间:2017-05-15 21:03:59

标签: r dplyr

我有一个示例数据框

df <- data.frame(cust = sample(1:100, 1000, TRUE),
             channel = sample(c("WEB", "POS"), 1000, TRUE))

我试图改变

get_channels <- function(data) {
    d <- data
    if(unique(d) %>% length() == 2){
        d <- "Both"
    } else {
        if(unique(d) %>% length() < 2 && unique(d) == "WEB") {
            d <- "Web"
        } else {
            d <- "POS"
            }
        }
    return(d)
}

这没有问题,在小型数据帧上,它根本不需要时间。

start.time <- Sys.time()

df %>%
    group_by(cust) %>%
    mutate(chan = get_channels(channel)) %>%
    group_by(cust) %>% 
    slice(1) %>%
    group_by(chan) %>%
    summarize(count = n()) %>%
    mutate(perc = count/sum(count))

end.time <- Sys.time()
time.taken <- end.time - start.time
time.taken
  

时差0.34602秒

然而,当数据框变得相当大时,例如,大约> 1000000或更多cust,我的基本if/else fx需要更多,更多更长

如何简化此功能以使其更快地运行?

4 个答案:

答案 0 :(得分:5)

您应该使用data.table。

setDT(df)
t1 = Sys.time()
df = df[ , .(channels = ifelse(uniqueN(channel) == 2, "both", as.character(channel[1]))), by = .(cust)]

> Sys.time() - t1
Time difference of 0.00500083 secs

> head(df)
   cust channels
1:   37     both
2:   45     both
3:   74     both
4:   20     both
5:    1     both
6:   68     both

答案 1 :(得分:3)

您可以使用类似的东西在基地R中执行此操作:

web_cust <- unique(df$cust[df$channel=="WEB"])
pos_cust <- unique(df$cust[df$channel=="POS"])

both <- length(intersect(web_cust, pos_cust))
web_only <- length(setdiff(web_cust, pos_cust))
pos_only <- length(setdiff(pos_cust, web_cust))

数据:

set.seed(1)
df <- data.frame(cust = sample(2e6, 1e7, TRUE),
                 channel = sample(c("WEB", "POS"), 1e7, TRUE), 
                 stringsAsFactors = F)

答案 2 :(得分:1)

更快的dplyr版本需要大约1/3的时间,但可能仍然比数据表版本慢。 uniqueN借用@Kristoferson回答。

 df %>%
    group_by(cust) %>%
    summarize(chan = if_else(uniqueN(channel) == 2, "Both", as.character(channel[1]))) %>%
    group_by(chan) %>%
    summarize(n = n() ) %>%
    mutate(perc = n /sum(n))

此外,通过优化您的功能可以显着改善您的原始状态:

  get_channels <- function(data) {
    ud <- unique(data)
    udl <- length(ud)
    if(udl == 2) {
      r <- "Both"
    } else {
      if(udl < 2 && ud == "WEB") {
        r <- "Web"
      } else {
        r <- "POS"
      }
    }
    return(r)
  }

答案 3 :(得分:1)

还有一些时间......

我在dplyrdata.table中尝试了三种不同的选择:(1)ifelse(请参阅@ Kristofersen的回答),(2)if / {{1} (因为else长度为1),(3)向量索引。不出所料,主要区别在于testdplyr之间,而不是替代1-3之间。

对于1000个客户,data.table的速度提高了约7倍。对于10000个客户来说,速度提高了约30倍。对于1e6客户,我只测试了data.table,而不是替代品之间的差异。

data.table