library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.4
## ✔ forcats 1.0.0 ✔ stringr 1.5.1
## ✔ ggplot2 3.4.4 ✔ tibble 3.2.1
## ✔ lubridate 1.9.3 ✔ tidyr 1.3.0
## ✔ purrr 1.0.2
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
chocolate <- read.csv("~/Desktop/chocolate.csv")
library(readr)
summary(chocolate)
## ref company_manufacturer company_location review_date
## Min. : 5 Length:2530 Length:2530 Min. :2006
## 1st Qu.: 802 Class :character Class :character 1st Qu.:2012
## Median :1454 Mode :character Mode :character Median :2015
## Mean :1430 Mean :2014
## 3rd Qu.:2079 3rd Qu.:2018
## Max. :2712 Max. :2021
## country_of_bean_origin specific_bean_origin_or_bar_name cocoa_percent
## Length:2530 Length:2530 Min. :0.4200
## Class :character Class :character 1st Qu.:0.7000
## Mode :character Mode :character Median :0.7000
## Mean :0.7164
## 3rd Qu.:0.7400
## Max. :1.0000
## ingredients most_memorable_characteristics rating
## Length:2530 Length:2530 Min. :1.000
## Class :character Class :character 1st Qu.:3.000
## Mode :character Mode :character Median :3.250
## Mean :3.196
## 3rd Qu.:3.500
## Max. :4.000
library(dplyr)
library(tidyverse)
library(ggthemes)
library(ggrepel)
library(AmesHousing)
library(boot)
library(broom)
library(lindia)
# remove scientific notation
options(scipen = 6)
# default theme, unless otherwise noted
theme_set(theme_minimal())
For last weeks data dive, I included the variables of cocoa_percent, rating, and country_of_bean_origin. This week I decided to add company_manufacturer as the rating of chocolates could be affected by the quality of the company manufacturer.
model1 <- lm(rating ~ cocoa_percent, chocolate) |>
summary()
print(model1)
##
## Call:
## lm(formula = rating ~ cocoa_percent, data = chocolate)
##
## Residuals:
## Min 1Q Median 3Q Max
## -2.21541 -0.23867 0.03459 0.28459 0.99393
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 4.0295 0.1121 35.949 < 2e-16 ***
## cocoa_percent -1.1630 0.1560 -7.456 1.22e-13 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.4406 on 2528 degrees of freedom
## Multiple R-squared: 0.02152, Adjusted R-squared: 0.02113
## F-statistic: 55.59 on 1 and 2528 DF, p-value: 1.218e-13
model2 <- lm(rating ~ cocoa_percent+ country_of_bean_origin, chocolate) |>
summary()
model3 <- lm(rating ~ cocoa_percent+ country_of_bean_origin+company_manufacturer, chocolate) |>
summary()
model1 <- lm(rating ~ cocoa_percent, chocolate) |>
summary()
model2 <- lm(rating ~ cocoa_percent+ country_of_bean_origin, chocolate) |>
summary()
model3 <- lm(rating ~ cocoa_percent+ country_of_bean_origin+company_manufacturer, chocolate) |>
summary()
df_plot <- data.frame(p = c(2, 3, 4),
r_squared = c(model1$r.squared,
model2$r.squared,
model3$r.squared),
adj_r_squared = c(model1$adj.r.squared,
model2$adj.r.squared,
model3$adj.r.squared)) |>
mutate(r_perc_increase = 100 * (r_squared - model1$r.squared) / model1$r.squared,
r_adj_perc_increase = 100 * (adj_r_squared - model1$adj.r.squared) / model1$adj.r.squared,
diff = r_perc_increase - r_adj_perc_increase)
ggplot(data=df_plot) +
geom_line(mapping = aes(x=p, y = r_perc_increase,
color = 'R Squared Improvement')) +
geom_line(mapping = aes(x=p, y = r_adj_perc_increase,
color = 'Adjusted R Squared Improvement')) +
geom_text_repel(mapping = aes(x=p,
y = r_adj_perc_increase,
label = paste("diff = ", round(diff, 1))),
color = "darkred",
nudge_x = 0.3) +
labs(color='',
x='Number of Coefficients (p)',
y='% Increase in R-Squared',
title = "Improving on a Model with 2 Coefficients")+
scale_color_brewer(palette = 'Dark2') +
theme(legend.position="bottom")
From the graph above there is a huge concern that the % increase in R-squared is from 0-2000%.
model <- lm(rating ~ cocoa_percent + country_of_bean_origin + company_manufacturer, data = chocolate)
tidy(model, conf.int = TRUE)
## # A tibble: 642 × 7
## term estimate std.error statistic p.value conf.low conf.high
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 (Intercept) 4.63 0.431 10.7 3.63e-26 3.78 5.47
## 2 cocoa_percent -1.40 0.167 -8.39 9.50e-17 -1.73 -1.08
## 3 country_of_bean_ori… -0.128 0.391 -0.327 7.43e- 1 -0.895 0.639
## 4 country_of_bean_ori… -0.357 0.390 -0.916 3.60e- 1 -1.12 0.407
## 5 country_of_bean_ori… -0.317 0.391 -0.812 4.17e- 1 -1.08 0.449
## 6 country_of_bean_ori… -0.244 0.392 -0.623 5.33e- 1 -1.01 0.525
## 7 country_of_bean_ori… -0.721 0.546 -1.32 1.87e- 1 -1.79 0.350
## 8 country_of_bean_ori… -0.610 0.454 -1.34 1.79e- 1 -1.50 0.280
## 9 country_of_bean_ori… -0.230 0.549 -0.419 6.75e- 1 -1.31 0.847
## 10 country_of_bean_ori… -0.224 0.393 -0.572 5.68e- 1 -0.994 0.545
## # ℹ 632 more rows
For each plot, I will point out any indications of issues with the model.
The first plot we will look at is the residual values vs. the fitted values:
gg_resfitted(model) +
geom_smooth(se=FALSE)
## `geom_smooth()` using method = 'gam' and formula = 'y ~ s(x, bs = "cs")'
The fitted values display the chocolate rating, and the plot displays that the variance in rating is huge for ratings 2-4.
plots <- gg_resX(model, plot.all = FALSE)
# for each variable of interest ...
plots$cocoa_percent+ theme(axis.text.x = element_text(angle = 90, hjust = 1))
plots$country_of_bean_origin+theme(axis.text.x = element_text(angle = 90, hjust = 1))
plots$company_manufacturer+theme(axis.text.x = element_text(angle = 90, hjust = 1))
What we want is for a constant variance of residuals across values of \(X\) variables. The plot for cocoa_percent follows the a pattern in the cocoa_percent from 0.6-0.8.
gg_reshist(model)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
There is a concern as the tails are not equally distributed, there is a “shorter” tail on the left, and a longer one on the right. This is not severe, but it implies that some aspect of the phenomenon is missing from the model such as we may need more/different explanatory variables.
# # the normal QQ plot
gg_qqplot(model)
Here, we can see a few rows of data have a high influence on the model. Each number represents the row in the data. Rows 9, 134, 177, and 254 may be troublesome, so we can investigate them by plotting various variables against the response variable.
For example:
# Fit your linear regression model
model <- lm(rating ~ cocoa_percent + country_of_bean_origin + company_manufacturer, data = chocolate)
# Compute Cook's distances
cooksd <- cooks.distance(model)
# Create a data frame with observation index and Cook's distances
cooksd_data <- data.frame(index = 1:length(cooksd), cooksd = cooksd)
# Plot Cook's distances by observation
library(ggplot2)
ggplot(cooksd_data, aes(x = index, y = cooksd)) +
geom_point() +
labs(title = "Cook's Distance by Observation",
x = "Observation Index",
y = "Cook's Distance") +
theme_minimal()
## Warning: Removed 117 rows containing missing values (`geom_point()`).
Clearly, some of these rows have a stronger influence when we
consider just the lot_area, but not all of them. We may
want to find where “highly influential rows” like these intersect across
the different explanatory variables of the model. I.e., which rows are
very highly influential among most (or all) explanatory variables.