Wine_Quality_f / src /streamlit_app.py
Man0707's picture
Update src/streamlit_app.py
994dec4 verified
import streamlit as st
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
# -------------------------- Page Config --------------------------
st.set_page_config(
page_title="Wine Quality Master 🍷",
page_icon="πŸ‡",
layout="centered",
initial_sidebar_state="expanded"
)
# -------------------------- Custom CSS - Dark Purple Magic --------------------------
st.markdown("""
<style>
.main {background: #0f001a; color: #e6e6fa;}
.stApp {background: linear-gradient(135deg, #2a0052, #000000);}
h1, h2 {background: linear-gradient(90deg, #9b59b6, #e91e63, #ff9800);
-webkit-background-clip: text; -webkit-text-fill-color: transparent;}
.wine-red {color: #c0392b;}
.wine-white {color: #f1c40f;}
.prediction-good {font-size: 2.5rem; font-weight: bold; text-align: center;
color: #8e44ad; text-shadow: 0 0 20px #9b59b6;}
</style>
""", unsafe_allow_html=True)
# -------------------------- Load Full Wine Quality Dataset (Red + White) --------------------------
@st.cache_data
def load_wine_data():
# Red wine
red = pd.read_csv("https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv", sep=";")
red["type"] = "Red"
# White wine
white = pd.read_csv("https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-white.csv", sep=";")
white["type"] = "White"
# Combine
df = pd.concat([red, white], ignore_index=True)
# Binary classification: Good (>=6), Bad (<6)
df["is_good"] = (df["quality"] >= 6).astype(int)
return df
df = load_wine_data()
# -------------------------- Hero Section --------------------------
col1, col2, col3 = st.columns([1,3,1])
with col2:
st.markdown("<h1 style='text-align:center;'>Wine Quality Master</h1>", unsafe_allow_html=True)
st.markdown("<h2 style='text-align:center;'>VinoVerdict</h2>", unsafe_allow_html=True)
st.markdown("<p style='text-align:center; font-size:1.4rem; opacity:0.9;'>Red or White – Will it be divine... or declined?</p>", unsafe_allow_html=True)
st.markdown("---")
# -------------------------- Dataset Info --------------------------
with st.container():
st.markdown("<div class='glass-card'>", unsafe_allow_html=True)
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric("Total Wines", f"{len(df):,}")
with col2:
st.metric("Red Wines", f"{len(df[df['type']=='Red']):,}", "Red Wine")
with col3:
st.metric("White Wines", f"{len(df[df['type']=='White']):,}", "White Wine")
with col4:
st.metric("Good Wines (β‰₯6)", f"{df['is_good'].sum():,}")
st.markdown("<br>", unsafe_allow_html=True)
st.dataframe(df.head(), use_container_width=True)
st.markdown("</div>", unsafe_allow_html=True)
# -------------------------- Prepare Features --------------------------
X = df.drop(columns=["quality", "is_good"])
y = df["is_good"]
# One-hot encode wine type (ensures consistent column order)
X = pd.get_dummies(X, columns=["type"], drop_first=False)
# Train-test split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
# Scale numerical features (preserve exact column order)
scaler = StandardScaler()
numerical_cols = [col for col in X.columns if col not in ["type_Red", "type_White"]]
X_train_num_scaled = scaler.fit_transform(X_train[numerical_cols])
X_train = pd.concat([
pd.DataFrame(X_train_num_scaled, columns=numerical_cols, index=X_train.index),
X_train[["type_Red", "type_White"]]
], axis=1)
X_test_num_scaled = scaler.transform(X_test[numerical_cols])
X_test = pd.concat([
pd.DataFrame(X_test_num_scaled, columns=numerical_cols, index=X_test.index),
X_test[["type_Red", "type_White"]]
], axis=1)
# Train model
@st.cache_resource
def train_model():
model = RandomForestClassifier(
n_estimators=1000,
max_depth=15,
random_state=42,
n_jobs=-1,
class_weight="balanced"
)
model.fit(X_train, y_train)
return model
model = train_model()
# Accuracy
y_pred = model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
with st.container():
st.markdown("<div class='glass-card'>", unsafe_allow_html=True)
st.success(f"Model Accuracy on Test Set: *{accuracy:.4f}* ({accuracy*100:.2f}%)")
st.markdown("</div>", unsafe_allow_html=True)
# -------------------------- Interactive Prediction --------------------------
st.markdown("<div class='glass-card'>", unsafe_allow_html=True)
st.header("Predict Your Wine's Destiny")
# Wine type selector
wine_type = st.selectbox("Choose Wine Type", options=["Red", "White"], index=0)
col1, col2 = st.columns(2)
input_data = {"type_Red": 0, "type_White": 0}
input_data[f"type_{wine_type}"] = 1
features = [col for col in X.columns if col not in ["type_Red", "type_White"]]
for i, feature in enumerate(features):
col = col1 if i % 2 == 0 else col2
with col:
min_v, max_v = float(df[feature].min()), float(df[feature].max())
mean_v = float(df[feature].mean())
val = st.slider(
feature.replace("_", " ").title(),
min_value=min_v,
max_value=max_v,
value=mean_v,
step=0.1,
format="%.2f"
)
input_data[feature] = val
if st.button("Reveal the Quality!", use_container_width=True, type="primary"):
input_df = pd.DataFrame([input_data])
# Scale numerical columns (FIX: Reconstruct to match exact column order)
input_num_scaled = scaler.transform(input_df[numerical_cols])
input_scaled = pd.concat([
pd.DataFrame(input_num_scaled, columns=numerical_cols, index=input_df.index),
input_df[["type_Red", "type_White"]]
], axis=1)
pred = model.predict(input_scaled)[0]
prob = model.predict_proba(input_scaled)[0]
st.markdown("<br>", unsafe_allow_html=True)
if pred == 1:
st.balloons()
st.markdown(f"<h2 class='prediction-good'>OUTSTANDING WINE! {wine_type} Wine</h2>", unsafe_allow_html=True)
st.success(f"Confidence: *{prob[1]:.1%}* – This belongs in a museum... or your glass right now!")
else:
st.error(f"Not quite a masterpiece... {wine_type} Wine")
st.warning(f"Confidence: *{prob[0]:.1%}* – Maybe use it for cooking?")
# Feature importance hint
st.info(f"Pro tip: For {wine_type.lower()} wines, alcohol, sulphates, and volatile acidity matter most!")
st.markdown("</div>", unsafe_allow_html=True)
# -------------------------- Footer --------------------------
st.markdown("---")
st.caption("Made with passion | Dataset: UCI Wine Quality (Red + White) | Model: Random Forest |Created by: MAN JETANI")