R语言 在几个约束条件下对数值向量使用optim

flvtvl50  于 12个月前  发布在  其他
关注(0)|答案(2)|浏览(158)

在R中,我想创建一个名为budget_optimal_allocation()的函数,它使用两个主要参数:

  • data: Dataframe
  • max_budget:数值

该函数的目的是找到产生最佳tot_ROI的数据中每个通道的最佳花费水平。
示例 Dataframe 被构造为:

example_data <- data.frame(
  channel_name = c("Channel 1", "Channel 2", "Channel 3"),
  a = c(5000, 8000, 6000),
  b = c(0.001, 0.002, 0.0015),
  c = c(-1.05, -1.2, -0.95),  
  spend_min = c(100, 200, 150),
  spend_max = c(500, 800, 600)
)

abc是响应曲线函数的(已知的)参数,该响应曲线函数将花费作为输入并生成渠道收入,遵循以下公式:
revenue <- a / (1 + b * (spend) ^ c)
另外,在一个实施例中,

tot_revenue <- sum(revenue)
tot_spend <- sum(spend)

tot_ROI计算为tot_revenue / tot_spend
我们希望优化支出(对于每个频道名称),以便tot_ROI最大。因此,该函数将优化支出向量,因此所有通道都在同一时间,而不是一个接一个。这一点非常重要,因此函数必须能够管理向量的优化,而不是一个通道一个通道地迭代循环。
在优化过程中会有一些限制:每个channel_name花费必须在输入 Dataframe 中定义的spend_minspend_max内,并且tot_spend必须等于budget_optimal_allocation()中定义的max_budget
下面的代码是我到目前为止得到的,但是

  • 我不相信这是正确的方法(optim是正确的函数吗?)
  • 我不能使optimal_spend = max_budget
budget_optimal_allocation <- function(data, max_budget) {
  # Extract channel names and parameters
  channel_names <- data$channel_name
  a <- data$a
  b <- data$b
  c <- data$c
  spend_min <- data$spend_min
  spend_max <- data$spend_max
  
  # Objective function to maximize total ROI
  objective_function <- function(spend, a, b, c) {
    # Calculate total revenue and total spend
    revenue <- a / (1 + b * (spend) ^ c)
    tot_revenue <- sum(revenue)
    tot_spend <- sum(spend)
    
    # Calculate total ROI
    tot_ROI <- tot_revenue / tot_spend
    
    # Minimize the negative total ROI (to maximize total ROI)
    return(-tot_ROI)
  }
  
  # Initial guess for spend values (equal allocation for each channel)
  initial_spend <- rep(max_budget / length(channel_names), length(channel_names))
  
  # Box constraints for spend
  lower_bounds <- rep(spend_min, length(channel_names))
  upper_bounds <- rep(spend_max, length(channel_names))
  
  # Optimize spend allocation to maximize total ROI with box constraints
  result <- optim(
    par = initial_spend,
    fn = objective_function,
    a = a,
    b = b,
    c = c,
    method = "L-BFGS-B",
    lower = lower_bounds,
    upper = upper_bounds,
    control = list(fnscale = -1)
  )
  
  # Extract optimal spend for each channel
  optimal_spend <- result$par
  
  # Combine channel names and optimal spend into a data frame
  optimal_allocation <- data.frame(
    channel_name = channel_names,
    optimal_spend = optimal_spend
  )
  
  return(optimal_allocation)
}

# Example data
example_data <- data.frame(
  channel_name = c("Channel 1", "Channel 2", "Channel 3"),
  a = c(5000, 8000, 6000),
  b = c(0.001, 0.002, 0.0015),
  c = c(-1.05, -1.2, -0.95),
  spend_min = c(100, 200, 150),
  spend_max = c(500, 800, 600)
)

# Max budget for optimization
max_budget <- 1000

# Run the optimization function
optimal_allocation <- budget_optimal_allocation(example_data, max_budget)

# Print the optimal allocation
print(optimal_allocation)
anauzrmj

anauzrmj1#

我不认为optim可以解决你的问题,因为这个问题是一个带有max_budget约束的 * 约束优化问题。一个有希望的候选者是constrOptim
有两件事你需要注意:
1.因为你已经指定了fnscale=-1,你不需要在你的objective_function中返回-tot_ROI;否则,返回到最小化过程。
1.你应该有一个sum(spend) <= max_budget的显式语句,这在你的约束优化问题中很关键。

代码

下面是一个实现(我想把objective_functionbudget_optimal_allocation中移出来,因为如果你需要改变目标函数,这样更容易维护代码)

# Objective function to maximize total ROI
objective_function <- function(spend, a, b, c) {
    # Calculate total revenue and total spend
    revenue <- a / (1 + b * (spend)^c)
    tot_revenue <- sum(revenue)
    tot_spend <- sum(spend)

    # Calculate total ROI
    tot_revenue / tot_spend
}

budget_optimal_allocation <- function(data, max_budget) {
    # Extract channel names and parameters
    channel_names <- data$channel_name
    a <- data$a
    b <- data$b
    c <- data$c
    spend_min <- data$spend_min
    spend_max <- data$spend_max

    # Initial guess for spend values (equal allocation for each channel)
    init <- spend_min + sqrt(.Machine$double.eps)
    # Box constraints for spend
    k <- nrow(data)
    ui <- rbind(diag(k), -diag(k), -rep(1, k))
    ci <- c(spend_min, -spend_max, -max_budget)
    # Optimize spend allocation to maximize total ROI with box constraints
    result <- constrOptim(
        theta = init,
        f = objective_function,
        a = a,
        b = b,
        c = c,
        ui = ui,
        ci = ci,
        method = "Nelder-Mead",
        control = list(fnscale = -1)
    )

    # Combine channel names and optimal spend into a data frame
    return(list(
        optimal_allocation = data.frame(
            channel_name = channel_names,
            optimal_spend = result$par
        ),
        max_tot_ROI = result$value
    ))
}

输出

与你问题中的数据相同

# Example data
example_data <- data.frame(
    channel_name = c("Channel 1", "Channel 2", "Channel 3"),
    a = c(5000, 8000, 6000),
    b = c(0.001, 0.002, 0.0015),
    c = c(-1.05, -1.2, -0.95),
    spend_min = c(100, 200, 150),
    spend_max = c(500, 800, 600)
)

# Max budget for optimization
max_budget <- 1000

# Run the optimization function
optimal_allocation <- budget_optimal_allocation(example_data, max_budget)

我们将看到

> optimal_allocation
$optimal_allocation
  channel_name optimal_spend
1    Channel 1           100
2    Channel 2           200
3    Channel 3           150

$max_tot_ROI
[1] 42.2219

如果你想 * 用尽 * 所有的预算

你可以有以下的变化init <- (spend_min + spend_max) / 2ui <- rbind(diag(k), -diag(k), rep(1, k))ci <- c(spend_min, -spend_max, max_budget),所以,代码是

budget_optimal_allocation <- function(data, max_budget) {
    # Extract channel names and parameters
    channel_names <- data$channel_name
    a <- data$a
    b <- data$b
    c <- data$c
    spend_min <- data$spend_min
    spend_max <- data$spend_max

    # Initial guess for spend values (equal allocation for each channel)
    eps <- sqrt(.Machine$double.eps)
    init <- (spend_min + spend_max) / 2
    # Box constraints for spend
    k <- nrow(data)
    ui <- rbind(diag(k), -diag(k), rep(1, k))
    ci <- c(spend_min, -spend_max, max_budget)
    # Optimize spend allocation to maximize total ROI with box constraints
    result <- constrOptim(
        theta = init,
        f = objective_function,
        a = a,
        b = b,
        c = c,
        ui = ui,
        ci = ci,
        method = "Nelder-Mead",
        control = list(fnscale = -1)
    )

    # Combine channel names and optimal spend into a data frame
    return(list(
        optimal_allocation = data.frame(
            channel_name = channel_names,
            optimal_spend = result$par
        ),
        max_tot_ROI = result$value
    ))
}

你会看到输出

> optimal_allocation
$optimal_allocation
  channel_name optimal_spend
1    Channel 1      252.1112
2    Channel 2      427.5322
3    Channel 3      320.3566

$max_tot_ROI
[1] 18.99994

并检查是否所有预算都用完了

> sum(optimal_allocation$optimal_allocation$optimal_spend)
[1] 1000
pu82cl6c

pu82cl6c2#

我认为你的问题可以用optim来解决,但你必须做一些代数运算,因为optim更适合于无约束优化。它可以使用方法L-BFGS-BBrent相对地处理边界,但没有其他约束比这更复杂。
您可以使用拉格朗日乘子将简单的约束优化问题转化为无约束优化问题。本质上,定义一个与原始函数相同的函数,加上约束时间常数。

L = function(a,b,c,lambda) {objective_function(a,b,c)-lambda*(max_budget-sum(a,b,c))}

最大化L.
我对拉格朗日乘数有点生疏了,但这应该会在你每次超出预算时惩罚目标。lambda越高,你对超出预算的宽容就越少。

相关问题