将自定义函数应用于多个data.table列

时间:2019-02-28 13:01:38

标签: r dataframe data.table lapply

我写了下面的函数,该函数采用非标准的时间格式,例如'730'(7:30)并将其转换为十进制小时数,例如'7.5'。

decimal_time <- function(x) {
  x <- as.character(x)
  tmp <- nchar(x)

  if (tmp < 4 & !is.na(tmp)){
    x <- paste0(strrep('0',4-tmp),as.character(x))
  }

  x <-  sub("([[:digit:]]{2,2})$", ":\\1", x)
  x <- strsplit(x,':')[[1]]
  x <- as.numeric(x)
  x[1]+x[2]/60
}

要将其应用于一列,请执行以下操作...

dt_times[, New_Time := lapply(Time, decimal_time)]

但是,我不知道如何将相同的功能应用于共享相同格式的许多列。当然,如果它是矢量化函数(例如“均值”),那么我可以编写...

dt_times[, lapply(.SD, mean), .SDcols = c('col1', 'col2')]

...但是如果我的函数首先使用lapply怎么办?请帮忙!

4 个答案:

答案 0 :(得分:2)

如果您的问题是您没有向量化函数,则可以在函数内部使用sapply

decimal_time <- function(y) {
  sapply(y,function(x) {
    x <- as.character(x)
    tmp <- nchar(x)

    if (tmp < 4 & !is.na(tmp)){
      x <- paste0(strrep('0',4-tmp),as.character(x))
    }

    x <-  sub("([[:digit:]]{2,2})$", ":\\1", x)
    x <- strsplit(x,':')[[1]]
    x <- as.numeric(x)
    x[1]+x[2]/60
  })
}

答案 1 :(得分:2)

您不需要任何循环(在函数的外部或内部)。您可以完全向量化您的功能:

decimal_time <- function(x) {
  x <- as.character(x)
  tmp <- nchar(x)
  ii <- tmp < 4 & !is.na(tmp)
  x[ii] <- paste0(strrep('0',4-tmp[ii]), x[ii])

   x <-  sub("([[:digit:]]{2,2})$", ":\\1", x)
  x <-  strsplit(x,':')
  x <- do.call(rbind, x)
  mode(x) <- "numeric"
  x[,1]+x[,2]/60
}

x <- c("1", "730")
decimal_time(x)
#[1] 0.01666667 7.50000000

使用整数除法比使用文本处理更容易:

decimal_time <- function(x) {
  x <- as.integer(x)
  if (any(x >= 2400)) warning("input >= 24 h")
  x %/% 100 + (x %% 100) / 60
}

x <- c("1", "730")
decimal_time(x)
#[1] 0.01666667 7.50000000

答案 2 :(得分:0)

这是我过去遇到的问题。我的解决方案通常是只运行for循环:

for(col in c('col1', 'col2'){
 dt_times[, (col):= vapply(col, function(x) decimal_time(get(x)), FUN.VALUE = numeric(1))]
}

也许不是最优雅的解决方案,但应该可以完成工作。

答案 3 :(得分:-1)

我建议您使用map_dfr包中的purrr函数在data.frame上应用函数,并同时返回data.frame。在幕后,map_ *系列函数的迭代方式与for循环相同,但是以一种更具可读性和整洁的方式进行。

此外,如果您希望将此函数映射到特定的列名,则也可以使用dplyrfilter函数的结合使用contains包,您可以修改这些特定变量。结合这些功能:

library(dplyr)
library(purrr)

df %>%
  filter(contains("some_string")) %>%
  map_dfr(decimal_time)