Spaces:
Sleeping
Sleeping
File size: 6,769 Bytes
a3ea68d 6b02d8e 9a50a9b 1d8b3fd 2bdddb6 9d19b33 2621100 9c8f863 769e16f 4c38bfa a3ea68d 769e16f b2daf89 769e16f 90c83e1 769e16f 90c83e1 a3ea68d 90c83e1 4c38bfa 90c83e1 b2daf89 90c83e1 b2daf89 769e16f 162af05 90c83e1 6d70640 c3aaf5d 90c83e1 17039ec b2daf89 6d70640 21b1ca2 6b02d8e 17039ec 21b1ca2 90c83e1 b2daf89 a4df621 21b1ca2 a4df621 11e4acb 2bdddb6 9d19b33 6b3fa2a 4c38bfa 21b1ca2 7fbc8d7 aebff32 a3ea68d b2daf89 a3ea68d b2daf89 a3ea68d b2daf89 a3ea68d 90c83e1 b2daf89 90c83e1 b2daf89 90c83e1 b2daf89 90c83e1 1ece60c 90c83e1 1ece60c 8d8bfbb 7fbc8d7 8d8bfbb 5250411 8d8bfbb a3ea68d 17039ec 1674d31 |
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 |
import streamlit as st
import pandas as pd
import plotly.graph_objects as go
import os
from zipfile import ZipFile
import plotly.io as pio
from PIL import Image, ImageDraw
from io import BytesIO
import numpy as np
import os
import shutil
try:
import kaleido
except ImportError:
st.error("請安裝 'kaleido' 套件以啟用圖像導出功能:\n\n $ pip install kaleido")
def load_data(uploaded_file):
"""載入並處理CSV檔案"""
try:
# 直接載入檔案
df = pd.read_csv(uploaded_file, encoding='utf-8')
# 移除空白列
df = df.dropna(how='all')
# 將數值欄位轉換為數字類型
numeric_columns = ['平均', '總分', '國文', '英文', '數學', '自科', '社會', '地理', '歷史', '公民']
for col in numeric_columns:
df[col] = pd.to_numeric(df[col], errors='coerce')
return df
except Exception as e:
st.error(f"載入檔案時發生錯誤:{e}")
return None
def create_radar_chart(df, selected_rows, selected_columns):
"""使用Plotly建立雷達圖"""
line_styles = ['solid', 'dot', 'dash', 'longdash', 'dashdot']
colors = ['#1F77B4', '#FF7F0E', '#2CA02C', '#D62728', '#9467BD']
fig = go.Figure()
for i, row_name in enumerate(selected_rows):
row_data = df[df['姓名'] == row_name][selected_columns].iloc[0]
fig.add_trace(go.Scatterpolar(
r=row_data.values,
theta=selected_columns,
fill='toself',
name=row_name,
line=dict(
color=colors[i % len(colors)],
dash=line_styles[i % len(line_styles)],
width=2
),
marker=dict(opacity=0.5)
))
if selected_columns:
max_value = df[selected_columns].values.max() * 1.1
else:
max_value = 100
fig.update_layout(
polar=dict(
radialaxis=dict(
visible=True,
range=[0, max_value],
tickfont=dict(size=12, color='black', family="Microsoft JhengHei, Noto Sans CJK, Arial")
),
angularaxis=dict(
tickfont=dict(size=16, color='black', family="Microsoft JhengHei, Noto Sans CJK, Arial")
)
),
showlegend=True,
legend=dict(
font=dict(size=14, color='black', family="Microsoft JhengHei, Noto Sans CJK, Arial")
),
title='學生成績雷達圖',
plot_bgcolor='white',
paper_bgcolor='white',
font=dict(family="Microsoft JhengHei, Noto Sans CJK, Arial")
)
return fig
def apply_font_to_all_text(fig):
"""強制設定圖表內所有文字元素的字型"""
for trace in fig.data:
if hasattr(trace, 'textfont'):
trace.textfont.family = "Microsoft JhengHei, Noto Sans CJK, Arial"
if hasattr(trace, 'marker') and hasattr(trace.marker, 'textfont'):
trace.marker.textfont.family = "Microsoft JhengHei, Noto Sans CJK, Arial"
fig.update_layout(
font=dict(
family="Microsoft JhengHei, Noto Sans CJK, Arial"
)
)
if hasattr(fig, 'layout') and hasattr(fig.layout, 'xaxis'):
fig.layout.xaxis.tickfont.family = "Microsoft JhengHei, Noto Sans CJK, Arial"
if hasattr(fig, 'layout') and hasattr(fig.layout, 'yaxis'):
fig.layout.yaxis.tickfont.family = "Microsoft JhengHei, Noto Sans CJK, Arial"
if hasattr(fig, 'layout') and hasattr(fig.layout, 'polar') and hasattr(fig.layout.polar, 'radialaxis'):
fig.layout.polar.radialaxis.tickfont.family = "Microsoft JhengHei, Noto Sans CJK, Arial"
if hasattr(fig, 'layout') and hasattr(fig.layout, 'polar') and hasattr(fig.layout.polar, 'angularaxis'):
fig.layout.polar.angularaxis.tickfont.family = "Microsoft JhengHei, Noto Sans CJK, Arial"
return fig
def save_radar_chart_image(fig):
"""使用 kaleido 輸出 png 的記憶體檔案"""
img_bytes = pio.to_image(fig, format="png", engine="kaleido")
return img_bytes
def create_composite_image(fig, student):
"""使用 PIL 合成圖片,確保學生的成績在最上層"""
img_bytes = pio.to_image(fig, format="png", engine="kaleido")
img = Image.open(BytesIO(img_bytes)).convert("RGBA")
background = Image.new('RGBA', img.size, (255, 255, 255, 255))
composite = Image.alpha_composite(background, img)
return composite
def main():
st.title('學生成績雷達圖產生器')
uploaded_file = st.file_uploader("上傳CSV檔案", type=['csv'])
if uploaded_file is not None:
df = load_data(uploaded_file)
if df is not None:
numeric_columns = ['平均', '總分', '國文', '英文', '數學', '自科', '社會', '地理', '歷史', '公民']
st.write("### 選擇要比較的欄位")
selected_columns = [col for col in numeric_columns if st.checkbox(col, key=col)]
st.write("### 選擇要比較的對象")
selected_rows = st.multiselect('選擇要比較的對象', df['姓名'].tolist())
if selected_columns and selected_rows:
try:
fig = create_radar_chart(df, selected_rows, selected_columns)
st.plotly_chart(fig, use_container_width=True)
except Exception as e:
st.error(f"生成雷達圖時發生錯誤:{e}")
st.write("### 批次繪製個別學生比較圖")
individual_students = st.multiselect("選擇要個別比較的學生", df['姓名'].tolist(), key = "student")
comparison_items = st.multiselect("選擇要比較的項目", df['姓名'].tolist(), key = "item")
if individual_students and comparison_items:
image_options = []
image_bytes = {}
for student in individual_students:
fig = create_radar_chart(df, [student] + comparison_items, selected_columns)
image_bytes[student] = create_composite_image(fig,student)
image_options.append(f"{student} 與 {', '.join(comparison_items)} 的比較")
selected_image_options = st.multiselect("選擇要顯示的圖片", options=image_options)
cols = st.columns(3) # 排成三列
for i, option in enumerate(selected_image_options):
student = option.split(" 與 ")[0]
with cols[i%3]:
st.image(image_bytes[student], use_container_width=True)
st.text(option)
if __name__ == "__main__":
import numpy as np
main() |