CoolFace
Apppublic

ChoCho66/radar_chart

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
app.py173 linesDownload Raw Back to root
1import streamlit as st2import pandas as pd3import plotly.graph_objects as go4import os5from zipfile import ZipFile6import plotly.io as pio7from PIL import Image, ImageDraw8from io import BytesIO9import numpy as np10import os11import shutil12 13try:14    import kaleido15except ImportError:16    st.error("請安裝 'kaleido' 套件以啟用圖像導出功能:\n\n    $ pip install kaleido")17 18def load_data(uploaded_file):19    """載入並處理CSV檔案"""20    try:21        # 直接載入檔案22        df = pd.read_csv(uploaded_file, encoding='utf-8')23 24        # 移除空白列25        df = df.dropna(how='all')26 27        # 將數值欄位轉換為數字類型28        numeric_columns = ['平均', '總分', '國文', '英文', '數學', '自科', '社會', '地理', '歷史', '公民']29        for col in numeric_columns:30            df[col] = pd.to_numeric(df[col], errors='coerce')31 32        return df33    except Exception as e:34        st.error(f"載入檔案時發生錯誤:{e}")35        return None36 37def create_radar_chart(df, selected_rows, selected_columns):38    """使用Plotly建立雷達圖"""39    line_styles = ['solid', 'dot', 'dash', 'longdash', 'dashdot']40    colors = ['#1F77B4', '#FF7F0E', '#2CA02C', '#D62728', '#9467BD']41 42    fig = go.Figure()43 44    for i, row_name in enumerate(selected_rows):45        row_data = df[df['姓名'] == row_name][selected_columns].iloc[0]46 47        fig.add_trace(go.Scatterpolar(48            r=row_data.values,49            theta=selected_columns,50            fill='toself',51            name=row_name,52            line=dict(53                color=colors[i % len(colors)],54                dash=line_styles[i % len(line_styles)],55                width=256            ),57            marker=dict(opacity=0.5)58        ))59    if selected_columns:60        max_value = df[selected_columns].values.max() * 1.161    else:62        max_value = 10063 64    fig.update_layout(65        polar=dict(66            radialaxis=dict(67                visible=True,68                range=[0, max_value],69                tickfont=dict(size=12, color='black', family="Microsoft JhengHei, Noto Sans CJK, Arial")70            ),71            angularaxis=dict(72                tickfont=dict(size=16, color='black', family="Microsoft JhengHei, Noto Sans CJK, Arial")73            )74        ),75        showlegend=True,76        legend=dict(77            font=dict(size=14, color='black', family="Microsoft JhengHei, Noto Sans CJK, Arial")78        ),79        title='學生成績雷達圖',80        plot_bgcolor='white',81        paper_bgcolor='white',82        font=dict(family="Microsoft JhengHei, Noto Sans CJK, Arial")83    )84 85    return fig86 87def apply_font_to_all_text(fig):88    """強制設定圖表內所有文字元素的字型"""89    for trace in fig.data:90        if hasattr(trace, 'textfont'):91            trace.textfont.family = "Microsoft JhengHei, Noto Sans CJK, Arial"92        if hasattr(trace, 'marker') and hasattr(trace.marker, 'textfont'):93            trace.marker.textfont.family = "Microsoft JhengHei, Noto Sans CJK, Arial"94    fig.update_layout(95        font=dict(96            family="Microsoft JhengHei, Noto Sans CJK, Arial"97        )98    )99 100    if hasattr(fig, 'layout') and hasattr(fig.layout, 'xaxis'):101        fig.layout.xaxis.tickfont.family = "Microsoft JhengHei, Noto Sans CJK, Arial"102    if hasattr(fig, 'layout') and hasattr(fig.layout, 'yaxis'):103        fig.layout.yaxis.tickfont.family = "Microsoft JhengHei, Noto Sans CJK, Arial"104    if hasattr(fig, 'layout') and hasattr(fig.layout, 'polar') and hasattr(fig.layout.polar, 'radialaxis'):105        fig.layout.polar.radialaxis.tickfont.family = "Microsoft JhengHei, Noto Sans CJK, Arial"106    if hasattr(fig, 'layout') and hasattr(fig.layout, 'polar') and hasattr(fig.layout.polar, 'angularaxis'):107        fig.layout.polar.angularaxis.tickfont.family = "Microsoft JhengHei, Noto Sans CJK, Arial"108 109    return fig110 111def save_radar_chart_image(fig):112  """使用 kaleido 輸出 png 的記憶體檔案"""113  img_bytes = pio.to_image(fig, format="png", engine="kaleido")114  return img_bytes115 116def create_composite_image(fig, student):117    """使用 PIL 合成圖片,確保學生的成績在最上層"""118    img_bytes = pio.to_image(fig, format="png", engine="kaleido")119    img = Image.open(BytesIO(img_bytes)).convert("RGBA")120    background = Image.new('RGBA', img.size, (255, 255, 255, 255))121    composite = Image.alpha_composite(background, img)122    return composite123 124def main():125    st.title('學生成績雷達圖產生器')126 127    uploaded_file = st.file_uploader("上傳CSV檔案", type=['csv'])128 129    if uploaded_file is not None:130        df = load_data(uploaded_file)131 132        if df is not None:133            numeric_columns = ['平均', '總分', '國文', '英文', '數學', '自科', '社會', '地理', '歷史', '公民']134 135            st.write("### 選擇要比較的欄位")136            selected_columns = [col for col in numeric_columns if st.checkbox(col, key=col)]137 138            st.write("### 選擇要比較的對象")139            selected_rows = st.multiselect('選擇要比較的對象', df['姓名'].tolist())140 141            if selected_columns and selected_rows:142                try:143                    fig = create_radar_chart(df, selected_rows, selected_columns)144                    st.plotly_chart(fig, use_container_width=True)145                except Exception as e:146                    st.error(f"生成雷達圖時發生錯誤:{e}")147                    148            st.write("### 批次繪製個別學生比較圖")149            150            individual_students = st.multiselect("選擇要個別比較的學生", df['姓名'].tolist(), key = "student")151            comparison_items = st.multiselect("選擇要比較的項目", df['姓名'].tolist(), key = "item")152          153            if individual_students and comparison_items:154                155                image_options = []156                image_bytes = {}157                for student in individual_students:158                    fig = create_radar_chart(df, [student] + comparison_items, selected_columns)159                    image_bytes[student] = create_composite_image(fig,student)160                    image_options.append(f"{student} 與 {', '.join(comparison_items)} 的比較")161                    162                selected_image_options = st.multiselect("選擇要顯示的圖片", options=image_options)163                164                cols = st.columns(3) # 排成三列165                for i, option in enumerate(selected_image_options):166                  student = option.split(" 與 ")[0]167                  with cols[i%3]:168                      st.image(image_bytes[student], use_container_width=True)169                      st.text(option)170 171if __name__ == "__main__":172    import numpy as np173    main()