File size: 6,960 Bytes
a7eecd2
2f1b44f
a7eecd2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d130bc1
a7eecd2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
994dec4
a7eecd2
994dec4
a7eecd2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5b4d4a5
a7eecd2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2f1b44f
a7eecd2
 
95708f1
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

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")