Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import pandas as pd | |
| import matplotlib | |
| import matplotlib.pyplot as plt | |
| import plotly.express as px | |
| import numpy as np | |
| import plotly.graph_objects as go | |
| import shutil | |
| import os | |
| # from blend_logic import run_dummy_prediction | |
| ##---- fucntions ------ | |
| import pandas as pd | |
| import streamlit as st | |
| # Load fuel data from CSV (create this file if it doesn't exist) | |
| SRC_CSV_PATH = os.path.join(os.path.dirname(__file__), "fuel_properties.csv") | |
| # Writable path (e.g., /tmp or home dir) | |
| FUEL_CSV_PATH = os.path.join("/tmp", "fuel_properties.csv") | |
| # If not already copied, copy it over | |
| if not os.path.exists(FUEL_CSV_PATH): | |
| shutil.copy(SRC_CSV_PATH, FUEL_CSV_PATH) | |
| def load_fuel_data(): | |
| """Load fuel data from CSV or create default if not exists""" | |
| try: | |
| df = pd.read_csv(FUEL_CSV_PATH, index_col=0) | |
| return df.to_dict('index') | |
| except FileNotFoundError: | |
| # Create default fuel properties if file doesn't exist | |
| default_fuels = { | |
| "Gasoline": {f"Property{i+1}": round(0.7 + (i*0.02), 1) for i in range(10)}, | |
| "Diesel": {f"Property{i+1}": round(0.8 + (i*0.02), 1) for i in range(10)}, | |
| "Ethanol": {f"Property{i+1}": round(0.75 + (i*0.02), 1) for i in range(10)}, | |
| "Biodiesel": {f"Property{i+1}": round(0.85 + (i*0.02), 1) for i in range(10)}, | |
| "Jet Fuel": {f"Property{i+1}": round(0.78 + (i*0.02), 1) for i in range(10)} | |
| } | |
| pd.DataFrame(default_fuels).T.to_csv(FUEL_CSV_PATH) | |
| return default_fuels | |
| # Initialize or load fuel data | |
| if 'FUEL_PROPERTIES' not in st.session_state: | |
| st.session_state.FUEL_PROPERTIES = load_fuel_data() | |
| def save_fuel_data(): | |
| """Save current fuel data to CSV""" | |
| pd.DataFrame(st.session_state.FUEL_PROPERTIES).T.to_csv(FUEL_CSV_PATH) | |
| # FUEL_PROPERTIES = st.session_state.FUEL_PROPERTIES | |
| # ---------------------- Page Config ---------------------- | |
| st.set_page_config( | |
| layout="wide", | |
| page_title="Eagle Blend Optimizer", | |
| page_icon="π¦ ", | |
| initial_sidebar_state="expanded" | |
| ) | |
| # ---------------------- Custom Styling ---------------------- ##e0e0e0; | |
| st.markdown(""" | |
| <style> | |
| .block-container { | |
| padding-top: 1rem; | |
| } | |
| /* Main app background */ | |
| .stApp { | |
| background-color: #f8f5f0; | |
| overflow: visible; | |
| padding-top: 0 | |
| } | |
| /* Remove unnecessary space at the top */ | |
| /* Remove any fixed headers */ | |
| .stApp > header { | |
| position: static !important; | |
| } | |
| /* Header styling */ | |
| .header { | |
| background: linear-gradient(135deg, #654321 0%, #8B4513 100%); | |
| color: white; | |
| padding: 2rem 1rem; | |
| margin-bottom: 2rem; | |
| border-radius: 0 0 15px 15px; | |
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
| } | |
| /* Metric card styling */ | |
| .metric-card { | |
| background: #ffffff; /* Pure white cards for contrast */ | |
| border-radius: 10px; | |
| padding: 1.5rem; | |
| box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15); | |
| height: 100%; | |
| transition: all 0.3s ease; | |
| border: 1px solid #CFB53B; | |
| } | |
| .metric-card:hover { | |
| transform: translateY(-3px); | |
| background: #FFF8E1; /* Very light blue tint on hover */ | |
| box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); | |
| border-color: #8B4513; | |
| } | |
| /* Metric value styling */ | |
| .metric-value { | |
| color: #8B4513 !important; /* Deep, vibrant blue */ | |
| font-weight: 700; | |
| font-size: 1.8rem; | |
| text-shadow: 0 1px 2px rgba(0, 82, 204, 0.1); | |
| } | |
| /* Metric label styling */ | |
| .metric-label { | |
| color: #654321; /* Navy blue-gray */ | |
| font-weight: 600; | |
| letter-spacing: 0.5px; | |
| } | |
| /* Metric delta styling */ | |
| .metric-delta { | |
| color: #A67C52; /* Medium blue-gray */ | |
| font-size: 0.9rem; | |
| font-weight: 500; | |
| } | |
| /* Tab styling */ | |
| /* Main tab container */ | |
| .stTabs [data-baseweb="tab-list"] { | |
| display: flex; | |
| justify-content: center; | |
| gap: 6px; | |
| padding: 8px; | |
| margin: 0 auto; | |
| width: 95% !important; | |
| } | |
| /* Individual tabs */ | |
| .stTabs [data-baseweb="tab"] { | |
| flex: 1; /* Equal width distribution */ | |
| min-width: 0; /* Allows flex to work */ | |
| height: 60px; /* Fixed height or use aspect ratio */ | |
| padding: 0 12px; | |
| margin: 0; | |
| font-weight: 600; | |
| font-size: 1rem; | |
| color: #654321; | |
| background: #FFF8E1; | |
| border: 2px solid #CFB53B; | |
| border-radius: 12px; | |
| transition: all 0.3s ease; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| text-align: center; | |
| } | |
| /* Hover state */ | |
| .stTabs [data-baseweb="tab"]:hover { | |
| background: #FFE8A1; | |
| transform: translateY(-2px); | |
| } | |
| /* Active tab */ | |
| .stTabs [aria-selected="true"] { | |
| background: #654321; | |
| color: #FFD700 !important; | |
| border-color: #8B4513; | |
| font-size: 1.05rem; | |
| } | |
| /* Icon sizing */ | |
| .stTabs [data-baseweb="tab"] svg { | |
| width: 24px !important; | |
| height: 24px !important; | |
| margin-right: 8px !important; | |
| } | |
| /* Button styling */ | |
| .stButton>button { | |
| background-color: #654321; | |
| color: #FFD700 !important; | |
| border-radius: 8px; | |
| padding: 0.5rem 1rem; | |
| transition: all 0.3s ease; | |
| } | |
| .stButton>button:hover { | |
| background-color: #8B4513; | |
| color: white; | |
| } | |
| /* Dataframe styling */ | |
| .table-container { | |
| display: flex; | |
| justify-content: center; | |
| margin-top: 30px; | |
| } | |
| .table-inner { | |
| width: 50%; | |
| } | |
| @media only screen and (max-width: 768px) { | |
| .table-inner { | |
| width: 90%; /* For mobile */ | |
| } | |
| } | |
| .stDataFrame { | |
| border-radius: 10px; | |
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); | |
| background-color:white !important; | |
| border: #CFB53B !important; | |
| } | |
| /* Section headers */ | |
| .st-emotion-cache-16txtl3 { | |
| padding-top: 1rem; | |
| } | |
| /* Custom hr style */ | |
| .custom-divider { | |
| border: 0; | |
| height: 1px; | |
| background: linear-gradient(90deg, transparent, #dee2e6, transparent); | |
| margin: 2rem 0; | |
| } | |
| /* Consistent chart styling */ | |
| .stPlotlyChart { | |
| border-radius: 10px; | |
| background: white; | |
| padding: 15px; | |
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); | |
| margin-bottom: 25px; | |
| } | |
| /* Match number inputs */ | |
| # .stNumberInput > div { | |
| # padding: 0.25rem 0.5rem !important; | |
| # } | |
| #/* Better select widget alignment */ | |
| # .stSelectbox > div { | |
| # margin-bottom: -15px; | |
| # } | |
| .custom-uploader > label div[data-testid="stFileUploadDropzone"] { | |
| border: 2px solid #4CAF50; | |
| background-color: #4CAF50; | |
| color: white; | |
| padding: 0.6em 1em; | |
| border-radius: 0.5em; | |
| text-align: center; | |
| cursor: pointer; | |
| } | |
| .custom-uploader > label div[data-testid="stFileUploadDropzone"]:hover { | |
| background-color: #45a049; | |
| } | |
| /* Color scale adjustments */ | |
| .plotly .colorbar { | |
| padding: 10px !important; | |
| color: #654321 !important; | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| # ---------------------- App Header ---------------------- | |
| st.markdown(""" | |
| <div class="header"> | |
| <h1 style='text-align: center; margin-bottom: 0.5rem;'>π¦ Eagle Blend Optimizer</h1> | |
| <h4 style='text-align: center; font-weight: 400; margin-top: 0;'> | |
| AI-Powered Fuel Blend Property Prediction & Optimization | |
| </h4> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| #------ universal variables | |
| # ---------------------- Tabs ---------------------- | |
| tabs = st.tabs([ | |
| "π Dashboard", | |
| "ποΈ Blend Designer", | |
| "π€ Nothing For Now", | |
| "βοΈ Optimization Engine", | |
| "π Fuel Registry", | |
| "π§ Model Insights" | |
| ]) | |
| # ---------------------- Dashboard Tab ---------------------- | |
| with tabs[0]: | |
| st.subheader("Performance Metrics") | |
| col1, col2, col3, col4 = st.columns(4) | |
| with col1: | |
| st.markdown(""" | |
| <div class="metric-card"> | |
| <div class="metric-label">Model Accuracy</div> | |
| <div class="metric-value">94.7%</div> | |
| <div class="metric-delta">RΒ² Score</div> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| with col2: | |
| st.markdown(""" | |
| <div class="metric-card"> | |
| <div class="metric-label">Predictions Made</div> | |
| <div class="metric-value">12,847</div> | |
| <div class="metric-delta">Today</div> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| with col3: | |
| st.markdown(""" | |
| <div class="metric-card"> | |
| <div class="metric-label">Optimizations</div> | |
| <div class="metric-value">156</div> | |
| <div class="metric-delta">This Week</div> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| with col4: | |
| st.markdown(""" | |
| <div class="metric-card"> | |
| <div class="metric-label">Cost Savings</div> | |
| <div class="metric-value">$2.4M</div> | |
| <div class="metric-delta">Estimated Annual</div> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| st.markdown('<hr class="custom-divider">', unsafe_allow_html=True) | |
| st.subheader("Current Blend Properties") | |
| blend_props = { | |
| "Property 1": 0.847, | |
| "Property 2": 0.623, | |
| "Property 3": 0.734, | |
| "Property 4": 0.912, | |
| "Property 5": 0.456, | |
| "Property 6": -1.234, | |
| } | |
| # Enhanced dataframe display | |
| df = pd.DataFrame(blend_props.items(), columns=["Property", "Value"]) | |
| # st.dataframe( | |
| # df.style | |
| # .background_gradient(cmap="YlOrBr", subset=["Value"]) | |
| # .format({"Value": "{:.3f}"}), | |
| # use_container_width=True | |
| # ) | |
| st.markdown('<div class="table-container"><div class="table-inner">', unsafe_allow_html=True) | |
| st.dataframe(df, use_container_width=True) | |
| st.markdown('</div></div>', unsafe_allow_html=True) | |
| with tabs[1]: | |
| col_header = st.columns([0.8, 0.2]) | |
| with col_header[0]: | |
| st.subheader("ποΈ Blend Designer") | |
| with col_header[1]: | |
| batch_blend = st.checkbox("Batch Blend Mode", value=False, | |
| help="Switch between manual input and predefined fuel selection", | |
| key="batch_blend_mode") | |
| # Initialize session state | |
| if 'show_visualization' not in st.session_state: | |
| st.session_state.show_visualization = False | |
| if 'blended_value' not in st.session_state: | |
| st.session_state.blended_value = None | |
| if 'selected_property' not in st.session_state: | |
| st.session_state.selected_property = "Property1" | |
| # Batch mode file upload | |
| if batch_blend: | |
| st.subheader("π€ Batch Processing") | |
| uploaded_file = st.file_uploader("Upload CSV File", type=["csv"], key="Batch_upload") | |
| weights = [0.1, 0.2, 0.25, 0.15, 0.3] # Default weights for batch mode | |
| if not uploaded_file: | |
| st.warning("Please upload a CSV file for batch processing") | |
| data_input = None | |
| else: | |
| try: | |
| data_input = pd.read_csv(uploaded_file) | |
| st.success("File uploaded successfully") | |
| st.dataframe(data_input.head()) | |
| except Exception as e: | |
| st.error(f"Error reading file: {str(e)}") | |
| data_input = None | |
| else: | |
| # Regular mode | |
| data_input = None | |
| weights, props = [], [] | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.markdown("##### βοΈ Component Weights") | |
| for i in range(5): | |
| weight = st.number_input( | |
| f"Weight for Component {i+1}", | |
| min_value=0.0, | |
| max_value=1.0, | |
| value=0.2, | |
| step=0.01, | |
| key=f"w_{i}" | |
| ) | |
| weights.append(weight) | |
| with col2: | |
| st.markdown("##### Fuel Selection") | |
| for i in range(5): | |
| fuel = st.selectbox( | |
| f"Component {i+1} Fuel Type", | |
| options=list(st.session_state.FUEL_PROPERTIES.keys()), | |
| key=f"fuel_{i}" | |
| ) | |
| props.append(st.session_state.FUEL_PROPERTIES[fuel]) | |
| if st.button("βοΈ Predict Blended Property", key="predict_btn"): | |
| if batch_blend: | |
| if data_input is None: | |
| st.error("β οΈ Please upload a valid CSV file first!") | |
| st.session_state.show_visualization = False | |
| else: | |
| st.session_state.show_visualization = True | |
| else: | |
| if abs(sum(weights) - 1.0) > 0.01: | |
| st.warning("β οΈ The total of weights must be **1.0**.") | |
| st.session_state.show_visualization = False | |
| else: | |
| st.session_state.show_visualization = True | |
| if st.session_state.show_visualization: | |
| # Show calculation details | |
| st.subheader("Blend Components Data") | |
| if not batch_blend: | |
| weights_data = {f"Component{i+1}_fraction": weights[i] for i in range(len(weights))} | |
| props_data = {f"Component{i+1}_{j}": props[i][j] for j in props[i].keys() for i in range(len(props))} | |
| combined = {**weights_data, **props_data} | |
| data_input = pd.DataFrame([combined]) | |
| st.write("Properties:", data_input) | |
| # Show visualization only if prediction was made | |
| if st.session_state.show_visualization: | |
| if not batch_blend: | |
| st.markdown('<hr class="custom-divider">', unsafe_allow_html=True) | |
| st.subheader("Blend Visualization") | |
| components = [f"Component {i+1}" for i in range(5)] | |
| # 1. Weight Distribution Pie Chart | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| fig1 = px.pie( | |
| names=components, | |
| values=weights, | |
| title="Weight Distribution", | |
| color_discrete_sequence=['#8B4513', '#CFB53B', '#654321'], | |
| hole=0.4 | |
| ) | |
| fig1.update_layout( | |
| margin=dict(t=50, b=10), | |
| showlegend=False | |
| ) | |
| fig1.update_traces( | |
| textposition='inside', | |
| textinfo='percent+label', | |
| marker=dict(line=dict(color='#ffffff', width=1)) | |
| ) | |
| st.plotly_chart(fig1, use_container_width=True) | |
| # 2. Property Comparison Bar Chart | |
| with col2: | |
| # Property selection for fuel mode | |
| viz_property = st.selectbox( | |
| "Select Property to View", | |
| [f"Property{i+1}" for i in range(10)], | |
| key="viz_property" | |
| ) | |
| bar_values = [p[viz_property] for p in props] | |
| blended_value = 123 #Modify | |
| fig2 = px.bar( | |
| x=components, | |
| y=bar_values, | |
| title=f"{viz_property} Values", | |
| color=bar_values, | |
| color_continuous_scale='YlOrBr' | |
| ) | |
| fig2.update_layout( | |
| yaxis_title=viz_property, | |
| xaxis_title="Component", | |
| margin=dict(t=50, b=10), | |
| coloraxis_showscale=False | |
| ) | |
| fig2.add_hline( | |
| y=blended_value, | |
| line_dash="dot", | |
| line_color="#ff6600", | |
| annotation_text="Blended Value", | |
| annotation_position="top right" | |
| ) | |
| st.plotly_chart(fig2, use_container_width=True) | |
| # Display the calculated value prominently | |
| st.markdown(f""" | |
| <div style=" | |
| background-color: #FAF3E6; | |
| border-left: 4px solid #8B4513; | |
| border-radius: 4px; | |
| padding: 12px; | |
| margin: 12px 0; | |
| "> | |
| <p style="margin: 0; color: #654321; | |
| font-size: 2.2rem; | |
| font-weight: 800; | |
| color: #000; | |
| text-align:center;"> | |
| Calculated <strong>{viz_property}</strong> = | |
| <strong style="color: #000">{blended_value:.4f}</strong> | |
| </p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| else: | |
| # Batch mode visualization placeholder | |
| st.markdown('<hr class="custom-divider">', unsafe_allow_html=True) | |
| st.subheader("Batch Processing Results") | |
| st.dataframe(data_input, use_container_width=True) | |
| # st.info("Batch processing complete. Add custom visualizations here.") | |
| with tabs[2]: | |
| st.subheader("π€ Nothing FOr NOw") | |
| # uploaded_file = st.file_uploader("Upload CSV File", type=["csv"]) | |
| # if uploaded_file: | |
| # df = pd.read_csv(uploaded_file) | |
| # st.success("File uploaded successfully") | |
| # st.dataframe(df.head()) | |
| # if st.button("βοΈ Run Batch Prediction"): | |
| # result_df = df.copy() | |
| # # result_df["Predicted_Property"] = df.apply( | |
| # # lambda row: run_dummy_prediction(row.values[:5], row.values[5:10]), axis=1 | |
| # # ) | |
| # st.success("Batch prediction completed") | |
| # st.dataframe(result_df.head()) | |
| # csv = result_df.to_csv(index=False).encode("utf-8") | |
| # st.download_button("Download Results", csv, "prediction_results.csv", "text/csv") | |
| with tabs[3]: | |
| st.subheader("βοΈ Optimization Engine") | |
| # Pareto frontier demo | |
| st.markdown("#### Cost vs Performance Trade-off") | |
| np.random.seed(42) | |
| optimization_data = pd.DataFrame({ | |
| 'Cost ($/ton)': np.random.uniform(100, 300, 50), | |
| 'Performance Score': np.random.uniform(70, 95, 50) | |
| }) | |
| fig3 = px.scatter( | |
| optimization_data, | |
| x='Cost ($/ton)', | |
| y='Performance Score', | |
| title="Potential Blend Formulations", | |
| color='Performance Score', | |
| color_continuous_scale='YlOrBr' | |
| ) | |
| # Add dummy pareto frontier | |
| x_pareto = np.linspace(100, 300, 10) | |
| y_pareto = 95 - 0.1*(x_pareto-100) | |
| fig3.add_trace(px.line( | |
| x=x_pareto, | |
| y=y_pareto, | |
| color_discrete_sequence= ['#8B4513', '#CFB53B', '#654321'] | |
| ).data[0]) | |
| fig3.update_layout( | |
| showlegend=False, | |
| annotations=[ | |
| dict( | |
| x=200, | |
| y=88, | |
| text="Pareto Frontier", | |
| showarrow=True, | |
| arrowhead=1, | |
| ax=-50, | |
| ay=-30 | |
| ) | |
| ] | |
| ) | |
| st.plotly_chart(fig3, use_container_width=True) | |
| # Blend optimization history | |
| st.markdown("#### Optimization Progress") | |
| iterations = np.arange(20) | |
| performance = np.concatenate([np.linspace(70, 85, 10), np.linspace(85, 89, 10)]) | |
| fig4 = px.line( | |
| x=iterations, | |
| y=performance, | |
| title="Best Performance by Iteration", | |
| markers=True | |
| ) | |
| fig4.update_traces( | |
| line_color='#1d3b58', | |
| marker_color='#2c5282', | |
| line_width=2.5 | |
| ) | |
| fig4.update_layout( | |
| yaxis_title="Performance Score", | |
| xaxis_title="Iteration" | |
| ) | |
| st.plotly_chart(fig4, use_container_width=True) | |
| with tabs[4]: | |
| st.subheader("π Fuel Registry") # Changed to book emoji for registry | |
| # Button to add new fuel | |
| st.markdown("#### β Add a New Fuel Type") | |
| with st.expander("Click to Add New Fuel", expanded=False): | |
| with st.form("new_fuel_form", clear_on_submit=False): | |
| fuel_name = st.text_input("Fuel Name", placeholder="e.g. Bioethanol") | |
| cols = st.columns(5) | |
| properties = {} | |
| for i in range(10): | |
| with cols[i % 5]: | |
| prop_val = st.number_input( | |
| f"Property {i+1}", | |
| min_value=0.0, | |
| step=0.1, | |
| key=f"prop_{i}", | |
| format="%.2f" | |
| ) | |
| properties[f"Property{i+1}"] = round(prop_val, 2) | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| submitted = st.form_submit_button("πΎ Save Fuel", use_container_width=True) | |
| with col2: | |
| cancelled = st.form_submit_button("β Cancel", use_container_width=True) | |
| if submitted: | |
| if not fuel_name.strip(): | |
| st.warning("Fuel name cannot be empty.") | |
| elif fuel_name in st.session_state.FUEL_PROPERTIES: | |
| st.error(f"{fuel_name} already exists in registry.") | |
| else: | |
| # Update both session state and CSV | |
| st.session_state.FUEL_PROPERTIES[fuel_name] = properties | |
| save_fuel_data() | |
| st.success(f"{fuel_name} successfully added!") | |
| st.rerun() # Refresh to show new fuel | |
| if cancelled: | |
| st.rerun() | |
| with st.expander("Batch Add New Fuel", expanded=False): | |
| uploaded_file = st.file_uploader( | |
| "π€ Upload Fuel Batch (CSV)", | |
| type=['csv'], | |
| accept_multiple_files=False, | |
| key="fuel_uploader", | |
| help="Upload a CSV file with the same format as the exported registry" | |
| ) | |
| if uploaded_file is not None: | |
| try: | |
| new_fuels = pd.read_csv(uploaded_file, index_col=0).to_dict('index') | |
| # Check for duplicates | |
| duplicates = [name for name in new_fuels if name in st.session_state.FUEL_PROPERTIES] | |
| if duplicates: | |
| st.warning(f"These fuels already exist and won't be updated: {', '.join(duplicates)}") | |
| # Only add new fuels | |
| new_fuels = {name: props for name, props in new_fuels.items() | |
| if name not in st.session_state.FUEL_PROPERTIES} | |
| if new_fuels: | |
| st.session_state.FUEL_PROPERTIES.update(new_fuels) | |
| save_fuel_data() | |
| st.success(f"Added {len(new_fuels)} new fuel(s) to registry!") | |
| st.rerun() | |
| else: | |
| st.info("No new fuels to add from the uploaded file.") | |
| except Exception as e: | |
| st.error(f"Error processing file: {str(e)}") | |
| st.error("Please ensure the file matches the expected format") | |
| # Display current fuel properties | |
| st.markdown("#### π Current Fuel Properties") | |
| st.dataframe( | |
| pd.DataFrame(st.session_state.FUEL_PROPERTIES).T.style | |
| .background_gradient(cmap="YlOrBr", axis=None) | |
| .format(precision=2), | |
| use_container_width=True, | |
| height=(len(st.session_state.FUEL_PROPERTIES) + 1) * 35 + 3, | |
| hide_index=False | |
| ) | |
| # File operations section | |
| st.download_button( | |
| label="π₯ Download Registry (CSV)", | |
| data=pd.DataFrame(st.session_state.FUEL_PROPERTIES).T.to_csv().encode('utf-8'), | |
| file_name='fuel_properties.csv', | |
| mime='text/csv', | |
| # use_container_width=True | |
| ) | |
| with tabs[5]: | |
| st.subheader("π§ Model Insights") | |
| # Feature importance | |
| st.markdown("#### Property Importance") | |
| features = ['Property 1', 'Property 2', 'Property 3', 'Property 4', 'Property 5'] | |
| importance = np.array([0.35, 0.25, 0.2, 0.15, 0.05]) | |
| fig5 = px.bar( | |
| x=importance, | |
| y=features, | |
| orientation='h', | |
| title="Feature Importance for Blend Prediction", | |
| color=importance, | |
| color_continuous_scale='YlOrBr' | |
| ) | |
| fig5.update_layout( | |
| xaxis_title="Importance Score", | |
| yaxis_title="Property", | |
| coloraxis_showscale=False | |
| ) | |
| st.plotly_chart(fig5, use_container_width=True) | |
| # SHAP values demo | |
| st.markdown("#### Property Impact Direction") | |
| fig6 = px.scatter( | |
| x=np.random.randn(100), | |
| y=np.random.randn(100), | |
| color=np.random.choice(features, 100), | |
| title="SHAP Values (Simulated)", | |
| labels={'x': 'Impact on Prediction', 'y': 'Property Value'} | |
| ) | |
| fig6.update_traces( | |
| marker=dict(size=10, opacity=0.7), | |
| selector=dict(mode='markers') | |
| ) | |
| fig6.add_vline(x=0, line_width=1, line_dash="dash") | |
| st.plotly_chart(fig6, use_container_width=True) | |
| # st.markdown(""" | |
| # <style> | |
| # /* Consistent chart styling */ | |
| # .stPlotlyChart { | |
| # border-radius: 10px; | |
| # background: white; | |
| # padding: 15px; | |
| # box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); | |
| # margin-bottom: 25px; | |
| # } | |
| # /* Better select widget alignment */ | |
| # .stSelectbox > div { | |
| # margin-bottom: -15px; | |
| # } | |
| # /* Color scale adjustments */ | |
| # .plotly .colorbar { | |
| # padding: 10px !important; | |
| # } | |
| # </style> | |
| # """, unsafe_allow_html=True) |