library(ggplot2)
## Warning: package 'ggplot2' was built under R version 3.6.3
library(lubridate)
##
## Attaching package: 'lubridate'
## The following object is masked from 'package:base':
##
## date
library(readxl)
## Warning: package 'readxl' was built under R version 3.6.2
library(readr)
library(scales)
##
## Attaching package: 'scales'
## The following object is masked from 'package:readr':
##
## col_factor
library(tidyverse)
## Warning: package 'tidyverse' was built under R version 3.6.2
## -- Attaching packages --------------------------------------- tidyverse 1.3.0 --
## v tibble 3.0.6 v dplyr 1.0.4
## v tidyr 1.1.2 v stringr 1.4.0
## v purrr 0.3.4 v forcats 0.4.0
## Warning: package 'tibble' was built under R version 3.6.3
## Warning: package 'tidyr' was built under R version 3.6.3
## Warning: package 'purrr' was built under R version 3.6.3
## Warning: package 'dplyr' was built under R version 3.6.3
## -- Conflicts ------------------------------------------ tidyverse_conflicts() --
## x lubridate::as.difftime() masks base::as.difftime()
## x scales::col_factor() masks readr::col_factor()
## x lubridate::date() masks base::date()
## x purrr::discard() masks scales::discard()
## x dplyr::filter() masks stats::filter()
## x lubridate::intersect() masks base::intersect()
## x dplyr::lag() masks stats::lag()
## x lubridate::setdiff() masks base::setdiff()
## x lubridate::union() masks base::union()
library(lubridate)
library(plotly)
##
## Attaching package: 'plotly'
## The following object is masked from 'package:ggplot2':
##
## last_plot
## The following object is masked from 'package:stats':
##
## filter
## The following object is masked from 'package:graphics':
##
## layout
library(corrplot)
## Warning: package 'corrplot' was built under R version 3.6.3
## corrplot 0.84 loaded
library(ggthemes)
## Warning: package 'ggthemes' was built under R version 3.6.3
library(viridisLite)
## Warning: package 'viridisLite' was built under R version 3.6.3
library(gclus)
## Warning: package 'gclus' was built under R version 3.6.3
## Loading required package: cluster
## Warning: package 'cluster' was built under R version 3.6.3
library(tidymodels)
## Warning: package 'tidymodels' was built under R version 3.6.3
## -- Attaching packages -------------------------------------- tidymodels 0.1.2 --
## v broom 0.7.4 v recipes 0.1.15
## v dials 0.0.9 v rsample 0.0.8
## v infer 0.5.4 v tune 0.1.2
## v modeldata 0.1.0 v workflows 0.2.1
## v parsnip 0.1.5 v yardstick 0.0.7
## Warning: package 'broom' was built under R version 3.6.3
## Warning: package 'dials' was built under R version 3.6.3
## Warning: package 'infer' was built under R version 3.6.3
## Warning: package 'modeldata' was built under R version 3.6.3
## Warning: package 'parsnip' was built under R version 3.6.3
## Warning: package 'recipes' was built under R version 3.6.3
## Warning: package 'rsample' was built under R version 3.6.3
## Warning: package 'tune' was built under R version 3.6.3
## Warning: package 'workflows' was built under R version 3.6.3
## Warning: package 'yardstick' was built under R version 3.6.3
## -- Conflicts ----------------------------------------- tidymodels_conflicts() --
## x purrr::discard() masks scales::discard()
## x plotly::filter() masks dplyr::filter(), stats::filter()
## x recipes::fixed() masks stringr::fixed()
## x dplyr::lag() masks stats::lag()
## x yardstick::spec() masks readr::spec()
## x recipes::step() masks stats::step()
setwd('C:/Users/Khanh/Desktop/GMUeducation/AIT_580/Final')
df <- read_csv("world-happiness-report-2021.csv")
## Parsed with column specification:
## cols(
## .default = col_double(),
## `Country name` = col_character(),
## `Regional indicator` = col_character()
## )
## See spec(...) for full column specifications.
head(df)
## # A tibble: 6 x 20
## `Country name` `Regional indic~ `Ladder score` `Standard error~ upperwhisker
## <chr> <chr> <dbl> <dbl> <dbl>
## 1 Finland Western Europe 7.84 0.032 7.90
## 2 Denmark Western Europe 7.62 0.035 7.69
## 3 Switzerland Western Europe 7.57 0.036 7.64
## 4 Iceland Western Europe 7.55 0.059 7.67
## 5 Netherlands Western Europe 7.46 0.027 7.52
## 6 Norway Western Europe 7.39 0.035 7.46
## # ... with 15 more variables: lowerwhisker <dbl>, `Logged GDP per
## # capita` <dbl>, `Social support` <dbl>, `Healthy life expectancy` <dbl>,
## # `Freedom to make life choices` <dbl>, Generosity <dbl>, `Perceptions of
## # corruption` <dbl>, `Ladder score in Dystopia` <dbl>, `Explained by: Log GDP
## # per capita` <dbl>, `Explained by: Social support` <dbl>, `Explained by:
## # Healthy life expectancy` <dbl>, `Explained by: Freedom to make life
## # choices` <dbl>, `Explained by: Generosity` <dbl>, `Explained by:
## # Perceptions of corruption` <dbl>, `Dystopia + residual` <dbl>
Upload the World Happiness Report 2021 csv file as a dataframe.
print(dim(df))
## [1] 149 20
names(df)
## [1] "Country name"
## [2] "Regional indicator"
## [3] "Ladder score"
## [4] "Standard error of ladder score"
## [5] "upperwhisker"
## [6] "lowerwhisker"
## [7] "Logged GDP per capita"
## [8] "Social support"
## [9] "Healthy life expectancy"
## [10] "Freedom to make life choices"
## [11] "Generosity"
## [12] "Perceptions of corruption"
## [13] "Ladder score in Dystopia"
## [14] "Explained by: Log GDP per capita"
## [15] "Explained by: Social support"
## [16] "Explained by: Healthy life expectancy"
## [17] "Explained by: Freedom to make life choices"
## [18] "Explained by: Generosity"
## [19] "Explained by: Perceptions of corruption"
## [20] "Dystopia + residual"
The World Happiness 2021 dataset has 149 rows and 20 columns. The “Ladder score” represents Happiness score. Among the columns, it has the Country name, Regional indicator are in character/string data type. The other variables are in Numerical data type. The “Ladder score in Dystopia” has the same value 2.43 for all rows, therefore, this variable can be ignored as it offers no value for the analysis.
sum(is.na(df))
## [1] 0
There is no NaN values in the dataset.
summary(df)
## Country name Regional indicator Ladder score
## Length:149 Length:149 Min. :2.523
## Class :character Class :character 1st Qu.:4.852
## Mode :character Mode :character Median :5.534
## Mean :5.533
## 3rd Qu.:6.255
## Max. :7.842
## Standard error of ladder score upperwhisker lowerwhisker
## Min. :0.02600 Min. :2.596 Min. :2.449
## 1st Qu.:0.04300 1st Qu.:4.991 1st Qu.:4.706
## Median :0.05400 Median :5.625 Median :5.413
## Mean :0.05875 Mean :5.648 Mean :5.418
## 3rd Qu.:0.07000 3rd Qu.:6.344 3rd Qu.:6.128
## Max. :0.17300 Max. :7.904 Max. :7.780
## Logged GDP per capita Social support Healthy life expectancy
## Min. : 6.635 Min. :0.4630 Min. :48.48
## 1st Qu.: 8.541 1st Qu.:0.7500 1st Qu.:59.80
## Median : 9.569 Median :0.8320 Median :66.60
## Mean : 9.432 Mean :0.8147 Mean :64.99
## 3rd Qu.:10.421 3rd Qu.:0.9050 3rd Qu.:69.60
## Max. :11.647 Max. :0.9830 Max. :76.95
## Freedom to make life choices Generosity Perceptions of corruption
## Min. :0.3820 Min. :-0.28800 Min. :0.0820
## 1st Qu.:0.7180 1st Qu.:-0.12600 1st Qu.:0.6670
## Median :0.8040 Median :-0.03600 Median :0.7810
## Mean :0.7916 Mean :-0.01513 Mean :0.7274
## 3rd Qu.:0.8770 3rd Qu.: 0.07900 3rd Qu.:0.8450
## Max. :0.9700 Max. : 0.54200 Max. :0.9390
## Ladder score in Dystopia Explained by: Log GDP per capita
## Min. :2.43 Min. :0.0000
## 1st Qu.:2.43 1st Qu.:0.6660
## Median :2.43 Median :1.0250
## Mean :2.43 Mean :0.9772
## 3rd Qu.:2.43 3rd Qu.:1.3230
## Max. :2.43 Max. :1.7510
## Explained by: Social support Explained by: Healthy life expectancy
## Min. :0.0000 Min. :0.0000
## 1st Qu.:0.6470 1st Qu.:0.3570
## Median :0.8320 Median :0.5710
## Mean :0.7933 Mean :0.5202
## 3rd Qu.:0.9960 3rd Qu.:0.6650
## Max. :1.1720 Max. :0.8970
## Explained by: Freedom to make life choices Explained by: Generosity
## Min. :0.0000 Min. :0.000
## 1st Qu.:0.4090 1st Qu.:0.105
## Median :0.5140 Median :0.164
## Mean :0.4987 Mean :0.178
## 3rd Qu.:0.6030 3rd Qu.:0.239
## Max. :0.7160 Max. :0.541
## Explained by: Perceptions of corruption Dystopia + residual
## Min. :0.0000 Min. :0.648
## 1st Qu.:0.0600 1st Qu.:2.138
## Median :0.1010 Median :2.509
## Mean :0.1351 Mean :2.430
## 3rd Qu.:0.1740 3rd Qu.:2.794
## Max. :0.5470 Max. :3.482
##Question 1: What factors affect the happiness score?
#We changed Ladder score to Happiness_Score and select 7 variables for Data Analysis
data <- df %>% mutate(Happiness_Score = `Ladder score`) %>% select(Happiness_Score, `Logged GDP per capita`, `Social support`, `Healthy life expectancy`, `Freedom to make life choices`, Generosity, `Perceptions of corruption`)
corr_score <- cor(data, use = 'complete', method = 'pearson')
corrplot(corr_score)
To answer the question, we selected 7 numeric variables in the dataset and look at the correlations between them. We created a correlation matrix and plotted a heatmap for some additional insights. The size and the code of the circles indicate the level of correlation between variables. According to this heatmap, we see that there are strong relationship from ‘Logged GDP per capita’, ‘Social support’, ‘Healthy life expectancy’ with the Happiness score. ‘Generosity’ and ‘Perception of corruption’ appears to be the least correlated with the Happiness score.
summary(data)
## Happiness_Score Logged GDP per capita Social support Healthy life expectancy
## Min. :2.523 Min. : 6.635 Min. :0.4630 Min. :48.48
## 1st Qu.:4.852 1st Qu.: 8.541 1st Qu.:0.7500 1st Qu.:59.80
## Median :5.534 Median : 9.569 Median :0.8320 Median :66.60
## Mean :5.533 Mean : 9.432 Mean :0.8147 Mean :64.99
## 3rd Qu.:6.255 3rd Qu.:10.421 3rd Qu.:0.9050 3rd Qu.:69.60
## Max. :7.842 Max. :11.647 Max. :0.9830 Max. :76.95
## Freedom to make life choices Generosity Perceptions of corruption
## Min. :0.3820 Min. :-0.28800 Min. :0.0820
## 1st Qu.:0.7180 1st Qu.:-0.12600 1st Qu.:0.6670
## Median :0.8040 Median :-0.03600 Median :0.7810
## Mean :0.7916 Mean :-0.01513 Mean :0.7274
## 3rd Qu.:0.8770 3rd Qu.: 0.07900 3rd Qu.:0.8450
## Max. :0.9700 Max. : 0.54200 Max. :0.9390
library(gclus)
data.col <- dmat.color(corr_score)
order_corr <- order.single(corr_score)
cpairs(data, order_corr, panel.colors= data.col, gap=0.6, main = "Correlation of rearranged variables" )
The heatmap with big circle did not show the order of the correlation. This new scatter plot matrix rearranged the variables so that those with higher correlation are closer to the principal diagonal. The color code of the cells reflect the level of the correlations. This graph shows that Life Expectancy and Logged GdP per capita has the highest correlation, then Happiness score. The lowest correlation are Perceptions of corruption and Generosity. So according to this dataset, Perceptions of corruption and Generosity variables are not important factors as GDP per capita and Healthy Life expectancy when it comes to Happiness score.
library(scatterplot3d)
attach(data)
scatterplot3d(`Happiness_Score`, `Logged GDP per capita`, `Healthy life expectancy`, color = 'red', main = "3D View for GDP, Happiness, Life expectancy")
We selected the 3 highest correlation variables:
Happiness_Score, Logged GDP per capita, Healthy life expectancy for further analysis. We saw them in 2 dimensions and they each linearly correlated to each other. In the 3 dimesional view, these 3 variables also show a positive linear relationship. The higher the Happiness_Score, the higher the other 2 variables are.
figure2 <- plot_ly(data, x = ~`Logged GDP per capita`, y=~`Happiness_Score`, z= ~`Healthy life expectancy`)
figure2 <- figure2 %>% add_markers()
figure2 <- figure2 %>% layout(title = "3D View for GDP, Happiness, Life expectancy")
figure2
This 3D interactive plot also help looking at the data from multiple angles in 3d to determine potential outliers. Each dot is a country being mapped into 3 dimensional space and the coordinates of the dots are the values for Happiness score, Life Expectancy and the Logged GDP per capita.
##Question 2: Are countries with high GdP per capita resulted in higher Happiness score?
#Country with highest Logged GDP per capita
df1 <- df %>% mutate(Happiness_Score = `Ladder score`) %>% select(`Country name`, `Regional indicator`, `Logged GDP per capita`, Happiness_Score, `Social support`, `Healthy life expectancy`, `Freedom to make life choices`, Generosity, `Perceptions of corruption`)
maxGDP <- max(df1$`Logged GDP per capita`)
df1[df1$`Logged GDP per capita` == maxGDP,]
## # A tibble: 1 x 9
## `Country name` `Regional indic~ `Logged GDP per~ Happiness_Score
## <chr> <chr> <dbl> <dbl>
## 1 Luxembourg Western Europe 11.6 7.32
## # ... with 5 more variables: `Social support` <dbl>, `Healthy life
## # expectancy` <dbl>, `Freedom to make life choices` <dbl>, Generosity <dbl>,
## # `Perceptions of corruption` <dbl>
#Country with lowest Logged GDP per capita score
minGDP<- min(df1$`Logged GDP per capita`)
df1[df1$`Logged GDP per capita` == minGDP,]
## # A tibble: 1 x 9
## `Country name` `Regional indic~ `Logged GDP per~ Happiness_Score
## <chr> <chr> <dbl> <dbl>
## 1 Burundi Sub-Saharan Afr~ 6.64 3.78
## # ... with 5 more variables: `Social support` <dbl>, `Healthy life
## # expectancy` <dbl>, `Freedom to make life choices` <dbl>, Generosity <dbl>,
## # `Perceptions of corruption` <dbl>
ggplot(df, aes(x=`Logged GDP per capita`, y=`Ladder score`)) + geom_point(aes(color = `Regional indicator`), size=2) + geom_smooth(method = 'lm') +
labs(x='Logged GDP per capita', y= 'Happiness score', title = 'Are high GDP countries happy countries?')
## `geom_smooth()` using formula 'y ~ x'
According to the scatter plot above, we found that countries with higher ‘Logged GDP per capita’ tends to have higher Happiness score. These 2 variables have positive linear relationship. Most of the countries with high GDP per capita are in Western Europe and North America regions. Teh countries with low Happiness score are mostly in Sub-Saharan Africa and South Asia regions.
##Question 3: Do countries with low perception of corruption have better Happiness score?
ggplot(df, aes(x=`Perceptions of corruption`, y = `Ladder score`)) + geom_point(aes(color = `Regional indicator`), size=2) + geom_smooth(method = 'lm') +
labs(x='Perception of corruption', y='Happiness score', title = 'Perceptions of corruption vs Happiness score?')
## `geom_smooth()` using formula 'y ~ x'
This plot shows that the linear line has a negative slope, indicate opposite correlation between Perception of corruption vs Happiness score. It tends to validate the common perception. Western Europe has low Perceptions of corruption and higher Happiness score, whereas, Countries from Sub-Saharan Africa have higher Corruption and Lower Happiness score. The Linear Relationship in there two variables are not as strongg as the slope is not steep and the scatter plot has more dots concentrated on the upper right quadrant.
##Question 4: Build a Machine Learning model to predict Life Expectancy for a country base on other variables
The first step in building regression models is to split our original data into a training and test set. We then perform all feature engineering and model fitting tasks on the training set and use the test set as an independent assessment of our model’s prediction accuracy.
We will be using the initial_split() function from rsample to partition the World Happiness 2021 data into training and test sets. We use set.seed() to ensure the results are reproducible.
set.seed(580)
# Create a split object
data_split <- initial_split(data, prop = 0.75,
strata = Happiness_Score)
# Build training data set
data_training <- data_split %>%
training()
# Build testing data set
data_test <- data_split %>%
testing()
The next step in the process is to build a linear regression model object to which we fit our training data. The parsnip package from tidymodels acts like an aggregator across the various modeling engines within R.
Linear regression is implemented with the linear_reg() function in parsnip. To the set the engine and mode, we use set_engine() and set_mode() respectively. Each one of these functions takes a parsnip object as an argument and updates its properties.
We create a linear regression model object with the lm engine.
lm_model <- linear_reg() %>%
set_engine('lm') %>% # adds lm implementation of linear regression
set_mode('regression')
# View object properties
lm_model
## Linear Regression Model Specification (regression)
##
## Computational engine: lm
The code below trains our linear regression model on the data_training data. In our formula, we have specified that Healthy life expectancy is the response variable, and the rest are our predictor variables.
We have assigned the name lm_fit to our trained linear regression model.
lm_fit <- lm_model %>%
fit(`Healthy life expectancy` ~ ., data = data_training)
# View lm_fit properties
lm_fit
## parsnip model object
##
## Fit time: 0ms
##
## Call:
## stats::lm(formula = `Healthy life expectancy` ~ ., data = data)
##
## Coefficients:
## (Intercept) Happiness_Score
## 21.7361 0.2939
## `Logged GDP per capita` `Social support`
## 3.6650 9.0844
## `Freedom to make life choices` Generosity
## 2.8762 -1.1109
## `Perceptions of corruption`
## -3.4546
The lm_fit object is list that contains all of the information about how our model was trained as well as the detailed results. The names() function printed out the named objects that are stored within lm_fit. The important objects are fit and preproc. These contain the trained model and pre-processing steps (if any are used), respectively.
names(lm_fit)
## [1] "lvl" "spec" "fit" "preproc" "elapsed"
To print a summary of our model, we extract fit from lm_fit and pass it to the summary() function. We explore the estimated coefficients, F-statistics, p-values, residual standard error (also known as RMSE) and R2 value. Below, we used the pluck() function from dplyr to extract the fitresults from lm_fit. This is the same as using lm_fit$fit or lm_fit[['fit']].
lm_fit %>%
# Extract fit element from the list
pluck('fit') %>%
# Pass to summary function
summary()
##
## Call:
## stats::lm(formula = `Healthy life expectancy` ~ ., data = data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -10.0744 -1.9134 0.1124 2.4796 5.3576
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 21.7361 4.2728 5.087 1.58e-06 ***
## Happiness_Score 0.2939 0.5779 0.509 0.6121
## `Logged GDP per capita` 3.6650 0.5035 7.279 6.16e-11 ***
## `Social support` 9.0844 5.0124 1.812 0.0728 .
## `Freedom to make life choices` 2.8762 3.7283 0.771 0.4422
## Generosity -1.1109 2.5223 -0.440 0.6605
## `Perceptions of corruption` -3.4546 2.0654 -1.673 0.0974 .
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 3.337 on 106 degrees of freedom
## Multiple R-squared: 0.7661, Adjusted R-squared: 0.7529
## F-statistic: 57.87 on 6 and 106 DF, p-value: < 2.2e-16
par(mfrow=c(2,2)) # plot all 4 plots in one
lm_fit %>%
pluck('fit') %>%
plot(pch = 16, col = '#006EA1')
# Data frame of estimated coefficients
tidy(lm_fit)
## # A tibble: 7 x 5
## term estimate std.error statistic p.value
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 (Intercept) 21.7 4.27 5.09 1.58e- 6
## 2 Happiness_Score 0.294 0.578 0.509 6.12e- 1
## 3 `Logged GDP per capita` 3.67 0.504 7.28 6.16e-11
## 4 `Social support` 9.08 5.01 1.81 7.28e- 2
## 5 `Freedom to make life choices` 2.88 3.73 0.771 4.42e- 1
## 6 Generosity -1.11 2.52 -0.440 6.61e- 1
## 7 `Perceptions of corruption` -3.45 2.07 -1.67 9.74e- 2
# Performance metrics on training data
glance(lm_fit)
## # A tibble: 1 x 12
## r.squared adj.r.squared sigma statistic p.value df logLik AIC BIC
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 0.766 0.753 3.34 57.9 3.18e-31 6 -293. 602. 624.
## # ... with 3 more variables: deviance <dbl>, df.residual <int>, nobs <int>
library(vip)
## Warning: package 'vip' was built under R version 3.6.3
##
## Attaching package: 'vip'
## The following object is masked from 'package:utils':
##
## vi
vip(lm_fit)
According to the Summary report and the Bar graph,
Logged GDP per capita is the most important to the Linear Model to predict the Healthy Life Expectancy. “Generosity” and “Happiness_Score” are not a strong indicator for healthy life expectancy.
To assess the accuracy of our trained linear regression model, lm_fit, we must use it to make predictions on our test data, data_test.
This is done with the predict() function from parnsip. The code below uses the predict function to generate a data frame with a single column, .pred, which contains the predicted Healthy life expectancy values on the data_test data.
predict(lm_fit, new_data = data_test)
## # A tibble: 36 x 1
## .pred
## <dbl>
## 1 70.8
## 2 72.4
## 3 71.8
## 4 71.5
## 5 72.5
## 6 70.6
## 7 69.7
## 8 64.3
## 9 67.2
## 10 71.4
## # ... with 26 more rows
We combine the test data set and the predictions into a single data frame. We create a data frame with the predictions on the data_test data and then use bind_cols to add the data_test data to the results.
Now we have the model results and the test data in a single data frame.
data_test_results <- predict(lm_fit, new_data = data_test) %>%
bind_cols(data_test)
# View results
data_test_results
## # A tibble: 36 x 8
## .pred Happiness_Score `Logged GDP per~ `Social support` `Healthy life e~
## <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 70.8 7.16 10.6 0.939 73.5
## 2 72.4 6.95 11.0 0.92 68.2
## 3 71.8 6.83 10.8 0.906 72.2
## 4 71.5 6.60 10.7 0.931 72.2
## 5 72.5 6.56 11.1 0.844 67.3
## 6 70.6 6.49 10.6 0.932 74.7
## 7 69.7 6.48 10.6 0.88 73.8
## 8 64.3 6.37 9.32 0.821 63.8
## 9 67.2 6.32 9.86 0.831 68.6
## 10 71.4 6.19 10.5 0.941 68.8
## # ... with 26 more rows, and 3 more variables: `Freedom to make life
## # choices` <dbl>, Generosity <dbl>, `Perceptions of corruption` <dbl>
To obtain the RMSE and R2 values on our test set results, we use the rmse() and rsq() functions.
We pass our data_test_results to these functions to obtain these values for our test set. The result is a data frame with the following columns: .metric, .estimator, and .estimate.
# RMSE on test set
rmse(data_test_results,
truth = `Healthy life expectancy`,
estimate = .pred)
## # A tibble: 1 x 3
## .metric .estimator .estimate
## <chr> <chr> <dbl>
## 1 rmse standard 3.57
# R2 on test set
rsq(data_test_results,
truth = `Healthy life expectancy`,
estimate = .pred)
## # A tibble: 1 x 3
## .metric .estimator .estimate
## <chr> <chr> <dbl>
## 1 rsq standard 0.738
Healthy life expectancy) versus the model predictions (.pred) as a scatter plot. It also plot the line y = x through the origin. This line is a visually representation of the perfect model where all predicted values are equal to the true values in the test set. The farther the points are from this line, the worse the model fit. In the code below, we use geom_point() and geom_abline() to make this plot using out data_test_results data. The geom_abline() function will plot a line with the provided slope and intercept arguments. The coord_obs_pred() function scales the x and y axis to have the same range for accurate comparisons.ggplot(data = data_test_results,
mapping = aes(x = .pred, y = `Healthy life expectancy`)) +
geom_point(alpha = 0.4) +
geom_abline(intercept = 0, slope = 1, linetype = 2, color = 'red') +
coord_obs_pred() +
labs(title = 'Linear Regression Results - data Test Set',
x = 'Predicted Healthy life expectancy',
y = 'Actual Healthy life expectancy') +
theme_light()