Как построить Edgeliste из списка посещенных мест (эффективно)? - PullRequest
2 голосов
/ 14 октября 2019

Мой оригинал data.table состоит из трех столбцов.
site, observation_number и id.

Например, вот все наблюдения для id = z

|site|observation_number|id
|a   |                 1| z                 
|b   |                 2| z
|c   |                 3| z

Это означает, что идентификатор z изменился с a до b до c.

Не существует фиксированного количества сайтов на один идентификатор.

Iхотите преобразовать данные в список краев, как это

|from |to||id|
|a    | b| z |
|b    | c| z |

фиктивные данные

sox <- data.table(site =  c('a','b','c','a','c','c','a','d','e'),
       obsnum =c(1,2,3,1,2,1,2,3,4),
       id     =c('z','z','z','y','y','k','k','k','k'))

То, как я сейчас это делаю, выглядит запутанным и очень медленным (у sox 1,5 миллионастрок и dt_out имеет около 7,5 млн. строк). Я в основном использую цикл for observation_number, чтобы разбить данные на порции, где каждый идентификатор присутствует только один раз (то есть - только одна поездка в / из). Затем я преобразую данные и объединяю все фрагменты в новый data.table.

dt_out <- data.table()
maksimum = sox[,max(observation_number)]
for (i in 1:maksimum-1) {
  i=1
  mini = i
  maxi = i+1
  sox_t <- sox[observation_number ==maxi | observation_number ==mini, ]
  temp_dt <- dcast(sox_t[id %in% sox_t[, .N, by = id][N>=2]$id,
                             .SD[, list(site, observation_number, a=rep(c('from', 'to')))] ,by=id],
                       id='id', value.var='site', formula=id~a)
  dt_out <- rbind(dt_out, temp_dt)
  i=max
  }

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

ОБНОВЛЕНИЕ

Использование системного времени (и системного времени несколько раз):

                             User - System - Elapsed
make_edgelist (data.table):  5.38     0.00      5.38
Data.table. with shift:     13.96     0.06     14.08 
dplyr, with arrange:         6.06     0.36      6.44

псmake_edgelist был обновлен, чтобы упорядочить data.table

make_edgelist <- function(DT, site_var = "site", id_var = "id", obsnum_var   = "rn1") {
    DT[order(get(obsnum_var)),
    list(from = get(site_var)[-.N], to = get(site_var)[-1]), by = id_var]
}

Я был удивлен, что dplyr (с lead) был почти так же быстр, как make_edgelist и намного быстрее, чем data.table с shift. Я предполагаю, что это означает, что dplyr будет на самом деле быстрее с более сложным лидерством / лагами / смещением.

Также я нахожу это озадачивающим - но не знаю достаточно, чтобы знать, имеет ли это какое-то значение, что dplyr использовал больше 'системное время, чем любое из двух решений data.table.

Входные данные: 1,5 миллиона строк. Результат: 0,6 млн. Строк.

Ответы [ 3 ]

4 голосов
/ 14 октября 2019

С dplyr вы можете попробовать:

sox %>%
 group_by(id) %>%
 transmute(from = site,
           to = lead(from)) %>%
 na.omit()

  id    from  to   
  <chr> <chr> <chr>
1 z     a     b    
2 z     b     c    
3 y     a     c    
4 k     c     a    
5 k     a     d    
6 k     d     e    

Как заметил @Sotos, может быть полезно сначала упорядочить данные:

sox %>%
 arrange(id, obsnum) %>%
 group_by(id) %>%
 transmute(from = site,
           to = lead(from)) %>%
 na.omit()
3 голосов
/ 14 октября 2019

Это то, что вы ищете?

sox[, .(from = site[-.N], to = site[-1]), by = id]

#    id from to
# 1:  z    a  b
# 2:  z    b  c
# 3:  y    a  c
# 4:  k    c  a
# 5:  k    a  d
# 6:  k    d  e

Обернут в функцию:

make_edgelist <- function(DT, site_var = "site", id_var = "id") {
  DT[, .(from = get(site_var)[-.N], to = get(site_var)[-1]), by = id_var]
}

Примечание. Это решение предполагает, что данные уже упорядочены по номеру наблюдения. Чтобы избежать этого предположения, добавьте order(obsnum) перед первой запятой.

2 голосов
/ 14 октября 2019

Используя data.table, если оно быстрее, чем решение dplyr, указанное выше, вы получаете:

sox <- sox[order(id, obsnum)]
sox[, from := shift(site), by = "id"]
sox <- sox[!is.na(from)]
setnames(sox, "site", "to")
sox[, obsnum := NULL]
setcolorder(sox, c("id", "from", "to"))
sox
#>    id from to
#> 1:  k    c  a
#> 2:  k    a  d
#> 3:  k    d  e
#> 4:  y    a  c
#> 5:  z    a  b
#> 6:  z    b  c
...