Фильтрация по различным диапазонам по группам в таблице данных

#r #data.table #non-equi-join

#r #данные.таблица #неравнозначное соединение

Вопрос:

У меня есть некоторые данные в таблице, и я хотел бы выполнить неравномерное объединение (я думаю, это правильная терминология) и отфильтровать их по разным диапазонам для разных групп. В приведенном ниже примере я хотел бы отфильтровать группу «а», чтобы она возвращала значения только от 1 до 20 (включительно), и группу «в», чтобы она возвращала значения только от 80 до 100 (включительно). Мое чтение предполагает, что inrange это должен быть предмет для использования. Я понимаю, как использовать его в общем случае, но я не уверен, как заставить его работать с разными диапазонами по группам. (пример кода, адаптированного из ?inrange )

создание образцов данных

 set.seed(1234) Y = data.table(a=sample(1:100,100), val=runif(100), group=c(rep("a",50),rep("b",50))) range = data.table(group=c("a","b"),start = c(1,80), end = c(20,100))  

Попробуйте отфильтровать

 Y[inrange(a, range$start, range$end),,by=group]  

Это, очевидно, не работает и вместо этого применяет эти диапазоны ко всему набору данных и выдает сообщение об ошибке Ignoring by= because j= is not supplied . Я думаю, мне ясно, что это не работает, потому что я не создал «соединение» между таблицей диапазонов и Y, но я не вижу, как заставить две таблицы взаимодействовать группировкой через inrange .

Примечание: На самом деле значениями в a будут даты posixct, но для простоты я здесь это не использую.

Комментарии:

1. Вы понимаете, что runif это взято из [0,1] и range , похоже, включено [0,100] ?

2. val это просто дополнительный столбец для аппроксимации реальных данных (есть дополнительные столбцы, которые есть в пути). Фильтрация должна быть включена a . Я не вижу в этом проблемы.

3. О, я пропустил вступление a , это кажется ясным (и очевидным), моя ошибка 🙂

Ответ №1:

Возможно:

 Y[range, K := TRUE, on = .(group, a gt;= start, a lt;= end)][!is.na(K),] # a val group K # lt;intgt; lt;numgt; lt;chargt; lt;lgclgt; # 1: 9 0.60189755 a TRUE # 2: 5 0.99874081 a TRUE # 3: 16 0.55512663 a TRUE # 4: 4 0.42944396 a TRUE # 5: 14 0.43101637 a TRUE # 6: 3 0.47880269 a TRUE # 7: 2 0.02220682 a TRUE # 8: 6 0.63891131 a TRUE # 9: 8 0.83470266 a TRUE # 10: 17 0.98304402 a TRUE # 11: 98 0.76785547 b TRUE # 12: 94 0.30766574 b TRUE # 13: 88 0.25814665 b TRUE # 14: 89 0.49954639 b TRUE # 15: 83 0.50892062 b TRUE # 16: 95 0.49443856 b TRUE # 17: 97 0.56695890 b TRUE # 18: 87 0.98970989 b TRUE # 19: 82 0.53190509 b TRUE # 20: 100 0.59662376 b TRUE # a val group K  

Есть и другие способы сделать это, но они включают переименование или потерю информации. Например,

  • влево-присоединяйтесь range , и Y мы проиграем a :
     Y[range, on = .(group, a gt;= start, a lt;= end)] # a val group a.1 # lt;intgt; lt;numgt; lt;chargt; lt;intgt; # 1: 1 0.60189755 a 20 # 2: 1 0.99874081 a 20 # 3: 1 0.55512663 a 20 # ... # 18: 80 0.98970989 b 100 # 19: 80 0.53190509 b 100 # 20: 80 0.59662376 b 100 # a val group a.1  

    Исправление состоит в том, чтобы скопировать Y$a в новую переменную и вместо этого присоединиться к ней:

     Y[,a1 := a][range, on = .(group, a1 gt;= start, a1 lt;= end)] # a val group a1 a1.1 # lt;intgt; lt;numgt; lt;chargt; lt;intgt; lt;intgt; # 1: 9 0.60189755 a 1 20 # 2: 5 0.99874081 a 1 20 # 3: 16 0.55512663 a 1 20 # ... # 18: 87 0.98970989 b 80 100 # 19: 82 0.53190509 b 80 100 # 20: 100 0.59662376 b 80 100 # a val group a1 a1.1  
  • левое соединение Y и range , мы a дублируемся в start и end , но нет четкого индикатора для фильтрации:
     range[Y, on = .(group, start lt;= a, end gt;= a)] # group start end val # lt;chargt; lt;intgt; lt;intgt; lt;numgt; # 1: a 28 28 0.85026492 # 2: a 80 80 0.23466126 # 3: a 22 22 0.98816745 # ... # 98: b 82 82 0.53190509 # 99: b 100 100 0.59662376 # 100: b 30 30 0.26388647 # group start end val  

    Решением было бы скопировать в другое поле, которое дало бы нам индикатор слияния, который нам нужно отфильтровать. Но даже при этом мы должны переименовать, чтобы восстановить a данные:

     range[, K := TRUE][Y, on = .(group, start lt;= a, end gt;= a)][ !is.na(K), ] # group start end K val # lt;chargt; lt;intgt; lt;intgt; lt;lgclgt; lt;numgt; # 1: a 9 9 TRUE 0.60189755 # 2: a 5 5 TRUE 0.99874081 # 3: a 16 16 TRUE 0.55512663 # ... # 18: b 87 87 TRUE 0.98970989 # 19: b 82 82 TRUE 0.53190509 # 20: b 100 100 TRUE 0.59662376 # group start end K val  

Комментарии:

1. Мне это нравится. Необходимость добавлять K кажется как-то неэлегантной, но это работает, мне просто нужно будет удалить ее позже. Я оставлю это открытым до завтра, чтобы посмотреть, есть ли какие-либо другие решения data.table для этого. Спасибо!

2. Я согласен с кажущейся неэлегантностью этого, но внутренние предположения о том, какие столбцы следует сохранить, а какие переименовать, не всегда очевидны для меня. (Очевидно , просто добавьте [, K := NULL] в конец, чтобы избавиться от K этого, я тоже хотел бы, чтобы в этом не было необходимости.) Потребность аналогична Y[,a1 := a][range, on = .(group, a1 gt;= start, a1 lt;= end)] той , для которой требуется очистка [, c("a1","a1.1"):=NULL] . В любом случае… уборка.