Spaces:
Running
Running
File size: 12,812 Bytes
9c88354 0a7969d 9c88354 0a7969d 9c88354 0c74c50 0a7969d c20d7c1 0a7969d c20d7c1 0c74c50 c20d7c1 0a7969d 0c74c50 0a7969d c20d7c1 0c74c50 c20d7c1 0c74c50 c20d7c1 0c74c50 c20d7c1 0c74c50 c20d7c1 0c74c50 c20d7c1 0c74c50 c20d7c1 0c74c50 c20d7c1 0a7969d 0c74c50 0a7969d c20d7c1 0c74c50 c20d7c1 0c74c50 c20d7c1 0c74c50 c20d7c1 0c74c50 c20d7c1 0c74c50 c20d7c1 0a7969d 0c74c50 0a7969d 0c74c50 0a7969d 89c8c01 0a7969d 89c8c01 0a7969d 0c74c50 c20d7c1 0a7969d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import io
import os
import sys
import contextlib
import pandas as pd
import streamlit as st
from navicat_spock.spock import run_spock_from_args
# Add spock directory to system path if not already present
spock_dir: str = os.path.dirname(os.path.abspath(__file__)) + "/spock"
if spock_dir not in sys.path:
sys.path.append(spock_dir)
ACCEPTED_FILE_EXT = ["csv", "xlsx", "xls", "xlsm", "xlsb", "odf", "ods", "odt"]
# Check if the dataframe contains a target column
def check_columns(df: pd.DataFrame) -> None:
if not any(["target" in col.lower() for col in df.columns]):
raise ValueError(
"Missing the target column. Please add a column that contains `target` in the name."
)
# Cache the function to run spock with the provided dataframe and arguments
@st.cache_data(
show_spinner=False,
# hash_funcs={pd.DataFrame: lambda df: df.to_numpy().tobytes()},
)
def cached_run_fn(df, wp, verb, imputer_strat, plotmode, seed, prefit, setcbms):
with capture_stdout_with_timestamp() as stdout_io:
fig, _ = run_spock_from_args(
df,
wp=wp,
verb=verb,
imputer_strat=imputer_strat,
plotmode=plotmode,
seed=seed,
prefit=prefit,
setcbms=setcbms,
fig=None,
ax=None,
save_fig=False,
save_csv=False,
)
return fig, stdout_io.getvalue()
# Mock function for testing purposes
def mock_fn(df, *args, **kwargs):
import numpy as np
import matplotlib.pyplot as plt
check_columns(df)
print("WORKING")
fig, ax = plt.subplots()
ax.plot(np.random.rand(10))
return fig
# Load data from the uploaded file
def load_data(filepath, filename):
if filename.split(".")[-1] not in ACCEPTED_FILE_EXT:
raise ValueError(
f"Invalid file type. Please upload a file with one of the following extensions: {ACCEPTED_FILE_EXT}"
)
return (
pd.read_csv(filepath) if filename.endswith(".csv") else pd.read_excel(filepath)
)
# Context manager to capture stdout with a timestamp
@contextlib.contextmanager
def capture_stdout_with_timestamp():
class TimestampedIO(io.StringIO):
def write(self, msg):
if msg.strip(): # Only add a timestamp if the message is not just a newline
timestamped_msg = f"[{pd.Timestamp.now()}] {msg}"
else:
timestamped_msg = msg
super().write(timestamped_msg)
new_stdout = TimestampedIO()
old_stdout = sys.stdout
sys.stdout = new_stdout
try:
yield new_stdout
finally:
sys.stdout = old_stdout
@st.experimental_dialog("Import Data")
def import_data():
filepath = None
filename = None
st.write("Choose a dataset or upload your own file")
option = st.radio("Select an option:", ["Use example dataset", "Upload file"])
if option == "Use example dataset":
examples = {
"Clean dataset": {
"filepath": "examples/clean_data_example.xlsx",
"description": "The clean dataset is a reference dataset that includes 1 target variable and 2 descriptors. This is a typical example, where the goal of the model is to find a single descriptor or a combined descriptor (mathematical function of descriptor 1 and 2) that gives volcano like correlation with the target variable.",
},
"Noisy dataset": {
"filepath": "examples/noisy_data_example.xlsx",
"description": "The noisy dataset is a reference dataset that includes 1 target variable and 1 descriptor. This is a specific example where the kinetic data was compiled from duplicate or triplicate experiments, and the performance metric (target variable) is represented by the average value and standard deviation. In such instances, a single experimental is populated over three rows, such the first, second, and third row contains information on the upper bound, mean, and lower bound data, respectively, of the performance metric. The descriptor is values corresponding to these observations remain the same. The model fits through the data and generates a volcano-like trend.",
},
}
selected_example = st.selectbox(
"Choose an example dataset",
list(examples.keys()),
# format_func=lambda x: examples[x]["description"],
)
st.info(examples[selected_example]["description"])
if st.button("Load Example", use_container_width=True):
filepath = examples[selected_example]["filepath"]
filename = filepath.split("/")[-1]
else:
file = st.file_uploader("Upload a file", type=ACCEPTED_FILE_EXT)
if file is not None:
filepath = file
filename = file.name
if filepath is not None and filename is not None:
try:
df = load_data(filepath, filename)
st.session_state.df = df
st.session_state.filename = filename
st.rerun()
except Exception as e:
st.error(f"Error loading file: {e}")
@st.experimental_dialog("Guidelines", width="large")
def guidelines():
st.write(
"""
To effectively populate an Excel sheet (.xlsx or .csv format) to upload on the SPOCK web-app, we recommend the following practices to ensure the data is curated and organized properly. Begin by placing the name of the catalyst in the first column, followed by performance metric (target variable) in the second column. The header of the second column must be labeled as Target Tr and ensure this column does not have any missing or erroneous entries. Next, place each descriptor of interest (input features) in the adjacent columns, one variable per column (see the example provided: “clean_data_example”). Label each column clearly and ensure all cells are filled correctly without missing values or placeholders like "NAN" or "NA". All variables, including the performance metric and descriptors, must be numerical values and follow consistent formatting styles and decimal points. Double-check for outliers or anomalies that could skew model training and remove or correct these entries if necessary.
In cases where the kinetic data was compiled from duplicate or triplicate experiments, the performance metric will be represented by the average value and standard deviation. In such instances, a single experimental observation needs to be populated over three rows, such the first, second, and third row contains information on the upper bound, mean, and lower bound data, respectively, of the performance metric. The descriptor values corresponding to these observations remain the same (see the example provided: “noisy_data_example”). Before proceeding with model training, validate the calculations for the mean and standard deviations by cross-checking with the original raw data and using statistical formulas to ensure accuracy. Document all essential transformations or preprocessing steps in a separate document linked to the main sheet. This documentation helps ensure transparency and reproducibility in subsequent steps of the project. Maintain version control to track changes and updates to the dataset, ensuring long term reproducibility of results.
"""
)
def main():
st.set_page_config(
page_title="Navicat Spock",
page_icon="🌋",
initial_sidebar_state="expanded",
)
_, center, _ = st.columns(spec=[0.2, 0.6, 0.2])
center.image("res/spock_logo.png")
st.subheader("Generate volcano plots from catalytic data")
# Instructions
with st.expander("Instructions", expanded=False):
known_tab, unknown_tab = st.tabs(["Descriptor Known", "Descriptor Unknown"])
with known_tab:
st.markdown(
"""
### When the Descriptor is Known
1. **Prepare Your Data**
- Organize data in a tabular format
- Column 1: Catalyst name
- Column 2: Performance metric
- Column 3: Descriptor
- Label columns according to guidelines
2. **Import Data**
- Click "Import Data" to upload your Excel or CSV file
3. **Review and Adjust**
- Check your data in the displayed table
- Modify plot settings in the sidebar if needed
4. **Generate and Analyze**
- Click "Generate plot"
- Examine the plot and logs in their respective tabs
5. **Refine Results**
- Adjust the weighting power parameter
- Repeat steps 4-5 until you achieve satisfactory results
"""
)
with unknown_tab:
st.markdown(
"""
### When the Descriptor is Unknown
1. **Prepare Your Data**
- Organize data in a tabular format
- Column 1: Catalyst name
- Column 2: Performance metric
- Columns 3+: Potential descriptors
- Label columns according to guidelines
2. **Import Data**
- Click "Import Data" to upload your Excel or CSV file
3. **Review and Adjust**
- Check your data in the displayed table
- Modify plot settings in the sidebar if needed
4. **Generate and Analyze**
- Click "Generate plot"
- Examine the plot and logs in their respective tabs
5. **Refine Results**
- Adjust the weighting power parameter
- Repeat steps 4-5 until you achieve satisfactory results
"""
)
if st.button(
"Click here for more information/guidelines", use_container_width=True
):
guidelines()
if "df" not in st.session_state:
if st.button("Import Data", type="primary", use_container_width=True):
import_data()
st.stop()
# Display the data
st.header(f"Dataset : {st.session_state.filename}")
st.dataframe(st.session_state.df, use_container_width=True)
# Settings
with st.sidebar:
st.header("Settings")
wp = st.number_input(
"Weighting Power",
value=1,
help="The weighting power is the tuning parameter to fit the line segments on the data. Default value is set to 1. We recommend to vary this value between 0-3 for desired results.",
)
verb = st.number_input(
"Verbosity",
min_value=0,
max_value=7,
value=3,
help="This parameter is used to generate reports based on the outcome of the mode liftting. Default value is set to 3. We recommend to vary this value between 2-5 for desired level of report (log) generation.",
)
imputer_strat_dict = {
None: "none",
"Iterative": "iterative",
"Simple": "simple",
"KNN": "knn",
}
imputer_strat_value = st.selectbox(
"Imputer Strategy",
filter(lambda x: x, list(imputer_strat_dict.keys())),
index=None,
help="Imputer Strategy used to fill missing values",
)
imputer_strat = imputer_strat_dict[imputer_strat_value]
plotmode = st.number_input(
"Plot Mode",
min_value=1,
max_value=3,
value=2,
help="Different plot modes",
)
seed = st.number_input(
"Seed", min_value=0, value=None, help="Seed number to fix the random state"
)
prefit = st.toggle("Prefit", value=False)
setcbms = st.toggle("CBMS", value=True)
# Option to import new data
if st.button("Import New Data", type="secondary", use_container_width=True):
import_data()
if st.button("Generate plot", type="primary", use_container_width=True):
with st.spinner("Generating plot..."):
fig, logs = cached_run_fn(
st.session_state.df,
wp=wp,
verb=verb,
imputer_strat=imputer_strat,
plotmode=plotmode,
seed=seed,
prefit=prefit,
setcbms=setcbms,
)
st.header("Results")
plot, logs_tab = st.tabs(["Plot", "Logs"])
with plot:
st.pyplot(fig)
with logs_tab:
st.code(logs, language="bash")
if __name__ == "__main__":
main()
|