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()