📊
Django + Pandas + Plotlyでホテル収益管理ダッシュボード構築
はじめに
ホテル業界では、GOP(総営業利益)、RevPAR(販売可能客室1室あたり収益)、ADR(平均客室単価)といった指標が経営判断の要となります。これらをリアルタイムで可視化するダッシュボードをDjango + Pandas + Plotlyで実装します。
主要指標の計算ロジック
まず、ホテル業界特有の指標を理解しましょう。
# models.py
from django.db import models
from decimal import Decimal
class DailyRevenue(models.Model):
date = models.DateField()
room_revenue = models.DecimalField(max_digits=10, decimal_places=2)
fb_revenue = models.DecimalField(max_digits=10, decimal_places=2) # 料飲収益
other_revenue = models.DecimalField(max_digits=10, decimal_places=2)
rooms_sold = models.IntegerField()
rooms_available = models.IntegerField()
operating_expenses = models.DecimalField(max_digits=10, decimal_places=2)
@property
def occupancy_rate(self):
return (self.rooms_sold / self.rooms_available * 100) if self.rooms_available else 0
@property
def adr(self):
"""平均客室単価"""
return self.room_revenue / self.rooms_sold if self.rooms_sold else Decimal('0')
@property
def revpar(self):
"""販売可能客室1室あたり収益"""
return self.room_revenue / self.rooms_available if self.rooms_available else Decimal('0')
@property
def gop(self):
"""総営業利益"""
total_revenue = self.room_revenue + self.fb_revenue + self.other_revenue
return total_revenue - self.operating_expenses
データパイプライン実装
PMSやOTAからのデータを集約し、Pandasで前処理を行います。
# data_pipeline.py
import pandas as pd
from datetime import datetime, timedelta
from .models import DailyRevenue
class RevenueDataPipeline:
def __init__(self):
self.df = None
def fetch_data(self, start_date, end_date):
"""データベースから期間指定でデータ取得"""
queryset = DailyRevenue.objects.filter(
date__range=[start_date, end_date]
).values()
self.df = pd.DataFrame(list(queryset))
return self
def calculate_metrics(self):
"""KPI計算と移動平均追加"""
if self.df.empty:
return self
# 基本指標計算
self.df['occupancy'] = (self.df['rooms_sold'] / self.df['rooms_available']) * 100
self.df['adr'] = self.df['room_revenue'] / self.df['rooms_sold']
self.df['revpar'] = self.df['room_revenue'] / self.df['rooms_available']
self.df['gop'] = (self.df['room_revenue'] + self.df['fb_revenue'] +
self.df['other_revenue']) - self.df['operating_expenses']
# 7日移動平均
self.df['revpar_ma7'] = self.df['revpar'].rolling(window=7).mean()
self.df['gop_ma7'] = self.df['gop'].rolling(window=7).mean()
return self
def get_dataframe(self):
return self.df
Plotlyでインタラクティブな可視化
# views.py
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import json
from django.shortcuts import render
from datetime import datetime, timedelta
from .data_pipeline import RevenueDataPipeline
def dashboard_view(request):
# 過去30日間のデータ取得
end_date = datetime.now().date()
start_date = end_date - timedelta(days=30)
pipeline = RevenueDataPipeline()
df = pipeline.fetch_data(start_date, end_date).calculate_metrics().get_dataframe()
# 複合グラフ作成
fig = make_subplots(
rows=2, cols=2,
subplot_titles=('RevPAR推移', 'ADR vs 稼働率', 'GOP推移', '収益構成'),
specs=[[{'secondary_y': False}, {'secondary_y': True}],
[{'secondary_y': False}, {'type': 'pie'}]]
)
# RevPAR推移(実績と移動平均)
fig.add_trace(
go.Scatter(x=df['date'], y=df['revpar'], name='RevPAR',
mode='lines+markers', line=dict(color='#1f77b4')),
row=1, col=1
)
fig.add_trace(
go.Scatter(x=df['date'], y=df['revpar_ma7'], name='7日移動平均',
line=dict(dash='dash', color='#ff7f0e')),
row=1, col=1
)
# ADRと稼働率の相関
fig.add_trace(
go.Bar(x=df['date'], y=df['adr'], name='ADR', marker_color='#2ca02c'),
row=1, col=2, secondary_y=False
)
fig.add_trace(
go.Scatter(x=df['date'], y=df['occupancy'], name='稼働率(%)',
line=dict(color='#d62728')),
row=1, col=2, secondary_y=True
)
# GOP推移
fig.add_trace(
go.Scatter(x=df['date'], y=df['gop'], name='GOP',
fill='tozeroy', fillcolor='rgba(0,100,80,0.2)'),
row=2, col=1
)
# 収益構成(最新日)
latest = df.iloc[-1]
fig.add_trace(
go.Pie(labels=['客室', 'F&B', 'その他'],
values=[latest['room_revenue'], latest['fb_revenue'], latest['other_revenue']]),
row=2, col=2
)
fig.update_layout(height=800, showlegend=True, title_text="ホテル収益管理ダッシュボード")
graph_json = json.dumps(fig, cls=plotly.utils.PlotlyJSONEncoder)
# KPIサマリー計算
kpi_summary = {
'current_revpar': f"¥{df['revpar'].iloc[-1]:,.0f}",
'revpar_change': f"{((df['revpar'].iloc[-1] / df['revpar'].iloc[-7] - 1) * 100):.1f}%",
'current_gop': f"¥{df['gop'].iloc[-1]:,.0f}",
'avg_occupancy': f"{df['occupancy'].mean():.1f}%"
}
return render(request, 'dashboard.html', {
'graph_json': graph_json,
'kpi_summary': kpi_summary
})
まとめ
この実装により、ホテルの経営指標をリアルタイムで監視できるダッシュボードが構築できます。Pandasによる効率的なデータ処理とPlotlyのインタラクティブな可視化により、経営判断に必要な情報を即座に把握できます。
次回は、このダッシュボードにWebSocketを組み込んでリアルタイム更新を実現する方法を解説予定です。
Discussion