Можно ли объединить логическую и позиционную индексацию в data.table? - PullRequest
0 голосов
/ 19 апреля 2019

Я пытаюсь просмотреть таблицу data.table по группам, чтобы присвоить значение каждой строке условно внутри каждой подгруппы. Я могу выбрать нужные строки, но не могу обновить интересующую переменную в выбранных строках.

Я думаю, это может быть связано с тем, что мне нужно дважды нарезать таблицу данных. Я работаю с data.table с именем dt, в котором есть столбцы group, center, date и var. Здесь цель состоит в том, чтобы сопоставить значение var нецентральных записей, то есть center==0, с их ближайшими (с точки зрения разницы дат) центральными записями, где center==1. Предположим, i - это позиционный индекс строки, и я хочу обновить запись на основе условной фильтрации, а затем выполнить поиск по date в подгруппе gp.

dt[group == gp][i, var:= "new value"] 

Но когда я бегу

dt[group == gp][i, var] 

Переменная var кажется неизменной, иначе возвращает "old value".

Дополнительная информация

Вышеприведенная команда находится внутри цикла for, возможно, я здесь не использую лучшие практики. Буду признателен, если кто-то поделится своим мнением по поводу следующего цикла for. Спасибо.

for( gp in unique(dt$group)){
  tmp = dt[group==gp]
  for( i in 1:nrow(tmp)){
  new_val = tmp[center==1][which.min(abs(tmp[i, date]-tmp[center==1, date]),var]

  dt[group == gp][i, var:= new_val] 
 }
}

Мне известно о set и .by в data.table. Но я не знаю, как легко применить условную функцию поиска к каждой подгруппе с синтаксисом dt[, j=somefunction ,by=group]. Может быть, я мог бы поставить saply на .SD, но намного ли он быстрее, чем цикл for? Означает ли выигрыш в производительности потерю читабельности?

Редактировать

В разделе комментариев ниже я нашел способ комбинировать логическую и позиционную индексацию в data.table одновременно:

dt[which(group == gp)[i], var := new_val]

Вопрос о том, является ли использование цикла for хорошей идеей, до сих пор остается без ответа. Любые вклады будут оценены!

* * Пример тысячи сорок четыре * * 1045

Предположим, что исходный dt (отсортированный по группе и дате) выглядит следующим образом:

group center  date     var
  1     0     10-01    NA
  1     1     10-02    val1
  1     0     10-03    NA
  1     1     11-05    val2
  2     1     10-02    val3

Я хочу, чтобы обновленный dt был:

group center  date     var
  1     0     10-01    val1
  1     1     10-02    val1
  1     0     10-03    val1
  1     1     11-05    val2
  2     1     10-02    val3

Допустим, у нас здесь около 10 000 групп, и в каждой группе может быть до 1000 строк.

1 Ответ

1 голос
/ 22 апреля 2019

Для этой проблемы вы можете рассмотреть возможность использования скользящего соединения следующим образом:

dt[center==0L, var := dt[center!=0L][.SD, var, on=.(group, date), roll="nearest"]]

объяснения:

  1. dt[center==0L фильтрует строки, подлежащие обновлению.

  2. var := сообщает data.table, что это столбец для обновления.

  3. dt[center!=0L] фильтры для строк, где центр не равен 0.

  4. dt[center!=0L][.SD, on=.(group, date)] left объединяет строки с шага 1 (.SD) с строками с шага 3, а , var, выбирает этот столбец в качестве выходных.

  5. см. ?data.table для справки по roll аргументу. Когда roll='nearest', он находит ближайший date в соединении. Обратите внимание, что скользящий ключ всегда должен быть последним элементом в аргументе on.

выход:

   group center       date  var
1:     1      0 2018-10-01 val1
2:     1      1 2018-10-02 val1
3:     1      0 2018-10-03 val1
4:     1      1 2018-11-05 val2
5:     2      1 2018-10-02 val3

данные:

library(data.table)
dt <- fread("group center  date     var
1     0     2018-10-01    NA
1     1     2018-10-02    val1
1     0     2018-10-03    NA
1     1     2018-11-05    val2
2     1     2018-10-02    val3")
dt[, date := as.Date(date, format="%Y-%m-%d")]
...