2015-10-17 2 views
0

Начиная с 2 объектов: 1 блок данных атрибутов заказа - номера заказов, веса и тома и 1 список комбинаций строк номеров заказов.Эффективное совпадение/поиск в R

attr <- data.frame(Order.No = c(111,222,333), Weight = c(20,75,50), Volume = c(10,30,25)) 
combn <- list(111, 222, 333, c(111,222), c(111,333), c(222,333), c(111,222,333)) 

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

настоящее время я использую следующий -

# Lookup weights for each Order.No in the attr table 
# Add up total weight for the combination and keep it if it's in the range 
wgts <- lapply(combn, function(x) { 
    temp <- attr$Weight[match(x, attr$Order.No)] 
    temp <- sum(temp) 
    temp[temp <= 50 & temp >= 20] 
}) 
> wgts 
[[1]] 
[1] 20 

[[2]] 
numeric(0) 

[[3]] 
[1] 50 

[[4]] 
numeric(0) 

[[5]] 
numeric(0) 

[[6]] 
numeric(0) 

[[7]] 
numeric(0) 

# Lookup volumes for each Order.No in the attr table 
# Add up total volume for the combination and keep it if it's in the range 
vols <- lapply(combn, function(x) { 
    temp <- attr$Volume[match(x, attr$Order.No)] 
    temp <- sum(temp) 
    temp[temp <= 50 & temp >= 10] 
}) 
> vols 
[[1]] 
[1] 10 

[[2]] 
[1] 30 

[[3]] 
[1] 25 

[[4]] 
[1] 40 

[[5]] 
[1] 35 

[[6]] 
numeric(0) 

[[7]] 
numeric(0) 

Затем используйте mapply, чтобы объединить два списка весов и объемов.

# Find and keep only the rows that have both the weights and volumes within their ranges 
which(lapply(mapply(c, wgts, vols), function(x) length(x)) == 2) 

# Yields position 1 and 3 which meet the subsetting conditions  
> value value 
    1  3 

Код выше ищет вес отдельных заказов и кубы, суммирует их все вместе, проверяет, чтобы убедиться, что они находятся в пределах каждого предела диапазона, объединяет оба список вместе и сохраняет только те, которые имеют как вес и кубики в допустимых пределах.

Мое текущее решение, которое успешно завершает задачу, очень медленное по объему производства и не очень хорошо масштабируется с миллионами записей. При использовании комбинаций порядка 11 ММ для поиска этот процесс занимает около 40 минут, что неприемлемо.

Я ищу более эффективный метод, который резко сократит время выполнения, необходимое для получения того же выхода.

+4

BTW, 'combn',' attr' - это имена функций – akrun

+1

Что такое 'c' в вашем примере? (последняя строка примерного кода) –

+2

Пожалуйста, укажите желаемый результат –

ответ

4
# changing names, assigning indices to order list 
atdf = data.frame(Order.No = c(111,222,333), Weight = c(20,75,50), Volume = c(10,30,25)) 
olist = list(111, 222, 333, c(111,222), c(111,333), c(222,333), c(111,222,333)) 
olist <- setNames(olist,seq_along(olist)) 

# defining filtering predicate: 

sel_orders = function(os, mins=c(20,10), maxs=c(50,50)) { 
    tot = colSums(atdf[match(os, atdf$Order.No), c("Weight","Volume")]) 
    all(maxs >= tot & tot >= mins) 
} 

# Filtering orders 

olist[sapply(olist, sel_orders)] 
# or 
Filter(x = olist, f = sel_orders) 

оба из которых дают

# $`1` 
# [1] 111 
# 
# $`3` 
# [1] 333 

Чтобы изменить максимумы и мин ...

olist[ sapply(olist, sel_orders, mins = c(0,0), maxs = c(70,70)) ] 

# $`1` 
# [1] 111 
# 
# $`3` 
# [1] 333 
# 
# $`5` 
# [1] 111 333 
+1

Снова спасибо, но это тоже дает по сути то же время, что и мой код. Я взял несколько вещей из вашего подхода, однако я ценю это. – Brian

+0

Да, я не удивлен, что это происходит не быстрее. Для реального ускорения, я подозреваю, что вам нужно, чтобы кто-то опубликовал что-то в соответствии с комментариями/ответами @ akrun на data.table. Для этого вам может потребоваться опубликовать несколько больший пример, чтобы можно было провести бенчмаркинг. Также (незначительная вещь), вы можете пометить вопрос с помощью [tag: performance]. – Frank

1

Не знаю, насколько быстрее это будет, но вот dplyr/тидир.

library(dplyr) 
library(tidyr) 

combination = 
    data_frame(Order.No = combn) %>% 
    mutate(combination_ID = 1:n()) %>% 
    unnest(Order.No) 

acceptable = 
    combination %>% 
    left_join(attr) %>% 
    group_by(combination_ID) %>% 
    summarize(total_weight = sum(Weight), 
     total_volume = sum(Volume)) %>% 
    filter(total_weight %>% between(20, 50) & 
      total_volume %>% between(10, 50)) 
+0

Хорошее решение, +1! Я придумал нечто подобное (но в одной операции цепочки) и добавил 'Order.No = toString (Order.No)' в вызове 'sumize()', чтобы иметь элементы исходного списка в конечном результате. –

+0

Я сохранил комбинационную рамку отдельно, так что к номерам заказов можно было обращаться реляционно, например, 'приемлемый%>% left_join (комбинация)' – bramtayl

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