Способ l oop для нескольких таблиц и сохранение только при выполнении условия? - PullRequest
2 голосов
/ 01 августа 2020

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

ТАБЛИЦА 1

Product Date        Cost
A       8/1/2020    10
A       8/2/2020    20
A       8/3/2020    30
B       8/4/2020    15
B       8/5/2020    25
B       8/6/2020    35

и ТАБЛИЦА 2:

Product Date    Price
A       9/1/2020    20
A       9/2/2020    30
A       9/3/2020    40
B       9/4/2020    27
B       9/5/2020    33
B       9/6/2020    42

Поэтому мне нужно перебрать каждую комбинацию из Таблицы 2 Цена - Таблица 1 Стоимость, и делайте это по Продуктам. Таким образом, вывод будет:

НОВАЯ ТАБЛИЦА

Product Date1         Date2          Profit
A       8/1/2020      9/1/2020       10
A       8/1/2020      9/2/2020       20
...

РЕДАКТИРОВАТЬ: Чтобы уточнить, новая таблица должна продолжаться. Продукт A должен иметь 27 различных прибылей (3 даты под A x 3 даты под ставкой дисконтирования A x 3) при условии, что все они выше 0. Если какая-либо из прибылей ниже 0, то я не хочу, чтобы они были частью Нового Таблица.

У меня также есть коэффициент скидки, который мне нужно применить к каждой перестановке цены, поскольку мы даем довольно небольшие скидки

Discount = c(10%,12%,18%)

Я пробовал использовать al oop и различные способы использования применяются, но циклы занимают слишком много времени, чтобы завершить sh (часов, а некоторые никогда не делают). Комбинации приводят к миллионам строк, но я хочу сохранить только прибыльные, где Цена * Скидка> Стоимость, которых может быть всего 10000.

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

CTbl =setkey(CTbl[,c(k=1,.SD)],k)[Price[,c(k=1,.SD)],allow.cartesian=TRUE][,k:=NULL]
CTbl[,Profit:=(Discount*Price - Cost]
CTbl = setDT(CTbl)[, .SD[Price > Cost ]]
DT = CTbl[,list(MinProfit = min(Profit)),by = Product]

Конечно, это работает довольно быстро, но это огромная трата памяти, когда все, что мне действительно нужно, это прибыльные строки и, конечно, постоянная проблема с памятью.

Кто-нибудь может помочь? Я спрашивал некоторых пользователей R на работе, но они тоже казались сбитыми с толку: созданные ими циклы не могли приблизиться к менее чем 5 минутам, необходимым для выполнения вышеуказанного. Я не возражаю, если это означает, что я могу увеличить масштаб.

Спасибо!

Ответы [ 2 ]

1 голос
/ 02 августа 2020

Звучит как проблема для пакета dplyr, который. Пакет dplyr позволяет объединять операции с данными в «конвейер», чтобы не хранить вещи в памяти. Оператор канала %>% принимает выходные данные функции слева и использует их в качестве первого аргумента функции справа. Каждая функция в пакете dplyr работает для всего вектора или таблицы данных, поэтому нет необходимости в циклах.

Итак, ваша операция может выглядеть следующим образом:

# Initialize random data like your first table
df1 <- data.frame(product = sample(LETTERS[1:10], 10000, replace = TRUE),
              date1 = sample(seq(as.Date("2020/08/01"), as.Date("2020/08/31"), 
                                 by = "day"), 10000, replace = TRUE),
              cost = round(runif(10000, 5, 100)))
# Initialize random data like your second table 
df2 <- data.frame(product = sample(LETTERS[1:10], 10000, replace = TRUE),
              date2 = sample(seq(as.Date("2020/09/01"), as.Date("2020/09/30"), 
                                 by = "day"), 10000, replace = TRUE),
              price = round(runif(10000, 5, 100)))
# Initialize discounts 
discounts <- data.frame(product = rep(LETTERS[1:10],4), 
                    discount = rep(c(0, 0.1, 0.12, 0.18), 10))
library(dplyr)
out_table <- df1 %>%
  full_join(df2) %>%
  full_join(discounts) %>%
  mutate(profit = price * discount - cost) %>%
  filter(profit > 0)

Для моего случайные данные, на моей машине это занимает около 3 секунд. Кроме того, команда filter сохраняет только те строки, которые нам нужны.

1 голос
/ 02 августа 2020

Это не полный ответ на ваш вопрос, но, возможно, вы можете выполнить итерацию al oop на product s. Следующая функция находит прибыль для указанного продукта. Функция не включает скидку , но ее можно добавить, если функция работает так, как вы хотите.

profit = function(product, df1, df2) {

    cost = with(df1, df1[which(Product == product), 'Cost'])
    price = with(df2, df2[which(Product == product), 'Price'])
    date = merge(
            with(df1, df1[which(Product == product), 'Date']), 
            (with(df2, df2[which(Product == product), 'Date']))
            )
    product = t(matrix(rep(price, length(cost)), nrow = length(cost)) - t(matrix(rep(cost, length(price)), ncol = length(price))))
    product = data.frame(cbind(date[which(product > 0), ], product[which(product > 0)]))
    names(product) = c('costdate', 'pricedate', 'profit')
    return(product)

}

Пример:

df1 = data.frame(Product = c('A', 'A', 'A', 'B', 'B', 'B'), 
                Date = c('8/1/2020', '8/2/2020', '8/3/2020', '8/4/2020', '8/5/2020', '8/6/2020'),
                Cost = c(10, 20, 30, 15, 25, 35))
df2 = data.frame(Product = c('A', 'A', 'A', 'B', 'B', 'B'), 
                Date = c('9/1/2020', '9/2/2020', '9/3/2020', '9/4/2020', '9/5/2020', '9/6/2020'),
                Price = c(20, 30, 40, 27, 33, 42))

> profit('A', df1, df2)
  costdate pricedate profit
1 8/1/2020  9/1/2020     10
4 8/1/2020  9/2/2020     20
5 8/2/2020  9/2/2020     10
7 8/1/2020  9/3/2020     30
8 8/2/2020  9/3/2020     20
9 8/3/2020  9/3/2020     10
> profit('B', df1, df2)

  costdate pricedate profit
1 8/4/2020  9/4/2020     12
2 8/5/2020  9/4/2020      2
4 8/4/2020  9/5/2020     18
5 8/5/2020  9/5/2020      8
7 8/4/2020  9/6/2020     27
8 8/5/2020  9/6/2020     17
9 8/6/2020  9/6/2020      7

Я не смог проверить это правильно, так как у меня ограниченные данные.

...