Exploring Chemical Indicators of Red Wine Quality

R-markdown Notebook | Github Repo

Detailed files are available above. The following is a summary of key results and brief discussion of parts of the analysis.

R is a programming language used for statistical computing. It is most commonly used with RStudio, a free programming environment that is reminiscent of the Matlab user interface. R is much different than almost all programming languages I have used. This distinctiveness reflects its purpose as a tool for mathematics and statistics. The syntax is very unique as compared with more traditional languages designed for large-scale software development (C++, Java, even Python). Some code snippets are included below.

This project is a dataset exploration. Despite its quirks, R appears to be well-suited to exploring data. R produces beautiful visualizations with relative code brevity.

The approach to dataset exploration advocated by Udacity is to begin by analyzing the distributions of single variables (univariate analysis). From there, relationships between two of these variables can be analyzed (bivariate analysis). Finally, datapoint coloration, size, and shape can be employed to examine the relationships between more than two variables at a time (multivariate analysis).

The publicly available dataset used for this project contains physiochemical properties and quality ratings for a set of nearly 1600 wines. Information regarding the dataset, including where it can be downloaded, are available here.

Feature Engineering

The dependent variable for the dataset is the wine quality. This quality factor is a rating between 0 and 10 based on the subjective rating of a group of wine experts. The actual distribution of these ratings are computed and plotted as follows.

ggplot(aes(x = quality_f),
       data = wine) +
  geom_histogram(stat = "count") +
  ggtitle('Histogram of Wine Quality') +
  theme_bw()
Histogram of Wine Quality

As shown in the histogram, the assigned ratings range from 3 to 8.

To aid in subsequent visualizations that benefit from a smaller number of groups, I create a wine “rating” that groups the quality factors as follows:

  • 0: Quality 3 or 4 (low quality, 63 wines)
  • 1: Quality 5 or 6 (moderate quality, 1319 wines)
  • 2: Quality 7 or 8 (high quality, 217 wines)

Admittedly, as “feature engineering” goes, this simple grouping of variables is very tame.

Univariate Plots

An examination of the histograms for the independent variables show that a majority of them are positively skewed. I test the skewness by running summaries for each of the independent factors.

summary(wine[, !names(wine) %in% c("X",
                                   "quality",
                                   "quality_f",
                                   "rating")])
Summary of independent variables

The only parameters for which the mean is not substantially above the median are density and pH. For the remainder, the data is positively skewed. To compensate, I perform a log-transform on the data. The resulting histograms appear as follows.

p6 <- ggplot(aes(x = free.sulfur.dioxide),
             data = wine) +
  geom_histogram() +
  ggtitle('Histogram of Free Sulfur Dioxide') +
  theme_bw()

p6_log <- ggplot(aes(x = log(free.sulfur.dioxide)),
                 data = wine) +
  geom_histogram() +
  ggtitle('Histogram of Free Sulfur Dioxide - Log Transform') +
  theme_bw()
...
grid.arrange(p6, p6_log,
             p7, p7_log,
             p10, p10_log,
             p11, p11_log, ncol = 2)
Summary of independent variables

In most cases, the spread of the data better approximates a normal distribution following the log transform.

Bivariate Plots

GGpairs is one of the most impressive tools for dataset exploration I have ever found. I am interested to learn what the Python equivalent is. The results of running ggpairs on this dataset follow.

wine_subset <- wine[, !names(wine) %in% c("X",
                                          "quality_f",
                                          "rating")]
ggpairs(wine_subset) + theme_bw()
Result of running ggpairs

As shown, for each combination of variables, ggpairs produces a scatterplot and calculates the correlation coefficient. From this generalized pairs plot, the factors with the strongest correlations to wine quality are alcohol, volatile acidity, sulphates, and citric acid, in that order. I re-run the ggpairs plot with just that subset of factors.

wine_feature_subset <- wine[,
    names(wine) %in% c("volatile.acidity",
                       "citric.acid",
                       "sulphates",
                       "alcohol",
                       "quality")]
ggpairs(wine_feature_subset) + theme_bw()
Result of running ggpairs

For each of the possible pairings between the independent variables and wine quality, I produce a scatterplot and a trendline to better understand the relationship. The parameter with the strongest relationship to wine quality is alcohol content.

s1 <- ggplot(aes(x = quality,
                 y = alcohol),
             data = wine) +
  geom_point() +
  geom_smooth(method = 'lm', color = 'red') +
  theme_bw()

b1 <- ggplot(aes(x = quality_f,
                 y = alcohol),
             data = wine) +
  geom_boxplot(outlier.alpha = 0) +
  geom_jitter(alpha = 0.7,
              width = 0.35,
              height = 0,
              shape = 21,
              color = 'orange') +
  theme_bw()

grid.arrange(s1, b1, ncol = 2)
Alcohol correlation to wine quality

I also create scatterplots describing a few of the relationships between key features of interest. These are shown below.

s5 <- ggplot(aes(x = citric.acid,
                 y = volatile.acidity),
             data = wine) +
  geom_point(alpha = 0.2) +
  geom_smooth(method = 'lm', color = 'red') +
  theme_bw()
...
grid.arrange(s5, s6, s7, s8, ncol = 2)
Correlations between independent variables

Multivariate Plots

In the following plots, the dimensionality of the quality factor is reduced by using the previously discussed (High, Medium, Low) rating system. In these plots, the best wines (blue) minimize volatile acidity (on the y-axis) and maximize each of the other features (on the x-axis).

The lefthand plot is on a linear scale (untransformed, raw data) whereas the righthand plot has been log-transformed to improve the spread of the data.

s9 <- ggplot(aes(x = citric.acid,
                 y = volatile.acidity),
             data = wine) +
  geom_point(aes(color = rating),
             alpha = 0.125,
             size = 3) +
  scale_color_hue(h = c(0, 240)) +
  theme_bw()

s9_log <- ggplot(aes(x = citric.acid,
                     y = log(volatile.acidity)),
             data = wine) +
  geom_point(aes(color = rating),
             alpha = 0.125,
             size = 3) +
  scale_color_hue(h = c(0, 240)) +
  theme_bw()
...
grid.arrange(s9, s9_log,
             s10, s10_log,
             s11, s11_log,
             ncol = 2)
Scatterplots with 3 tier rating

The following plots are similar, except the best wines maximize the parameters on both axes. The code used to generate these plots are similar to the previous code block.

Scatterplots with 3 tier rating

The final permutation follows.

Scatterplots with 3 tier rating

Another way to visualize the same data is to split the quality factor into two groups: less than or equal to five, and greater than 5. The following data are plotted with and without log transforms. The advantage of this visualization is the location of the highest quality, blue group is very apparent by comparing the left-hand group to the right-hand.

s19 <- ggplot(aes(x = alcohol,
                  y = citric.acid),
             data = subset(wine, quality <= 5)) +
  geom_point(aes(color = quality_f),
             alpha = 0.125,
             size = 3) +
  scale_color_hue(h = c(0, 120)) +
  expand_limits(x = c(8, 15),
                y = c(0, 1.0)) +
  theme_bw()

s19_h <- ggplot(aes(x = alcohol,
                    y = citric.acid),
             data = subset(wine, quality >= 6)) +
  geom_point(aes(color = quality_f),
             alpha = 0.125,
             size = 3) +
  scale_color_hue(h = c(120, 240)) +
  expand_limits(x = c(8, 15),
                y = c(0, 1.0)) +
  theme_bw()
...
grid.arrange(s19, s19_h,
             s19_log, s19_h_log,
             ncol = 2)
Scatterplots with 6 tier rating
Scatterplots with 6 tier rating

For all of these plots, the clustering of the quality groups becomes more evident as the plots are printed larger.

Larger scatterplot

Analytics Lessons Learned

  • R seems to be a great tool for visualization and exploration. It is able to produce great visuals with relatively few lines of code. However, I find the syntax unintuitive and strange. Given a choice between Python and R, I would prefer Python. I would, however, like to learn how to produce the identical plots of this project with Python. I imagine it requires more libraries and code to produce similar plots as R, overall.