Sharing my delivery system recipe
Draft Disclaimer: Please note that this article is currently in draft form and may undergo revisions before final publication. The content, including information, opinions, and recommendations, is subject to change and may not represent the final version. We appreciate your understanding and patience as we work to refine and improve the quality of this article. Your feedback is valuable in shaping the final release.
Sharing my delivery system recipe
Features set
- geocoding: get GPS coordinate from address (text)
- directions: get itinerary from two addresses (text or GPS)
- dynamic pricing
Pricing
so far static
- area 1 : Centre-Ville 2.000 francs
- area 2 : Pikine, Guédiawaye, Parcelles 2.500 francs
- area 3 : Rufisque, Keur Massar 3.500 francs
- area 4 : Bargny, Diamniadio 5.000 Francs
- area x: price ?
Price = f(origin, destination)
The goal being to determine price per delivery. At first, I thought about having the pricing system to call the geocoding and direction services but separation of concerns principle. So from the addresses (origin, destination) -> geocoding for marker on map -> direction for itinerary -> pricing ()
What do you think the price for a delivery should depend on ? My answer to this question, is: distance and duration because traffic is something we do think about when taking a cab.
Price = f(distance, duration)
Data set 1
Direction service gave us
Price | Address | Distance | Duration |
---|---|---|---|
2000 | dakar plateau | 7.6km | 13m |
2500 | Pikine | 15.7km | 29m |
2500 | Guédiawaye | 17km | 30m |
2500 | Parcelles | 10.5km | 21m |
3500 | Rufisque | 33.7km | 43m |
3500 | Keur Massar | 26.8km | 43m |
3500 | Bargny | 35.8km | 45m |
4000 | Diamniadio | 37.6km | 41m |
4000 | Bargny | 35.8km | 45m |
f(distance, duration) | address | distance | duration |
I am seeing this a linear function f(x,y) = ax + by + c
Numbers are in meters, seconds. I am taking only the average for the values above. That would give us
-
7600a + (13 * 60)b + c = 2000
-
14400a + (26 * 60 + 40)b + c = 2500
-
30250a + (43 * 60)b + c = 3500
-
36700a + (43 * 60)b + c = 5000
-
(A) 7600a + 780b + c = 2000
-
(B) 14400a + 1600b + c = 2500
-
(C) 30250a + 2580b + c = 3500
-
(D) 36700a + 2580b + c = 5000
B-A:6800a + 820b = 500 C-B:15850a + 980b = 1000
find a 6800a = 500 - 820b a = 1/6800(500 - 820b)
replace in C-B: 15850/6800(500 - 820b) + 980b = 1000 2.33(500 - 820b) + 980b = 1000 1165 - 930.6b = 1000 165 = 930.6b b = 0.1774
replace a a = 0.05215
7600a + 780b + c = 2000
c = 1465.37
a = 0.05215 b = 0.1774 c = 1465.37
price = 0.5215 * distance + 0.1774 * duration + 1465.37
Data set 2
Direction service gave us
Price | Address | Distance (km) | Duration (minute) |
---|---|---|---|
1000 | point E | 2.1 | 7 |
1000 | fann | 2.1 | 5 |
1000 | sacré coeur | 2.3 | 9 |
1000 | amitié | 1.8 | 5 |
1000 | castor | 4.8 | 17 |
1000 | dieuppeul | 5.2 | 15 |
1000 | HLM | 4.1 | 12 |
1000 | colobane | 3.9 | 7 |
1000 | fass | 3.1 | 10 |
1000 | grand dakar | 2.8 | 9 |
1000 | sicap liberté | 4.4 | 11 |
1500 | ouakam | 3.3 | 8 |
1500 | front de terre | 5.5 | 16 |
1500 | zone de captage | 5.9 | 22 |
1500 | liberté 6 | 4.1 | 9 |
1500 | biscuiterie | 4.2 | 15 |
1500 | grand yoff | 5.6 | 15 |
2000 | Dakar plateau | 7.6 | 13 |
2000 | almadies | 8.2 | 14 |
2000 | mamelles | 8.1 | 15 |
2500 | Yarakh | 4.8 | 16 |
2500 | Dalifort | 13.4 | 23 |
2500 | Poste thiaroye | 16.7 | 24 |
2500 | Camberene | 12.2 | 23 |
3000 | Sicap mbao | 27.2 | 39 |
3000 | Grand mbao | 26.9 | 39 |
3000 | Guediawaye | 17 | 30 |
3000 | Pikine | 15.7 | 29 |
3000 | Thiaroye sur mer | 15.7 | 27 |
3000 | Guinaw rails | 18.6 | 29 |
3500 | Yeumbeul | 20.4 | 41 |
3500 | Keur massar | 26.8 | 43 |
3500 | Keur mbaye fall | 23 | 35 |
3500 | Malika | 23.2 | 36 |
3500 | Wakhinane nimzatt | 18 | 30 |
4000 | Rufisque | 33.7 | 43 |
4000 | Bargny | 35.8 | 45 |
4000 | Diamniadio | 37.6 | 41 |
4000 | Sebikhotane | 50.3 | 67m |
4000 | Sangalkam | 36.7 | 48 |
f(distance, duration) | address | distance | duration |
- normalize data
const data = [
[1000,'point E',2.1,7],
[1000,'fann',2.1,5],
[1000,'sacré coeur',2.3,9],
[1000,'amitié',1.8,5],
[1000,'castor',4.8,17],
[1000,'dieuppeul',5.2,15],
[1000,'HLM',4.1,12],
[1000,'colobane',3.9,7],
[1000,'fass',3.1,10],
[1000,'grand dakar',2.8,9],
[1000,'sicap liberté',4.4,11],
[1500,'ouaka',3.3,8],
[1500,'front de terre',5.5,16],
[1500,'zone de captage',5.9 ,22],
[1500,'liberté 6',4.1,9],
[1500,'biscuiterie',4.2,15],
[1500,'grand yoff',5.6,15],
[2000,'Dakar plateau',7.6,13],
[2000,'almadies',8.2,14],
[2000,'mamelles',8.1,15],
[2500,'Yarakh',4.8,16],
[2500,'Dalifort',13.4,23],
[2500,'Poste thiaroye',16.7,24],
[2500,'Camberene',12.2,23],
[3000,'Sicap mbao',27.2,39],
[3000,'Grand mbao',26.9,39],
[3000,'Guediawaye',17,30],
[3000,'Pikine',15.7,29],
[3000,'Thiaroye sur mer',15.7,27],
[3000,'Guinaw rails',18.6,29],
[3500,'Yeumbeul',20.4,41],
[3500,'Keur massar',26.8,43],
[3500,'Keur mbaye fall',23,35],
[3500,'Malika',23.2,36],
[3500,'Wakhinane nimzatt',18,30],
[4000,'Rufisque',33.7,43],
[4000,'Bargny',35.8,45],
[4000,'Diamniadio',37.6,41],
[4000,'Sebikhotane',50.3,67],
[4000,'Sangalkam',36.7,48],
]
data.map(x => {
return x[0] + ',' + x[2] + "\r\n"
})
const getColumnData = (data, columnIndex) => {
return data.map(i => i[columnIndex])
}
const normalize = (columnData) => {
const min = Math.min(...columnData)
const max = Math.max(...columnData)
return columnData.map(x => {
return (x - min) / (max - min)
})
}
const distances = getColumnData(data, 2)
const durations = getColumnData(data, 3)
normalize(distances)
normalize(durations)
| Price | Address | Distance (km) | Duration (minute) | 0.00000000e+00, 252.346741, -8.53235354e+00, 1.63897871e-01, -1.30360123e-03 price = (distance, duration) => 252.346741 * distance + -8.53235354 * duration - 0.00130360123
- Price per bracket
Price | Min | Max | Avg |
---|---|---|---|
1000 | 1.8 | 5.2 | 3.5 |
1500 | 1.8 | 5.2 | 3.5 |
Retrospectives
it has been fun so far to have the system calculate it but it does need some level of human intervention:
- add smoothing for too small distance (ie 10m distance) or same point deliveries (ie dakar, dakar)
- allow user to move marker point
Wrap up
Maths are useful for a developer. Inference to determine pricing from a set of data