Интерполяция данных в объекте data.table внутри функции - PullRequest
0 голосов
/ 06 августа 2020

Сейчас я пытаюсь преобразовать скрипт, который выполняет интерполяцию в объекте data.table (по группе), в функцию.

У меня есть данные в длинном формате, как в этом примере:

   UniqueId  Time   Var1      Var2    Var3
1       Id1     0     10   "cat-a"       2
2       Id1     5     12   "cat-a"       4
3       Id1    10     14   "cat-a"       6
4       Id2     0      8   "cat-b"       3
5       Id2    10     16   "cat-b"      10
6       Id3     0      6   "cat-a"       4
7       Id3    10      9   "cat-a"       8
[...]

Как видите, количество записей для каждого UniqueId может отличаться. Я хочу извлечь все уникальные значения столбца «Время» и убедиться, что все UniqueIds имеют запись для каждого из них, выполнив линейную интерполяцию для переменных numeri c, а для факторов или строк, поскольку они не меняются с течением времени, я беру первое значение для каждого UniqueId и повторяю его n раз (где n = length (unique (Time))), в результате получается что-то вроде этого:

   UniqueId  Time   Var1      Var2    Var3
1       Id1     0     10   "cat-a"       2
2       Id1     5     12   "cat-a"       4
3       Id1    10     14   "cat-a"       6
4       Id2     0      8   "cat-b"       3
5       Id2     5     12   "cat-b"     6.5
6       Id2    10     16   "cat-b"      10
7       Id3     0      6   "cat-a"       4
8       Id3     5    7.5   "cat-a"       6
9       Id3    10      9   "cat-a"       8
[...]

Как данные, над которыми я работаю, могут быть довольно большими (несколько Go), я решил использовать пакет data.table. Мой текущий код выглядит так:

Time.Common <- unique(MyData[, Time])
Time.Common <- Time.Common[order(Time.Common)]


MyData[, mapply(function(colVals, colName) {
  if(colName == "Time"){ # If we are operating on the time column, we replace its values with those of Time.Common
    return(Time.Common)
  }
  if(is.factor(colVals) | is.character(colVals)){ # If we are working on character or factor columns, we just repeat the value "length(Time.Common)" times
    rep(colVals[1], length(Time.Common))}
  else { # If it's a numeric column, we perform an interpolation
    withCallingHandlers(approx(x      = .SD[,Time],
                               y      = colVals,
                               xout   = Time.Common)$y,
                        warning = function(w){invokeRestart("muffleWarning")})
  }
}, .SD, names(.SD), SIMPLIFY = FALSE), by = eval(expr = "UniqueId")] # The process is repeated for each unique key value

Этот код дает ожидаемый Результаты. Проблема в том, что когда я пытаюсь преобразовать в функцию, я не могу заставить ее работать, и, поскольку я не эксперт по data.table, я не понимаю, в чем проблема.

Вот функция:

interpolation <- function(Data, 
                          UniqueKey     = "UniqueId",
                          TimeCol       = "Time",
                          InterMethod   = "linear"){
  # Recovering all unique values of the time column
  Time.Common <- unique(Data[, get(TimeCol, inherits = F)])
  Time.Common <- Time.Common[order(Time.Common)]
  
  # Interpolating
  Data[, mapply(function(colVals, colName) {
    if(colName == TimeCol){ 
      return(Time.Common)
    }
    if(is.factor(colVals) | is.character(colVals)){
      rep(colVals[1], length(Time.Common))}
    else {
      withCallingHandlers(approx(x      = .SD[,eval(parse(text = TimeCol))],
                                 y      = colVals,
                                 xout   = Time.Common,
                                 method = InterMethod)$y,
                          warning = function(w){invokeRestart("muffleWarning")})
    }
  }, .SDcols = .SD, names(.SD), SIMPLIFY = FALSE), by = eval(parse(text = UniqueKey))]
}

Это практически тот же код, за исключением той части, где я передаю имена столбцов в качестве переменных, где я должен использовать eval(parse()) или get(). Это функция не производит ce ошибок, когда я его использую, но также не выполняет интерполяцию (мои данные остаются без изменений). Может ли кто-нибудь помочь мне в этом и помочь найти мою ошибку?

Заранее спасибо!

1 Ответ

0 голосов
/ 06 августа 2020

Я бы сделал интерполяцию в data.table следующим образом

MyData[,c(
  list(Time = Time.Common),
  lapply(.SD, function(col) approx(x = Time, y = col, xout = Time.Common)$y)
  ), by = c('UniqueId','Var2'), .SDcols = c('Var1','Var3')]

и, возможно, использовал бы lapply(MyData,typeof), чтобы определить, какие столбцы я собирался интерполировать (в .SDcols =), а какие были в ключ (to go in the by =)

Отредактировано, чтобы добавить для этого общую c функцию, вы можете использовать другие методы, чтобы выбрать, какие столбцы являются ключами, а какие должны быть интерполированы, но в конечном итоге вы можете просто передать векторы символов в аргументах by и .SDcols таблицы data.table.

interpolation <- function(mydata, time_col) {
  col_types <- lapply(mydata[,-time_col,with=FALSE],typeof)
  interp_cols <- names(col_types)[col_types %in% c('double', 'integer')]
  key_cols <- names(col_types)[!(col_types %in% c('double', 'integer'))]
  mydata[,c(
    list(Time = Time.Common),
    lapply(.SD, function(col) approx(x = get(time_col), y = col, xout = Time.Common)$y)
  ),by=key_cols, .SDcols = interp_cols]
}
interpolation(MyData, "Time")
...