Примените пользовательскую функцию к нескольким столбцам data.table - PullRequest
0 голосов
/ 28 февраля 2019

Я написал нижеприведенную функцию, которая принимает нестандартный формат времени, например, «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 голосов
/ 28 февраля 2019

Вам не нужно никаких циклов (вне или внутри функции).Вы можете полностью векторизовать свою функцию:

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
0 голосов
/ 28 февраля 2019

Это проблема, с которой я сталкивался в прошлом.Мое решение обычно состоит в том, чтобы просто запустить цикл for:

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

Возможно, это не самое элегантное решение, но оно должно выполнить свою работу.

0 голосов
/ 28 февраля 2019

Я бы порекомендовал вам использовать функцию map_dfr из пакета purrr, чтобы применить функцию к data.frame, возвращая также data.frame.Под капотом семейство функций map_ * выполняет итерации так же, как и для циклов for, но более читабельно и аккуратно.

Кроме того, если вы хотите сопоставить эту функцию с конкретными именами столбцов, вы можетеТакже используйте пакет dplyr, комбинируя использование функций filter и contains, и вы можете изменять эти конкретные переменные.Сочетание этих функций:

library(dplyr)
library(purrr)

df %>%
  filter(contains("some_string")) %>%
  map_dfr(decimal_time)   
0 голосов
/ 28 февраля 2019

Если ваша проблема в том, что у вас нет векторизованной функции, вы можете использовать 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
  })
}
...