2016-07-12 3 views
1

У меня есть dataframe следующим образом:Подсчет строк, основанные на условной группировке dplyr

 position_time telematic_trip_no lat_dec lon_dec 
1 2016-06-05 00:00:9 -26.6641 27.8733 
2 2016-06-05 00:00:01   526028387 -26.6402 27.8059 
3 2016-06-05 00:00:01   526081476 -26.5545 28.3263 
4 2016-06-05 00:00:04   526140512 -26.5310 27.8704 
5 2016-06-05 00:00:05   526140518 -26.5310 27.8704 
6 2016-06-05 00:00:19   526006880 -26.5010 27.8490 
    is_stolen hour_of_day time_of_day day_of_week lat_min 
1   0   0   0  Sunday -26.6651 
2   0   0   0  Sunday -26.6412 
3   0   0   0  Sunday -26.5555 
4   0   0   0  Sunday -26.5320 
5   0   0   0  Sunday -26.5320 
6   0   0   0  Sunday -26.5020 
    lat_max lon_max lon_min 
1 -26.6631 27.8743 27.8723  
2 -26.6392 27.8069 27.8049  
3 -26.5535 28.3273 28.3253  
4 -26.5300 27.8714 27.8694  
5 -26.5300 27.8714 27.8694  
6 -26.5000 27.8500 27.8480  

Теперь то, что я хочу сделать, это подсчитать для каждой строки, где is_stolen = 1, то число строк в dataframe что выполнить следующие условия:

  • lat_dec и lon_dec находятся между lat_max, lat_min, lon_max и lon_min (т. вписываться в «коробке» вокруг этой точки GPS)
  • TIME_OF_DAY и DAY_OF_WEEK такие же, как и у ряда интерес
  • telematic_trip_no строк должны быть разными, чтобы что из ряд интересных
  • и, наконец, is_stolen тег согласующих строки должен быть равен 0

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

PS Если вам интересно, я действительно пытаюсь вычислить, сколько машин украденного автомобиля проходит во время обычной поездки :)

+3

'dput()' из достаточного количества данных, которое включает в себя необходимые условия, помогая другим помочь вам. – hrbrmstr

+0

Можете ли вы также включить код «для цикла», который не работает? – Sotos

+0

Что означает «ряд интересов»? Вы выбираете одну строку, а затем сравниваете ее со всеми остальными строками? – Tav

ответ

1

Учитывая ваше описание проблемы, должен работать

library(dplyr) 
library(stats) 
# df is the data.frame (see below) 
df <- cbind(ID=seq_len(nrow(df)),df) 
r.stolen <- which(df$is_stolen == 1) 
r.not <- which(df$is_stolen != 1) 
print(df[rep(r.not, times=length(r.stolen)),] %>% 
    setNames(.,paste0(names(.),"_not")) %>% 
    bind_cols(df[rep(r.stolen, each=length(r.not)),], .) %>% 
     mutate(in_range = as.numeric(telematic_trip_no != telematic_trip_no_not & time_of_day == time_of_day_not & day_of_week == day_of_week_not & lat_dec >= lat_min_not & lat_dec <= lat_max_not & lon_dec >= lon_min_not & lon_dec <= lon_max_not)) %>% 
     group_by(ID) %>% 
      summarise(count = sum(in_range)) %>% 
      arrange(desc(count))) 

В первой строке просто добавляется столбец с именем ID - df, который идентифицирует строку по ее номеру строки, которую мы можем позже dplyr::group_by, чтобы сделать счет.

Следующие две строки делят ряды на украденные и не украденные автомобили. Ключ к:

  1. повторности каждый ряд угнанных автомобилей N раз, когда N является количество не-украденных строк автомобиля,
  2. копируют ряды не-угнанных автомобилей (в виде блока) M раз, где M это число украденных строк автомобилей, а также
  3. добавить результат (2) в (1) в качестве новых столбцов и изменить имена этих новых столбцов, так что мы можем ссылаться на них в состоянии

Результат (3) га ve строк, которые перечисляют все пары украденных и не украденных строк из исходного фрейма данных, так что ваше условие может применяться в массиве.dplyr конвейерный R рабочего процесса, четвертая строка кода (завернутый в print()) делает это:

  • первая команда копирует не-украденных строк автомобиля с помощью times
  • вторая команда добавляет _not к имена столбцов, чтобы отличить их от украденных столбцов автомобилей, когда мы привязываем столбцы. Благодаря this SO answer для этого драгоценного камня.
  • третья команда копирует украденные строки автомобиля, используя each и присоединяет предыдущий результат как новые столбцы, используя dplyr::bind_cols
  • четвертой команды использует dplyr::mutate, чтобы создать новый столбец с именем in_range, который является результатом применения условия. Логический результат преобразуется в {0,1}, чтобы обеспечить легкое накопление
  • Остальная часть команд в трубе составляет in_range, сгруппированных по ID и размещая результаты в порядке убывания количества. Обратите внимание, что теперь ID это столбец, который идентифицирует строки исходного кадра данных, для которых is_stolen = 1 тогда ID_not это столбец для строк, которые is_stolen = 0

Это предполагает, что вы хотите счетчик для каждой строки, is_stolen = 1 в исходных данных кадр, что вы сказали в своем вопросе. Если вместо этого вы действительно хотите, чтобы счетчик для каждого telematic_trip_no, который украден, то вы можете использовать

group_by(telematic_trip_no) %>% 

в трубе вместо.

Я проверил это, используя следующие данные фрагмент

df <- structure(list(position_time = structure(c(1L, 1L, 1L, 2L, 3L, 
       4L, 4L, 5L, 6L, 7L, 8L, 9L, 10L), .Label = c("2016-06-05 00:00:01", 
       "2016-06-05 00:00:04", "2016-06-05 00:00:05", "2016-06-05 00:00:19", 
       "2016-06-05 00:00:20", "2016-06-05 00:00:22", "2016-06-05 00:00:23", 
       "2016-06-05 00:00:35", "2016-06-05 00:09:34", "2016-06-06 01:00:06" 
       ), class = "factor"), telematic_trip_no = c(9L, 526028387L, 
       526081476L, 526140512L, 526140518L, 526006880L, 526017880L, 526027880L, 
       526006880L, 526006890L, 526106880L, 526005880L, 526007880L), 
       lat_dec = c(-26.6641, -26.6402, -26.5545, -26.531, -26.531, 
       -26.501, -26.5315, -26.5325, -26.501, -26.5315, -26.5007, 
       -26.5315, -26.5315), lon_dec = c(27.8733, 27.8059, 28.3263, 
       27.8704, 27.8704, 27.849, 27.88, 27.87, 27.849, 27.87, 27.8493, 
       27.87, 27.87), is_stolen = c(0L, 0L, 0L, 0L, 0L, 0L, 1L, 
       1L, 1L, 1L, 1L, 1L, 1L), hour_of_day = c(0L, 0L, 0L, 0L, 
       0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L), time_of_day = c(0L, 
       0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 9L, 0L), day_of_week = structure(c(2L, 
       2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L), .Label = c("Monday", 
       "Sunday"), class = "factor"), lat_min = c(-26.6651, -26.6412, 
       -26.5555, -26.532, -26.532, -26.502, -26.532, -26.532, -26.502, 
       -26.532, -26.502, -26.532, -26.532), lat_max = c(-26.6631, 
       -26.6392, -26.5535, -26.53, -26.53, -26.5, -26.53, -26.53, 
       -26.5, -26.53, -26.5, -26.53, -26.53), lon_max = c(27.8743, 
       27.8069, 28.3273, 27.8714, 27.8714, 27.85, 27.8714, 27.8714, 
       27.85, 27.8714, 27.85, 27.8714, 27.8714), lon_min = c(27.8723, 
       27.8049, 28.3253, 27.8694, 27.8694, 27.848, 27.8694, 27.8694, 
       27.848, 27.8694, 27.848, 27.8694, 27.8694)), .Names = c("position_time", 
       "telematic_trip_no", "lat_dec", "lon_dec", "is_stolen", "hour_of_day", 
       "time_of_day", "day_of_week", "lat_min", "lat_max", "lon_max", 
       "lon_min"), class = "data.frame", row.names = c(NA, -13L)) 

Здесь я приложил 7 новые строки с is_stolen = 1 к исходным 6 строк, все is_stolen = 0:

  1. первый добавлен ряд с telematic_trip_no = 526005880 нарушает условие долготы для всех не украденных рядов, поэтому его счет должен быть 0
  2. на второй добавленной строке с telematic_trip_no = 526006880 нарушает условие широты для всех не-украденных строк, поскольку его счет должен быть 0
  3. третий добавлен ряд с telematic_trip_no = 526007880 нарушает условие telematic_trip_no для всех не-украденных строк, поскольку его счет должен быть 0
  4. четвертая добавлена ​​строка с telematic_trip_no = 526006890 удовлетворяет условие для строк 4 и 5, которые не-украдены, поскольку его счет должна быть 2
  5. пятой добавлена ​​строка с telematic_trip_no = 526106880 удовлетворяет условие для строки 6, что не-украден, поэтому его счет должен быть 1
  6. шестой добавлен ряд с telematic_trip_no = 526017880 нарушает условие time_of_day для всех не-украденных строк, поскольку его счет должен быть 0
  7. седьмой добавляются строка с telematic_trip_no = 526027880 нарушает условие day_of_week для всех не-украденных строк, поэтому его кол должны быть 0

Выполнение кода на этих данных дает:

# A tibble: 7 x 2 
    ID count 
    <int> <dbl> 
1 10  2 
2 11  1 
3  7  0 
4  8  0 
5  9  0 
6 12  0 
7 13  0 

, который, как ожидается, напомнив, что приложенные строки с is_stolen = 1 начинаются в строке 7 с ID = 7.

Если один сгруппировали по telematic_trip_no вместо этого, мы получим результат:

# A tibble: 7 x 2 
    telematic_trip_no count 
       <int> <dbl> 
1   526006890  2 
2   526106880  1 
3   526005880  0 
4   526006880  0 
5   526007880  0 
6   526017880  0 
7   526027880  0 

Как предостережение, вышеописанный подход делает стоимость памяти. В худшем случае количество строк растет до N^2/4, где N - это количество строк в исходном фрейме данных, а количество столбцов удваивается для фрейма данных, который используется для оценки условия. Как и в большинстве методов обработки массивов, между скоростью и памятью существует торговля.

Надеюсь, что это поможет.

1

The current development version of data.table, v1.9.7 имеет новую функцию non-equi Соединения, которые делают условные соединения довольно простыми. данные с помощью @ aichao в:

require(data.table) # v1.9.7+ 
setDT(df)[, ID := .I] # add row numbers 
not_stolen = df[is_stolen == 0L] 
is_stolen = df[is_stolen == 1L] 

not_stolen[is_stolen, 
    .(ID = i.ID, N = .N - sum(telematic_trip_no == i.telematic_trip_no)), 
    on = .(time_of_day, day_of_week, lat_min <= lat_dec, 
      lat_max >= lat_dec, lon_min <= lon_dec, lon_max >= lon_dec), 
    by=.EACHI][, .(ID, N)] 
# ID N 
# 1: 7 NA 
# 2: 8 NA 
# 3: 9 0 
# 4: 10 2 
# 5: 11 1 
# 6: 12 NA 
# 7: 13 NA 

Часть not_stolen[is_stolen, выполняет подмножества типа операции соединения .. т.е. для каждой строки в is_stolen, соответствие ряд индексов (на основе состояния предоставленного on= аргумента) извлекаются ,

by = .EACHI гарантирует, что для каждой строки в i (первый) аргумент, здесь is_stolen, на соответствующие индексы соответствующих строк, то выражение предусмотрено в j, второй аргумент, .(ID = i.ID, N = .N-sum(telematic_trip_no==i.telematic_trip_no)), оценивается. Это возвращает результат, показанный выше.

HTH.

+1

Я искал функцию для подобных объединений. Хорошо знать. Благодарю. – aichao

Смежные вопросы