Lashtw commited on
Commit
e1707ed
·
verified ·
1 Parent(s): c685636

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +81 -120
app.py CHANGED
@@ -1,142 +1,103 @@
1
- import plotly.express as px
2
- from plotly import graph_objects as go
3
- import pandas as pd
4
  import streamlit as st
5
- from io import BytesIO
6
- from zipfile import ZipFile
7
  import os
8
-
9
- CHINESE_FONT = "SimHei"
10
 
11
  def setup_chinese_font():
12
- """设置中文字体,确保图表中的中文显示正常"""
13
- px.defaults.template = "plotly_white"
14
- go.FigureWidget._default_font = {"family": CHINESE_FONT}
15
-
16
- def create_radar_chart(df, students, subjects):
17
- """
18
- 创建雷达图
19
-
20
- Args:
21
- df: 数据框
22
- students: 学生列表
23
- subjects: 科目列表
24
-
25
- Returns:
26
- plotly figure 对象
27
- """
28
- # 筛选指定学生和科目
29
- filtered_df = df[df['姓名'].isin(students)]
30
- filtered_df = filtered_df[subjects]
31
-
32
- # 计算每个学生的平均分
33
- avg_scores = filtered_df.mean()
34
-
35
- fig = go.Figure()
36
- for student in students:
37
- student_data = df[df['姓名'] == student][subjects]
38
- if not student_data.empty:
39
- scores = student_data.values.flatten().tolist()
40
- fig.add_trace(go.Scatterpolar(
41
- r=scores,
42
- theta=subjects,
43
- name=f"{student} ({avg_scores[student]:.1f})",
44
- mode='lines+markers'
45
- ))
46
-
47
- # 设置图表样式
48
- fig.update_layout(
49
- polar=dict(
50
- radialaxis=dict(range=[0, 100], showline=True),
51
- angularaxis=dict(direction="clockwise")
52
- ),
53
- title=f"学生成绩对比 - {', '.join(students)}",
54
- font=dict(family=CHINESE_FONT)
55
- )
56
-
57
  return fig
58
 
59
  def generate_radar_image(fig):
60
- """
61
- 生成图表的图像数据
62
-
63
- Args:
64
- fig: plotly figure 对象
65
-
66
- Returns:
67
- 图像数据 bytes
68
- """
69
- try:
70
- # 使用 kaleido 转换为 PNG 格式
71
- img_bytes = fig.to_image(format="png", engine="kaleido")
72
- return img_bytes
73
- except Exception as e:
74
- st.error(f"生成图表时出错: {str(e)}")
75
- return None
76
 
77
  def main():
78
- # 设置页面标题和宽屏模式
79
- st.set_page_config(page_title="学生成绩分析工具", layout="wide")
 
 
 
 
 
 
 
80
 
81
- # 设置中文字体
82
- setup_chinese_font()
 
 
 
 
 
 
 
 
 
83
 
84
- # 上传数据
85
- with st.expander("上传成绩数据"):
86
- uploaded_file = st.file_uploader("上传Excel文件", type=['xlsx', 'xls'])
87
- if uploaded_file is not None:
88
- try:
89
- df = pd.read_excel(uploaded_file)
90
- st.success("数据上传成功!")
91
- except Exception as e:
92
- st.error(f"数据读取失败: {str(e)}")
93
  return
94
- else:
95
- st.info("请上传包含学生成绩的Excel文件。")
96
- return
97
 
98
- # 选择学生和科目
99
- with st.expander("选择学生和科目"):
100
- students = df['姓名'].unique().tolist()
101
- selected_students = st.multiselect("选择学生", students)
102
- subjects = [col for col in df.columns if col != '姓名']
103
- selected_subjects = st.multiselect("选择科目", subjects, default=subjects)
104
 
105
- # 生成图表
106
- with st.expander("生成雷达图"):
107
- if not selected_students or not selected_subjects:
108
- st.warning("请选择至少一个学生和一个科目。")
109
- return
 
 
110
 
111
- fig = create_radar_chart(df, selected_students, selected_subjects)
112
- st.plotly_chart(fig)
 
 
113
 
114
- # 批量下载功能
115
- with st.expander("批量下载图表"):
116
- if st.button("生成并下载所有图表"):
117
- # 创建临时文件夹存储图表
118
- temp_dir = "temp_charts"
119
- os.makedirs(temp_dir, exist_ok=True)
120
 
121
- # 为每个学生生成图表并保存
122
- zip_buffer = BytesIO()
123
- with ZipFile(zip_buffer, mode="w") as zf:
124
- for student in selected_students:
125
- fig_student = create_radar_chart(df, [student], selected_subjects)
126
- img_bytes = generate_radar_image(fig_student)
127
- if img_bytes is not None:
128
- file_path = os.path.join(temp_dir, f"{student}.png")
129
- with open(file_path, "wb") as f:
130
- f.write(img_bytes)
131
- zf.write(file_path, os.path.basename(file_path))
132
-
133
- # 下载Zip文件
134
  st.download_button(
135
- label="下载所有图表",
136
- data=zip_buffer.getvalue(),
137
- file_name="student_charts.zip",
138
- mime="application/zip"
139
  )
140
 
 
 
 
141
  if __name__ == "__main__":
142
  main()
 
 
 
 
1
  import streamlit as st
2
+ import pandas as pd
3
+ import plotly.express as px
4
  import os
5
+ from io import BytesIO
6
+ import zipfile
7
 
8
  def setup_chinese_font():
9
+ import plotly.io as pio
10
+ pio.templates["custom_radar"] = {
11
+ "layout": {
12
+ "font": {"family": "SimSun-ExtB", "size": 12},
13
+ "title_x": 0.5,
14
+ "title_y": 0.98,
15
+ "title_font_size": 14,
16
+ "polar": {
17
+ "radialaxis": {"tickfont": {"family": "SimSun-ExtB"}},
18
+ "angularaxis": {"tickfont": {"family": "SimSun-ExtB"}}
19
+ }
20
+ }
21
+ }
22
+
23
+ def create_radar_chart(student_data, avg_score, student_id):
24
+ fig = px.line_polar(r=student_data, theta=student_data.index,
25
+ name=f"學生 {student_id}", line_close=True)
26
+ fig.add_scatterpolar(r=[avg_score]*len(student_data),
27
+ theta=student_data.index,
28
+ name="平均分數", fill=None)
29
+ fig.update_traces(fill='toself')
30
+ fig.update_layout(title_text=f"學生 {student_id} 的成績分析",
31
+ title_x=0.5, showlegend=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  return fig
33
 
34
  def generate_radar_image(fig):
35
+ img_bytes = fig.to_image(format="png", width=800, height=600)
36
+ return img_bytes
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
  def main():
39
+ st.title("學生成绩分析工具")
40
+ st.write("上傳你的成績文件,我們將為你生成詳細的分析報告。")
41
+
42
+ # 文件上传组件
43
+ uploaded_file = st.file_uploader(
44
+ "請選擇要上傳的文件",
45
+ accept_multiple_files=False,
46
+ key="fileUploader"
47
+ )
48
 
49
+ if uploaded_file is not None:
50
+ try:
51
+ # 判断文件类型
52
+ file_ext = os.path.splitext(uploaded_file.name)[1].lower()
53
+ if file_ext == ".csv":
54
+ df = pd.read_csv(uploaded_file)
55
+ elif file_ext in [".xlsx", ".xls"]:
56
+ df = pd.read_excel(uploaded_file, engine='openpyxl')
57
+ else:
58
+ st.error("不支援的文件格式,請上傳 CSV 或 Excel 文件。")
59
+ return
60
 
61
+ # 检查數據格式
62
+ if '學生ID' not in df.columns or '科目' not in df.columns:
63
+ st.error("文件格式不符合要求,請確保包含 '學生ID' '科目' 這兩列。")
 
 
 
 
 
 
64
  return
 
 
 
65
 
66
+ # 獲取獨特的學生ID和科目列表
67
+ student_ids = df['學生ID'].unique()
68
+ subjects = df['科目'].unique()
 
 
 
69
 
70
+ # 用戶選擇
71
+ st.write("### 選擇分析條件")
72
+ selected_student = st.selectbox(
73
+ "請選擇要分析的学生",
74
+ options=student_ids,
75
+ key="selectedStudent"
76
+ )
77
 
78
+ # 確定選定學生的數據
79
+ filtered_data = df[df['學生ID'] == selected_student]
80
+ student_subjects = filtered_data['科目']
81
+ scores = filtered_data['分數']
82
 
83
+ # 計算平均分
84
+ avg_score = scores.mean()
 
 
 
 
85
 
86
+ # 生成雷達圖
87
+ fig = create_radar_chart(scores, avg_score, selected_student)
88
+ st.plotly_chart(fig)
89
+
90
+ # 提供下載按鈕
91
+ img_bytes = generate_radar_image(fig)
 
 
 
 
 
 
 
92
  st.download_button(
93
+ "下載圖片",
94
+ data=img_bytes,
95
+ file_name=f"student_{selected_student}_analysis.png",
96
+ mime="image/png"
97
  )
98
 
99
+ except Exception as e:
100
+ st.error(f"發生錯誤:{e}")
101
+
102
  if __name__ == "__main__":
103
  main()