複数のシナリオを比較できる施設配置最適化のweb appを作ってください
To upload files, please first save the app
import streamlit as st
import numpy as np
import pandas as pd
import plotly.express as px
from scipy.spatial.distance import cdist
# アプリのタイトルと説明
st.title("施設配置最適化シミュレーター")
st.write("複数のシナリオで施設配置を比較・検討できるアプリケーションです。")
# セッション状態の初期化
if 'scenarios' not in st.session_state:
st.session_state.scenarios = {}
# サイドバーでシナリオの管理
with st.sidebar:
st.header("シナリオ管理")
scenario_name = st.text_input("新規シナリオ名", "")
# 新規シナリオの作成
if st.button("シナリオを追加"):
if scenario_name and scenario_name not in st.session_state.scenarios:
st.session_state.scenarios[scenario_name] = {
'demand_points': [],
'facilities': []
}
st.success(f"シナリオ '{scenario_name}' を追加しました")
elif not scenario_name:
st.error("シナリオ名を入力してください")
else:
st.error("同名のシナリオが既に存在します")
# シナリオの選択
selected_scenario = st.selectbox(
"シナリオを選択",
options=list(st.session_state.scenarios.keys()),
key="selected_scenario"
)
# メインエリアの設定
if selected_scenario:
st.subheader(f"シナリオ: {selected_scenario}")
# タブの作成
tab1, tab2, tab3 = st.tabs(["需要点の設定", "施設の設定", "分析結果"])
current_scenario = st.session_state.scenarios[selected_scenario]
# 需要点の設定タブ
with tab1:
st.write("需要点の座標とウェイトを設定してください")
col1, col2, col3, col4 = st.columns(4)
with col1:
dp_x = st.number_input("X座標", key=f"dp_x_{selected_scenario}")
with col2:
dp_y = st.number_input("Y座標", key=f"dp_y_{selected_scenario}")
with col3:
dp_weight = st.number_input("需要量", min_value=0.0, value=1.0, key=f"dp_weight_{selected_scenario}")
with col4:
if st.button("需要点を追加", key=f"add_dp_{selected_scenario}"):
current_scenario['demand_points'].append({
'x': dp_x,
'y': dp_y,
'weight': dp_weight
})
# 需要点の表示
if current_scenario['demand_points']:
dp_df = pd.DataFrame(current_scenario['demand_points'])
st.write("登録済みの需要点:")
st.dataframe(dp_df)
# 需要点の可視化
fig_dp = px.scatter(dp_df, x='x', y='y', size='weight',
title="需要点の分布")
st.plotly_chart(fig_dp)
# 施設の設定タブ
with tab2:
st.write("施設の座標と容量を設定してください")
col1, col2, col3, col4 = st.columns(4)
with col1:
f_x = st.number_input("X座標", key=f"f_x_{selected_scenario}")
with col2:
f_y = st.number_input("Y座標", key=f"f_y_{selected_scenario}")
with col3:
f_capacity = st.number_input("処理能力", min_value=0.0, value=100.0, key=f"f_capacity_{selected_scenario}")
with col4:
if st.button("施設を追加", key=f"add_f_{selected_scenario}"):
current_scenario['facilities'].append({
'x': f_x,
'y': f_y,
'capacity': f_capacity
})
# 施設の表示
if current_scenario['facilities']:
f_df = pd.DataFrame(current_scenario['facilities'])
st.write("登録済みの施設:")
st.dataframe(f_df)
# 施設の可視化
fig_f = px.scatter(f_df, x='x', y='y', size='capacity',
title="施設の分布")
st.plotly_chart(fig_f)
# 分析結果タブ
with tab3:
if current_scenario['demand_points'] and current_scenario['facilities']:
# データの準備
dp_array = np.array([[p['x'], p['y']] for p in current_scenario['demand_points']])
f_array = np.array([[f['x'], f['y']] for f in current_scenario['facilities']])
weights = np.array([p['weight'] for p in current_scenario['demand_points']])
capacities = np.array([f['capacity'] for f in current_scenario['facilities']])
# 距離行列の計算
distances = cdist(dp_array, f_array)
# 総移動距離の計算
total_distance = np.sum(np.min(distances, axis=1) * weights)
# 結果の表示
st.write("### 分析結果")
st.write(f"総移動距離: {total_distance:.2f}")
# 需要点と施設の統合可視化
combined_df = pd.DataFrame({
'x': list(dp_array[:, 0]) + list(f_array[:, 0]),
'y': list(dp_array[:, 1]) + list(f_array[:, 1]),
'type': ['需要点'] * len(dp_array) + ['施設'] * len(f_array),
'size': list(weights) + list(capacities)
})
fig_combined = px.scatter(combined_df, x='x', y='y',
color='type', size='size',
title="需要点と施設の分布")
st.plotly_chart(fig_combined)
# 最近接施設への割り当ての可視化
for i, dp in enumerate(dp_array):
nearest_facility_idx = np.argmin(distances[i])
nearest_facility = f_array[nearest_facility_idx]
# 線を引く
fig_combined.add_shape(
type="line",
x0=dp[0],
y0=dp[1],
x1=nearest_facility[0],
y1=nearest_facility[1],
line=dict(color="gray", width=1, dash="dot")
)
st.plotly_chart(fig_combined)
else:
st.warning("需要点と施設の両方を設定してください")
Hi! I can help you with any questions about Streamlit and Python. What would you like to know?