SPKC / src /streamlit_app.py
Atikarahmanda's picture
Update src/streamlit_app.py
5247ddc verified
Raw
History Blame
5.62 kB
import streamlit as st
import pandas as pd
import numpy as np
st.set_page_config(page_title="SPK Lokasi Kedai Kopi", layout="wide")
st.title("πŸ“Š Sistem Pendukung Keputusan Lokasi Kedai Kopi")
st.markdown("Metode: **AHP**, **TOPSIS**, dan **Profile Matching**")
# --- Step 1: User Inputs for Criteria and Alternatives ---
st.sidebar.header("βš™οΈ Pengaturan")
criteria = st.sidebar.text_area("Masukkan Kriteria (pisahkan dengan koma)", "Building Area, Road Access, Distance, Rental Price")
criteria = [c.strip() for c in criteria.split(",") if c.strip()]
alternatives = st.sidebar.text_area("Masukkan Alternatif (pisahkan dengan koma)", "Location 1, Location 2, Location 3, Location 4")
alternatives = [a.strip() for a in alternatives.split(",") if a.strip()]
if not criteria or not alternatives:
st.warning("Masukkan minimal satu kriteria dan alternatif.")
st.stop()
# --- Step 2: Input Matrix (User-Editable) ---
st.subheader("πŸ“‹ Input Nilai Alternatif terhadap Kriteria")
empty_data = pd.DataFrame(np.nan, index=alternatives, columns=criteria)
df = st.data_editor(empty_data, use_container_width=True, key="input_matrix")
# Validasi isian
if df.isnull().values.any():
st.warning("⚠️ Harap lengkapi semua nilai pada tabel sebelum menjalankan perhitungan.")
st.stop()
if method == "AHP":
st.subheader("πŸ”— Perbandingan Berpasangan Antar Kriteria (AHP)")
pairwise_matrix = pd.DataFrame(np.ones((len(criteria), len(criteria))), index=criteria, columns=criteria)
for i in range(len(criteria)):
for j in range(i + 1, len(criteria)):
val = st.number_input(
f"Seberapa penting '{criteria[i]}' dibandingkan '{criteria[j]}'?",
min_value=1/9.0, max_value=9.0, value=1.0, step=0.1,
key=f"{criteria[i]}_{criteria[j]}"
)
pairwise_matrix.iloc[i, j] = val
pairwise_matrix.iloc[j, i] = 1 / val
st.write("πŸ“Š Matriks Perbandingan Kriteria:")
st.dataframe(pairwise_matrix)
norm_matrix = pairwise_matrix / pairwise_matrix.sum()
weights = norm_matrix.mean(axis=1).values
weights /= weights.sum()
st.write("🎯 Bobot Kriteria dari AHP:")
st.dataframe(pd.Series(weights, index=criteria, name="Bobot"))
else:
# --- Step 3 (non-AHP): Manual Bobot Kriteria ---
st.subheader("βš–οΈ Bobot Kriteria")
weight_dict = {}
cols = st.columns(len(criteria))
for i, c in enumerate(criteria):
with cols[i]:
weight_dict[c] = st.number_input(f"Bobot untuk '{c}'", min_value=1.0, max_value=10.0, value=5.0, step=0.1)
weights = np.array([weight_dict[c] for c in criteria])
weights /= weights.sum() # normalize
# --- Step 4: Ideal Profile ---
st.subheader("🎯 Ideal Profile (untuk Profile Matching)")
ideal_profile_dict = {}
cols = st.columns(len(criteria))
for i, c in enumerate(criteria):
with cols[i]:
ideal_profile_dict[c] = st.number_input(f"Ideal '{c}' (1-5)", min_value=1, max_value=5, value=5)
ideal_profile = pd.Series(ideal_profile_dict)
# --- Step 5: Metode ---
st.subheader("🧠 Pilih Metode")
method = st.selectbox("Metode yang ingin dijalankan", ["AHP", "TOPSIS", "Profile Matching"])
# --- UTILITIES ---
def scale_to_five(val):
if val >= 90: return 5
elif val >= 80: return 4
elif val >= 70: return 3
elif val >= 60: return 2
elif val >= 50: return 1
else: return 1
# --- ALGORITHMS ---
def run_ahp(df, weights):
normalized = df / df.sum()
weighted = normalized * weights
return weighted.sum(axis=1)
def run_topsis(df, weights, types):
norm_df = df / np.sqrt((df**2).sum())
weighted = norm_df * weights
ideal_pos = np.where(types == 1, weighted.max(), weighted.min())
ideal_neg = np.where(types == 1, weighted.min(), weighted.max())
d_pos = np.sqrt(((weighted - ideal_pos)**2).sum(axis=1))
d_neg = np.sqrt(((weighted - ideal_neg)**2).sum(axis=1))
return d_neg / (d_pos + d_neg)
def run_profile_matching(df_scaled, ideal, cf_weight, sf_weight, cf_indices):
gap_weights = {
0: 5, 1: 4.5, -1: 4.5, 2: 4, -2: 4,
3: 3.5, -3: 3.5, 4: 3, -4: 3, 5: 2.5, -5: 2.5
}
df_gap = df_scaled - ideal
df_wgap = df_gap.applymap(lambda x: gap_weights.get(int(x), 0))
cf_cols = df_scaled.columns[cf_indices]
sf_cols = df_scaled.columns.drop(cf_cols)
ncf = df_wgap[cf_cols].mean(axis=1)
nsf = df_wgap[sf_cols].mean(axis=1)
return cf_weight * ncf + sf_weight * nsf
# --- Step 6: Run ---
if st.button("πŸ” Jalankan Perhitungan"):
df_raw = df.copy()
st.write("πŸ“Š Data Masukan", df_raw)
if method == "AHP":
st.subheader("πŸ“ˆ Hasil AHP")
result = run_ahp(df_raw, weights)
st.dataframe(result.rename("Skor").sort_values(ascending=False))
elif method == "TOPSIS":
st.subheader("πŸ“ˆ Hasil TOPSIS")
types = np.array([1 if "price" not in c.lower() else -1 for c in criteria])
result = run_topsis(df_raw, weights, types)
st.dataframe(result.rename("Skor").sort_values(ascending=False))
elif method == "Profile Matching":
st.subheader("πŸ“ˆ Hasil Profile Matching")
df_scaled = df_raw.applymap(scale_to_five)
st.write("πŸ“ Data Skala 1–5", df_scaled)
core_factors = [criteria[-1]] # default: last one is core (e.g., Rental Price)
cf_indices = [criteria.index(c) for c in core_factors]
result = run_profile_matching(df_scaled, ideal_profile, cf_weight=0.6, sf_weight=0.4, cf_indices=cf_indices)
st.dataframe(result.rename("Skor").sort_values(ascending=False))