To begin creating a model, I first decided I wanted to examine gross sales. I currently do not know if a linear model or a gneralized linear model would be good for this scenario, so I just stared with a regular linear model. For my explanatory variables, I thought that the average rating of the book, sales rank, price of the book, and author rating could all be factors that influence how much money the book makes.
lm_model <- lm(`gross sales` ~ Book_average_rating + `sales rank` + `sale price` + Author_Rating, data = book_sales)
summary(lm_model)
##
## Call:
## lm(formula = `gross sales` ~ Book_average_rating + `sales rank` +
## `sale price` + Author_Rating, data = book_sales)
##
## Residuals:
## Min 1Q Median 3Q Max
## -5292 -1571 -676 748 40025
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -1883.0616 2807.1134 -0.671 0.50248
## Book_average_rating 1059.5598 653.0375 1.623 0.10499
## `sales rank` -4.5508 0.3035 -14.993 < 2e-16 ***
## `sale price` 338.5657 28.5028 11.878 < 2e-16 ***
## Author_RatingFamous -138.1847 515.1082 -0.268 0.78855
## Author_RatingIntermediate 933.9305 301.2118 3.101 0.00198 **
## Author_RatingNovice 3217.6443 822.3647 3.913 9.71e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 3298 on 1063 degrees of freedom
## Multiple R-squared: 0.3024, Adjusted R-squared: 0.2984
## F-statistic: 76.79 on 6 and 1063 DF, p-value: < 2.2e-16
ggplot(book_sales, aes(x = Book_average_rating, y = `gross sales`)) +
geom_point(color = "black") +
geom_smooth(method = "lm", se = FALSE, color = "red") +
labs(x = "Book Average Rating", y = "Gross Sales") +
ggtitle("Scatterplot with Linear Regression Line") +
theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'
Based on the summary of the linear model and the scatter plot of the average book rating and gross sales, a linear model does not seem like the best fit in this scenario. Based on the R-squared value of .3024, the linear model is only capable of modeling about 30% of the variance of gross sales. For these reasons, I will continue with a generalized linear model.
Since gross sales is a form of count, I chose to indicate a poisson distribution.
str(book_sales)
## spc_tbl_ [1,070 × 15] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ index : num [1:1070] 0 1 2 3 4 5 6 7 8 9 ...
## $ Publishing Year : num [1:1070] 1975 1987 2015 2008 2011 ...
## $ Book Name : chr [1:1070] "Beowulf" "Batman: Year One" "Go Set a Watchman" "When You Are Engulfed in Flames" ...
## $ Author : chr [1:1070] "Unknown, Seamus Heaney" "Frank Miller, David Mazzucchelli, Richmond Lewis, Dennis O'Neil" "Harper Lee" "David Sedaris" ...
## $ language_code : chr [1:1070] "en-US" "eng" "eng" "en-US" ...
## $ Author_Rating : chr [1:1070] "Novice" "Intermediate" "Novice" "Intermediate" ...
## $ Book_average_rating: num [1:1070] 3.42 4.23 3.31 4.04 4.04 4.08 4.03 3.9 4.34 4.2 ...
## $ Book_ratings_count : num [1:1070] 155903 145267 138669 150898 198283 ...
## $ genre : chr [1:1070] "fiction" "fiction" "fiction" "fiction" ...
## $ gross sales : num [1:1070] 34160 12438 47795 41250 37952 ...
## $ publisher revenue : num [1:1070] 20496 7462 28677 24750 22772 ...
## $ sale price : num [1:1070] 4.88 1.99 8.69 7.5 7.99 4.99 6.99 6.89 6.99 6.49 ...
## $ sales rank : num [1:1070] 1 2 3 3 4 5 6 8 9 10 ...
## $ Publisher : chr [1:1070] "HarperCollins Publishers" "HarperCollins Publishers" "Amazon Digital Services, Inc." "Hachette Book Group" ...
## $ units sold : num [1:1070] 7000 6250 5500 5500 4750 ...
## - attr(*, "spec")=
## .. cols(
## .. index = col_double(),
## .. `Publishing Year` = col_double(),
## .. `Book Name` = col_character(),
## .. Author = col_character(),
## .. language_code = col_character(),
## .. Author_Rating = col_character(),
## .. Book_average_rating = col_double(),
## .. Book_ratings_count = col_double(),
## .. genre = col_character(),
## .. `gross sales` = col_double(),
## .. `publisher revenue` = col_double(),
## .. `sale price` = col_double(),
## .. `sales rank` = col_double(),
## .. Publisher = col_character(),
## .. `units sold` = col_double()
## .. )
## - attr(*, "problems")=<externalptr>
#convert author rating to factor
book_sales$Author_Rating <- factor(book_sales$Author_Rating)
# Check if Author_Rating is a factor variable
is.factor(book_sales$Author_Rating)
## [1] TRUE
#round integers
book_sales$Book_average_rating <- round(book_sales$Book_average_rating)
book_sales$sales_rank <- round(book_sales$`sales rank`)
book_sales$sale_price <- round(book_sales$`sale price`)
#generalized linear model with poisson distribution
glm_model <- glm(`gross sales` ~ Book_average_rating + `sales rank` + `sale price` + Author_Rating,
data = book_sales, family = poisson)
summary(glm_model)
##
## Call:
## glm(formula = `gross sales` ~ Book_average_rating + `sales rank` +
## `sale price` + Author_Rating, family = poisson, data = book_sales)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 9.600e+00 3.346e-02 286.911 <2e-16 ***
## Book_average_rating -3.425e-01 8.314e-03 -41.195 <2e-16 ***
## `sales rank` -3.456e-03 3.376e-06 -1023.636 <2e-16 ***
## `sale price` 1.097e-01 1.138e-04 964.171 <2e-16 ***
## Author_RatingFamous -2.705e-03 4.730e-03 -0.572 0.567
## Author_RatingIntermediate 1.096e-01 2.342e-03 46.777 <2e-16 ***
## Author_RatingNovice 3.681e-01 9.125e-03 40.342 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for poisson family taken to be 1)
##
## Null deviance: 3678126 on 1069 degrees of freedom
## Residual deviance: 1010821 on 1063 degrees of freedom
## AIC: Inf
##
## Number of Fisher Scoring iterations: 5
#extract predicted values from the Poisson regression model
predicted <- predict(glm_model, type = "response")
#sscatterplot of observed vs. predicted gross sales
plot(book_sales$`gross sales`, predicted, pch = 20, col = "blue",
xlab = "Observed Gross Sales", ylab = "Predicted Gross Sales",
main = "Observed vs. Predicted Gross Sales")
#represents perfect predictions
abline(0, 1, col = "red")
legend("topleft", legend = "Perfect Prediction", col = "red", lty = 1, pch = NA)
To diagnose, I have chose to do a Residuals vs. \(\hat{y}\) values plot.
# residuals vs. predicted values plot
plot(glm_model$fitted.values, residuals(glm_model),
xlab = "Predicted Values", ylab = "Residuals",
main = "Residuals vs. Predicted Values")
abline(h = 0, col = "red")
The plot does show widening of residuals as the predicted values increase, indicating the presence of heteroscedasticity. This shows a problem with my model, as utilizing a poisson regression model indicates constant variance, which is not currently present.