|
import streamlit as st |
|
import pandas as pd |
|
import matplotlib.pyplot as plt |
|
import numpy as np |
|
from io import BytesIO |
|
from scipy.signal import savgol_filter |
|
from scipy.ndimage import gaussian_filter1d |
|
|
|
|
|
def download_button(fig, file_name, file_format, dpi): |
|
buffer = BytesIO() |
|
fig.savefig(buffer, format=file_format.lower(), dpi=dpi, bbox_inches="tight") |
|
buffer.seek(0) |
|
st.download_button( |
|
label=f"Download as {file_format.upper()} ({dpi} DPI)", |
|
data=buffer, |
|
file_name=f"{file_name}_{dpi}dpi.{file_format.lower()}", |
|
mime=f"image/{file_format.lower()}", |
|
) |
|
|
|
|
|
def apply_smoothing(data, method, **params): |
|
if method == "None": |
|
return data |
|
elif method == "Moving Average": |
|
window = params.get('window_size', 5) |
|
return pd.Series(data).rolling(window=window, center=True).mean() |
|
elif method == "Gaussian": |
|
sigma = params.get('sigma', 2) |
|
return gaussian_filter1d(data, sigma=sigma) |
|
elif method == "Savitzky-Golay": |
|
window = params.get('window_size', 5) |
|
poly_order = params.get('poly_order', 2) |
|
return savgol_filter(data, window_length=window, polyorder=poly_order) |
|
return data |
|
|
|
|
|
st.set_page_config(layout="wide") |
|
st.title("Advanced CSV Data Visualization App") |
|
|
|
|
|
uploaded_file = st.file_uploader("Upload your CSV file", type="csv") |
|
|
|
if uploaded_file is not None: |
|
try: |
|
|
|
df = pd.read_csv(uploaded_file) |
|
|
|
|
|
def clean_column(column): |
|
return pd.to_numeric(column.str.replace(r"[^\d.-]", "", regex=True), errors='coerce') |
|
|
|
|
|
df = df.apply(lambda col: clean_column(col) if col.dtype == "object" else col) |
|
|
|
|
|
with st.sidebar: |
|
st.subheader("Graph Settings") |
|
col1, col2 = st.columns(2) |
|
|
|
with col1: |
|
|
|
x_column = st.selectbox("X-axis Column:", options=df.columns) |
|
x_label = st.text_input("X-axis Label:", value="Frequency") |
|
x_unit = st.text_input("X-axis Unit (e.g., Hz, MHz):", value="GHz") |
|
scale_option = st.selectbox("X Scaling Option:", |
|
["None", "Hz β MHz", "Hz β GHz", "mm β cm", "mm β m", |
|
"cm β mm", "m β mm", "Frequency β Wavelength", "Wavelength β Frequency", |
|
"Linear β dB", "dB β Linear"], index=2) |
|
x_min = st.number_input("Lower X-axis Limit:", value=None, format="%f") |
|
x_max = st.number_input("Upper X-axis Limit:", value=None, format="%f") |
|
x_step = st.number_input("X-axis Step Size:", value=5.0, format="%f") |
|
x_font_size = st.slider("X-axis Font Size:", 8, 20, 14) |
|
x_tick_position = st.selectbox("X-axis Tick Position:", ["out", "in", "inout"], index=2) |
|
|
|
with col2: |
|
|
|
y_columns = st.multiselect("Y-axis Column(s):", options=df.columns) |
|
y_label = st.text_input("Y-axis Label:", value="S21") |
|
y_unit = st.text_input("Y-axis Unit (e.g., dB, Linear):", value="dB") |
|
y_scale_option = st.selectbox("Y Scaling Option:", |
|
["None", "Hz β MHz", "Hz β GHz", "mm β cm", "mm β m", |
|
"cm β mm", "m β mm", "Frequency β Wavelength", "Wavelength β Frequency", |
|
"Linear β dB", "dB β Linear"], index=0) |
|
y_min = st.number_input("Lower Y-axis Limit:", value=None, format="%f") |
|
y_max = st.number_input("Upper Y-axis Limit:", value=None, format="%f") |
|
y_step = st.number_input("Y-axis Step Size:", value=10.0, format="%f") |
|
y_font_size = st.slider("Y-axis Font Size:", 8, 20, 14) |
|
y_tick_position = st.selectbox("Y-axis Tick Position:", ["out", "in", "inout"], index=2) |
|
|
|
|
|
st.subheader("Title and Font Settings") |
|
title = st.text_input("Graph Title:", value="Advanced Graph") |
|
font_style = st.selectbox("Font Style:", ["Normal", "Italic", "Bold"], index=0).lower() |
|
font_theme = st.selectbox("Font Theme:", ["Times New Roman", "Arial", "Courier New", "Helvetica", "Verdana"], index=0) |
|
title_font_size = st.slider("Title Font Size:", 10, 30, 16) |
|
|
|
|
|
st.subheader("Grid Settings") |
|
show_grid = st.checkbox("Show Grid", value=True) |
|
show_minor_grid = st.checkbox("Show Sub-grid (Minor Grid)", value=False) |
|
grid_direction = st.selectbox("Grid Direction:", ["x", "y", "both"], index=2) |
|
grid_line_style = st.selectbox("Grid Line Style:", ["-", "--", "-.", ":", "None"], index=0) |
|
grid_color = st.color_picker("Grid Line Color:", "#DDDDDD") |
|
grid_line_width = st.slider("Grid Line Width:", 0.5, 2.5, 1.0) |
|
|
|
|
|
st.subheader("Advanced Smoothing Settings") |
|
smoothing_method = st.selectbox( |
|
"Smoothing Method:", |
|
["None", "Moving Average", "Gaussian", "Savitzky-Golay", "Median", "Combined", "Exponential Moving Average", "LOWESS", "Butterworth", "Fourier Transform"] |
|
) |
|
|
|
|
|
smoothing_params = {} |
|
if smoothing_method == "Moving Average": |
|
smoothing_params['window_size'] = st.slider( |
|
"Window Size:", |
|
3, 101, 5, step=2 |
|
) |
|
elif smoothing_method == "Gaussian": |
|
smoothing_params['sigma'] = st.slider( |
|
"Sigma (Blur Amount):", |
|
0.1, 10.0, 2.0, step=0.1 |
|
) |
|
elif smoothing_method == "Savitzky-Golay": |
|
smoothing_params['window_size'] = st.slider( |
|
"Window Size:", |
|
5, 101, 21, step=2 |
|
) |
|
smoothing_params['poly_order'] = st.slider( |
|
"Polynomial Order:", |
|
1, 5, 3 |
|
) |
|
elif smoothing_method == "Median": |
|
smoothing_params['kernel_size'] = st.slider( |
|
"Kernel Size:", |
|
3, 51, 5, step=2 |
|
) |
|
elif smoothing_method == "Combined": |
|
smoothing_params['kernel_size'] = st.slider( |
|
"Median Kernel Size:", |
|
3, 51, 5, step=2 |
|
) |
|
smoothing_params['window_size'] = st.slider( |
|
"Savitzky-Golay Window:", |
|
5, 101, 21, step=2 |
|
) |
|
smoothing_params['poly_order'] = st.slider( |
|
"Polynomial Order:", |
|
1, 5, 3 |
|
) |
|
elif smoothing_method == "Exponential Moving Average": |
|
smoothing_params['span'] = st.slider( |
|
"Span (Smoothing Factor):", |
|
1, 50, 10 |
|
) |
|
elif smoothing_method == "LOWESS": |
|
smoothing_params['frac'] = st.slider( |
|
"Fraction (Smoothing Proportion):", |
|
0.01, 0.5, 0.1, step=0.01 |
|
) |
|
elif smoothing_method == "Butterworth": |
|
smoothing_params['order'] = st.slider( |
|
"Filter Order:", |
|
1, 10, 3 |
|
) |
|
smoothing_params['cutoff'] = st.slider( |
|
"Cutoff Frequency:", |
|
0.01, 0.5, 0.05, step=0.01 |
|
) |
|
elif smoothing_method == "Fourier Transform": |
|
smoothing_params['keep_fraction'] = st.slider( |
|
"Keep Fraction of Frequencies:", |
|
0.01, 1.0, 0.1, step=0.01 |
|
) |
|
|
|
|
|
|
|
st.subheader("Vertical Marker Settings") |
|
marker_x_values = st.text_input("Enter X-axis Values for Markers (comma-separated):", value="") |
|
|
|
|
|
dpi = st.selectbox("Select DPI for Download:", [100, 200, 300, 600], index=2) |
|
|
|
|
|
st.subheader("Legend Customization") |
|
legend_font_size = st.slider("Font Size:", 8, 20, 10) |
|
legend_font_weight = st.selectbox("Font Weight:", ["Normal", "Bold"], index=0) |
|
legend_bg_color = st.color_picker("Background Color:", "#FFFFFF") |
|
legend_border_color = st.color_picker("Border Color:", "#000000") |
|
legend_border_width = st.slider("Border Width:", 0.5, 2.0, 1.0) |
|
legend_transparency = st.slider("Transparency (0 = fully opaque, 1 = fully transparent):", 0.0, 1.0, 0.5) |
|
legend_title = st.text_input("Legend Title:", value="") |
|
legend_location = st.selectbox("Legend Location:", |
|
["upper right", "upper center", "upper left", "center right", |
|
"center", "center left", "lower right", |
|
"lower center", "lower left"], index=1) |
|
legend_columns = st.selectbox("Legend Columns:", [1, 2, 3], index=1) |
|
|
|
|
|
st.subheader("Line Style Settings") |
|
style_settings = {} |
|
line_styles = { |
|
"Solid": "-", "Dashed": "--", "Dash-Dot": "-.", "Dotted": ":" |
|
} |
|
marker_styles = { |
|
"None": "", "Circle": "o", "Square": "s", "Star": "*", "Diamond": "D", "Triangle": "^", |
|
"Pentagon": "p", "Hexagon": "H", "Plus": "+", "X": "x" |
|
} |
|
for y_column in y_columns: |
|
with st.expander(f"Line Style for '{y_column}'", expanded=False): |
|
color = st.color_picker(f"Color for '{y_column}':", "#1f77b4", key=f"color_{y_column}") |
|
line_style = st.selectbox("Line Style:", list(line_styles.keys()), key=f"ls_{y_column}") |
|
marker_style = st.selectbox("Marker Style:", list(marker_styles.keys()), key=f"ms_{y_column}") |
|
line_width = st.slider("Line Width:", 0.5, 5.0, 2.0, key=f"lw_{y_column}") |
|
style_settings[y_column] = { |
|
"color": color, |
|
"line_style": line_styles[line_style], |
|
"marker_style": marker_styles[marker_style], |
|
"line_width": line_width, |
|
} |
|
|
|
|
|
st.subheader("Graph Output") |
|
if st.button("Generate Graph"): |
|
fig, ax = plt.subplots() |
|
|
|
|
|
def apply_scaling(df, column, scale_option, unit): |
|
if scale_option == "Hz β MHz": |
|
df[column] = df[column] / 1e6 |
|
unit = "MHz" |
|
elif scale_option == "Hz β GHz": |
|
df[column] = df[column] / 1e9 |
|
unit = "GHz" |
|
elif scale_option == "mm β cm": |
|
df[column] = df[column] / 10 |
|
unit = "cm" |
|
elif scale_option == "mm β m": |
|
df[column] = df[column] / 1000 |
|
unit = "m" |
|
elif scale_option == "cm β mm": |
|
df[column] = df[column] * 10 |
|
unit = "mm" |
|
elif scale_option == "m β mm": |
|
df[column] = df[column] * 1000 |
|
unit = "mm" |
|
elif scale_option == "Frequency β Wavelength": |
|
df[column] = 3e8 / df[column] |
|
unit = "Wavelength (m)" |
|
elif scale_option == "Wavelength β Frequency": |
|
df[column] = 3e8 / df[column] |
|
unit = "Frequency (Hz)" |
|
elif scale_option == "Linear β dB": |
|
df[column] = 10 * np.log10(df[column]) |
|
unit = "dB" |
|
elif scale_option == "dB β Linear": |
|
df[column] = 10**(df[column] / 10) |
|
unit = "Linear" |
|
return df, unit |
|
|
|
|
|
df, x_unit = apply_scaling(df, x_column, scale_option, x_unit) |
|
|
|
|
|
for col in y_columns: |
|
df, y_unit = apply_scaling(df, col, y_scale_option, y_unit) |
|
|
|
|
|
for col in y_columns: |
|
|
|
y_values = apply_smoothing(df[col].values, smoothing_method, **smoothing_params) |
|
|
|
ax.plot(df[x_column], y_values, |
|
label=col, |
|
linestyle=style_settings[col]["line_style"], |
|
marker=style_settings[col]["marker_style"], |
|
color=style_settings[col]["color"], |
|
linewidth=style_settings[col]["line_width"]) |
|
|
|
|
|
if x_min and x_max: |
|
ax.set_xticks(np.arange(x_min, x_max + x_step, x_step)) |
|
if y_min and y_max: |
|
ax.set_yticks(np.arange(y_min, y_max + y_step, y_step)) |
|
|
|
ax.set_xlim(x_min, x_max) |
|
ax.set_ylim(y_min, y_max) |
|
|
|
|
|
ax.set_xlabel(f"{x_label} ({x_unit})", fontsize=x_font_size) |
|
ax.set_ylabel(f"{y_label} ({y_unit})", fontsize=y_font_size) |
|
ax.set_title(title, fontsize=title_font_size, fontstyle=font_style, family=font_theme) |
|
ax.tick_params(axis='x', direction=x_tick_position) |
|
ax.tick_params(axis='y', direction=y_tick_position) |
|
|
|
if show_grid: |
|
ax.grid(True, linestyle=grid_line_style, linewidth=grid_line_width, color=grid_color, axis=grid_direction) |
|
if show_minor_grid: |
|
ax.minorticks_on() |
|
ax.grid(which='minor', linestyle=grid_line_style, linewidth=grid_line_width, color=grid_color) |
|
|
|
|
|
if marker_x_values: |
|
x_vals = [float(val.strip()) for val in marker_x_values.split(",")] |
|
for val in x_vals: |
|
ax.axvline(x=val, color="r", linestyle="--", linewidth=1.5) |
|
|
|
|
|
ax.legend(title=legend_title, fontsize=legend_font_size, title_fontsize=legend_font_size, |
|
loc=legend_location, frameon=True, facecolor=legend_bg_color, edgecolor=legend_border_color, |
|
framealpha=legend_transparency, borderpad=legend_border_width, ncol=legend_columns) |
|
|
|
|
|
st.pyplot(fig) |
|
|
|
|
|
download_button(fig, "graph", "PNG", dpi) |
|
|
|
except Exception as e: |
|
st.error(f"Error: {str(e)}") |