nuriyev commited on
Commit
cc23ec8
ยท
1 Parent(s): a18676a

first commit

Browse files
README.md CHANGED
@@ -1,12 +1,51 @@
1
  ---
2
- title: Znum Mcdm
3
- emoji: ๐Ÿƒ
4
  colorFrom: blue
5
- colorTo: yellow
6
- sdk: docker
 
 
7
  pinned: false
8
  license: mit
9
- short_description: Get the best alternative from your z-number decision matrix
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Z-Number MCDM Calculator
3
+ emoji: ๐Ÿงฎ
4
  colorFrom: blue
5
+ colorTo: purple
6
+ sdk: gradio
7
+ sdk_version: 4.44.0
8
+ app_file: app.py
9
  pinned: false
10
  license: mit
 
11
  ---
12
 
13
+ # Z-Number MCDM Calculator
14
+
15
+ A web application for Multi-Criteria Decision Making (MCDM) using Z-numbers with **TOPSIS** and **PROMETHEE** methods.
16
+
17
+ ## What are Z-Numbers?
18
+
19
+ A Z-number is a fuzzy number with two components:
20
+ - **A part**: The restriction on values (trapezoidal fuzzy number)
21
+ - **B part**: The reliability/confidence of the information (values between 0 and 1)
22
+
23
+ ## Supported Methods
24
+
25
+ ### TOPSIS
26
+ Technique for Order of Preference by Similarity to Ideal Solution. Ranks alternatives based on their distance from the ideal best and worst solutions.
27
+
28
+ ### PROMETHEE
29
+ Preference Ranking Organization Method for Enrichment Evaluation. Uses pairwise preference comparisons and outranking flows.
30
+
31
+ ## Excel File Format
32
+
33
+ Your Excel file should have this structure:
34
+
35
+ | Row | Column A | Criterion 1 (8 cols) | Criterion 2 (8 cols) | ... |
36
+ |-----|----------|---------------------|---------------------|-----|
37
+ | 1 | W | Weight Z-number | Weight Z-number | ... |
38
+ | 2 | A1 | Alternative 1 values| Alternative 1 values| ... |
39
+ | 3 | A2 | Alternative 2 values| Alternative 2 values| ... |
40
+ | ... | ... | ... | ... | ... |
41
+ | N | T | B or C | B or C | ... |
42
+
43
+ Each Z-number uses 8 consecutive columns:
44
+ - First 4: A part (Aโ‚ โ‰ค Aโ‚‚ โ‰ค Aโ‚ƒ โ‰ค Aโ‚„)
45
+ - Last 4: B part (Bโ‚ โ‰ค Bโ‚‚ โ‰ค Bโ‚ƒ โ‰ค Bโ‚„, values 0-1)
46
+
47
+ Criteria types: **B** = Benefit, **C** = Cost
48
+
49
+ ## Powered by
50
+
51
+ [znum](https://github.com/maganuriyev/znum) - Python library for Z-number arithmetic and MCDM
app.py ADDED
@@ -0,0 +1,305 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Z-Number MCDM Web Application
3
+ Multi-Criteria Decision Making with TOPSIS and PROMETHEE methods
4
+ """
5
+
6
+ import gradio as gr
7
+ import pandas as pd
8
+ import numpy as np
9
+ import copy
10
+ from znum import Znum, Topsis, Promethee, Beast
11
+
12
+
13
+ def parse_excel_to_table(df: pd.DataFrame):
14
+ """
15
+ Parse Excel DataFrame to MCDM table format.
16
+
17
+ Expected format:
18
+ - Row 1: Weights (label 'W' in first column)
19
+ - Rows 2 to N-1: Alternatives (labels 'A1', 'A2', etc.)
20
+ - Row N: Criteria types ('B' for Benefit, 'C' for Cost)
21
+
22
+ Each Z-number uses 8 columns: A1, A2, A3, A4, B1, B2, B3, B4
23
+ """
24
+ ZNUM_SIZE = 8
25
+
26
+ rows = df.values.tolist()
27
+
28
+ def parse_row(row):
29
+ """Parse a row into list of Znum objects."""
30
+ znums = []
31
+ # Skip the first column (label) and filter out NaN values
32
+ values = [float(v) for v in row[1:] if pd.notna(v) and v != '']
33
+
34
+ for i in range(0, len(values), ZNUM_SIZE):
35
+ if i + ZNUM_SIZE <= len(values):
36
+ znum_vals = values[i:i + ZNUM_SIZE]
37
+ half = ZNUM_SIZE // 2
38
+ znum = Znum(A=znum_vals[:half], B=znum_vals[half:])
39
+ znums.append(znum)
40
+ return znums
41
+
42
+ def parse_types(row):
43
+ """Parse criteria types row."""
44
+ types = []
45
+ values = row[1:]
46
+ i = 0
47
+ while i < len(values):
48
+ val = values[i]
49
+ if pd.notna(val) and str(val).strip().upper() in ['B', 'C']:
50
+ types.append(str(val).strip().upper())
51
+ i += ZNUM_SIZE # Skip to next criteria
52
+ return types
53
+
54
+ # Parse weights (first row)
55
+ weights = parse_row(rows[0])
56
+
57
+ # Parse alternatives (middle rows)
58
+ alternatives = []
59
+ for row in rows[1:-1]:
60
+ alt = parse_row(row)
61
+ if alt: # Only add non-empty alternatives
62
+ alternatives.append(alt)
63
+
64
+ # Parse criteria types (last row)
65
+ criteria_types = parse_types(rows[-1])
66
+
67
+ # Validate
68
+ n_criteria = len(weights)
69
+ if not all(len(alt) == n_criteria for alt in alternatives):
70
+ raise ValueError(f"All alternatives must have {n_criteria} criteria (matching weights)")
71
+
72
+ if len(criteria_types) != n_criteria:
73
+ raise ValueError(f"Number of criteria types ({len(criteria_types)}) must match number of criteria ({n_criteria})")
74
+
75
+ # Build table: [weights, alt1, alt2, ..., altN, criteria_types]
76
+ table = [weights] + alternatives + [criteria_types]
77
+
78
+ return table, len(alternatives), n_criteria
79
+
80
+
81
+ def run_topsis(table, normalize_weights, distance_method):
82
+ """Run TOPSIS method and return results."""
83
+ table_copy = copy.deepcopy(table)
84
+
85
+ dist_type = (Topsis.DistanceMethod.HELLINGER
86
+ if distance_method == "Hellinger"
87
+ else Topsis.DistanceMethod.SIMPLE)
88
+
89
+ topsis = Topsis(table_copy, shouldNormalizeWeight=normalize_weights, distanceType=dist_type)
90
+ scores = topsis.solve()
91
+
92
+ # Create results DataFrame
93
+ results = []
94
+ ranked_indices = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True)
95
+
96
+ for rank, idx in enumerate(ranked_indices, 1):
97
+ results.append({
98
+ "Rank": rank,
99
+ "Alternative": f"A{idx + 1}",
100
+ "Closeness Coefficient": f"{scores[idx]:.6f}",
101
+ "Original Index": idx + 1
102
+ })
103
+
104
+ return pd.DataFrame(results), scores
105
+
106
+
107
+ def run_promethee(table, normalize_weights):
108
+ """Run PROMETHEE method and return results."""
109
+ table_copy = copy.deepcopy(table)
110
+
111
+ promethee = Promethee(table_copy, shouldNormalizeWeight=normalize_weights)
112
+ sorted_results = promethee.solve()
113
+
114
+ # Create results DataFrame
115
+ results = []
116
+ for rank, (idx, znum) in enumerate(sorted_results, 1):
117
+ # Calculate a score representation from the Znum
118
+ score = np.mean(znum.A) # Use mean of A part as score indicator
119
+ results.append({
120
+ "Rank": rank,
121
+ "Alternative": f"A{idx + 1}",
122
+ "Net Flow Score": f"{score:.6f}",
123
+ "Original Index": idx + 1
124
+ })
125
+
126
+ return pd.DataFrame(results), sorted_results
127
+
128
+
129
+ def process_file(file, method, normalize_weights, distance_method):
130
+ """Main processing function."""
131
+ if file is None:
132
+ return None, "Please upload an Excel file."
133
+
134
+ try:
135
+ # Read Excel file
136
+ df = pd.read_excel(file.name, header=None)
137
+
138
+ # Parse to table format
139
+ table, n_alternatives, n_criteria = parse_excel_to_table(df)
140
+
141
+ info = f"โœ… Parsed successfully: {n_alternatives} alternatives, {n_criteria} criteria\n\n"
142
+
143
+ # Run selected method
144
+ if method == "TOPSIS":
145
+ results_df, _ = run_topsis(table, normalize_weights, distance_method)
146
+ info += f"**Method:** TOPSIS ({distance_method} distance)\n"
147
+ else: # PROMETHEE
148
+ results_df, _ = run_promethee(table, normalize_weights)
149
+ info += f"**Method:** PROMETHEE II\n"
150
+
151
+ info += f"**Weight Normalization:** {'Enabled' if normalize_weights else 'Disabled'}\n\n"
152
+ info += f"### ๐Ÿ† Best Alternative: {results_df.iloc[0]['Alternative']}\n"
153
+ info += f"### ๐Ÿ“‰ Worst Alternative: {results_df.iloc[-1]['Alternative']}"
154
+
155
+ return results_df, info
156
+
157
+ except Exception as e:
158
+ return None, f"โŒ Error: {str(e)}\n\nPlease check your Excel file format."
159
+
160
+
161
+ def create_sample_excel():
162
+ """Create a sample Excel file for download."""
163
+ # Sample data with 3 alternatives and 3 criteria
164
+ data = [
165
+ # Weights row
166
+ ['W', 0.2, 0.3, 0.4, 0.5, 0.1, 0.2, 0.3, 0.4, # Criterion 1 weight
167
+ 0.3, 0.4, 0.5, 0.6, 0.2, 0.3, 0.4, 0.5, # Criterion 2 weight
168
+ 0.1, 0.2, 0.3, 0.4, 0.05, 0.1, 0.15, 0.2], # Criterion 3 weight
169
+ # Alternative 1
170
+ ['A1', 7, 8, 9, 10, 0.6, 0.7, 0.8, 0.9, # A1, C1
171
+ 5, 6, 7, 8, 0.5, 0.6, 0.7, 0.8, # A1, C2
172
+ 6, 7, 8, 9, 0.6, 0.65, 0.7, 0.75], # A1, C3
173
+ # Alternative 2
174
+ ['A2', 4, 5, 6, 7, 0.4, 0.5, 0.6, 0.7, # A2, C1
175
+ 8, 9, 10, 11, 0.7, 0.75, 0.8, 0.85, # A2, C2
176
+ 3, 4, 5, 6, 0.3, 0.4, 0.5, 0.6], # A2, C3
177
+ # Alternative 3
178
+ ['A3', 6, 7, 8, 9, 0.5, 0.6, 0.7, 0.8, # A3, C1
179
+ 6, 7, 8, 9, 0.55, 0.65, 0.75, 0.85, # A3, C2
180
+ 7, 8, 9, 10, 0.6, 0.7, 0.8, 0.9], # A3, C3
181
+ # Criteria types
182
+ ['T', 'B', '', '', '', '', '', '', '', # Criterion 1: Benefit
183
+ 'B', '', '', '', '', '', '', '', # Criterion 2: Benefit
184
+ 'C', '', '', '', '', '', '', ''], # Criterion 3: Cost
185
+ ]
186
+
187
+ df = pd.DataFrame(data)
188
+
189
+ # Save to file
190
+ filepath = "/tmp/sample_mcdm_template.xlsx"
191
+ df.to_excel(filepath, index=False, header=False)
192
+
193
+ return filepath
194
+
195
+
196
+ # Create Gradio interface
197
+ with gr.Blocks(title="Z-Number MCDM Calculator", theme=gr.themes.Soft()) as demo:
198
+ gr.Markdown("""
199
+ # ๐ŸŽฏ Z-Number MCDM Calculator
200
+ ## Multi-Criteria Decision Making with TOPSIS & PROMETHEE
201
+
202
+ Upload your decision matrix in Excel format and get instant rankings!
203
+ """)
204
+
205
+ with gr.Row():
206
+ with gr.Column(scale=1):
207
+ gr.Markdown("### ๐Ÿ“ Input")
208
+
209
+ file_input = gr.File(
210
+ label="Upload Excel File (.xlsx)",
211
+ file_types=[".xlsx", ".xls"],
212
+ type="filepath"
213
+ )
214
+
215
+ method = gr.Radio(
216
+ choices=["TOPSIS", "PROMETHEE"],
217
+ value="TOPSIS",
218
+ label="Select Method"
219
+ )
220
+
221
+ normalize_weights = gr.Checkbox(
222
+ value=True,
223
+ label="Normalize Weights"
224
+ )
225
+
226
+ distance_method = gr.Radio(
227
+ choices=["Hellinger", "Simple"],
228
+ value="Hellinger",
229
+ label="Distance Method (TOPSIS only)",
230
+ visible=True
231
+ )
232
+
233
+ submit_btn = gr.Button("๐Ÿš€ Calculate Rankings", variant="primary")
234
+
235
+ sample_btn = gr.Button("๐Ÿ“ฅ Download Sample Template", variant="secondary")
236
+ sample_file = gr.File(label="Sample Template", visible=False)
237
+
238
+ with gr.Column(scale=2):
239
+ gr.Markdown("### ๐Ÿ“Š Results")
240
+ info_output = gr.Markdown()
241
+ results_output = gr.DataFrame(label="Rankings")
242
+
243
+ gr.Markdown("""
244
+ ---
245
+ ## ๐Ÿ“‹ Excel File Format
246
+
247
+ Your Excel file should follow this structure:
248
+
249
+ | Row | Column A | Columns B-I (Criterion 1) | Columns J-Q (Criterion 2) | ... |
250
+ |-----|----------|---------------------------|---------------------------|-----|
251
+ | 1 | W | Aโ‚ Aโ‚‚ Aโ‚ƒ Aโ‚„ Bโ‚ Bโ‚‚ Bโ‚ƒ Bโ‚„ | Aโ‚ Aโ‚‚ Aโ‚ƒ Aโ‚„ Bโ‚ Bโ‚‚ Bโ‚ƒ Bโ‚„ | ... |
252
+ | 2 | A1 | Aโ‚ Aโ‚‚ Aโ‚ƒ Aโ‚„ Bโ‚ Bโ‚‚ Bโ‚ƒ Bโ‚„ | Aโ‚ Aโ‚‚ Aโ‚ƒ Aโ‚„ Bโ‚ Bโ‚‚ Bโ‚ƒ Bโ‚„ | ... |
253
+ | 3 | A2 | Aโ‚ Aโ‚‚ Aโ‚ƒ Aโ‚„ Bโ‚ Bโ‚‚ Bโ‚ƒ Bโ‚„ | Aโ‚ Aโ‚‚ Aโ‚ƒ Aโ‚„ Bโ‚ Bโ‚‚ Bโ‚ƒ Bโ‚„ | ... |
254
+ | ... | ... | ... | ... | ... |
255
+ | N | T | B or C | B or C | ... |
256
+
257
+ ### Z-Number Structure (8 values per criterion):
258
+ - **A part** (4 values): `Aโ‚ โ‰ค Aโ‚‚ โ‰ค Aโ‚ƒ โ‰ค Aโ‚„` โ€” The fuzzy restriction on values
259
+ - **B part** (4 values): `Bโ‚ โ‰ค Bโ‚‚ โ‰ค Bโ‚ƒ โ‰ค Bโ‚„` โ€” The reliability/confidence (values between 0 and 1)
260
+
261
+ ### Criteria Types (last row):
262
+ - **B** = Benefit (higher is better)
263
+ - **C** = Cost (lower is better)
264
+
265
+ ### Example Z-Number:
266
+ `7, 8, 9, 10, 0.6, 0.7, 0.8, 0.9` means:
267
+ - A = [7, 8, 9, 10] (trapezoidal fuzzy number)
268
+ - B = [0.6, 0.7, 0.8, 0.9] (reliability)
269
+
270
+ ---
271
+ ## ๐Ÿ”ฌ Methods
272
+
273
+ **TOPSIS** (Technique for Order of Preference by Similarity to Ideal Solution):
274
+ - Ranks alternatives based on distance from ideal best and worst solutions
275
+ - Distance methods: Hellinger (recommended) or Simple
276
+
277
+ **PROMETHEE** (Preference Ranking Organization Method for Enrichment Evaluation):
278
+ - Ranks alternatives based on pairwise preference comparisons
279
+ - Uses outranking flows to determine final ranking
280
+
281
+ ---
282
+ *Powered by [znum](https://github.com/maganuriyev/znum) library*
283
+ """)
284
+
285
+ # Event handlers
286
+ def toggle_distance_visibility(method):
287
+ return gr.update(visible=(method == "TOPSIS"))
288
+
289
+ method.change(toggle_distance_visibility, inputs=[method], outputs=[distance_method])
290
+
291
+ submit_btn.click(
292
+ process_file,
293
+ inputs=[file_input, method, normalize_weights, distance_method],
294
+ outputs=[results_output, info_output]
295
+ )
296
+
297
+ def download_sample():
298
+ filepath = create_sample_excel()
299
+ return gr.update(value=filepath, visible=True)
300
+
301
+ sample_btn.click(download_sample, outputs=[sample_file])
302
+
303
+
304
+ if __name__ == "__main__":
305
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ gradio>=4.0.0
2
+ pandas>=2.0.0
3
+ numpy>=1.24.0
4
+ openpyxl>=3.1.0
5
+ znum>=0.5.0
6
+ scipy>=1.10.0
sample_template.xlsx ADDED
Binary file (5.71 kB). View file
 
sample_template_with_headers.xlsx ADDED
Binary file (6.01 kB). View file