はじめに
カーシェアリングサービス「タイムズカープラス」のプラン料金を計算し,最安プランを選びます.
予約作業について
タイムズカープラスで車を予約をする際には,WEBページ上で次のような作業をする必要があります.
- 借りたい車を選択する
- 料金プランを選択する
- 利用開始時刻と返却予定時刻を入力する
- 料金を確認して問題なければ予約を確定する
ここで「料金プラン」には選択肢が幾つかあるのですが,それぞれの料金を確認するには画面遷移が発生します.また,距離に応じた料金がかかるプランとかからないプランがあるのですが,想定走行距離は入力できないため,確認する際の料金には含まれていないことになります.
ということで,想定走行距離を含めた全プランの料金を計算し,最も小さな金額を自動的に選ぶ,というコードを書いてみます.
料金プランについて
タイムズカープラスで,「ベーシック」と呼ばれる車種を借りる場合の利用料金は,次の表のようになっています.
プラン名 | 料金 | 距離料金 | 延長料金 |
---|---|---|---|
ショート | 0円 | 0円/km | 206円/15分 |
6時間パック | 4,020円 | 0円/km | 206円/15分 |
12時間パック | 6,690円 | 16円/km | 206円/15分 |
24時間パック | 8,230円 | 16円/km | 206円/15分 |
アーリーナイトパック 18-0時 | 2,060円 | 16円/km | 206円/15分 |
レイトナイトパック 0-9時 | 2,060円 | 16円/km | 206円/15分 |
ダブルナイトパック 18-9時 | 2,580円 | 16円/km | 206円/15分 |
「ショート」のプランは,「延長料金が206円/15分の0時間(0円)パック」と言う風に捉えると,他の「○時間パック」と同じように計算できます.
なお,複数のプランを組み合わせることはできません1.また,延長時間には上限があり,ショートの場合は72時間まで,パックの場合は6時間までです.
(別途,初期費用や基本料金がかかる場合があります.また,36時間パック,48時間パック,60時間パックというプランが選択可能な車両もあります.詳細は公式サイトを参照してください)
Pythonコード
各プランの定義
まず,pandasのDataFrameを使って金額を定義します.plansはプラン名の配列で,今後計算時のキーとして使います.
import math
import datetime as dt
import pandas as pd
# plan names
plans = ['short', 'hour6', 'hour12', 'hour24', 'early', 'late', 'double']
# fare amount
a = {'base':[0, 4020, 6690, 8230, 2060, 2060, 2580],
'extra':[206, 206, 206, 206, 206, 206, 206],
'distance':[0, 0, 16, 16, 16, 16, 16]}
amount = pd.DataFrame(data=a, index=plans)
続いて,各プランを計算するための条件もDataFrameにして,金額と結合します.
# fare condition
c = {'type':['length', 'length', 'length', 'length', 'time', 'time', 'time'],
'length':[0, 6, 12, 24, 6, 9, 15],
'start':['', '', '', '', 18, 0, 18],
'by':['', '', '', '', 24, 9, 24],
'max':[72, 6, 6, 6, 6, 6, 6]}
condition = pd.DataFrame(data=c, index=plans)
fare = pd.concat([amount, condition], axis=1)
ちょっと分かりづらいですが,各列の意味は次のようにしました.
列名 | 説明 |
---|---|
type | プランが時間の長さを持つか(length),開始時刻と終了時刻を持つか(time) |
length | typeがlengthの場合は時間の長さ,timeの場合はパックの開始時刻〜終了時刻まで.単位は[hour] |
start | typeがtimeの場合のパックの開始時刻 |
by | typeがtimeの場合,利用開始時刻はstart以降で,byよりも前でなければならない |
max | 延長時間の上限.単位は[hour] |
料金を計算するクラス
「計算機(Calculator)」を表すクラスを1つ作ります.コンストラクタに利用開始時刻,返却予定時刻,距離をセットし,calc_fareメソッドにプラン名を与えると,料金を計算します.なお,下記のような場合にはmath.infを返します.
- プランのtypeがtimeで,かつ,そのプランが使えない時間帯である
- 延長時間が上限を超えている
class FareCalculator():
def __init__(self, start, end, distance):
self.s = start
self.e = end
self.d = distance
def __calc_extra(self, plan):
if fare.at[plan, 'type'] == 'length':
extra = self.e - self.s - dt.timedelta(
hours=int(fare.at[plan, 'length']))
elif fare.at[plan, 'type'] == 'time':
plan_start = dt.datetime(year=self.s.year,
month=self.s.month,
day=self.s.day,
hour=fare.at[plan, 'start'])
plan_end = plan_start + dt.timedelta(
hours=int(fare.at[plan, 'length']))
extra = self.e - plan_end
else:
extra = math.inf
return max(0, math.ceil((extra.total_seconds()/3600)*4))
def calc_fare(self, plan):
if (fare.at[plan, 'type'] == 'time') and (
self.s.hour < fare.at[plan, 'start'] or
self.s.hour >= fare.at[plan, 'by']):
return math.inf
else:
extra_qnum = self.__calc_extra(plan)
if extra_qnum > fare.at[plan, 'max']*4:
return math.inf
else:
return (fare.at[plan, 'base'] +
fare.at[plan, 'extra'] * extra_qnum +
fare.at[plan, 'distance'] * self.d)
__calc_extraメソッドについて少し説明します.
このメソッドは,延長時間を15分単位で計算するためのものです(例えば延長時間が1時間なら,戻り値は4).延長時間の計算方法は,typeがlengthかtimeかで変わります.どちらにしても,datetime.timedelta型で延長時間(extra)を求めて,total_secondsプロパティを3600で割って時間[hour]にして,さらに4を乗じて15分単位に直しています.
if文を使って色々と処理をしちゃってますが,まあとりあえず良しとします.
計算してみる
これで料金を計算する準備が整いました.利用開始時刻,返却予定時刻,距離を与えてみましょう.例として,
- 利用開始時刻 2018年8月15日 15:30
- 返却予定時刻 2018年8月16日 08:00
- 距離 120km
を与えます.時刻はdatetime.datetime型を利用します.
start = dt.datetime(year=2018, month=8, day=15, hour=15, minute=30)
end = dt.datetime(year=2018, month=8, day=16, hour=8, minute=0)
distance = 120
これをFareCalculatorクラスに与えた後,各プランの料金を計算して,最安値となるプランを選びます.結果はresultに書き込むことにします.
calculator = FareCalculator(start, end, distance)
result = pd.DataFrame(columns=[], index=plans)
for plan in plans:
result.at[plan, 'amount'] = calculator.calc_fare(plan)
最安プランの選択には,pandasのidxminを使います.
print('Plan:', result.amount.idxmin(), ', Amount: ', result.amount.min())
答えはこんな感じになります.
Plan: hour24 , Amount: 10150.0
環境
Python 3.6.3
pandas 0.20.3
終わりに
Pythonの勉強がてら書いてみました.今後は別の勉強も兼ねて,WEBサービスにでもして公開してみたいと思っています2.