├── .DS_Store ├── requirements.txt ├── ReadMe.md └── app.py /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AammarTufail/growth_curve_phenotype_data_viz_app/HEAD/.DS_Store -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | streamlit==1.36.0 2 | pandas==2.2.0 3 | numpy==1.26.4 4 | matplotlib==3.8.3 5 | seaborn==0.13.2 6 | openpyxl==3.1.2 7 | xlsxwriter==3.2.0 8 | -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | # Growth Curve Visualization Application 2 | 3 | Welcome to the **Growth Curve Visualization Application**! This interactive Streamlit app allows you to visualize the growth of any microorganism over time using your own data. Customize plots extensively and export high-quality figures for your presentations or publications. 4 | 5 | Developed by [**Muhammad Aammar Tufail (PhD)**](https://github.com/AammarTufail) 6 | 7 | ## Table of Contents 8 | 9 | - [Growth Curve Visualization Application](#growth-curve-visualization-application) 10 | - [Table of Contents](#table-of-contents) 11 | - [Features](#features) 12 | - [Installation](#installation) 13 | - [Prerequisites](#prerequisites) 14 | - [Installation Steps](#installation-steps) 15 | - [Usage Instructions](#usage-instructions) 16 | - [Dependencies](#dependencies) 17 | - [License](#license) 18 | - [Acknowledgments](#acknowledgments) 19 | - [Contact](#contact) 20 | 21 | --- 22 | 23 | ## Features 24 | 25 | - **Data Upload**: Import your dataset in CSV or Excel format. 26 | - **Template Dataset**: Download a sample dataset to understand the required data format. 27 | - **Interactive Customization**: Adjust plot settings, fonts, colors, and more in real-time. 28 | - **Statistical Options**: Choose between mean or median calculations and select error types (Standard Deviation or Standard Error). 29 | - **Visualization Options**: 30 | - Customize axis titles, limits, and scales. 31 | - Select different themes and color palettes. 32 | - Adjust line styles, markers, and scatter point sizes. 33 | - **Export Options**: Save your customized plots in PNG, SVG, or PDF formats with adjustable dimensions and resolution. 34 | 35 | ## Installation 36 | 37 | ### Prerequisites 38 | 39 | - **Python**: Version 3.7 or higher 40 | - **pip**: Python package installer 41 | 42 | ### Installation Steps 43 | 44 | 1. **Clone the Repository** 45 | 46 | ```bash 47 | git clone https://github.com/AammarTufail/growth_curve_phenotype_data_viz_app 48 | cd growth_curve_phenotype_data_viz_app 49 | ``` 50 | 51 | 52 | 2. **Create a Virtual Environment (Optional but Recommended)** 53 | 54 | ```bash 55 | # Create virtual environment 56 | python -m venv venv 57 | 58 | # Activate virtual environment 59 | # On Windows: 60 | venv\Scripts\activate 61 | # On macOS/Linux: 62 | source venv/bin/activate 63 | ``` 64 | 65 | 3. **Install Dependencies** 66 | 67 | Install the required packages using the `requirements.txt` file: 68 | 69 | ```bash 70 | pip install -r requirements.txt 71 | ``` 72 | 73 | *Alternatively, you can install packages individually:* 74 | 75 | ```bash 76 | pip install streamlit pandas numpy matplotlib seaborn openpyxl xlsxwriter 77 | ``` 78 | 79 | ## Usage Instructions 80 | 81 | 1. **Run the Application** 82 | 83 | ```bash 84 | streamlit run app.py 85 | ``` 86 | 87 | - The app will open in your default web browser. 88 | - If it doesn't open automatically, navigate to the URL provided in the terminal (usually `http://localhost:8501`). 89 | 90 | 2. **Uploading Data** 91 | 92 | - Use the sidebar to upload your dataset in CSV or Excel format. 93 | - If you don't have your own data, you can download the template dataset provided in the sidebar. 94 | 95 | 3. **Data Format** 96 | 97 | Your dataset should contain the following columns: 98 | 99 | - **Time**: The time points of your measurements. 100 | - **OD**: The observed data (e.g., optical density). 101 | - **Replicate**: Replicate number (if applicable). 102 | - **Sample**: Sample or condition name. 103 | 104 | **Note:** If the 'Sample' column is missing, a default sample name 'Sample 1' will be assigned. 105 | 106 | 4. **Customizing the Plot** 107 | 108 | - **Data Settings**: Choose calculation type (Mean or Median) and error type (Standard Deviation or Standard Error). Select the Y-axis scale (Linear, Log, Log10). 109 | - **Axis Settings**: Adjust axis titles, limits, tick intervals, and decide whether to show grid lines or remove borders. 110 | - **Font Settings**: Customize font family, size, style, and weight for axis labels, titles, and tick labels. 111 | - **Legend Settings**: Set legend position and customize font properties. 112 | - **Line and Color Settings**: 113 | - Choose to use different colors, markers, or line styles for each sample. 114 | - Select color palettes or use custom colors. 115 | - Adjust line width, opacity, error bar properties, and scatter point size. 116 | - **Plot Settings**: Set the plot title and adjust the plot's width and height. 117 | 118 | *All these options are available in the sidebar under respective sections.* 119 | 120 | 5. **Viewing the Plot** 121 | 122 | - The plot updates automatically as you adjust settings. 123 | - Use the "Display data before plotting?" checkbox to preview your dataset within the app. 124 | 125 | 6. **Saving the Plot** 126 | 127 | - Scroll to the "Save the Plot" section below the plot. 128 | - Adjust the width, height, and resolution (DPI) for the saved plot. 129 | - Choose the file format (PNG, SVG, PDF). 130 | - Click the "Download plot as [format]" button to save your customized plot. 131 | 132 | ## Dependencies 133 | 134 | - **Python Packages**: 135 | - streamlit 136 | - pandas 137 | - numpy 138 | - matplotlib 139 | - seaborn 140 | - openpyxl 141 | - xlsxwriter 142 | 143 | - **`requirements.txt` File**: 144 | 145 | ```plaintext 146 | streamlit==1.36.0 147 | pandas==2.2.0 148 | numpy==1.26.4 149 | matplotlib==3.8.3 150 | seaborn==0.13.2 151 | openpyxl==3.1.2 152 | xlsxwriter==3.2.0 153 | ``` 154 | 155 | *You can install all dependencies using:* 156 | 157 | ```bash 158 | pip install -r requirements.txt 159 | ``` 160 | 161 | ## License 162 | 163 | This project is licensed under the [MIT License](LICENSE). 164 | 165 | ## Acknowledgments 166 | 167 | - **Developer**: [Muhammad Aammar Tufail (PhD)](https://github.com/AammarTufail) 168 | - **Contributors**: Thanks to all contributors of the open-source libraries utilized in this project. 169 | 170 | ## Contact 171 | 172 | For questions, suggestions, or contributions, please contact: 173 | 174 | - **Email**: [your.email@example.com](mailto:m.aammar.tufail@gmail.com) 175 | - **GitHub**: [https://github.com/AammarTufail/growth_curve_phenotype_data_viz_app](https://github.com/AammarTufail/growth_curve_phenotype_data_viz_app) 176 | 177 | *Feel free to open an issue or pull request if you encounter any problems or have ideas for enhancements.* 178 | 179 | --- 180 | 181 | *This README was generated to provide clear instructions and facilitate the use of the Growth Curve Visualization Application.* -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | import seaborn as sns 6 | from io import BytesIO 7 | from matplotlib.ticker import MultipleLocator 8 | import matplotlib.colors as mc 9 | 10 | # Function to adjust color brightness 11 | def adjust_color_brightness(color, amount=0.7): 12 | try: 13 | c = mc.cnames[color] 14 | except: 15 | c = color 16 | c = mc.to_rgb(c) 17 | c = [max(0, min(1, amount * x)) for x in c] 18 | return c 19 | 20 | # Cache the data generation function 21 | @st.cache_data 22 | def generate_example_data(): 23 | np.random.seed(42) 24 | time_points = np.arange(0, 100, 10) 25 | replicates = 3 26 | samples = ['Methanosarcina mazei +N', 'Methanosarcina mazei -N', 'Methanosarcina mazei +S'] 27 | data_list = [] 28 | for sample in samples: 29 | if sample == 'Methanosarcina mazei +N': 30 | mean_values = np.repeat(np.linspace(0.1, 1.2, len(time_points)), replicates) 31 | elif sample == 'Methanosarcina mazei -N': 32 | mean_values = np.repeat(np.linspace(0.2, 1.0, len(time_points)), replicates) 33 | elif sample == 'Methanosarcina mazei +S': 34 | mean_values = np.repeat(np.linspace(0.3, 1.5, len(time_points)), replicates) 35 | else: 36 | mean_values = np.repeat(np.linspace(0.1, 1.2, len(time_points)), replicates) 37 | data = pd.DataFrame({ 38 | 'Time': np.repeat(time_points, replicates), 39 | 'Replicate': np.tile(np.arange(1, replicates + 1), len(time_points)), 40 | 'OD': np.random.normal(loc=mean_values, scale=0.1, size=len(mean_values)), 41 | 'Sample': sample 42 | }) 43 | data_list.append(data) 44 | return pd.concat(data_list) 45 | 46 | # Cache the calculation function 47 | @st.cache_data 48 | def calculate_mean_and_error(df, x_column, y_column, error_type='SD', calculation_type='Mean'): 49 | if calculation_type == 'Mean': 50 | summary = df.groupby([x_column, 'Sample'])[y_column].agg(['mean', 'std', 'count']).reset_index() 51 | summary.rename(columns={x_column: 'X', 'mean': 'Y'}, inplace=True) 52 | else: 53 | summary = df.groupby([x_column, 'Sample'])[y_column].agg(['median', 'std', 'count']).reset_index() 54 | summary.rename(columns={x_column: 'X', 'median': 'Y'}, inplace=True) 55 | if error_type == 'SD': 56 | summary['error'] = summary['std'] 57 | else: 58 | summary['error'] = summary['std'] / np.sqrt(summary['count']) 59 | return summary 60 | 61 | def plot_seaborn(summary_df, x_axis_title, y_axis_title, plot_title, theme, width, height, show_grid, 62 | x_limits, y_limits, x_tick_interval, y_tick_interval, legend_position, 63 | y_scale, remove_borders, use_different_colors, use_custom_colors, use_different_markers, 64 | use_different_line_styles, custom_colors, 65 | font_family, axis_label_font_size, axis_label_font_style, axis_label_font_weight, 66 | title_font_size, title_font_style, title_font_weight, tick_label_font_size, 67 | tick_label_font_style, tick_label_font_weight, 68 | legend_font_size, legend_font_family, legend_font_style, legend_font_weight, 69 | line_width, opacity, errorbar_line_width, errorbar_capsize, scatter_point_size, 70 | selected_palette='Set2'): 71 | plt.style.use(theme) 72 | fig, ax = plt.subplots(figsize=(width, height)) 73 | samples = summary_df['Sample'].unique() 74 | markers_list = ['o', 's', '^', 'D', 'v', 'P', '*', 'X'] 75 | line_styles_list = ['-', '--', '-.', ':'] 76 | if use_different_markers: 77 | markers = [markers_list[i % len(markers_list)] for i in range(len(samples))] 78 | else: 79 | markers = ['o'] * len(samples) 80 | if use_different_line_styles: 81 | line_styles = [line_styles_list[i % len(line_styles_list)] for i in range(len(samples))] 82 | else: 83 | line_styles = ['-'] * len(samples) 84 | if use_different_colors: 85 | if use_custom_colors and custom_colors: 86 | colors = [] 87 | for idx, sample in enumerate(samples): 88 | if custom_colors.get(sample): 89 | colors.append(custom_colors[sample]) 90 | else: 91 | colors.append(sns.color_palette(selected_palette, n_colors=len(samples))[idx]) 92 | else: 93 | colors = sns.color_palette(selected_palette, n_colors=len(samples)) 94 | else: 95 | default_color = sns.color_palette(selected_palette, n_colors=1)[0] 96 | colors = [default_color] * len(samples) 97 | for idx, sample in enumerate(samples): 98 | sample_data = summary_df[summary_df['Sample'] == sample] 99 | marker = markers[idx] 100 | line_style = line_styles[idx] 101 | color = colors[idx] 102 | darker_color = adjust_color_brightness(color, amount=0.7) 103 | ax.plot(sample_data['X'], sample_data['Y'], linestyle=line_style, marker=marker, 104 | color=darker_color, label=sample, linewidth=line_width, alpha=opacity, 105 | markersize=scatter_point_size) 106 | ax.errorbar(sample_data['X'], sample_data['Y'], yerr=sample_data['error'], fmt='None', 107 | ecolor=color, elinewidth=errorbar_line_width, capsize=errorbar_capsize, alpha=opacity) 108 | ax.fill_between(sample_data['X'], sample_data['Y'] - sample_data['error'], 109 | sample_data['Y'] + sample_data['error'], color=color, alpha=opacity*0.3) 110 | ax.set_xlabel(x_axis_title, fontsize=axis_label_font_size, fontfamily=font_family, 111 | fontstyle=axis_label_font_style, fontweight=axis_label_font_weight) 112 | ax.set_ylabel(y_axis_title, fontsize=axis_label_font_size, fontfamily=font_family, 113 | fontstyle=axis_label_font_style, fontweight=axis_label_font_weight) 114 | ax.set_title(plot_title, fontsize=title_font_size, fontfamily=font_family, 115 | fontstyle=title_font_style, fontweight=title_font_weight) 116 | for tick in ax.get_xticklabels(): 117 | tick.set_fontsize(tick_label_font_size) 118 | tick.set_fontfamily(font_family) 119 | tick.set_fontstyle(tick_label_font_style) 120 | tick.set_fontweight(tick_label_font_weight) 121 | for tick in ax.get_yticklabels(): 122 | tick.set_fontsize(tick_label_font_size) 123 | tick.set_fontfamily(font_family) 124 | tick.set_fontstyle(tick_label_font_style) 125 | tick.set_fontweight(tick_label_font_weight) 126 | ax.grid(show_grid) 127 | ax.legend(loc=legend_position, prop={'size': legend_font_size, 'family': legend_font_family, 128 | 'style': legend_font_style, 'weight': legend_font_weight}) 129 | if x_limits[0] is not None and x_limits[1] is not None: 130 | ax.set_xlim(x_limits) 131 | if y_limits[0] is not None and y_limits[1] is not None: 132 | ax.set_ylim(y_limits) 133 | if x_tick_interval is not None: 134 | ax.xaxis.set_major_locator(MultipleLocator(x_tick_interval)) 135 | if y_tick_interval is not None: 136 | ax.yaxis.set_major_locator(MultipleLocator(y_tick_interval)) 137 | if y_scale == "Linear": 138 | pass 139 | elif y_scale == "Log": 140 | ax.set_yscale('log') 141 | elif y_scale == "Log10": 142 | ax.set_yscale('log', base=10) 143 | if remove_borders: 144 | ax.spines['right'].set_visible(False) 145 | ax.spines['top'].set_visible(False) 146 | ax.tick_params(axis='both', which='both', top=False, right=False) 147 | return fig 148 | 149 | # Streamlit Layout 150 | st.set_page_config(layout="wide", page_title="Growth Curve Visualization") 151 | 152 | st.title("Growth Curve Visualization Application") 153 | # subtitle 154 | st.subheader("Visualize the growth of any microorganism over time Developed by [Muhammad Aammar Tufail (PhD)](https://github.com/AammarTufail)") 155 | 156 | st.write(""" 157 | Welcome to the growth curve visualization tool. Use this app to visualize the growth of any microorganism over time. You can upload your own data, select different error types, customize the plot colors, and adjust the plot appearance. 158 | """) 159 | # Add major Heading of Sidebar with Emojis 160 | st.sidebar.title("Use your Data or Download Template Dataset 📊") 161 | 162 | # Sidebar - Download Template Dataset 163 | st.sidebar.header("Download Template Dataset") 164 | st.sidebar.write(""" 165 | **Note:** You can download the template dataset to see the required format for the data, and then upload your own data within same format 166 | """) 167 | # Generate example data 168 | df_template = generate_example_data() 169 | 170 | def to_excel(df): 171 | output = BytesIO() 172 | writer = pd.ExcelWriter(output, engine='xlsxwriter') 173 | df.to_excel(writer, index=False) 174 | writer.close() 175 | processed_data = output.getvalue() 176 | return processed_data 177 | 178 | data_xlsx = to_excel(df_template) 179 | st.sidebar.download_button(label='Download Template as Excel', data=data_xlsx, file_name='template_dataset.xlsx', mime='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') 180 | 181 | # Allow the user to upload their own data 182 | st.sidebar.header("Upload Your Data") 183 | uploaded_file = st.sidebar.file_uploader("Upload a CSV or Excel file", type=["csv", "xlsx"]) 184 | 185 | # Data processing 186 | if uploaded_file is not None: 187 | try: 188 | if uploaded_file.name.endswith('.csv'): 189 | data_to_plot = pd.read_csv(uploaded_file) 190 | elif uploaded_file.name.endswith('.xlsx'): 191 | data_to_plot = pd.read_excel(uploaded_file) 192 | except Exception as e: 193 | st.error(f"Error processing file: {e}") 194 | st.stop() 195 | else: 196 | data_to_plot = df_template 197 | 198 | # Check if 'Sample' column exists, if not, add a default sample 199 | if 'Sample' not in data_to_plot.columns: 200 | data_to_plot['Sample'] = 'Sample 1' 201 | 202 | # Set x_column and y_column directly 203 | x_column = 'Time' 204 | y_column = 'OD' 205 | 206 | # Option to display data before plotting 207 | show_data = st.checkbox("Display data before plotting?") 208 | if show_data: 209 | st.write("Data Preview:") 210 | st.dataframe(data_to_plot) 211 | 212 | # Settings in Sidebar 213 | st.sidebar.title("📊 Plot Settings") 214 | # Data Settings 215 | with st.sidebar.expander("Data Settings", expanded=False): 216 | calculation_type = st.radio("Select Calculation Type", options=["Mean", "Median"], index=0) 217 | error_type = st.radio("Select Error Type", options=["Standard Deviation (SD)", "Standard Error (SE)"], index=1) 218 | y_scale = st.selectbox("Select Y-axis Scale", options=["Linear", "Log", "Log10"], index=0) 219 | 220 | # Axis Settings 221 | with st.sidebar.expander("Axis Settings", expanded=False): 222 | x_axis_title = st.text_input("X-axis Title", x_column) 223 | use_x_limits = st.checkbox("Set X-axis limits?", value=False) 224 | if use_x_limits: 225 | x_min_default = data_to_plot[x_column].min() 226 | x_max_default = data_to_plot[x_column].max() 227 | x_min = st.number_input("X-axis minimum", value=float(x_min_default), step=0.1, format="%.2f") 228 | x_max = st.number_input("X-axis maximum", value=float(x_max_default), step=0.1, format="%.2f") 229 | x_limits = (x_min, x_max) 230 | else: 231 | x_limits = (None, None) 232 | 233 | use_x_tick_interval = st.checkbox("Set X-axis tick interval?", value=False) 234 | if use_x_tick_interval: 235 | x_tick_interval = st.number_input("X-axis tick interval", value=10.0, min_value=0.0, max_value=10000.0, 236 | step=0.1, format="%.2f") 237 | else: 238 | x_tick_interval = None 239 | 240 | y_axis_title = st.text_input("Y-axis Title", y_column) 241 | use_y_limits = st.checkbox("Set Y-axis limits?", value=False) 242 | if use_y_limits: 243 | y_min_default = data_to_plot[y_column].min() 244 | y_max_default = data_to_plot[y_column].max() 245 | y_min = st.number_input("Y-axis minimum", value=float(y_min_default), step=0.1, format="%.2f") 246 | y_max = st.number_input("Y-axis maximum", value=float(y_max_default), step=0.1, format="%.2f") 247 | y_limits = (y_min, y_max) 248 | else: 249 | y_limits = (None, None) 250 | 251 | use_y_tick_interval = st.checkbox("Set Y-axis tick interval?", value=False) 252 | if use_y_tick_interval: 253 | y_tick_interval = st.number_input("Y-axis tick interval", value=0.2, min_value=0.0, max_value=10000.0, 254 | step=0.1, format="%.2f") 255 | else: 256 | y_tick_interval = None 257 | 258 | show_grid = st.checkbox("Show Grid Lines", value=False) 259 | remove_borders = st.checkbox("Remove top and right borders?", value=True) 260 | 261 | static_themes = plt.style.available 262 | theme = st.selectbox("Select Plot Theme", options=static_themes, index=static_themes.index('classic')) 263 | 264 | # Font Settings 265 | with st.sidebar.expander("Font Settings", expanded=False): 266 | font_families = ['sans-serif', 'serif', 'monospace', 'cursive', 'fantasy'] 267 | font_family = st.selectbox("Select Font Family", options=font_families, index=0) 268 | 269 | axis_label_font_size = st.number_input("Axis Label Font Size", value=12, min_value=1, max_value=100) 270 | axis_label_font_style = st.selectbox("Axis Label Font Style", options=['normal', 'italic', 'oblique'], index=0) 271 | axis_label_font_weight = st.selectbox("Axis Label Font Weight", options=['normal', 'bold'], index=0) 272 | 273 | title_font_size = st.number_input("Title Font Size", value=14, min_value=1, max_value=100) 274 | title_font_style = st.selectbox("Title Font Style", options=['normal', 'italic', 'oblique'], index=0) 275 | title_font_weight = st.selectbox("Title Font Weight", options=['normal', 'bold'], index=0) 276 | 277 | tick_label_font_size = st.number_input("Tick Label Font Size", value=10, min_value=1, max_value=100) 278 | tick_label_font_style = st.selectbox("Tick Label Font Style", options=['normal', 'italic', 'oblique'], index=0) 279 | tick_label_font_weight = st.selectbox("Tick Label Font Weight", options=['normal', 'bold'], index=0) 280 | 281 | # Legend Settings 282 | with st.sidebar.expander("Legend Settings", expanded=False): 283 | legend_positions = ['best', 'upper right', 'upper left', 'lower left', 'lower right', 'right', 284 | 'center left', 'center right', 'lower center', 'upper center', 'center'] 285 | legend_position = st.selectbox("Select Legend Position", options=legend_positions, index=0) 286 | legend_font_size = st.number_input("Legend Font Size", value=10, min_value=1, max_value=100) 287 | legend_font_family = st.selectbox("Legend Font Family", options=font_families, index=0) 288 | legend_font_style = st.selectbox("Legend Font Style", options=['normal', 'italic', 'oblique'], index=0) 289 | legend_font_weight = st.selectbox("Legend Font Weight", options=['normal', 'bold'], index=0) 290 | 291 | # Line and Color Settings 292 | with st.sidebar.expander("Line and Color Settings", expanded=False): 293 | use_different_colors = st.checkbox("Use different colors for samples?", value=True) 294 | color_palettes = ['deep', 'muted', 'bright', 'pastel', 'dark', 'colorblind', 'Set1', 'Set2', 'Set3', 'tab10'] 295 | selected_palette = st.selectbox("Select Color Palette", options=color_palettes, index=color_palettes.index('Set2')) 296 | use_custom_colors = False 297 | custom_colors = {} 298 | if use_different_colors: 299 | use_custom_colors = st.checkbox("Use custom colors?", value=False) 300 | if use_custom_colors: 301 | samples = data_to_plot['Sample'].unique() 302 | for sample in samples: 303 | color = st.color_picker(f"Select color for {sample}") 304 | custom_colors[sample] = color 305 | else: 306 | default_color = sns.color_palette(selected_palette, n_colors=1)[0] 307 | custom_colors['default'] = default_color 308 | 309 | use_different_markers = st.checkbox("Use different markers for samples?", value=False) 310 | use_different_line_styles = st.checkbox("Use different line styles for samples?", value=False) 311 | line_width = st.number_input("Line Width", value=1.0, min_value=0.1, max_value=10.0, step=0.1) 312 | opacity = st.slider("Color Opacity", min_value=0.0, max_value=1.0, value=0.7, step=0.05) 313 | 314 | # Error bar settings 315 | errorbar_line_width = st.number_input("Error Bar Line Width", value=0.5, min_value=0.1, max_value=10.0, step=0.1) 316 | errorbar_capsize = st.number_input("Error Bar Capsize", value=2.0, min_value=0.0, max_value=20.0, step=0.5) 317 | 318 | # Add the size of scatter points 319 | scatter_point_size = st.number_input("Scatter Point Size", value=5, min_value=1, max_value=20) 320 | 321 | # Plot Settings 322 | with st.sidebar.expander("Plot Settings", expanded=False): 323 | plot_title = st.text_input("Plot Title", "Growth Curve") 324 | width = st.slider("Plot Width (inches)", value=8.0, min_value=1.0, max_value=20.0, step=0.5) 325 | height = st.slider("Plot Height (inches)", value=3.0, min_value=1.0, max_value=20.0, step=0.5) 326 | 327 | # Process data and calculate mean and error 328 | error_type_mapping = {'Standard Deviation (SD)': 'SD', 'Standard Error (SE)': 'SE'} 329 | error_type_selected = error_type_mapping[error_type] 330 | 331 | summary_df = calculate_mean_and_error(data_to_plot, x_column, y_column, error_type=error_type_selected, calculation_type=calculation_type) 332 | 333 | # Call the plotting function 334 | fig = plot_seaborn(summary_df, x_axis_title, y_axis_title, plot_title, theme, width, height, show_grid, 335 | x_limits, y_limits, x_tick_interval, y_tick_interval, legend_position, 336 | y_scale, remove_borders, use_different_colors, use_custom_colors, use_different_markers, 337 | use_different_line_styles, custom_colors, 338 | font_family, axis_label_font_size, axis_label_font_style, axis_label_font_weight, 339 | title_font_size, title_font_style, title_font_weight, tick_label_font_size, 340 | tick_label_font_style, tick_label_font_weight, 341 | legend_font_size, legend_font_family, legend_font_style, legend_font_weight, 342 | line_width, opacity, errorbar_line_width, errorbar_capsize, scatter_point_size, 343 | selected_palette=selected_palette) 344 | 345 | st.pyplot(fig) 346 | # Save Plot Options 347 | st.header("Save the Plot") 348 | 349 | col1, col2, col3 = st.columns(3) 350 | with col1: 351 | save_width = st.number_input("Width (inches) for saved plot", value=8.0, min_value=1.0, max_value=100.0, step=1.0) 352 | with col2: 353 | save_height = st.number_input("Height (inches) for saved plot", value=6.0, min_value=1.0, max_value=100.0, step=1.0) 354 | with col3: 355 | save_dpi = st.number_input("Resolution (DPI) for saved plot", value=300, min_value=50, max_value=1200, step=50) 356 | file_format = st.selectbox("Select File Format", options=["PNG", "SVG", "PDF"]) 357 | fig.set_size_inches(save_width, save_height) 358 | 359 | buf = BytesIO() 360 | fig.savefig(buf, format=file_format.lower(), dpi=save_dpi, bbox_inches='tight') 361 | st.download_button( 362 | label=f"Download plot as {file_format}", 363 | data=buf.getvalue(), 364 | file_name=f'growth_curve.{file_format.lower()}', 365 | mime=f'image/{file_format.lower()}' 366 | ) --------------------------------------------------------------------------------