title: "R Notebook"
author: "Harry Zhu"
output: html_notebook
天池最后一公里
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配送在非高峰时段可以解放出配送的生产力。
O2O实际的配送情况是,对于加盟配送员(默认3公里范围内)在找到取餐地点可能就会花费10分钟,在中间行驶还会花费10分钟,最后10分钟还要花在找到送餐地点。所以,O2O配送对于配送员对地理情况的了解有比较高的要求,熟悉地理情况将大大缩短配送时长。
O2O在用餐高峰时期的时间约束是非常紧急的,对于送餐员是分秒必争,这一点和电商配送是有显著差别的,所以我们在算法设计上可以从这个边界条件入手(OR的基本原理:最优解总是发生在边界条件上)。
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
#
# 1 S062 121.217 31.04957 Shop
# > Geo[Geo$ID=="B1958",]
# # A tibble: 1 x 4
# ID Lng Lat type
#
# 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的具体业务,所以说不敢对这个配送时长的合理性做判断。
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
#
#1 E0036 B1958 S062 3 12 14 13 44 121.217 31.04973 Spot
# 配送时长最长可为 90分钟,看来就是怎么利用之间的 20分钟的时间来增加配送量。
接下来,我们继续分解任务,将O2O配送分为三种类型
一对多:同一家店铺取,顺序配送到多个目的地(旺铺型)
多对一:多家店铺顺序取件,一次配送到目的地(大客户型)
其他
# 一对多的情形
# reference https://www.youtube.com/watch?v=A1wsIFDKqBk