| import gradio as gr |
| import pandas as pd |
| import numpy as np |
| import plotly.express as px |
| import plotly.graph_objects as go |
| import io |
| import base64 |
| from PIL import Image |
|
|
| |
| default_data = pd.DataFrame({ |
| "類別": ["A", "B", "C", "D", "E"], |
| "數值": [10, 20, 15, 25, 30] |
| }) |
|
|
| |
| COLOR_SCHEMES = { |
| "默認": px.colors.qualitative.Plotly, |
| "藍綠色系": px.colors.sequential.Blues, |
| "紅色系": px.colors.sequential.Reds, |
| "綠色系": px.colors.sequential.Greens, |
| "彩虹色": px.colors.sequential.Turbo |
| } |
|
|
| def create_plot(df, chart_type, x_column, y_column, color_scheme, title, width, height, show_grid, show_legend): |
| """創建圖表函數""" |
| |
| |
| if df is None or df.empty: |
| df = default_data |
| |
| |
| if x_column not in df.columns: |
| x_column = df.columns[0] if len(df.columns) > 0 else "類別" |
| |
| if y_column not in df.columns: |
| y_column = df.columns[1] if len(df.columns) > 1 else "數值" |
| |
| |
| colors = COLOR_SCHEMES[color_scheme] |
| |
| |
| fig_params = { |
| "width": width, |
| "height": height, |
| "title": title |
| } |
| |
| |
| if chart_type == "長條圖": |
| fig = px.bar(df, x=x_column, y=y_column, color_discrete_sequence=colors, **fig_params) |
| |
| elif chart_type == "折線圖": |
| fig = px.line(df, x=x_column, y=y_column, markers=True, color_discrete_sequence=colors, **fig_params) |
| |
| elif chart_type == "圓餅圖": |
| fig = px.pie(df, names=x_column, values=y_column, color_discrete_sequence=colors, **fig_params) |
| |
| elif chart_type == "散點圖": |
| fig = px.scatter(df, x=x_column, y=y_column, color_discrete_sequence=colors, **fig_params) |
| |
| elif chart_type == "區域圖": |
| fig = px.area(df, x=x_column, y=y_column, color_discrete_sequence=colors, **fig_params) |
| |
| else: |
| fig = px.bar(df, x=x_column, y=y_column, color_discrete_sequence=colors, **fig_params) |
| |
| |
| fig.update_layout( |
| showlegend=show_legend, |
| xaxis=dict(showgrid=show_grid), |
| yaxis=dict(showgrid=show_grid) |
| ) |
| |
| return fig |
|
|
| def process_upload(file): |
| """處理上傳的文件""" |
| try: |
| if file is None: |
| return None, "未上傳文件" |
| |
| |
| file_type = file.name.split('.')[-1].lower() |
| |
| if file_type == 'csv': |
| df = pd.read_csv(file.name) |
| elif file_type in ['xls', 'xlsx']: |
| df = pd.read_excel(file.name) |
| else: |
| return None, f"不支持的文件類型: {file_type}。請上傳CSV或Excel文件。" |
| |
| return df, f"成功載入數據,共{len(df)}行,{len(df.columns)}列" |
| |
| except Exception as e: |
| return None, f"載入文件時出錯: {str(e)}" |
|
|
| def parse_data(csv_data): |
| """解析CSV文本數據""" |
| try: |
| if not csv_data or csv_data.strip() == "": |
| return None, "未提供數據" |
| |
| |
| df = pd.read_csv(io.StringIO(csv_data)) |
| return df, f"成功解析數據,共{len(df)}行,{len(df.columns)}列" |
| |
| except Exception as e: |
| return None, f"解析數據時出錯: {str(e)}" |
|
|
| def export_data(df, format_type): |
| """導出數據為各種格式""" |
| if df is None or df.empty: |
| return None, "沒有數據可以導出" |
| |
| try: |
| if format_type == "CSV": |
| buffer = io.StringIO() |
| df.to_csv(buffer, index=False) |
| data = buffer.getvalue() |
| filename = "exported_data.csv" |
| mime_type = "text/csv" |
| |
| elif format_type == "Excel": |
| buffer = io.BytesIO() |
| df.to_excel(buffer, index=False) |
| data = buffer.getvalue() |
| filename = "exported_data.xlsx" |
| mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" |
| |
| elif format_type == "JSON": |
| buffer = io.StringIO() |
| data = df.to_json(orient="records") |
| filename = "exported_data.json" |
| mime_type = "application/json" |
| |
| else: |
| return None, f"不支持的導出格式: {format_type}" |
| |
| return (data, filename, mime_type), f"數據已成功導出為{format_type}格式" |
| |
| except Exception as e: |
| return None, f"導出數據時出錯: {str(e)}" |
|
|
| def update_columns(df): |
| """更新列選擇下拉菜單""" |
| if df is None or df.empty: |
| |
| return gr.Dropdown(choices=["類別", "數值"], value="類別"), gr.Dropdown(choices=["類別", "數值"], value="數值") |
| |
| columns = df.columns.tolist() |
| x_dropdown = gr.Dropdown(choices=columns, value=columns[0] if columns else None) |
| y_dropdown = gr.Dropdown(choices=columns, value=columns[1] if len(columns) > 1 else columns[0]) |
| |
| return x_dropdown, y_dropdown |
|
|
| def download_figure(fig): |
| """導出圖表為圖像""" |
| if fig is None: |
| return None, "沒有圖表可以導出" |
| |
| try: |
| |
| img_bytes = fig.to_image(format="png") |
| |
| |
| img = Image.open(io.BytesIO(img_bytes)) |
| |
| return img, "圖表已成功導出為PNG圖像" |
| |
| except Exception as e: |
| return None, f"導出圖表時出錯: {str(e)}" |
|
|
| |
| with gr.Blocks(title="數據可視化工具") as demo: |
| gr.Markdown("# 數據可視化工具") |
| gr.Markdown("上傳CSV或Excel文件,或直接在下方輸入數據來創建各種圖表") |
| |
| |
| data_state = gr.State(None) |
| |
| with gr.Tabs(): |
| |
| with gr.TabItem("數據輸入"): |
| with gr.Row(): |
| with gr.Column(): |
| file_upload = gr.File(label="上傳CSV或Excel文件") |
| upload_button = gr.Button("載入文件") |
| upload_status = gr.Textbox(label="上傳狀態") |
| |
| with gr.Column(): |
| csv_input = gr.Textbox(label="或直接輸入CSV數據(逗號分隔)", placeholder="類別,數值\nA,10\nB,20\nC,15\nD,25\nE,30", lines=10) |
| parse_button = gr.Button("解析數據") |
| parse_status = gr.Textbox(label="解析狀態") |
| |
| with gr.Row(): |
| data_preview = gr.Dataframe(label="數據預覽") |
| |
| with gr.Column(): |
| export_format = gr.Dropdown(["CSV", "Excel", "JSON"], label="導出格式", value="CSV") |
| export_button = gr.Button("導出數據") |
| export_result = gr.File(label="導出結果") |
| export_status = gr.Textbox(label="導出狀態") |
| |
| |
| with gr.TabItem("圖表創建"): |
| with gr.Row(): |
| with gr.Column(): |
| chart_type = gr.Dropdown( |
| ["長條圖", "折線圖", "圓餅圖", "散點圖", "區域圖"], |
| label="圖表類型", |
| value="長條圖" |
| ) |
| |
| x_column = gr.Dropdown(["類別"], label="X軸(或類別)") |
| y_column = gr.Dropdown(["數值"], label="Y軸(或數值)") |
| |
| chart_title = gr.Textbox(label="圖表標題", placeholder="我的數據圖表") |
| |
| color_scheme = gr.Dropdown( |
| list(COLOR_SCHEMES.keys()), |
| label="顏色方案", |
| value="默認" |
| ) |
| |
| with gr.Column(): |
| chart_width = gr.Slider(300, 1200, 700, label="圖表寬度") |
| chart_height = gr.Slider(300, 800, 500, label="圖表高度") |
| |
| show_grid = gr.Checkbox(label="顯示網格", value=True) |
| show_legend = gr.Checkbox(label="顯示圖例", value=True) |
| |
| update_button = gr.Button("更新圖表") |
| download_button = gr.Button("導出為PNG圖像") |
| |
| with gr.Row(): |
| chart_output = gr.Plot(label="圖表預覽") |
| download_output = gr.Image(label="導出的圖表", visible=False) |
| |
| |
| upload_button.click( |
| process_upload, |
| inputs=[file_upload], |
| outputs=[data_state, upload_status] |
| ).then( |
| lambda df: df if df is not None else pd.DataFrame(), |
| inputs=[data_state], |
| outputs=[data_preview] |
| ).then( |
| update_columns, |
| inputs=[data_state], |
| outputs=[x_column, y_column] |
| ) |
| |
| parse_button.click( |
| parse_data, |
| inputs=[csv_input], |
| outputs=[data_state, parse_status] |
| ).then( |
| lambda df: df if df is not None else pd.DataFrame(), |
| inputs=[data_state], |
| outputs=[data_preview] |
| ).then( |
| update_columns, |
| inputs=[data_state], |
| outputs=[x_column, y_column] |
| ) |
| |
| export_button.click( |
| export_data, |
| inputs=[data_state, export_format], |
| outputs=[export_result, export_status] |
| ) |
| |
| update_button.click( |
| create_plot, |
| inputs=[data_state, chart_type, x_column, y_column, color_scheme, |
| chart_title, chart_width, chart_height, show_grid, show_legend], |
| outputs=[chart_output] |
| ) |
| |
| download_button.click( |
| download_figure, |
| inputs=[chart_output], |
| outputs=[download_output, gr.Textbox(label="下載狀態")] |
| ).then( |
| lambda: gr.update(visible=True), |
| outputs=[download_output] |
| ) |
| |
| |
| chart_type.change( |
| create_plot, |
| inputs=[data_state, chart_type, x_column, y_column, color_scheme, |
| chart_title, chart_width, chart_height, show_grid, show_legend], |
| outputs=[chart_output] |
| ) |
| |
| x_column.change( |
| create_plot, |
| inputs=[data_state, chart_type, x_column, y_column, color_scheme, |
| chart_title, chart_width, chart_height, show_grid, show_legend], |
| outputs=[chart_output] |
| ) |
| |
| y_column.change( |
| create_plot, |
| inputs=[data_state, chart_type, x_column, y_column, color_scheme, |
| chart_title, chart_width, chart_height, show_grid, show_legend], |
| outputs=[chart_output] |
| ) |
|
|
| |
| demo.launch() |