Graph / app.py
s880453's picture
Update app.py
705707d verified
raw
history blame
10.9 kB
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, "未提供數據"
# 使用StringIO讀取CSV文本
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:
# 將Plotly圖表轉換為PNG圖像
img_bytes = fig.to_image(format="png")
# 創建PIL圖像對象
img = Image.open(io.BytesIO(img_bytes))
return img, "圖表已成功導出為PNG圖像"
except Exception as e:
return None, f"導出圖表時出錯: {str(e)}"
# 建立Gradio界面
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()