title: "R Notebook"
author: "Harry Zhu"html
天池最后一千米算法
library(readr) library(plyr) library(dplyr) Site = readr::read_csv("1.csv") %>% mutate(type = "Site") %>% rename(c("Site_id"='ID')) Spot = readr::read_csv("2.csv") %>% mutate(type = "Spot") %>% rename(c("Spot_id" = "ID")) Shop = readr::read_csv("3.csv") %>% mutate(type = "Shop") %>% rename(c("Shop_id" = "ID")) library(tidyr) Geo = rbind(Site,Shop,Spot) E_Order = readr::read_csv("4.csv") O2O_Order = readr::read_csv("5.csv") Courier = readr::read_csv("6.csv") #plot(Site$Lng,Site$Lat) plot(Geo$Lng,Geo$Lat) library(ggplot2) ggplot(data=Geo, aes(x=Lng, y=Lat,color=type))+geom_point(0.1)
# O2O订单时间统计与分布 O2O_ts = O2O_Order %>% tidyr::extract(col = Pickup_time,"Pickup_hour") %>% tidyr::extract(col = Delivery_time,"Delivery_hour") # 这里能够看到11-13点和17-18点是一个O2O下单高峰时段 plot(table(O2O_ts$Pickup_hour))
# 而配送时间则稍有滞后,在15-点到18点为低谷期 plot(table(O2O_ts$Delivery_hour))
total_Order = rbind(E_Order,O2O_Order) total_Spot_rank = total_Order %>% group_by(Spot_id) %>% summarise(Order_num = sum(Num)) %>% arrange(Order_num) # 观察订单合并分布状况 # 配送点的总订单状况 plot(table(total_Spot_rank$Order_num)) # 咱们观察到,绝大多数配送点(9202)都是符合合单配送的,只有12单是不知足合单配送,须要单独配送。
# 在早上8点-10点集中配送电商订单,快递员在11-13点集中配送O2O订单,在14-17点混合配送电商和O2O订单,17-19点集中配送O2O订单,19点-20点混合配送电商和O2O订单
时间段 | 包裹类型 | 持续时长 | 解决顺序 |
---|---|---|---|
8-11点 | 电商 | 2小时 | 5 |
11-13点 | O2O | 2小时 | 1 |
13-17点 | 混合 | 4小时 | 4 |
17-19点 | O2O | 2小时 | 2 |
19-20点 | 混合 | 1小时 | 3 |
在实际商业场景中,O2O配送是赚钱的,并且客单价不低,无论是自营仍是加盟,上海O2O一次配送能够有6-10元左右的提成。而电商配送须要区分是自营仍是加盟,若是是加盟配送在送件上是不赚钱的,在取件上才赚钱。因此,电商快递员们其实天天都是很是辛苦的,上午须要集中取件配送,下午通常是集中收件,到是O2O配送在非高峰时段能够解放出配送的生产力。spa
O2O实际的配送状况是,对于加盟配送员(默认3千米范围内)在找到取餐地点可能就会花费10分钟,在中间行驶还会花费10分钟,最后10分钟还要花在找到送餐地点。因此,O2O配送对于配送员对地理状况的了解有比较高的要求,熟悉地理状况将大大缩短配送时长。设计
O2O在用餐高峰时期的时间约束是很是紧急的,对于送餐员是分秒必争,这一点和电商配送是有显著差异的,因此咱们在算法设计上能够从这个边界条件入手(OR的基本原理:最优解老是发生在边界条件上)。3d
O2O_Pickup_time = ldply(strsplit(O2O_Order$Pickup_time, ":"), rbind) %>% plyr::rename(c("1"='P_hour',"2"="P_minute")) O2O_Delivery_time = ldply(strsplit(O2O_Order$Delivery_time, ":"), rbind) %>% plyr::rename(c("1"='D_hour',"2"="D_minute")) O2O_Order_ts <- O2O_Order %>% cbind(O2O_Pickup_time,O2O_Delivery_time) %>% select(-Pickup_time,-Delivery_time) %>% tbl_df() # 首先筛选出高峰订单 rush_Order_1 <- O2O_Order_ts %>% dplyr::filter(P_hour == c(11,12)) library(DT) DT::datatable(rush_Order_1 %>% arrange(Spot_id,Source_id))
# 咱们观察到有一些配送点会从多家店铺分时下单(脑补一个公司你们各自订餐的场景) # 有的场景是一个店家向多个配送点配送。 # 这样就有了一取多送和多取一送的两个基本问题。
# 根据题目写了一个距离计算公式 distLastMile <- function(lat1,lat2,lng1,lng2){ # parameter type check if(class(lat1) != "numeric" ||class(lat2) != "numeric" ||class(lng1) != "numeric" ||class(lng2) != "numeric" ){ stop("parameter must be numeric") } # reference https://img.alicdn.com/tps/TB1CiDVKXXXXXcpXFXXXXXXXXXX-866-255.png diff_lat = (lat1 - lat2)/2 diff_lng = (lng1 - lng2)/2 radius = 6378137 temp = (sin((pi * diff_lat )/180))^2 + cos((pi * lat1)/180) * cos((pi * lat2)/180)*(sin((pi * diff_lng )/180))^2 result = 2 * radius * asin (sqrt(temp)) return(result) } # 以题中给定15km/h的移动速度,估算 # > Geo[Geo$ID=="S062",] # # A tibble: 1 x 4 # ID Lng Lat type # <chr> <dbl> <dbl> <chr> # 1 S062 121.217 31.04957 Shop # > Geo[Geo$ID=="B1958",] # # A tibble: 1 x 4 # ID Lng Lat type # <chr> <dbl> <dbl> <chr> # 1 B1958 121.217 31.04973 Spot # distance = distLastMile(lat1=31.04973,lat2=31.04957,lng1=121.217,lng2=121.217) # [1] 17.81112 # distance * 4 # [1] 71.24448
这里计算出来的结果是至少71分钟左右的配送时长,17km左右的配送距离。因为不太肯定O2O的具体业务,因此说不敢对这个配送时长的合理性作判断。code
rush_info_1[rush_info_1$Order_id == "E0036",] # A tibble: 1 x 11 # Order_id Spot_id Source_id Num P_hour P_minute D_hour D_minute Lng Lat type # <chr> <chr> <chr> <int> <fctr> <fctr> <fctr> <fctr> <dbl> <dbl> <chr> #1 E0036 B1958 S062 3 12 14 13 44 121.217 31.04973 Spot # 配送时长最长可为 90分钟,看来就是怎么利用之间的 20分钟的时间来增长配送量。
接下来,咱们继续分解任务,将O2O配送分为三种类型cdn
一对多:同一家店铺取,顺序配送到多个目的地(旺铺型)htm
多对一:多家店铺顺序取件,一次配送到目的地(大客户型)blog
其余ip
# 一对多的情形 # reference https://www.youtube.com/watch?v=A1wsIFDKqBk