Loading...

Wine Quality

This example uses the Wine Quality dataset to predict the quality of white wine. You can download the Jupyter Notebook of the study here.

  • fixed acidity

  • volatile acidity

  • citric acid

  • residual sugar

  • total sulfur dioxide

  • free sulfur dioxide

  • density

  • pH

  • sulphates

  • alcohol

  • quality (score between 0 and 10)

We will follow the data science cycle (Data Exploration - Data Preparation - Data Modeling - Model Evaluation - Model Deployment) to solve this problem.

Initialization

This example uses the following version of VerticaPy:

import verticapy as vp

vp.__version__
Out[2]: '1.1.0'

Connect to Vertica. This example uses an existing connection called VerticaDSN. For details on how to create a connection, see the Connection tutorial. You can skip the below cell if you already have an established connection.

vp.connect("VerticaDSN")

Let’s create a Virtual DataFrame of the dataset.

from verticapy.datasets import load_winequality

winequality = load_winequality()
winequality.head(5)
123
fixed_acidity
Numeric(6,3)
100%
...
123
good
Int
100%
Abc
color
Varchar(20)
100%
13.9...1white
24.7...0white
34.7...1white
44.7...0white
54.9...0white

Data Exploration and Preparation

Let’s explore the data by displaying descriptive statistics of all the columns.

winequality.describe()
...
approx_75%
max
"fixed_acidity"...7.715.9
"volatile_acidity"...0.41.58
"citric_acid"...0.391.66
"residual_sugar"...8.165.8
"chlorides"...0.0650.611
"free_sulfur_dioxide"...41.0289.0
"total_sulfur_dioxide"...156.0440.0
"density"...0.996991.03898
"pH"...3.324.01
"sulphates"...0.62.0
"alcohol"...11.314.9
"quality"...6.09.0
"good"...0.01.0
The quality of a wine is based on the equilibrium between certain components:
  • For red wines: tannin/smoothness/acidity

  • For white wines: smoothness/acidity

Based on this, we don’t have the data to create a good model for red wines (the tannins weren’t extracted). We do, however, have enough data to make a good model for white wines, so let’s filter out red wines from our study.

winequality.filter(winequality["color"] == "white").drop(["good", "color"])
123
fixed_acidity
Numeric(6,3)
100%
...
123
volatile_acidity
Numeric(7,4)
100%
123
quality
Int
100%
14.4...0.328
24.4...0.466
34.4...0.547
44.7...0.675
54.8...0.335
64.8...0.346
74.9...0.476
85.0...0.277
95.0...0.277
105.0...0.298
115.0...0.336
125.0...0.558
135.0...0.615
145.1...0.215
155.1...0.235
165.1...0.257
175.1...0.266
185.1...0.356
195.1...0.356
205.1...0.356

Let’s draw the correlation matrix of the dataset.

winequality.corr(method = "spearman")

We can see a strong correlation between the density and the alcohol degree (the alcohol degree describes the density of pure ethanol in the wine).

We can drop the density column since it doesn’t influence the quality of the white wine (instead, its presence will just bias the data).

winequality.drop(["density"])
123
fixed_acidity
Numeric(6,3)
100%
...
123
alcohol
Float
100%
123
quality
Int
100%
14.2...12.07
24.2...8.03
34.5...8.05
44.6...10.25
54.8...11.87
64.8...11.37
74.8...12.27
84.8...10.36
94.8...11.96
104.9...9.46
114.9...14.08
124.9...10.46666666666675
134.9...10.46666666666675
145.0...11.36
155.0...13.07
165.0...11.86
175.0...14.07
185.1...12.33333333333335
195.1...10.16
205.1...11.07

We’re working with the scores given by wine tasters, so it’s likely that two closely competing wines will have a similar score. Knowing this, a k-nearest neighbors (KNN) model would be best.

KNN is sensitive to unnormalized data so we’ll have to normalize our data.

winequality.normalize(
    [
        "free_sulfur_dioxide",
        "residual_sugar",
        "pH",
        "sulphates",
        "volatile_acidity",
        "fixed_acidity",
        "citric_acid",
        "chlorides",
        "total_sulfur_dioxide",
        "alcohol"
    ],
    method = "robust_zscore",
)
123
fixed_acidity
Float
100%
...
123
alcohol
Float
100%
123
quality
Int
100%
1-3.237555645487657...1.618777822743838
2-3.237555645487657...1.821125050586816
3-3.237555645487657...1.214083367057877
4-2.8328611898017...2.15837043032515
5-2.697963037906381...-0.3372453797382985
6-2.697963037906381...-0.3372453797382986
7-2.563064886011062...0.7419398354242546
8-2.428166734115743...1.483879670848517
9-2.428166734115743...1.483879670848517
10-2.428166734115743...1.686226898691498
11-2.428166734115743...0.4046944556859576
12-2.428166734115743...1.416430594900858
13-2.428166734115743...2.090921354377445
14-2.293268582220424...0.05
15-2.293268582220424...0.7419398354242545
16-2.293268582220424...1.146634291110217
17-2.293268582220424...-0.8093889113719156
18-2.293268582220424...0.7419398354242546
19-2.293268582220424...0.7419398354242546
20-2.293268582220424...0.7419398354242546

Machine Learning

Let’s create our KNN model.

from verticapy.machine_learning.vertica import KNeighborsRegressor
from verticapy.machine_learning.model_selection import cross_validate

predictors = winequality.get_columns(exclude_columns = ["quality"])
model = KNeighborsRegressor(name = "winequality_KNN", n_neighbors = 50)
cross_validate(model, winequality, predictors, "quality")
...
bic
time
1-fold...-961.5345914427710.6060566902160645
2-fold...-849.6824030200940.586998701095581
3-fold...-919.7640399483110.5348219871520996
avg...-910.3270114703920.5759591261545817
std...46.1484643558540950.03011090490515871

Our model is pretty good. Our predicted scores have a median absolute error of less than 0.5. If we want to improve this model, we’ll probably need more relevant features.

Conclusion

We’ve solved our problem in a Pandas-like way, all without ever loading data into memory!