Разделение R в группе для параллельного исполнения - PullRequest
3 голосов
/ 27 мая 2020

У меня есть простая таблица:

ID|Value
1|10
1|20
1|-5
2|25
3|2
3|15
4|8
5|18
6|33
6|5
6|50

Фактически Я использую этот код:

for (row in 1:nrow(Table)) {
   ID <- Table[row, 1]
   Value <- Table[row, 2]
   if ( oldID == ID) {
      currentValue <- currentValue * ((100 - Value)/100) }
   else {
      addrow <- data.frame(oldID, currentValue)
      PriceRR <- rbind(PriceRR, addrow)
      oldID <- ID
      currentValue <- 100 - Value
      }
   }

Чтобы назначить скидку для более позднего значения DAX в Power BI.

Но он чертовски медленный. Поэтому я хочу распараллелить его.

daply может сделать эту работу. Но я не знаю, как это работает.

Итак, в основном то, что мне нужно.

Разделить таблицу на наборы по группе идентификаторов.

Set1 1,10 1,20 1,5
Set2 2,25
Set3 3,2 3,15
.
.
.

Применить функцию к Устанавливает параллельно.

First call of function in set, initialize currentValue <- 100

после

currentValue <- currentValue * ((100 - Value)/100)
For Set1.1 90 <- 100 * ((100 - 10)/100)
For Set1.2 72 <- 90 * ((100 - 20)/100)
For Set1.3 68,4 <- 72 * ((100 - 5)/100)
It should return ID=1 Value=68,4

Мне нужно знать, можно ли сделать переменную постоянной в памяти на время выполнения функции набора, если она живет?

Будет ли daply или другая функция создавать новый рабочий поток, чтобы применить его к набору?

Я новичок в R и должен сразу перейти к внутренней работе среды R. : -)

Свен

Ответы [ 3 ]

1 голос
/ 27 мая 2020

Ваш исходный сценарий работает медленно по нескольким причинам. Сначала вы просматриваете каждый элемент в исходной таблице и не пользуетесь преимуществами векторизованной природы R. Во-вторых, в l oop есть функция rbind. Связывание - медленный процесс, особенно при увеличении размера объекта.

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

Вот базовый R решение, использующее стратегию разделения, применения и слияния.

Table <-structure(list(ID = c(1L, 1L, 1L, 2L, 3L, 3L, 4L, 5L, 6L, 6L, 
6L), Value = c(10L, 20L, -5L, 25L, 2L, 15L, 8L, 18L, 33L, 5L, 
50L)), class = "data.frame", row.names = c(NA, -11L))

#Create column for the ((100 - Value)/100) factor
Table$factor<- ((100 - Table$Value)/100)

#split by ID
dfs<-split(Table, Table$ID)

currentValue<-sapply(dfs, function(x){
  #find the cumulative product of the factor column
  product<-cumprod(x$factor)
  #return the last value fron the cumprod
  return(100*product[length(product)])
})

#create the final answer
PriceRR<-data.frame(oldID=as.integer(names(dfs)), currentValue)
PriceRR

  oldID currentValue
1     1       75.600
2     2       75.000
3     3       83.300
4     4       92.000
5     5       82.000
6     6       31.825

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

1 голос
/ 27 мая 2020

Вариант с reduce из purrr

library(dplyr)
library(purrr)
data %>%
    group_by(ID) %>% 
    summarise(Result = reduce(Value, ~ .x * (100 -.y)/100, .init = 100))
# A tibble: 6 x 2
#     ID Result
#* <int>  <dbl>
#1     1   68.4
#2     2   75  
#3     3   83.3
#4     4   92  
#5     5   82  

данные

data <- structure(list(ID = c(1L, 1L, 1L, 2L, 3L, 3L, 4L, 5L, 6L, 6L, 
6L), Value = c(10L, 20L, 5L, 25L, 2L, 15L, 8L, 18L, 33L, 5L, 
50L)), class = "data.frame", row.names = c(NA, -11L))
1 голос
/ 27 мая 2020

Вот подход с dplyr и Reduce из базы R:

library(dplyr)
data %>%
  group_by(ID) %>%
  summarize(Result = Reduce(function(x,y) x * ((100 - y)/ 100),
                            Value, init = 100))
# A tibble: 6 x 2
     ID Result
  <int>  <dbl>
1     1   68.4
2     2   75  
3     3   83.3
4     4   92  
5     5   82  
6     6   31.8

Reduce - сложная функция, в основном потому, что документация ужасна. Reduce применяет функцию с двумя аргументами к элементам в векторе последовательно с предыдущим значением в качестве первого аргумента и текущим значением в качестве второго аргумента. Вы можете установить начальное значение с помощью init =.

В вашем объяснении я заметил, что ваш ожидаемый результат для группы 1 равен 68.4. Это верно только в том случае, если значение для строки 3 - 5, а не -5, которое вы опубликовали. Поскольку это было единственное отрицательное значение в ваших данных, я изменил его на 5.

Data

data <- structure(list(ID = c(1L, 1L, 1L, 2L, 3L, 3L, 4L, 5L, 6L, 6L, 
6L), Value = c(10L, 20L, 5L, 25L, 2L, 15L, 8L, 18L, 33L, 5L, 
50L)), class = "data.frame", row.names = c(NA, -11L))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...