EGYADMIN commited on
Commit
e4b89ed
·
verified ·
1 Parent(s): a6b8b17

Upload data_analysis_app.py

Browse files
modules/data_analysis/data_analysis_app.py CHANGED
@@ -1,330 +1,508 @@
1
  import streamlit as st
2
  import pandas as pd
3
  import numpy as np
 
4
  import plotly.express as px
5
  import plotly.graph_objects as go
 
6
  from datetime import datetime
7
- import io
8
  import os
9
- import json
10
  from pathlib import Path
11
 
 
 
 
 
 
 
12
  class DataAnalysisApp:
 
 
13
  def __init__(self):
 
14
  self.data = None
15
  self.file_path = None
16
- # تهيئة حالة الجلسة في البداية
17
- if 'analysis_data' not in st.session_state:
18
- st.session_state.analysis_data = {
19
- 'uploaded_files': {},
20
- 'analysis_results': {},
21
- 'ai_insights': {}
22
- }
23
-
24
- def render(self):
25
- """عرض واجهة تحليل البيانات"""
26
- st.title("تحليل البيانات")
27
- self.run()
28
 
29
  def run(self):
30
- st.title("تحليل البيانات المتقدم")
 
 
 
31
 
32
- # إنشاء التبويبات
33
- tabs = st.tabs([
34
- "تحميل وإدارة البيانات",
35
- "التحليل الإحصائي",
36
- "التحليل المرئي",
37
- "تحليل الذكاء الاصطناعي",
38
- "التقارير"
39
- ])
40
 
41
  with tabs[0]:
42
- self._render_data_management()
43
 
44
  with tabs[1]:
45
- self._render_statistical_analysis()
46
 
47
  with tabs[2]:
48
- self._render_visualization()
49
 
50
  with tabs[3]:
51
- self._render_ai_analysis()
52
 
53
  with tabs[4]:
54
- self._render_reports()
55
 
56
- def _render_data_management(self):
57
- st.header("تحميل وإدارة البيانات")
 
58
 
59
- # منطقة تحميل الملفات
60
- uploaded_files = st.file_uploader(
61
- "قم بتحميل ملفات البيانات",
62
- type=["csv", "xlsx", "xls", "pdf"],
63
- accept_multiple_files=True,
64
- key="data_files"
65
  )
66
 
67
- if uploaded_files:
68
- for file in uploaded_files:
 
 
69
  try:
70
- if file.name.endswith('.pdf'):
71
- import PyPDF2
72
- pdf_reader = PyPDF2.PdfReader(file)
73
- text_content = ""
74
- for page in pdf_reader.pages:
75
- text_content += page.extract_text()
76
-
77
- st.session_state.analysis_data['uploaded_files'][file.name] = {
78
- 'data': text_content,
79
- 'metadata': {
80
- 'pages': len(pdf_reader.pages),
81
- 'upload_time': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
82
- }
83
- }
84
- st.success(f"تم تحميل الملف PDF {file.name} بنجاح!")
85
-
86
- elif file.name.endswith('.csv'):
87
- df = pd.read_csv(file)
88
- st.session_state.analysis_data['uploaded_files'][file.name] = {
89
- 'data': df,
90
- 'metadata': {
91
- 'rows': len(df),
92
- 'columns': len(df.columns),
93
- 'upload_time': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
94
- }
95
- }
96
- st.success(f"تم تحميل الملف {file.name} بنجاح!")
97
  else:
98
- df = pd.read_excel(file)
99
- st.session_state.analysis_data['uploaded_files'][file.name] = {
100
- 'data': df,
101
- 'metadata': {
102
- 'rows': len(df),
103
- 'columns': len(df.columns),
104
- 'upload_time': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
105
- }
106
- }
107
- st.success(f"تم تحميل الملف {file.name} بنجاح!")
108
 
 
 
 
109
  except Exception as e:
110
- st.error(f"خطأ في تحميل الملف {file.name}: {str(e)}")
111
-
112
- # عرض الملفات المحملة
113
- if 'uploaded_files' in st.session_state.analysis_data and st.session_state.analysis_data['uploaded_files']:
114
- st.subheader("الملفات المحملة")
115
- for filename, file_info in st.session_state.analysis_data['uploaded_files'].items():
116
- with st.expander(f"📄 {filename}"):
117
- st.write("معلومات الملف:")
118
- if 'rows' in file_info['metadata']:
119
- st.write(f"- عدد الصفوف: {file_info['metadata']['rows']}")
120
- if 'columns' in file_info['metadata']:
121
- st.write(f"- عدد الأعمدة: {file_info['metadata']['columns']}")
122
- if 'pages' in file_info['metadata']:
123
- st.write(f"- عدد الصفحات: {file_info['metadata']['pages']}")
124
- st.write(f"- وقت التحميل: {file_info['metadata']['upload_time']}")
125
-
126
- if st.button(f"عرض البيانات", key=f"show_{filename}"):
127
- st.write(file_info['data'].head() if isinstance(file_info['data'], pd.DataFrame) else file_info['data'])
128
-
129
- def _render_statistical_analysis(self):
130
- st.header("التحليل الإحصائي")
131
-
132
- if 'uploaded_files' not in st.session_state.analysis_data or not st.session_state.analysis_data['uploaded_files']:
133
- st.info("الرجاء تحميل البيانات أولاً")
 
 
 
 
 
 
 
 
134
  return
135
 
136
- # اختيار الملف للتحليل
137
- selected_file = st.selectbox(
138
- "اختر الملف للتحليل",
139
- list(st.session_state.analysis_data['uploaded_files'].keys())
140
- )
141
 
142
- if selected_file:
143
- data = st.session_state.analysis_data['uploaded_files'][selected_file]['data']
144
- if isinstance(data, pd.DataFrame):
145
- # الإحصاءات الوصفية
146
- st.subheader("الإحصاءات الوصفية")
147
- st.dataframe(data.describe())
148
-
149
- # تحليل القيم المفقودة
150
- st.subheader("تحليل القيم المفقودة")
151
- missing_data = pd.DataFrame({
152
- 'العمود': data.columns,
153
- 'عدد القيم المفقودة': data.isnull().sum(),
154
- 'نسبة القيم المفقودة (%)': (data.isnull().sum() / len(data) * 100).round(2)
155
- })
156
- st.dataframe(missing_data)
157
- else:
158
- st.info("لا يمكن إجراء تحليل إحصائي على ملفات PDF.")
159
 
 
160
 
161
- def _render_visualization(self):
162
- st.header("التحليل المرئي")
163
 
164
- if 'uploaded_files' not in st.session_state.analysis_data or not st.session_state.analysis_data['uploaded_files']:
165
- st.info("الرجاء تحميل البيانات أولاً")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  return
167
 
168
- selected_file = st.selectbox(
169
- "اختر الملف للتحليل المرئي",
170
- list(st.session_state.analysis_data['uploaded_files'].keys()),
171
- key="viz_file_select"
172
  )
173
 
174
- if selected_file:
175
- data = st.session_state.analysis_data['uploaded_files'][selected_file]['data']
176
- if isinstance(data, pd.DataFrame):
177
- # اختيار نوع المخطط
178
- chart_type = st.selectbox(
179
- "اختر نوع المخطط",
180
- ["رسم بياني شريطي", "رسم بياني خطي", "رسم ب��اني دائري", "مخطط التشتت", "مخطط الصندوق"],
181
- key="chart_type"
182
- )
183
-
184
- # تخصيص المخطط
185
- if chart_type == "رسم بياني شريطي":
186
- x_col = st.selectbox("اختر محور X", data.columns, key="bar_x")
187
- y_col = st.selectbox("اختر محور Y", data.select_dtypes(include=['number']).columns, key="bar_y")
188
- fig = px.bar(data, x=x_col, y=y_col)
189
- st.plotly_chart(fig, use_container_width=True)
190
- elif chart_type == "رسم بياني خطي":
191
- x_col = st.selectbox("اختر محور X", data.columns, key="line_x")
192
- y_cols = st.multiselect("اختر محاور Y", data.select_dtypes(include=['number']).columns, key="line_y")
193
- if y_cols:
194
- fig = go.Figure()
195
- for y_col in y_cols:
196
- fig.add_trace(go.Scatter(x=data[x_col], y=data[y_col], mode='lines+markers', name=y_col))
197
- fig.update_layout(title=f"مخطط خطي", xaxis_title=x_col, yaxis_title="القيمة")
198
- st.plotly_chart(fig, use_container_width=True)
199
- else:
200
- st.warning("الرجاء اختيار عمود واحد على الأقل للمحور الرأسي")
201
-
202
- elif chart_type == "رسم بياني دائري":
203
- col = st.selectbox("اختر العمود", data.columns, key="pie_column")
204
- value_counts_df = data[col].value_counts().reset_index()
205
- value_counts_df.columns = ['القيمة', 'العدد']
206
- fig = px.pie(value_counts_df, names='القيمة', values='العدد', title=f"توزيع {col}")
207
- st.plotly_chart(fig, use_container_width=True)
208
- elif chart_type == "مخطط التشتت":
209
- numeric_columns = data.select_dtypes(include=['number']).columns.tolist()
210
- if len(numeric_columns) < 2:
211
- st.warning("يجب أن يكون هناك عمودان رقميان على الأقل لإنشاء مخطط تشتت")
212
- return
213
- x_column = st.selectbox("اختر عمود المحور الأفقي (x):", numeric_columns, key="scatter_x")
214
- y_column = st.selectbox("اختر عمود المحور الرأسي (y):", numeric_columns, key="scatter_y")
215
- fig = px.scatter(data, x=x_column, y=y_column)
216
- st.plotly_chart(fig, use_container_width=True)
217
- elif chart_type == "مخطط الصندوق":
218
- numeric_columns = data.select_dtypes(include=['number']).columns.tolist()
219
- if not numeric_columns:
220
- st.warning("يجب أن يكون هناك عمود رقمي واحد على الأقل لإنشاء مخطط صندوقي")
221
- return
222
- y_column = st.selectbox("اختر عمود القيمة:", numeric_columns, key="box_y")
223
- fig = px.box(data, y=y_column, title=f"مخطط صندوقي لـ {y_column}")
224
- st.plotly_chart(fig, use_container_width=True)
225
  else:
226
- st.info("لا يمكن إنشاء مخططات مرئية من ملفات PDF.")
 
 
 
 
227
 
228
- def _render_ai_analysis(self):
229
- st.header("تحليل الذكاء الاصطناعي")
 
230
 
231
- if 'uploaded_files' not in st.session_state.analysis_data or not st.session_state.analysis_data['uploaded_files']:
232
- st.info("الرجاء تحميل البيانات أولاً")
 
 
 
 
 
 
 
 
233
  return
234
 
235
- selected_file = st.selectbox(
236
- "اختر الملف للتحليل",
237
- list(st.session_state.analysis_data['uploaded_files'].keys()),
238
- key="ai_file_select"
239
  )
240
 
241
- if selected_file:
242
- data = st.session_state.analysis_data['uploaded_files'][selected_file]['data']
243
- if isinstance(data, pd.DataFrame):
244
- analysis_type = st.selectbox(
245
- "اختر نوع التحليل",
246
- ["تحليل الاتجاهات", "التنبؤ", "اكتشاف الأنماط", "تحليل العلاقات"]
247
- )
248
-
249
- if st.button("تحليل البيانات"):
250
- with st.spinner("جاري تحليل البيانات..."):
251
- # محاكاة تحليل الذكاء الاصطناعي
252
- st.session_state.analysis_data['ai_insights'][selected_file] = {
253
- 'trends': self._analyze_trends(data),
254
- 'patterns': self._analyze_patterns(data),
255
- 'correlations': self._analyze_correlations(data)
256
- }
257
-
258
- st.success("تم اكتمال التحليل!")
259
-
260
- # عرض النتائج
261
- st.json(st.session_state.analysis_data['ai_insights'][selected_file])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
  else:
263
- st.info("لا يمكن إجراء تحليل ذكاء اصطناعي على ملفات PDF.")
264
 
265
- def _render_reports(self):
 
266
  st.header("التقارير")
267
 
268
- if 'uploaded_files' not in st.session_state.analysis_data or not st.session_state.analysis_data['uploaded_files']:
269
- st.info("الرجاء تحميل البيانات أولاً")
270
  return
271
 
272
- # إنشاء تقرير
273
- if st.button("إنشاء تقرير تحليلي شامل"):
274
- report_data = self._generate_comprehensive_report()
275
-
276
- # تصدير التقرير
277
- output = io.BytesIO()
278
- with pd.ExcelWriter(output, engine='xlsxwriter') as writer:
279
- for sheet_name, data in report_data.items():
280
- pd.DataFrame(data).to_excel(writer, sheet_name=sheet_name)
281
-
282
- st.download_button(
283
- label="تحميل التقرير",
284
- data=output.getvalue(),
285
- file_name=f"analytical_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx",
286
- mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
287
- )
288
-
289
- def _analyze_trends(self, df):
290
- """تحليل الاتجاهات في البيانات"""
291
- # محاكاة تحليل الاتجاهات
292
- return {
293
- "trend_1": "اتجاه تصاعدي في المبيعات",
294
- "trend_2": "انخفاض في التكاليف التشغيلية",
295
- "trend_3": "زيادة في رضا العملاء"
296
- }
297
 
298
- def _analyze_patterns(self, df):
299
- """اكتشاف الأنماط في البيانات"""
300
- # محاكاة اكتشاف الأنماط
301
- return {
302
- "pattern_1": "نمط موسمي في الطلب",
303
- "pattern_2": "نمط دوري في الإنتاج",
304
- "pattern_3": "نمط جغرافي في التوزيع"
305
- }
306
 
307
- def _analyze_correlations(self, df):
308
- """تحليل العلاقات بين المتغيرات"""
309
- # محاكاة تحليل العلاقات
310
- numeric_cols = df.select_dtypes(include=['number']).columns
311
- correlations = df[numeric_cols].corr().round(2).to_dict()
312
- return correlations
313
-
314
- def _generate_comprehensive_report(self):
315
- """إنشاء تقرير شامل"""
316
- report = {
317
- 'ملخص_البيانات': {},
318
- 'التحليل_الإحصائي': {},
319
- 'تحليل_الذكاء_الاصطناعي': {},
320
- 'التوصيات': {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
  }
322
 
323
- for filename, file_info in st.session_state.analysis_data['uploaded_files'].items():
324
- report['ملخص_البيانات'][filename] = file_info['metadata']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
325
 
326
- if 'ai_insights' in st.session_state.analysis_data:
327
- report['تحليل_الذكاء_الاصطناعي'] = st.session_state.analysis_data['ai_insights']
328
 
329
- return report
330
-
 
 
1
  import streamlit as st
2
  import pandas as pd
3
  import numpy as np
4
+ import matplotlib.pyplot as plt
5
  import plotly.express as px
6
  import plotly.graph_objects as go
7
+ import seaborn as sns
8
  from datetime import datetime
 
9
  import os
10
+ import sys
11
  from pathlib import Path
12
 
13
+ # إضافة المسار للوصول إلى الوحدات الأخرى
14
+ current_dir = os.path.dirname(os.path.abspath(__file__))
15
+ parent_dir = os.path.dirname(os.path.dirname(current_dir))
16
+ if parent_dir not in sys.path:
17
+ sys.path.append(parent_dir)
18
+
19
  class DataAnalysisApp:
20
+ """تطبيق تحليل البيانات"""
21
+
22
  def __init__(self):
23
+ """تهيئة تطبيق تحليل البيانات"""
24
  self.data = None
25
  self.file_path = None
26
+ self.ui = None
 
 
 
 
 
 
 
 
 
 
 
27
 
28
  def run(self):
29
+ """تشغيل تطبيق تحليل البيانات"""
30
+ # استيراد مدير التكوين
31
+ from config_manager import ConfigManager
32
+
33
 
34
+ # عرض عنوان التطبيق
35
+ st.title("تحليل البيانات")
36
+ st.write("استخدم هذه الأداة لتحليل بيانات المناقصات والمشاريع")
37
+
38
+ # إنشاء علامات تبويب للتطبيق
39
+ tabs = st.tabs(["تحميل البيانات", "استكشاف البيانات", "تحليل متقدم", "التصور المرئي", "التقارير"])
 
 
40
 
41
  with tabs[0]:
42
+ self._load_data_tab()
43
 
44
  with tabs[1]:
45
+ self._explore_data_tab()
46
 
47
  with tabs[2]:
48
+ self._advanced_analysis_tab()
49
 
50
  with tabs[3]:
51
+ self._visualization_tab()
52
 
53
  with tabs[4]:
54
+ self._reports_tab()
55
 
56
+ def _load_data_tab(self):
57
+ """علامة تبويب تحميل البيانات"""
58
+ st.header("تحميل البيانات")
59
 
60
+ # خيارات تحميل البيانات
61
+ data_source = st.radio(
62
+ "اختر مصدر البيانات:",
63
+ ["تحميل ملف", "استيراد من قاعدة البيانات", "استخدام بيانات نموذجية"]
 
 
64
  )
65
 
66
+ if data_source == "تحميل ملف":
67
+ uploaded_file = st.file_uploader("اختر ملف CSV أو Excel", type=["csv", "xlsx", "xls"])
68
+
69
+ if uploaded_file is not None:
70
  try:
71
+ if uploaded_file.name.endswith('.csv'):
72
+ self.data = pd.read_csv(uploaded_file)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  else:
74
+ self.data = pd.read_excel(uploaded_file)
 
 
 
 
 
 
 
 
 
75
 
76
+ st.success(f"تم تحميل الملف بنجاح! عدد الصفوف: {self.data.shape[0]}, عدد الأعمدة: {self.data.shape[1]}")
77
+ st.write("معاينة البيانات:")
78
+ st.dataframe(self.data.head())
79
  except Exception as e:
80
+ st.error(f"حدث خطأ أثناء تحميل الملف: {str(e)}")
81
+
82
+ elif data_source == "استيراد من قاعدة البيانات":
83
+ st.info("هذه الميزة قيد التطوير")
84
+
85
+ # محاكاة الاتصال بقاعدة البيانات
86
+ if st.button("اتصال بقاعدة البيانات"):
87
+ with st.spinner("جاري الاتصال بقاعدة البيانات..."):
88
+ # محاكاة تأخير الاتصال
89
+ import time
90
+ time.sleep(2)
91
+
92
+ # إنشاء بيانات نموذجية
93
+ self.data = self._create_sample_data()
94
+
95
+ st.success("تم الاتصال بقاعدة البيانات بنجاح!")
96
+ st.write("معاينة البيانات:")
97
+ st.dataframe(self.data.head())
98
+
99
+ elif data_source == "استخدام بيانات نموذجية":
100
+ if st.button("تحميل بيانات نموذجية"):
101
+ self.data = self._create_sample_data()
102
+ st.success("تم تحميل البيانات النموذجية بنجاح!")
103
+ st.write("معاينة البيانات:")
104
+ st.dataframe(self.data.head())
105
+
106
+ def _explore_data_tab(self):
107
+ """علامة تبويب استكشاف البيانات"""
108
+ st.header("استكشاف البيانات")
109
+
110
+ if self.data is None:
111
+ st.info("الرجاء تحميل البيانات أولاً من علامة تبويب 'تحميل البيانات'")
112
  return
113
 
114
+ # عرض معلومات عامة عن البيانات
115
+ st.subheader("معلومات عامة")
116
+ col1, col2 = st.columns(2)
 
 
117
 
118
+ with col1:
119
+ st.write(f"عدد الصفوف: {self.data.shape[0]}")
120
+ st.write(f"عدد الأعمدة: {self.data.shape[1]}")
121
+ st.write(f"القيم المفقودة: {self.data.isna().sum().sum()}")
122
+
123
+ with col2:
124
+ st.write(f"أنواع البيانات:")
125
+ st.write(self.data.dtypes)
126
+
127
+ # عرض إحصاءات وصفية
128
+ st.subheader("إحصاءات وصفية")
129
+ st.dataframe(self.data.describe())
130
+
131
+ # عرض معلومات عن الأعمدة
132
+ st.subheader("معلومات الأعمدة")
 
 
133
 
134
+ selected_column = st.selectbox("اختر عمودًا لتحليله:", self.data.columns)
135
 
136
+ if selected_column:
137
+ col1, col2 = st.columns(2)
138
 
139
+ with col1:
140
+ st.write(f"نوع البيانات: {self.data[selected_column].dtype}")
141
+ st.write(f"القيم الفريدة: {self.data[selected_column].nunique()}")
142
+ st.write(f"القيم المفقودة: {self.data[selected_column].isna().sum()}")
143
+
144
+ with col2:
145
+ if pd.api.types.is_numeric_dtype(self.data[selected_column]):
146
+ st.write(f"الحد الأدنى: {self.data[selected_column].min()}")
147
+ st.write(f"الحد الأقصى: {self.data[selected_column].max()}")
148
+ st.write(f"المتوسط: {self.data[selected_column].mean()}")
149
+ st.write(f"الوسيط: {self.data[selected_column].median()}")
150
+ else:
151
+ st.write("القيم الأكثر تكرارًا:")
152
+ st.write(self.data[selected_column].value_counts().head())
153
+
154
+ # عرض رسم بياني للعمود المحدد
155
+ st.subheader(f"رسم بياني لـ {selected_column}")
156
+
157
+ if pd.api.types.is_numeric_dtype(self.data[selected_column]):
158
+ fig = px.histogram(self.data, x=selected_column, title=f"توزيع {selected_column}")
159
+ st.plotly_chart(fig, use_container_width=True)
160
+ else:
161
+ # الكود المعدل لحل مشكلة الرسم البياني
162
+ value_counts_df = self.data[selected_column].value_counts().reset_index()
163
+ value_counts_df.columns = ['القيمة', 'العدد'] # تسمية الأعمدة بأسماء واضحة
164
+ fig = px.bar(value_counts_df, x='القيمة', y='العدد', title=f"توزيع {selected_column}")
165
+ fig.update_layout(xaxis_title="القيمة", yaxis_title="العدد")
166
+ st.plotly_chart(fig, use_container_width=True)
167
+
168
+ def _advanced_analysis_tab(self):
169
+ """علامة تبويب التحليل المتقدم"""
170
+ st.header("تحليل متقدم")
171
+
172
+ if self.data is None:
173
+ st.info("الرجاء تحميل البيانات أولاً من علامة تبويب 'تحميل البيانات'")
174
  return
175
 
176
+ # أنواع التحليل المتقدم
177
+ analysis_type = st.selectbox(
178
+ "اختر نوع التحليل:",
179
+ ["تحليل الارتباط", "تحليل الاتجاهات", "تحليل المجموعات", "تحليل التباين"]
180
  )
181
 
182
+ if analysis_type == "تحليل الارتباط":
183
+ st.subheader("تحليل الارتباط")
184
+
185
+ # اختيار الأعمدة الرقمية فقط
186
+ numeric_columns = self.data.select_dtypes(include=['number']).columns.tolist()
187
+
188
+ if len(numeric_columns) < 2:
189
+ st.warning("يجب أن يكون هناك عمودان رقميان على الأقل لإجراء تحليل الارتباط")
190
+ return
191
+
192
+ # حساب مصفوفة الارتباط
193
+ correlation_matrix = self.data[numeric_columns].corr()
194
+
195
+ # عرض مصفوفة الارتباط
196
+ st.write("مصفوفة الارتباط:")
197
+ st.dataframe(correlation_matrix)
198
+
199
+ # رسم خريطة حرارية للارتباط
200
+ st.write("خريطة حرارية للارتباط:")
201
+ fig = px.imshow(correlation_matrix, text_auto=True, aspect="auto",
202
+ title="خريطة حرارية لمصفوفة الارتباط")
203
+ st.plotly_chart(fig, use_container_width=True)
204
+
205
+ # تحليل الارتباط بين عمودين محددين
206
+ st.subheader("تحليل الارتباط بين عمودين محددين")
207
+
208
+ col1 = st.selectbox("اختر العمود الأول:", numeric_columns, key="corr_col1")
209
+ col2 = st.selectbox("اختر العمود الثاني:", numeric_columns, key="corr_col2")
210
+
211
+ if col1 != col2:
212
+ # حساب معامل الارتباط
213
+ correlation = self.data[col1].corr(self.data[col2])
214
+
215
+ st.write(f"معا��ل الارتباط بين {col1} و {col2}: {correlation:.4f}")
216
+
217
+ # رسم مخطط التشتت
218
+ fig = px.scatter(self.data, x=col1, y=col2, title=f"مخطط التشتت: {col1} مقابل {col2}")
219
+ fig.update_layout(xaxis_title=col1, yaxis_title=col2)
220
+ st.plotly_chart(fig, use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
221
  else:
222
+ st.warning("الرجاء اختيار عمودين مختلفين")
223
+
224
+ elif analysis_type == "تحليل الاتجاهات":
225
+ st.subheader("تحليل الاتجاهات")
226
+ st.info("هذه الميزة قيد التطوير")
227
 
228
+ elif analysis_type == "تحليل المجموعات":
229
+ st.subheader("تحليل المجموعات")
230
+ st.info("هذه الميزة قيد التطوير")
231
 
232
+ elif analysis_type == "تحليل التباين":
233
+ st.subheader("تحليل التباين")
234
+ st.info("هذه الميزة قيد التطوير")
235
+
236
+ def _visualization_tab(self):
237
+ """علامة تبويب التصور المرئي"""
238
+ st.header("التصور المرئي")
239
+
240
+ if self.data is None:
241
+ st.info("الرجاء تحميل البيانات أولاً من علامة تبويب 'تحميل البيانات'")
242
  return
243
 
244
+ # أنواع الرسوم البيانية
245
+ chart_type = st.selectbox(
246
+ "اختر نوع الرسم البياني:",
247
+ ["مخطط شريطي", "مخطط خطي", "مخطط دائري", "مخطط تشتت", "مخطط صندوقي", "مخطط حراري"]
248
  )
249
 
250
+ # اختيار الأعمدة حسب نوع الرسم البياني
251
+ if chart_type == "مخطط شريطي":
252
+ st.subheader("مخطط شريطي")
253
+
254
+ x_column = st.selectbox("اختر عمود المحور الأفقي (x):", self.data.columns, key="bar_x")
255
+ y_column = st.selectbox("اختر عمود المحور الرأسي (y):",
256
+ self.data.select_dtypes(include=['number']).columns.tolist(),
257
+ key="bar_y")
258
+
259
+ # خيارات إضافية
260
+ color_column = st.selectbox("اختر عمود اللون (اختياري):",
261
+ ["لا يوجد"] + self.data.columns.tolist(),
262
+ key="bar_color")
263
+
264
+ # إنشاء الرسم البياني
265
+ if color_column == "لا يوجد":
266
+ fig = px.bar(self.data, x=x_column, y=y_column, title=f"{y_column} حسب {x_column}")
267
+ else:
268
+ fig = px.bar(self.data, x=x_column, y=y_column, color=color_column,
269
+ title=f"{y_column} حسب {x_column} (مصنف حسب {color_column})")
270
+
271
+ fig.update_layout(xaxis_title=x_column, yaxis_title=y_column)
272
+ st.plotly_chart(fig, use_container_width=True)
273
+
274
+ elif chart_type == "مخطط خطي":
275
+ st.subheader("مخطط خطي")
276
+
277
+ x_column = st.selectbox("اختر عمود المحور الأفقي (x):", self.data.columns, key="line_x")
278
+ y_columns = st.multiselect("اخت�� أعمدة المحور الرأسي (y):",
279
+ self.data.select_dtypes(include=['number']).columns.tolist(),
280
+ key="line_y")
281
+
282
+ if y_columns:
283
+ # إنشاء الرسم البياني
284
+ fig = go.Figure()
285
+
286
+ for y_column in y_columns:
287
+ fig.add_trace(go.Scatter(x=self.data[x_column], y=self.data[y_column],
288
+ mode='lines+markers', name=y_column))
289
+
290
+ fig.update_layout(title=f"مخطط خطي", xaxis_title=x_column, yaxis_title="القيمة")
291
+ st.plotly_chart(fig, use_container_width=True)
292
+ else:
293
+ st.warning("الرجاء اختيار عمود واحد على الأقل للمحور الرأسي")
294
+
295
+ elif chart_type == "مخطط دائري":
296
+ st.subheader("مخطط دائري")
297
+
298
+ column = st.selectbox("اختر العمود:", self.data.columns, key="pie_column")
299
+
300
+ # إنشاء الرسم البياني
301
+ # تعديل لحل مشكلة مماثلة في مخطط دائري
302
+ value_counts_df = self.data[column].value_counts().reset_index()
303
+ value_counts_df.columns = ['القيمة', 'العدد']
304
+ fig = px.pie(value_counts_df, names='القيمة', values='العدد', title=f"توزيع {column}")
305
+ st.plotly_chart(fig, use_container_width=True)
306
+
307
+ elif chart_type == "مخطط تشتت":
308
+ st.subheader("مخطط تشتت")
309
+
310
+ numeric_columns = self.data.select_dtypes(include=['number']).columns.tolist()
311
+
312
+ if len(numeric_columns) < 2:
313
+ st.warning("يجب أن يكون هناك عمودان رقميان على الأقل لإنشاء مخطط تشتت")
314
+ return
315
+
316
+ x_column = st.selectbox("اختر عمود المحور الأفقي (x):", numeric_columns, key="scatter_x")
317
+ y_column = st.selectbox("اختر عمود المحور الرأسي (y):", numeric_columns, key="scatter_y")
318
+
319
+ # خيارات إضافية
320
+ color_column = st.selectbox("اختر عمود اللون (اختياري):",
321
+ ["لا يوجد"] + self.data.columns.tolist(),
322
+ key="scatter_color")
323
+
324
+ size_column = st.selectbox("اختر عمود الحجم (اختياري):",
325
+ ["لا يوجد"] + numeric_columns,
326
+ key="scatter_size")
327
+
328
+ # إنشاء الرسم البياني
329
+ if color_column == "لا يوجد" and size_column == "لا يوجد":
330
+ fig = px.scatter(self.data, x=x_column, y=y_column,
331
+ title=f"{y_column} مقابل {x_column}")
332
+ elif color_column != "لا يوجد" and size_column == "لا يوجد":
333
+ fig = px.scatter(self.data, x=x_column, y=y_column, color=color_column,
334
+ title=f"{y_column} مقابل {x_column} (مصنف حسب {color_column})")
335
+ elif color_column == "لا يوجد" and size_column != "لا يوجد":
336
+ fig = px.scatter(self.data, x=x_column, y=y_column, size=size_column,
337
+ title=f"{y_column} مقابل {x_column} (الحجم حسب {size_column})")
338
+ else:
339
+ fig = px.scatter(self.data, x=x_column, y=y_column, color=color_column, size=size_column,
340
+ title=f"{y_column} مقابل {x_column} (مصنف حسب {color_column}, الحجم حسب {size_column})")
341
+
342
+ fig.update_layout(xaxis_title=x_column, yaxis_title=y_column)
343
+ st.plotly_chart(fig, use_container_width=True)
344
+
345
+ elif chart_type == "مخطط صندوقي":
346
+ st.subheader("مخطط صندوقي")
347
+
348
+ numeric_columns = self.data.select_dtypes(include=['number']).columns.tolist()
349
+
350
+ if not numeric_columns:
351
+ st.warning("يجب أن يكون هناك عمود رقمي واحد على الأقل لإنشاء مخطط صندوقي")
352
+ return
353
+
354
+ y_column = st.selectbox("اختر عمود القيمة:", numeric_columns, key="box_y")
355
+
356
+ # خيارات إضافية
357
+ x_column = st.selectbox("اختر عمود التصنيف (اختياري):",
358
+ ["لا يوجد"] + self.data.columns.tolist(),
359
+ key="box_x")
360
+
361
+ # إنشاء الرسم البياني
362
+ if x_column == "لا يوجد":
363
+ fig = px.box(self.data, y=y_column, title=f"مخطط صندوقي لـ {y_column}")
364
+ else:
365
+ fig = px.box(self.data, x=x_column, y=y_column,
366
+ title=f"مخطط صندوقي لـ {y_column} حسب {x_column}")
367
+
368
+ st.plotly_chart(fig, use_container_width=True)
369
+
370
+ elif chart_type == "مخطط حراري":
371
+ st.subheader("مخطط حراري")
372
+
373
+ numeric_columns = self.data.select_dtypes(include=['number']).columns.tolist()
374
+
375
+ if len(numeric_columns) < 2:
376
+ st.warning("يجب أن يكون هناك عمودان رقميان على الأقل لإنشاء مخطط حراري")
377
+ return
378
+
379
+ # اختيار الأعمدة للمخطط الحراري
380
+ selected_columns = st.multiselect("اختر الأعمدة للمخطط الحراري:",
381
+ numeric_columns,
382
+ default=numeric_columns[:5] if len(numeric_columns) > 5 else numeric_columns)
383
+
384
+ if selected_columns:
385
+ # حساب مصفوفة الارتباط
386
+ correlation_matrix = self.data[selected_columns].corr()
387
+
388
+ # إنشاء الرسم البياني
389
+ fig = px.imshow(correlation_matrix, text_auto=True, aspect="auto",
390
+ title="مخطط حراري لمصفوفة الارتباط")
391
+ st.plotly_chart(fig, use_container_width=True)
392
  else:
393
+ st.warning("الرجاء اختيار عمود واحد على الأقل")
394
 
395
+ def _reports_tab(self):
396
+ """علامة تبويب التقارير"""
397
  st.header("التقارير")
398
 
399
+ if self.data is None:
400
+ st.info("الرجاء تحميل البيانات أولاً من علامة تبويب 'تحميل البيانات'")
401
  return
402
 
403
+ st.subheader("إنشاء تقرير")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
404
 
405
+ # خيارات التقرير
406
+ report_type = st.selectbox(
407
+ "اختر نوع التقرير:",
408
+ ["تقرير ملخص", "تقرير تحليلي", "تقرير مقارنة"]
409
+ )
 
 
 
410
 
411
+ if report_type == "تقرير ملخص":
412
+ st.write("محتوى التقرير:")
413
+
414
+ # إنشاء ملخص للبيانات
415
+ st.write("### ملخص البيانات")
416
+ st.write(f"عدد الصفوف: {self.data.shape[0]}")
417
+ st.write(f"عدد الأعمدة: {self.data.shape[1]}")
418
+
419
+ # إحصاءات وصفية
420
+ st.write("### إحصاءات وصفية")
421
+ st.dataframe(self.data.describe())
422
+
423
+ # معلومات عن القيم المفقودة
424
+ st.write("### ا��قيم المفقودة")
425
+ missing_data = pd.DataFrame({
426
+ 'العمود': self.data.columns,
427
+ 'عدد القيم المفقودة': self.data.isna().sum().values,
428
+ 'نسبة القيم المفقودة (%)': (self.data.isna().sum().values / len(self.data) * 100).round(2)
429
+ })
430
+ st.dataframe(missing_data)
431
+
432
+ # توزيع البيانات الرقمية
433
+ st.write("### توزيع البيانات الرقمية")
434
+ numeric_columns = self.data.select_dtypes(include=['number']).columns.tolist()
435
+
436
+ if numeric_columns:
437
+ for i in range(0, len(numeric_columns), 2):
438
+ cols = st.columns(2)
439
+ for j in range(2):
440
+ if i + j < len(numeric_columns):
441
+ col = numeric_columns[i + j]
442
+ with cols[j]:
443
+ fig = px.histogram(self.data, x=col, title=f"توزيع {col}")
444
+ st.plotly_chart(fig, use_container_width=True)
445
+
446
+ # خيارات تصدير التقرير
447
+ st.subheader("تصدير التقرير")
448
+ export_format = st.radio("اختر صيغة التصدير:", ["PDF", "Excel", "HTML"])
449
+
450
+ if st.button("تصدير التقرير"):
451
+ st.success(f"تم تصدير التقرير بصيغة {export_format} بنجاح!")
452
+
453
+ elif report_type == "تقرير تحليلي":
454
+ st.info("هذه الميزة قيد التطوير")
455
+
456
+ elif report_type == "تقرير مقارنة":
457
+ st.info("هذه الميزة قيد التطوير")
458
+
459
+ def _create_sample_data(self):
460
+ """إنشاء بيانات نموذجية للمناقصات"""
461
+ # إنشاء تواريخ عشوائية
462
+ start_date = datetime(2023, 1, 1)
463
+ end_date = datetime(2025, 3, 31)
464
+ days = (end_date - start_date).days
465
+
466
+ # إنشاء بيانات نموذجية
467
+ data = {
468
+ 'رقم المناقصة': [f'T-{i:04d}' for i in range(1, 101)],
469
+ 'اسم المشروع': [f'مشروع {i}' for i in range(1, 101)],
470
+ 'نوع المشروع': np.random.choice(['بناء', 'صيانة', 'تطوير', 'توريد', 'خدمات'], 100),
471
+ 'الموقع': np.random.choice(['الرياض', 'جدة', 'الدمام', 'مكة', 'المدينة', 'تبوك', 'أبها'], 100),
472
+ 'تاريخ الإعلان': [start_date + pd.Timedelta(days=np.random.randint(0, days)) for _ in range(100)],
473
+ 'تاريخ الإغلاق': [start_date + pd.Timedelta(days=np.random.randint(30, days)) for _ in range(100)],
474
+ 'الميزانية التقديرية': np.random.uniform(1000000, 50000000, 100),
475
+ 'عدد المتقدمين': np.random.randint(1, 20, 100),
476
+ 'سعر العرض': np.random.uniform(900000, 55000000, 100),
477
+ 'نسبة الفوز (%)': np.random.uniform(0, 100, 100),
478
+ 'مدة التنفيذ (أشهر)': np.random.randint(3, 36, 100),
479
+ 'عدد العمال': np.random.randint(10, 500, 100),
480
+ 'تكلفة المواد': np.random.uniform(500000, 30000000, 100),
481
+ 'تكلفة العمالة': np.random.uniform(200000, 15000000, 100),
482
+ 'تكلفة المعدات': np.random.uniform(100000, 10000000, 100),
483
+ 'هامش الربح (%)': np.random.uniform(5, 25, 100),
484
+ 'درجة المخاطرة': np.random.choice(['منخفضة', 'متوسطة', 'عالية'], 100),
485
+ 'حالة المناقصة': np.random.choice(['جارية', 'مغلقة', 'ملغاة', 'فائزة', 'خاسرة'], 100)
486
  }
487
 
488
+ # إنشاء DataFrame
489
+ df = pd.DataFrame(data)
490
+
491
+ # إضافة بعض العلاقات المنطقية
492
+ df['إجمالي التكلفة'] = df['تكلفة المواد'] + df['تكلفة العمالة'] + df['تكلفة المعدات']
493
+ df['الربح المتوقع'] = df['سعر العرض'] - df['إجمالي التكلفة']
494
+ df['نسبة التكلفة من العرض (%)'] = (df['إجمالي التكلفة'] / df['سعر العرض'] * 100).round(2)
495
+
496
+ return df
497
+
498
+ class UIEnhancer: #Dummy Class
499
+ def __init__(self, page_title, page_icon):
500
+ st.set_page_config(page_title=page_title, page_icon=page_icon, layout="wide", initial_sidebar_state="expanded")
501
+
502
+ def apply_theme_colors(self):
503
+ pass # Dummy function
504
 
 
 
505
 
506
+ if __name__ == "__main__":
507
+ app = DataAnalysisApp()
508
+ app.run()