AMKAPP / app4.py
AMKhakbaz's picture
Rename app.py to app4.py
dc9de67 verified
raw
history blame
28.2 kB
import pandas as pd
import numpy as np
import plotly.graph_objs as go
from plotly.subplots import make_subplots
import streamlit as st
from scipy.stats import norm
def is_matching_pattern(column, prefix):
"""
بررسی می‌کند که آیا نام ستون با الگوی prefix-number (1 تا 3 رقم) مطابقت دارد یا خیر.
پارامترها:
- column: نام ستون برای بررسی.
- prefix: پیشوندی که باید قبل از خط فاصله باشد.
بازگشت:
- True اگر مطابقت دارد، در غیر این صورت False.
"""
if not str(column).startswith(prefix + '_'):
return False
suffix = column[len(prefix) + 1:]
if 1 <= len(suffix) <= 3 and suffix.isdigit():
return True
return False
def analyze_single_column(df, column_name, type_option):
"""
تابع 1: تحلیل یک ستون بر اساس گزینه انتخاب شده.
پارامترها:
- df: DataFrame پانداس.
- column_name: نام ستون یا پیشوند.
- type_option: 1 یا 2.
بازگشت:
- table: DataFrame شامل فراوانی‌ها و درصدها.
- fig: شیء Figure از Plotly.
"""
total_rows = len(df)
if type_option == 1:
if column_name not in df.columns:
st.error(f"❌ ستون '{column_name}' در فایل Excel یافت نشد.")
return None, None
# محاسبه فراوانی
frequency = df[column_name].value_counts(dropna=False).sort_index()
percentage = (frequency / total_rows) * 100
table = pd.DataFrame({
column_name: frequency.index,
'Frequency': frequency.values,
'Percentage': percentage.values
})
st.subheader("📊 جدول فراوانی و درصد:")
st.dataframe(table)
# ترسیم هیستوگرام تعاملی
fig = go.Figure(data=[go.Bar(
x=table[column_name].astype(str),
y=table['Percentage'],
marker=dict(color='rgba(173, 216, 230, 0.6)',
line=dict(color='rgba(173, 216, 230, 1.0)', width=1)),
width=0.4
)])
fig.update_layout(
title=f"📈 هیستوگرام '{column_name}'",
xaxis_title=column_name,
yaxis_title='Percentage',
bargap=0.2,
template='plotly_white'
)
st.plotly_chart(fig, use_container_width=True)
return table, fig
elif type_option == 2:
# انتخاب ستون‌ها مطابق الگوی name-number
selected_columns = [col for col in df.columns if is_matching_pattern(col, column_name)]
if not selected_columns:
st.error(f"❌ هیچ ستونی مطابق الگوی '{column_name}-<number>' در فایل Excel یافت نشد.")
return None, None
results_freq = {}
results_pct = {}
for col in selected_columns:
unique_values = df[col].dropna().unique()
if len(unique_values) > 2:
st.warning(f"⚠️ ستون '{col}' بیش از دو مقدار یکتا دارد و نادیده گرفته شد.")
continue
value_counts = df[col].value_counts(dropna=False)
results_freq[col] = sum(list(value_counts))
if not results_freq:
st.error("❌ هیچ ستون معتبری با دقیقاً دو مقدار یکتا یافت نشد.")
return None, None
# ایجاد جدول فراوانی
freq_table = pd.DataFrame(results_freq).fillna(0).astype(int)
freq_table.insert(0, column_name, freq_table.index)
st.subheader("📊 جدول فراوانی:")
st.dataframe(freq_table)
# محاسبه درصدها
# برای هر ستون، درصدها بر اساس تعداد سطرهای فیلتر شده محاسبه می‌شوند
pct_table = freq_table.copy()
for col in selected_columns:
pct_table[col] = pct_table[col] / freq_table[col].sum() * 100
st.subheader("📊 جدول درصدها:")
st.dataframe(pct_table)
# ترسیم هیستوگرام تعاملی برای درصدها
fig = go.Figure()
for col in selected_columns:
fig.add_trace(go.Bar(
x=pct_table[column_name].astype(str),
y=pct_table[col],
name=col,
width=0.4
))
fig.update_layout(
title=f"📈 هیستوگرام درصدها برای ستون‌های '{column_name}-<number>'",
xaxis_title=column_name,
yaxis_title='Percentage',
barmode='group',
bargap=0.2,
template='plotly_white'
)
st.plotly_chart(fig, use_container_width=True)
return freq_table, pct_table, fig
else:
st.error("❌ گزینه 'Type Option' باید 1 یا 2 باشد.")
return None, None
def read_excel_sheets(file):
"""خواندن یک فایل Excel با چندین شیت و بازگشت دیکشنری از DataFrame‌ها."""
try:
xls = pd.ExcelFile(file)
sheets_data = {sheet: xls.parse(sheet) for sheet in xls.sheet_names}
return sheets_data
except Exception as e:
st.error(f"❌ خطا در خواندن فایل Excel: {e}")
return None
def z_testes(n1, n2, p1, p2):
"""انجام Z-test برای نسبت‌ها و بازگشت p-value."""
try:
pooled_p = (n1 * p1 + n2 * p2) / (n1 + n2)
se = np.sqrt(pooled_p * (1 - pooled_p) * (1 / n1 + 1 / n2))
z = (p1 - p2) / se
p_value = 2 * (1 - norm.cdf(abs(z)))
return p_value
except ZeroDivisionError:
return np.nan
def Z_test_dataframes(sheets_data):
"""پردازش DataFrame هر شیت و محاسبه DataFrame‌های جدید با نتایج Z-test."""
result_dataframes = {}
for sheet_name, df in sheets_data.items():
if df.empty:
st.warning(f"⚠️ شیت '{sheet_name}' خالی است. نادیده گرفته شد.")
continue
df = df.set_index(df.columns[0]) # استفاده از اولین ستون به عنوان ایندکس
rows, cols = df.shape
if cols < 2:
st.warning(f"⚠️ شیت '{sheet_name}' ستون‌های کافی برای تحلیل ندارد. نادیده گرفته شد.")
continue
new_df = pd.DataFrame(index=df.index[:-1], columns=df.columns[1:])
for i, row_name in enumerate(df.index[:-1]):
for j, col_name in enumerate(df.columns[1:]):
try:
n1 = df.iloc[-1, 0] # x_I1
n2 = df.iloc[-1, j+1] # x_Ij
p1 = df.iloc[i, 0] # x_1J
p2 = df.iloc[i, j+1] # x_ij
p_value = z_testes(n1, n2, p1, p2)
new_df.iloc[i, j] = p_value
except Exception as e:
st.error(f"❌ خطا در پردازش شیت '{sheet_name}', ردیف '{row_name}', ستون '{col_name}': {e}")
new_df.iloc[i, j] = np.nan
result_dataframes[sheet_name] = new_df
return result_dataframes
def analyze_z_test(file):
"""
تابع 3: انجام تحلیل Z-Test بر روی فایل Excel بارگذاری شده.
پارامترها:
- file: فایل Excel بارگذاری شده
بازگشت:
- result_dataframes: دیکشنری از DataFrame‌ها با p-value
"""
sheets_data = read_excel_sheets(file)
if sheets_data is None:
return None
result_dataframes = Z_test_dataframes(sheets_data)
if not result_dataframes:
st.error("❌ هیچ شیت معتبری برای تحلیل Z-Test یافت نشد.")
return None
st.write("### 📈 جداول پردازش شده با نتایج Z-Test")
for sheet_name, df in result_dataframes.items():
st.write(f"#### شیت: {sheet_name}")
# اعمال رنگ‌بندی بر اساس p-value
def color_p_value(val):
try:
if pd.isna(val):
return 'background-color: lightgray'
elif val < 0.05:
return 'background-color: lightgreen'
else:
return 'background-color: lightcoral'
except:
return 'background-color: lightgray'
styled_df = df.style.applymap(color_p_value)
# نمایش DataFrame رنگی
st.dataframe(styled_df, use_container_width=True)
return result_dataframes
def analyze_multiple_columns(df, first_name, second_name, first_type, second_type):
"""
تابع 2: تحلیل داده‌ها بر اساس ترکیب انواع اول و دوم.
پارامترها:
- df: DataFrame پانداس
- first_name: نام یا پیشوند ستون اول
- second_name: نام یا پیشوند ستون دوم
- first_type: 1 یا 2
- second_type: 1 یا 2
بازگشت:
- freq_table: DataFrame شامل فراوانی‌ها
- pct_table: DataFrame شامل درصدها
- fig: شیء Figure از Plotly
"""
total_rows = len(df)
# توابع کمکی برای انتخاب ستون‌ها بر اساس نوع
def select_columns_type1(name):
if name in df.columns:
return [name]
else:
st.error(f"❌ ستون '{name}' در فایل Excel یافت نشد.")
return []
def select_columns_type2(name):
selected = [col for col in df.columns if is_matching_pattern(col, name)]
if not selected:
st.error(f"❌ هیچ ستونی مطابق الگوی '{name}-<number>' در فایل Excel یافت نشد.")
return selected
# انتخاب ستون‌ها بر اساس نوع
first_columns = select_columns_type1(first_name) if first_type == 1 else select_columns_type2(first_name)
second_columns = select_columns_type1(second_name) if second_type == 1 else select_columns_type2(second_name)
if not first_columns or not second_columns:
st.error("❌ انتخاب ستون‌ها ناموفق بود.")
return None, None, None
figs = []
if first_type == 1 and second_type == 1:
# هر دو نوع 1
if len(first_columns) != 1 or len(second_columns) != 1:
st.error("❌ وقتی هر دو نوع 1 هستند، دقیقاً یک ستون برای هر کدام انتخاب کنید.")
return None, None, None
col1 = first_columns[0]
col2 = second_columns[0]
contingency = pd.crosstab(df[col1], df[col2])
freq_table = contingency.copy()
st.subheader("📊 جدول فراوانی مشترک:")
st.dataframe(freq_table)
# محاسبه درصدها
pct_table = contingency.div(contingency.sum().sum()) * 100
st.subheader("📊 جدول درصدها:")
st.dataframe(pct_table)
# ترسیم Heatmap
fig = go.Figure(data=go.Heatmap(
z=pct_table.values,
x=pct_table.columns,
y=pct_table.index,
colorscale='Blues',
colorbar=dict(title='Percentage')
))
fig.update_layout(
title=f"📈 Heatmap بین '{col1}' و '{col2}'",
xaxis_title=col2,
yaxis_title=col1,
template='plotly_white'
)
st.plotly_chart(fig, use_container_width=True)
figs.append(fig)
elif first_type == 1 and second_type == 2:
# نوع اول 1 و نوع دوم 2
if len(first_columns) != 1:
st.error("❌ وقتی نوع اول 1 است، دقیقاً یک ستون انتخاب کنید.")
return None, None, None
col1 = first_columns[0]
col2_list = second_columns
unique_values_col1 = df[col1].dropna().unique()
freq_results = {}
pct_results = {}
for val in unique_values_col1:
filter_df = df[df[col1] == val]
freq_table = {}
pct_table = {}
for col2 in col2_list:
freq = filter_df[col2].value_counts(dropna=False)
freq_table[col2] = freq.to_dict()
pct = (freq / len(filter_df)) * 100
pct_table[col2] = pct.to_dict()
freq_results[val] = freq_table
pct_results[val] = pct_table
# ایجاد DataFrame‌های فراوانی
freq_df = pd.DataFrame(freq_results).T.fillna(0).astype(int)
freq_df.insert(0, col1, freq_df.index)
st.subheader("📊 جدول فراوانی بر اساس گروه:")
st.dataframe(freq_df)
# ایجاد DataFrame‌های درصد
pct_df = pd.DataFrame(pct_results).T.fillna(0).round(2)
pct_df.insert(0, col1, pct_df.index)
st.subheader("📊 جدول درصد بر اساس گروه:")
st.dataframe(pct_df)
# ترسیم نمودارهای بار برای درصدها
for val, data in pct_results.items():
st.subheader(f"📈 درصدها برای '{col1}' = {val}")
fig = make_subplots(rows=1, cols=len(col2_list), subplot_titles=col2_list)
for i, col2 in enumerate(col2_list, 1):
categories = list(data[col2].keys())
values = list(data[col2].values())
fig.add_trace(go.Bar(
x=categories,
y=values,
marker=dict(color='rgba(173, 216, 230, 0.6)',
line=dict(color='rgba(173, 216, 230, 1.0)', width=1)),
width=0.4
), row=1, col=i)
fig.update_xaxes(title_text=col2, row=1, col=i)
fig.update_yaxes(title_text='Percentage', row=1, col=i)
fig.update_layout(
title=f"📈 درصدها برای '{col1}' = {val}",
template='plotly_white',
showlegend=False
)
st.plotly_chart(fig, use_container_width=True)
figs.append(fig)
elif first_type == 2 and second_type == 1:
# نوع اول 2 و نوع دوم 1
if len(second_columns) != 1:
st.error("❌ وقتی نوع دوم 1 است، دقیقاً یک ستون انتخاب کنید.")
return None, None, None
col2 = second_columns[0]
col1_list = first_columns
unique_values_col2 = df[col2].dropna().unique()
freq_results = {}
pct_results = {}
for val in unique_values_col2:
filter_df = df[df[col2] == val]
freq_table = {}
pct_table = {}
for col1 in col1_list:
freq = filter_df[col1].value_counts(dropna=False)
freq_table[col1] = freq.to_dict()
pct = (freq / len(filter_df)) * 100
pct_table[col1] = pct.to_dict()
freq_results[val] = freq_table
pct_results[val] = pct_table
# ایجاد DataFrame‌های فراوانی
freq_df = pd.DataFrame(freq_results).T.fillna(0).astype(int)
freq_df.insert(0, col2, freq_df.index)
st.subheader("📊 جدول فراوانی بر اساس گروه:")
st.dataframe(freq_df)
# ایجاد DataFrame‌های درصد
pct_df = pd.DataFrame(pct_results).T.fillna(0).round(2)
pct_df.insert(0, col2, pct_df.index)
st.subheader("📊 جدول درصد بر اساس گروه:")
st.dataframe(pct_df)
# ترسیم نمودارهای بار برای درصدها
for val, data in pct_results.items():
st.subheader(f"📈 درصدها برای '{col2}' = {val}")
fig = make_subplots(rows=1, cols=len(col1_list), subplot_titles=col1_list)
for i, col1 in enumerate(col1_list, 1):
categories = list(data[col1].keys())
values = list(data[col1].values())
fig.add_trace(go.Bar(
x=categories,
y=values,
marker=dict(color='rgba(173, 216, 230, 0.6)',
line=dict(color='rgba(173, 216, 230, 1.0)', width=1)),
width=0.4
), row=1, col=i)
fig.update_xaxes(title_text=col1, row=1, col=i)
fig.update_yaxes(title_text='Percentage', row=1, col=i)
fig.update_layout(
title=f"📈 درصدها برای '{col2}' = {val}",
template='plotly_white',
showlegend=False
)
st.plotly_chart(fig, use_container_width=True)
figs.append(fig)
elif first_type == 2 and second_type == 2:
# هر دو نوع 2
col1_list = first_columns
col2_list = second_columns
freq_results = {}
pct_results = {}
for col2 in col2_list:
for val in df[col2].dropna().unique():
filter_df = df[df[col2] == val]
frequency = {}
percentage = {}
for col1 in col1_list:
count = filter_df[col1].count()
frequency[col1] = count
percentage[col1] = (count / len(filter_df)) * 100 if len(filter_df) > 0 else np.nan
freq_results[(col2, val)] = frequency
pct_results[(col2, val)] = percentage
if not freq_results:
st.error("❌ هیچ ترکیب معتبری برای هر دو نوع 2 یافت نشد.")
return None, None, None
# ایجاد DataFrame‌های فراوانی
freq_df = pd.DataFrame(list(freq_results.values()), index=pd.MultiIndex.from_tuples(freq_results.keys(), names=['Second Column', 'Second Column Value']))
st.subheader("📊 جدول فراوانی چندبعدی:")
st.dataframe(freq_df)
# ایجاد DataFrame‌های درصد
pct_df = pd.DataFrame(list(pct_results.values()), index=pd.MultiIndex.from_tuples(pct_results.keys(), names=['Second Column', 'Second Column Value']))
st.subheader("📊 جدول درصد چندبعدی:")
st.dataframe(pct_df.round(2))
# ترسیم نمودارهای بار برای درصدها
for (col2, val), data in pct_results.items():
st.subheader(f"📈 درصدها برای '{col2}' = {val}")
fig = go.Figure(data=[go.Bar(
x=list(data.keys()),
y=list(data.values()),
marker=dict(color='rgba(173, 216, 230, 0.6)',
line=dict(color='rgba(173, 216, 230, 1.0)', width=1)),
width=0.4
)])
fig.update_layout(
title=f"📈 درصدها برای '{col2}' = {val}",
xaxis_title=first_name,
yaxis_title='Percentage',
template='plotly_white'
)
st.plotly_chart(fig, use_container_width=True)
figs.append(fig)
else:
st.error("❌ 'First Type' و 'Second Type' باید هر کدام 1 یا 2 باشند.")
return None, None, None
return freq_table, pct_table, fig
def Z_test_dataframes(sheets_data):
"""پردازش DataFrame هر شیت و محاسبه DataFrame‌های جدید با نتایج Z-test."""
result_dataframes = {}
for sheet_name, df in sheets_data.items():
if df.empty:
st.warning(f"⚠️ شیت '{sheet_name}' خالی است. نادیده گرفته شد.")
continue
df = df.set_index(df.columns[0]) # استفاده از اولین ستون به عنوان ایندکس
rows, cols = df.shape
if cols < 2:
st.warning(f"⚠️ شیت '{sheet_name}' ستون‌های کافی برای تحلیل ندارد. نادیده گرفته شد.")
continue
new_df = pd.DataFrame(index=df.index[:-1], columns=df.columns[1:])
for i, row_name in enumerate(df.index[:-1]):
for j, col_name in enumerate(df.columns[1:]):
try:
n1 = df.iloc[-1, 0] # x_I1
n2 = df.iloc[-1, j+1] # x_Ij
p1 = df.iloc[i, 0] # x_1J
p2 = df.iloc[i, j+1] # x_ij
p_value = z_testes(n1, n2, p1, p2)
new_df.iloc[i, j] = p_value
except Exception as e:
st.error(f"❌ خطا در پردازش شیت '{sheet_name}', ردیف '{row_name}', ستون '{col_name}': {e}")
new_df.iloc[i, j] = np.nan
result_dataframes[sheet_name] = new_df
return result_dataframes
def analyze_z_test(file):
"""
تابع 3: انجام تحلیل Z-Test بر روی فایل Excel بارگذاری شده.
پارامترها:
- file: فایل Excel بارگذاری شده
بازگشت:
- result_dataframes: دیکشنری از DataFrame‌ها با p-value
"""
sheets_data = read_excel_sheets(file)
if sheets_data is None:
return None
result_dataframes = Z_test_dataframes(sheets_data)
if not result_dataframes:
st.error("❌ هیچ شیت معتبری برای تحلیل Z-Test یافت نشد.")
return None
st.write("### 📈 جداول پردازش شده با نتایج Z-Test")
for sheet_name, df in result_dataframes.items():
st.write(f"#### شیت: {sheet_name}")
# اعمال رنگ‌بندی بر اساس p-value
def color_p_value(val):
try:
if pd.isna(val):
return 'background-color: lightgray'
elif val < 0.05:
return 'background-color: lightgreen'
else:
return 'background-color: lightcoral'
except:
return 'background-color: lightgray'
styled_df = df.style.applymap(color_p_value)
# نمایش DataFrame رنگی
st.dataframe(styled_df, use_container_width=True)
return result_dataframes
def read_excel_sheets(file):
"""خواندن یک فایل Excel با چندین شیت و بازگشت دیکشنری از DataFrame‌ها."""
try:
xls = pd.ExcelFile(file)
sheets_data = {sheet: xls.parse(sheet) for sheet in xls.sheet_names}
return sheets_data
except Exception as e:
st.error(f"❌ خطا در خواندن فایل Excel: {e}")
return None
def main():
st.title("📊 Data Analysis Application")
st.write("""
خوش آمدید به **Data Analysis Application**! این ابزار به شما امکان می‌دهد یک فایل Excel بارگذاری کنید و بر اساس انتخاب خود، تحلیل‌های مختلفی را انجام دهید.
""")
# File uploader
uploaded_file = st.file_uploader("📂 فایل Excel خود را بارگذاری کنید", type=["xlsx", "xls"])
if uploaded_file is not None:
try:
df = pd.read_excel(uploaded_file)
st.success("✅ فایل با موفقیت بارگذاری شد!")
st.subheader("🗂️ پیش‌نمایش داده‌های بارگذاری شده:")
st.dataframe(df.head()) # نمایش چند ردیف اول
except Exception as e:
st.error(f"❌ خطا در خواندن فایل Excel: {e}")
return
st.markdown("---")
# Function Selection
st.header("🔍 انتخاب تابع تحلیل")
analysis_function = st.radio("Choose Function", ("Analyze Single Column", "Analyze Multiple Columns", "Run Z-Test Analysis"))
if analysis_function == "Analyze Single Column":
st.header("📈 تحلیل یک ستون")
column_name = st.text_input("🔍 نام ستون یا پیشوند را وارد کنید:", "")
type_option = st.selectbox("📊 انتخاب گزینه نوع تحلیل", ("1", "2"))
if st.button("▶️ اجرای تحلیل"):
if column_name.strip() == "":
st.error("❗ لطفاً نام ستون معتبر وارد کنید.")
else:
type_option_int = int(type_option)
result = analyze_single_column(df, column_name, type_option_int)
if result is not None:
if type_option_int == 1:
table, fig = result
else:
freq_table, pct_table, fig = result
st.success("✅ تحلیل با موفقیت انجام شد!")
elif analysis_function == "Analyze Multiple Columns":
st.header("📊 تحلیل چندین ستون")
first_name = st.text_input("🔍 نام ستون اول یا پیشوند آن را وارد کنید:", "")
second_name = st.text_input("🔍 نام ستون دوم یا پیشوند آن را وارد کنید:", "")
first_type = st.selectbox("📊 انتخاب نوع تحلیل برای ستون اول", ("1", "2"), key='first_type')
second_type = st.selectbox("📊 انتخاب نوع تحلیل برای ستون دوم", ("1", "2"), key='second_type')
if st.button("▶️ اجرای تحلیل"):
if first_name.strip() == "" or second_name.strip() == "":
st.error("❗ لطفاً نام ستون‌های معتبر وارد کنید.")
else:
first_type_int = int(first_type)
second_type_int = int(second_type)
result = analyze_multiple_columns(df, first_name, second_name, first_type_int, second_type_int)
if result is not None:
if first_type_int ==1 and second_type_int ==1:
freq_table, pct_table, fig = result
else:
freq_table, pct_table, fig = result
st.success("✅ تحلیل با موفقیت انجام شد!")
elif analysis_function == "Run Z-Test Analysis":
st.header("📉 اجرای تحلیل Z-Test")
st.write("""
این تحلیل Z-Test برای نسبت‌ها را روی هر شیت از فایل Excel شما انجام می‌دهد.
اطمینان حاصل کنید که هر شیت به درستی ساختاربندی شده باشد:
- **ستون اول** باید ایندکس باشد (مثلاً دسته‌بندی‌ها).
- **آخرین ردیف** باید شامل تعداد کل‌ها (`n1`, `n2`, ...) باشد.
- **ردیف‌های قبلی** باید شامل تعداد‌ها (`p1`, `p2`, ...) باشند.
""")
if st.button("▶️ اجرای تحلیل Z-Test"):
result_dataframes = analyze_z_test(uploaded_file)
if result_dataframes is not None:
st.success("✅ تحلیل Z-Test با موفقیت انجام شد!")
else:
st.info("ℹ️ لطفاً یک فایل Excel بارگذاری کنید تا تحلیل شروع شود.")
if __name__ == "__main__":
main()