如何发放优惠券的一个小项目
需求分析:
公司里有很多客户,客户之所以不继续用我们的产品了,是因为他账户余额是负的,所以,为了重新赢回这些客户,公司决定发放优惠券cover掉客户账户的负余额。
具体细节:
- 只有8元,80元,200元的优惠券
- 发放给一个客户的优惠券总张数不能超过15张
- 要既能cover掉客户的负余额,又要保证发放给客户的优惠券张数最少
- 发放给客户的总金额-客户的亏损额不能大于8,且越小越好。(不能送太多便宜了)
思路:
开始把这个问题理解为线性规划,觉得不好解,遂放弃。之后想用一大堆的逻辑判断来解决这个问题,总能解出来了吧(实在是烦 写一堆的 if else,最后都被绕晕了,遂放弃)。最后的思路,先列出所有送券的组合,然后把亏损和送券金额相加(亏损是负值),得到gap最小的券组合,就可以搞定了。有点暴力破解法的味道。
下面是代码:
## 检查是否安装 sqldf 和 reshape2 这两个包,若没有则安装,若已安装,则跳过。
if(!'sqldf' %in% installed.packages()[,1]) (install.packages('sqldf'))
if(!'reshape2' %in% installed.packages()[,1]) (install.packages('reshape2'))
## 如何发优惠券
## step 1
####################### 构造一个数据框,里面包含所有可能的送券组合################################
x=data.frame(x=rep(0:15,1)) # 表示 200的券 的张数
y=data.frame(y=rep(0:15,1)) # 表示 80的券 的张数
z=data.frame(z=rep(0:15,1)) # 表示 8的券 的张数
library(sqldf)
# 做笛卡尔积
df <- sqldf('select * from x,y,z')
df$coupon_sum <-apply(df,1,sum)
df$amt_sum <- df$x*200+df$y*80+df$z*8
#过滤掉 sum>15的 组合
df <- sqldf('select * from df where coupon_sum<=15 order by amt_sum asc')
## step 2
#######################################################
### 下面是给出任意一个 亏损 比如 loss=-987,则 fun2(-987) 返回出用200,80,8各几张,能获得gap最小
fun2 <- function(i){
if(i< -3000){
return(data.frame(loss=i,x=15,y=0,z=0,coupon_sum=15,amt_sum=3000,gap=3000+i))
} else {
df$gap <- i+df$amt_sum
df_positive <- sqldf('select * from df where gap>=0')
res <- sqldf('select * from df
where gap in (select gap from df_positive order by gap limit 1)
order by gap,coupon_sum
limit 1')
return(cbind(loss=i,res))
}
}
## step 3
# #### 建一个 函数 fun3,其中调用了fun2
fun3 <- function(original.df){
final.res <- data.frame()
for(m in 1:length(original.df[,1])){
row.res <- cbind(original.df[m,],fun2(original.df[m,2]))
final.res <- rbind(final.res,row.res)
}
return(final.res)
}
# n <- length(test.df[,2])
# res <- lapply(test.df[2:100,2],fun2)
# do.call(rbind,res)
## step 4
# 构造一个测试数据集 test.df 进行测试
test.df <- data.frame(phone_num=rep(1:200,1),loss=abs(rnorm(200))*(-2000))
final_res <- fun3(dd)
## step 5
## 变换数据形式 宽表变窄表,然后写出结果数据集
library(reshape2)
final <- melt(final_res[,c(1,3:5)],id=c('phone_num'))
## 重新排序一下
final <- sqldf('select * from final order by phone_num')
# final
# write.csv()
for(i in 1:length(dd[,2])){
print(paste0('round ',i))
print(fun2(dd[i,2]))
}
总结:
- 由于代码是发给别人用的,但是经常别人有些包没安装,用不了。所以学会了一句code,搞定。
## 检查是否安装 sqldf 和 reshape2 这两个包,若没有则安装,若已安装,则跳过。
if(!'sqldf' %in% installed.packages()[,1]) (install.packages('sqldf'))
if(!'reshape2' %in% installed.packages()[,1]) (install.packages('reshape2'))
- sqldf 真是一个好用的包。10-R-packages-I-wish-I-knew-about-earlier 排第一名。
- sql 对数据框做 笛卡尔积 好用到哭。

浙公网安备 33010602011771号