-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathahp.py
296 lines (262 loc) · 15 KB
/
ahp.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
import streamlit as st
import pandas as pd
import numpy as np
# import google.generativeai as genai
from scipy.stats import rankdata
from sklearn.preprocessing import MinMaxScaler
# KONSTANTA
COLUMN_EXCLUDE = 'alternatif'
INDEKS_ACAK = [0,0,0.58,0.90,1.12,1.24,1.32,1.41,1.45,1.49,1.51,1.48,1.56,1.57,1.59]
# KONFIGURASI HALAMAN
st.set_page_config(
page_title="SPK-AHP Lokasi Bisnis",
layout="wide",
)
# def check_gemini_api_key_is_true(gemini_api_key: str):
# st.info("If you don't have Gemini API Key, go to link below:", icon="ℹ️")
# st.markdown(f"""
# [https://aistudio.google.com/app/apikey](https://aistudio.google.com/app/apikey)
# """)
# if len(gemini_api_key) != 0:
# try:
# genai.configure(api_key=gemini_api_key)
# model = genai.GenerativeModel('gemini-pro')
# response = model.generate_content("Hello")
# st.success("Gemini API key is valid!")
# except Exception as e:
# st.warning(e)
def data_asli() -> pd.DataFrame:
df = pd.DataFrame({
'alternatif': ['Lokasi 1', 'Lokasi 2', 'Lokasi 3'],
'jarak_pusat_km': [1, 2, 4],
'harga_sewa_pertahun': [3_000_000, 1_800_000, 1_200_000],
'jarak_perumahan_km': [3, 4, 3]
})
return df
def create_filtered_dataframe(df: pd.DataFrame, exclude_column: str) -> pd.DataFrame:
# Mengambil semua nama kolom kecuali 'exclude_column'
columns_to_keep = [col for col in df.columns if col != exclude_column]
# Membuat DataFrame baru dengan satu kolom 'index_column' yang berisi nama kolom sesuai urutan
df_filtered = pd.DataFrame(index=columns_to_keep)
# Menambahkan kolom yang sesuai dengan df kecuali COLUMN_EXCLUDE
for col in columns_to_keep:
df_filtered[col] = ""
return df_filtered
class Manipulasi_df:
@staticmethod
def tambah_kolom_kriteria(df, nama_kriteria, nilai_kriteria):
if nama_kriteria:
df[nama_kriteria] = nilai_kriteria
st.session_state.df_perbandingan_kriteria = create_filtered_dataframe(st.session_state.df, COLUMN_EXCLUDE)
st.experimental_rerun() # Refresh halaman untuk memperbarui DataFrame
else:
st.warning("Harap masukkan nama kolom baru.")
@staticmethod
def hapus_kolom_kriteria(df, nama_kriteria):
if nama_kriteria:
if nama_kriteria == COLUMN_EXCLUDE:
st.warning("Harap pilih kolom lain yang ingin dihapus.")
else:
df.drop(columns=[nama_kriteria], inplace=True)
st.session_state.df_perbandingan_kriteria = create_filtered_dataframe(st.session_state.df, COLUMN_EXCLUDE)
st.experimental_rerun() # Refresh halaman untuk memperbarui DataFrame
else:
st.warning("Harap pilih kolom yang ingin dihapus.")
@staticmethod
def hapus_baris(df, baris):
if baris in df[COLUMN_EXCLUDE].values:
st.session_state.df = df[df[COLUMN_EXCLUDE] != baris]
st.session_state.df_perbandingan_kriteria = create_filtered_dataframe(st.session_state.df, COLUMN_EXCLUDE)
st.experimental_rerun() # Refresh halaman untuk memperbarui DataFrame
else:
st.warning("Harap pilih baris yang ingin dihapus.")
@staticmethod
def tambah_baris(df, baris_baru):
if all(baris_baru.values()):
new_row_df = pd.DataFrame([baris_baru])
st.session_state.df = pd.concat([df, new_row_df], ignore_index=True)
st.session_state.df_perbandingan_kriteria = create_filtered_dataframe(st.session_state.df, COLUMN_EXCLUDE)
st.experimental_rerun() # Refresh halaman untuk memperbarui DataFrame
else:
st.warning("Harap masukkan semua nilai untuk baris baru.")
def ahp(df: pd.DataFrame):
# Mengubah nilai-nilai dalam DataFrame menjadi tipe data float dan mengubahnya menjadi matriks numpy
weights_matrix = df.astype(float).values
# Menghitung nilai eigen dan vektor eigen dari matriks bobot
eigvals, eigvecs = np.linalg.eig(weights_matrix)
# Menentukan nilai eigen maksimum
max_eigval = np.max(eigvals)
# Mengambil vektor eigen yang berasosiasi dengan nilai eigen maksimum
eigvec = eigvecs[:, np.argmax(eigvals)].real
# Menghitung bobot dengan membagi vektor eigen dengan jumlah total elemen dalam vektor eigen
weights = eigvec / np.sum(eigvec)
# Membuat DataFrame baru untuk menampilkan bobot dengan indeks yang sama seperti DataFrame asli
weights_df = pd.DataFrame(weights, index=df.index, columns=['Bobot'])
# Mengembalikan DataFrame yang berisi bobot
return weights_df
def calculate_consistency_ratio(df: pd.DataFrame) -> float:
# Langkah 1: Mengonversi DataFrame menjadi numpy array
matrix = df.astype(float).values
n = len(matrix)
# Langkah 2: Menghitung eigenvector dan nilai eigen maksimum (λ_max)
eigenvalues, eigenvectors = np.linalg.eig(matrix)
max_eigenvalue = np.max(eigenvalues)
principal_eigenvector = eigenvectors[:, np.argmax(eigenvalues)]
principal_eigenvector = principal_eigenvector / principal_eigenvector.sum() # Normalisasi
# Langkah 3: Menghitung Indeks Konsistensi (CI)
CI = (max_eigenvalue - n) / (n - 1)
# Langkah 4: Menghitung Rasio Konsistensi (CR) menggunakan Indeks Acak (RI)
# Tabel nilai RI untuk ukuran matriks tertentu
RI_dict = {1: 0.00, 2: 0.00, 3: 0.58, 4: 0.90, 5: 1.12, 6: 1.24, 7: 1.32, 8: 1.41, 9: 1.45, 10: 1.49}
RI = RI_dict.get(n, 1.49) # Default ke 1.49 jika ukuran matriks di luar tabel RI
if RI == 0:
st.warning("Kriteria hanya bisa minimal 3, jika kurang dari itu maka akan error dan menghasilkan nilai NaN")
CR = CI / RI
return CR
# Fungsi untuk membuat matriks perbandingan berpasangan
def pairwise_comparison(values):
size = len(values)
comparison_matrix = np.zeros((size, size))
for i in range(size):
for j in range(size):
comparison_matrix[i, j] = values[i] / values[j]
return comparison_matrix
# Fungsi untuk menghitung bobot prioritas
def calculate_priority(comparison_matrix):
eigvals, eigvecs = np.linalg.eig(comparison_matrix)
max_eigval_index = np.argmax(eigvals)
max_eigvec = eigvecs[:, max_eigval_index]
priorities = max_eigvec / np.sum(max_eigvec)
return priorities.real
def calculate_final_priority(weights_df: pd.DataFrame, priorities_dict: dict) -> pd.DataFrame:
# Mendapatkan nama kriteria dari indeks DataFrame bobot kriteria
criteria_names = weights_df.index
# Menghitung prioritas akhir untuk setiap alternatif
final_priorities = {}
for alt in priorities_dict['alternatif']:
final_priority = 0
alt_index = int(alt.split()[-1]) - 1 # Mengambil angka dari string 'Lokasi X' dan mengubahnya menjadi indeks yang benar
try:
for criterion in criteria_names:
col_weight = weights_df.loc[criterion, 'Bobot'] # Mengakses bobot kriteria menggunakan indeks
col_priorities_key = f'prioritas_{criterion}' # Kunci yang digunakan dalam priorities_dict
priorities_array = priorities_dict[col_priorities_key] # Mengambil array prioritas dari priorities_dict
alt_priority = float(priorities_array[alt_index]) # Mengambil nilai prioritas alternatif dari array prioritas
final_priority += alt_priority * col_weight
except IndexError as e:
st.write(f"IndexError: {e}")
st.write(f"Prioritas Array: {priorities_array}")
st.write(f"Alt Index: {alt_index}")
return
final_priorities[alt] = final_priority
# Membuat DataFrame untuk menampilkan prioritas akhir
final_priorities_df = pd.DataFrame(final_priorities.values(), index=final_priorities.keys(), columns=['Prioritas Akhir'])
return final_priorities_df
def main():
st.title('🗺️ Sistem Pengambil Keputusan untuk Saran Lokasi Bisnis menggunakan Analytic Hierarcy Process')
st.divider()
# Memuat data asli
if 'df' not in st.session_state:
st.session_state.df = data_asli()
if 'df_perbandingan_kriteria' not in st.session_state:
st.session_state.df_perbandingan_kriteria = create_filtered_dataframe(st.session_state.df, COLUMN_EXCLUDE)
# Menampilkan editor data
st.header('1️⃣ Dataframe Alternatif')
st.markdown("""
> Jika ingin mengganti nilai, ganti saja isi dari dataframe di bawah untuk menyesuaikan data aslimu.
> Jika ingin memanipulasi dataframe, manfaatkan beberapa fungsi dibawah.
""")
edited_df_original = st.data_editor(st.session_state.df)
st.markdown("### **Manipulasi Dataframe**")
col_tambah_kriteria, col_hapus_kriteria = st.columns(2)
with col_tambah_kriteria:
with st.expander('Tambah Kriteria'):
# Input untuk nama kolom baru
new_column_name = st.text_input('Masukkan nama kolom baru:')
# Input untuk nilai kolom baru
new_column_value = st.text_input('Masukkan nilai untuk kolom baru:')
# Tombol untuk menambahkan kolom baru
if st.button("Tambah Kolom Baru"):
Manipulasi_df.tambah_kolom_kriteria(st.session_state.df, new_column_name, new_column_value)
with col_hapus_kriteria:
with st.expander('Hapus Kriteria'):
# Pilih kolom untuk dihapus
column_to_drop = st.selectbox('Pilih kolom yang ingin dihapus:', st.session_state.df.columns)
# Tombol untuk menghapus kolom
if st.button("Hapus Kolom"):
Manipulasi_df.hapus_kolom_kriteria(st.session_state.df, column_to_drop)
col_tambah_baris, col_hapus_baris = st.columns(2)
with col_hapus_baris:
with st.expander('Hapus Baris'):
# Pilih baris untuk dihapus
row_to_drop = st.selectbox('Pilih baris yang ingin dihapus (berdasarkan alternatif):', st.session_state.df[COLUMN_EXCLUDE])
if st.button("Hapus Baris"):
Manipulasi_df.hapus_baris(st.session_state.df, row_to_drop)
with col_tambah_baris:
with st.expander('Tambah Baris'):
# Input untuk menambah baris baru
new_row = {}
for col in st.session_state.df.columns:
new_row[col] = st.text_input(f'Masukkan nilai untuk {col}', key=f'input_{col}')
# Tombol untuk menambahkan baris baru
if st.button("Tambah Baris Baru"):
Manipulasi_df.tambah_baris(st.session_state.df, new_row)
st.divider()
# Dataframe kriteria
st.header('2️⃣ Dataframe Perbandingan Kriteria')
st.markdown("""
> Jika kosong maka isi dulu pakai aturan pengisian hirarki AHP.
""")
edited_df_kriteria = st.data_editor(st.session_state.df_perbandingan_kriteria)
# Validasi bahwa semua nilai di edited_df_kriteria telah diisi
if edited_df_kriteria.isnull().values.any():
st.warning("Harap isi semua nilai dalam dataframe perbandingan kriteria.")
return
# Menghitung bobot kriteria menggunakan AHP dan rasio konsistensi
st.header('3️⃣ Bobot Kriteria dan Konsistensi Rasio')
col_bobot, col_consistency_ratio = st.columns(2)
with col_bobot:
try:
weights_df = ahp(edited_df_kriteria)
st.write(weights_df)
# st.write(type(weights))
except Exception as e:
st.error(f"Terjadi kesalahan dalam perhitungan bobot: {e}")
return
with col_consistency_ratio:
rasio_konsisten = calculate_consistency_ratio(edited_df_kriteria)
ambang_batas = 1
st.write(f"Rasio konsistensi: {rasio_konsisten:.5f}")
st.write(f"Ambang batas: {ambang_batas}")
if rasio_konsisten < ambang_batas:
st.write('Matriks di samping konsisten')
else:
st.write('Matriks di samping tidak konsisten')
st.divider()
st.header('4️⃣ Pairwise Comparison untuk setiap alternatif di setiap kriteria')
# Loop melalui setiap kriteria kecuali kolom 'alternatif'
priorities_dict = {'alternatif': edited_df_original['alternatif']}
with st.expander("Tekan untuk melihat hasilnya"):
for column in edited_df_original.columns[1:]:
values = edited_df_original[column].values
comparison_matrix = pairwise_comparison(values)
priorities = calculate_priority(comparison_matrix)
priorities_dict[f'prioritas_{column}'] = priorities
col_1, col_2 = st.columns(2)
with col_1:
st.write(f"Matriks Perbandingan Berpasangan {column}:\n", comparison_matrix)
with col_2:
st.write(f"\nPrioritas {column}:\n", priorities)
st.divider()
# Menghitung dan menampilkan prioritas akhir
st.header('5️⃣ Prioritas Akhir untuk Setiap Alternatif')
final_priorities_df = calculate_final_priority(weights_df, priorities_dict)
col_priorities_df, col_kriteria_terbesar = st.columns(2)
with col_priorities_df:
st.write(final_priorities_df)
with col_kriteria_terbesar:
# Mendapatkan nama alternatif dengan prioritas akhir paling besar
max_priority_alternative = final_priorities_df['Prioritas Akhir'].idxmax()
st.write(f"Alternatif dengan Prioritas Akhir Paling Besar: {max_priority_alternative}")
if __name__ == "__main__":
main()