R - Как максимизировать на основе суммы другого столбца из фрейма данных - PullRequest
0 голосов
/ 12 декабря 2018

Итак, я создал фрейм данных, представляющий команду фантазий, где каждая позиция (a, b, c) имеет заданные очки (очки) и цену (цену):

library(reshape2)
x<-102 #num players

replicate(x/3,paste(letters[1:3]))->l
l<-melt(l)
l<-l[,3]
l<-data.frame(l)

# pnt<- as.integer(runif(90,min=-4, max=30))
pnt<-pmax(round(as.numeric(rnorm(x,mean=6,sd=4)),digits=0),-3)
prc<-pmax(sort(round(as.numeric(rnorm(x,mean=6,sd=3)),digits=1)),4.5)

df1<-as.data.frame(pnt)
df2<-as.data.frame(prc)
df<-data.frame(df1,df2,l)
rownames(df) <- seq(from=1,to=x)
colnames(df)<-c("points","price","pos")

p<-df[rev(order(df$pos)),]
a<-p[((2*x/3)+1):x,1:3]
a<-a[rev(order(a$points)),]

Я сейчас использую только свой фрейм данных «а», который выглядит следующим образом:

 price points pos
1   7    14     a
2   8    12     a
3   3    8      a
4   10   7      a

Я пытаюсь выбрать максимально возможное количество очков в комбинациях из 4 игроков (представленных в каждой строке).Обычно это было бы легко, просто упорядочив кадр данных по точкам и выбрав верхнюю 4. Однако я хочу наложить максимальный ценовой лимит на четырех игроков из 28. (Это произвольное число, чтобы показать проблему). Это потенциально исключаетпервые 4 игрока, и, возможно, допускают, чтобы максимальное количество очков в рамках этого ценового предела не было последовательным (в порядке очков).

Есть ли у вас какие-либо предложения о том, как это сделать?Я попробовал следующее, но он позволяет выбирать только последовательные точки.

z<-integer()
y<-integer()
for(i in 1:31){
  j<-i+2
  x<-sum(a[i:j,]$point)
  xx<-sum(a[i:j,]$price)
  y<-c(y,x)
  z<-c(z,xx)
  yz<-data.frame(y,z)
}
yz

#add points per price
yz$c<-with(yz,y/z)

yz[which(match(yz$c,max(yz$c))==TRUE),]

У меня такое ощущение, что это проблема оптимизации

1 Ответ

0 голосов
/ 12 декабря 2018

Здесь может быть решение.Могут быть и более разумные способы, однако, используя превосходный пакет RcppAlgos, задачу можно выполнить довольно быстро

# Setting the framework
numOfElements <- 4
maxPrice <- 28

# Taking all combinations of prices and points
priceCombs <- RcppAlgos::comboGeneral(a$price, numOfElements)
pointCombs <- RcppAlgos::comboGeneral(a$points, numOfElements)

# Computing best choice
magicIndex <- which.max(rowSums(pointCombs[rowSums(priceCombs) <= maxPrice,])) # gives the corresponding index

# results
# points
pointCombs[magicIndex,]
[1] 15 14 13 10 # total of 52
# prices
priceCombs[magicIndex,]
[1] 11.7  6.9  4.5  4.5 # total of 27.6

Я использовал приведенный в вопросе код для генерации данных (ивключенные 28 как приемлемые).Для воспроизводимости я применил set.seed(123) перед генерацией данных - таким образом можно наблюдать те же числа.


Редактировать: Лучшая комбинация двух элементов на позицию

С добавленными ограничениями (два элемента a;b;c каждый) добавляется сложность.Я написал функцию dumm в том смысле, что она проверяет все (допустимые) возможности.Тем не менее, я попытался записать его эффективно, задав подмножество

bestAllocation <- function (p, maxPrice) {

  # Prelims
  # Create frames per position
  myList <- list(a = p[p$pos == "a",], b = p[p$pos == "b",], c = p[p$pos == "c",])
  # Determining max prices per position
  minPriceA <- min(myList$a$price)
  minPriceB <- min(myList$b$price)
  minPriceC <- min(myList$c$price)
  maxAllowedPriceA <- maxPrice - minPriceB - minPriceC
  maxAllowedPriceB <- maxPrice - minPriceA - minPriceC
  maxAllowedPriceC <- maxPrice - minPriceB - minPriceA
  # Subsetting for efficiency
  myList$a <- myList$a[myList$a$price < maxAllowedPriceA,]
  myList$b <- myList$b[myList$b$price < maxAllowedPriceB,]
  myList$c <- myList$c[myList$c$price < maxAllowedPriceC,]
  # Recode position variables as integers
  myList$a$pos <- 0L
  myList$b$pos <- 1L
  myList$c$pos <- 2L

  # Variables used for the loops
  remainingPrice1 <- remainingPrice2 <- numeric(1)
  indA1 <- indA2 <- indB1 <- indB2 <- indC1 <- indC2 <- logical(nrow(myList$b))
  bestPointsC <- numeric(1)
  resultDF <- data.frame(matrix(0, ncol = 2*3, nrow = 2*3))
  currentMax <- numeric(1)

  # To the loops
  indA1 <- .subset2(myList$a,2L) < maxPrice - minPriceA - 2*minPriceB - 2*minPriceC # keep a's such that we can afford  1a & 2b & 2c 
  indA2 <- .subset2(myList$a,2L) %in% unique(RcppAlgos::comboGeneral(.subset2(myList$a,2L)[indA1], 
                                                                 2L, 
                                                                 constraintFun = "sum", 
                                                                 comparisonFun = "<=", 
                                                                 limitConstraints = maxPrice - 2*minPriceA - 2*minPriceC))

  if (!any(indA1 & indA2)) stop("\nAll combinations of a's exceed the admissible price.") # no admissible tuple of a's

  for (k in 1:nrow(RcppAlgos::comboGeneral(which(indA1 & indA2), 2L)) )
  {
    k1 <- RcppAlgos::comboGeneral(which(indA1 & indA2), 2L)[k,1L]
    k2 <- RcppAlgos::comboGeneral(which(indA1 & indA2), 2L)[k,2L]

    if (sum(.subset2(myList$a,2)[c(k1,k2)]) >= maxPrice - 2*minPriceB - 2*minPriceC) next # not enough money for 2b & 2c

    remainingPrice1 <- maxPrice - sum(.subset2(myList$a,2)[c(k1,k2)])  

    if (all(.subset2(myList$b,2) >= remainingPrice1 - minPriceB - 2*minPriceC )) next # not enough money for 1b & 2c
    indB1 <- .subset2(myList$b,2) < remainingPrice1 - minPriceB - 2*minPriceC 
    indB2 <- .subset2(myList$b,2) %in% unique(RcppAlgos::comboGeneral(.subset2(myList$b,2)[indB1], 
                                                                  2L, 
                                                                  constraintFun = "sum", 
                                                                  comparisonFun = "<=", 
                                                                  limitConstraints = 2*minPriceC))
    if (!any(indB1 & indB2)) next # no admissible tuple of b's

    for (s in 1:nrow(RcppAlgos::comboGeneral(which(indB1 & indB2), 2L))) 
    {
      s1 <- RcppAlgos::comboGeneral(which(indB1 & indB2), 2L)[s,1L]
      s2 <- RcppAlgos::comboGeneral(which(indB1 & indB2), 2L)[s,2L]
      remainingPrice2 <- maxPrice - sum(.subset2(myList$a,2)[c(k1,k2)]) - sum(.subset2(myList$b,2L)[c(s1,s2)])

      if (all(.subset2(myList$c, 2) >= remainingPrice2 - minPriceC)) next # not enough money for 2c

      indC1 <- .subset2(myList$c,2L) < remainingPrice2 - minPriceC
      indC2 <- .subset2(myList$c,2L) %in% unique(RcppAlgos::comboGeneral(.subset2(myList$c,2L)[indC1],
                                                                     2L, 
                                                                     constraintFun = "sum", 
                                                                     comparisonFun = "<=", 
                                                                     limitConstraints = remainingPrice2))
      if (!any(indC1 & indC2)) next # no admissible tuple of c's

      bestPointsC <- sort(.subset2(myList$c,1L)[indC1 & indC2], partial = (sum(indC1 & indC2) - 1L):sum(indC1 & indC2))[(sum(indC1 & indC2) - 1L):sum(indC1 & indC2)]

      if (sum(.subset2(myList$a,1L)[c(k1,k2)]) + sum(.subset2(myList$b,1L)[c(s1,s2)]) + sum(bestPointsC) <= currentMax) next # points value in this iteration lower than the current max 

      currentMax <- sum(.subset2(myList$a,1L)[c(k1,k2)]) + sum(.subset2(myList$b,1L)[c(k1,k2)]) + sum(bestPointsC)
      resultDF <- rbind(myList$a[c(k1,k2),], 
                        myList$b[c(s1,s2),], 
                        myList$c[(.subset2(myList$c,1L) %in% bestPointsC) & indC1 & indC2,]) # maybe add a safety measure (e.g order myList$c[...] by pts, price and keep only first two)
      cat(paste0("\n\nUpdated result",
                 "\nPoints:\t", sum(resultDF$points),
                 "\nPrice :\t", sum(resultDF$price)))  
    }
  }
  return(resultDF)
} 

Вот что делает функция

> ans <- bestAllocation(p, maxPrice = 28)


Updated result
Points :    55
Price  :    27.9

Updated result
Points :    58
Price  :    27.9

Updated result
Points :    61
Price  :    27.9

Updated result
Points :    64
Price  :    27.9

Updated result
Points :    67
Price  :    27.8

Updated result
Points :    68
Price  :    27

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

Краткое объяснение

  1. Узнайте, какНаиболее наименее дорогие элементы a;b;c равны
  2. . Рассмотрим все комбинации двух a, такие, что достаточно объединенной цены, то есть так, чтобы можно было, по крайней мере, в два раза дешевле b 's и c s
  3. Выполните аналогичные действия для b и c
  4. Сохраните и запомните лучшее распределение
...