Cats Data

Like we have with the previous R examples, we’ll start by loading the packages and getting our data:

library(tidyverse); library(broom)
cats <- read.csv('https://raw.githubusercontent.com/Shammalamala/STA2410/refs/heads/main/data/Ch3/cats_with_names.csv')

slice_sample(cats, n = 10)
##       name sex body heart
## 1     Dash   M  3.2  11.6
## 2  Jasmine   F  2.3   9.0
## 3     Lily   F  2.2  11.0
## 4  Biscuit   M  2.5   9.3
## 5     Thor   M  2.9  10.6
## 6  Nibbles   M  3.5  12.9
## 7   Stripe   M  2.7   9.0
## 8    Comet   M  2.7   9.8
## 9  Charlie   M  2.4   7.9
## 10   Sassy   F  2.1   8.7

Fitting the linear model

We’ll fit our linear model using the built-in function, lm() and save the results as cats_lm.

cats_lm <- lm(heart ~ body, cats)

# View the resulting coefficients using tidy() from the broom package:
tidy(cats_lm) 
## # A tibble: 2 × 5
##   term        estimate std.error statistic  p.value
##   <chr>          <dbl>     <dbl>     <dbl>    <dbl>
## 1 (Intercept)   -0.357     0.692    -0.515 6.07e- 1
## 2 body           4.03      0.250    16.1   6.97e-34

The p-value in the table above is only reliable IF the other pieces of our model are true. The complete model for linear regression is:

\[Y_i \sim N(\beta_0 + \beta_1 X_i, \sigma^2)\]

If any part of the model is not true, the p-value can be small, even when \(\beta_1 = 0\). So we need to check the other assumptions are correct.

Assumptions:

The assumptions for linear regression can be remembered with the acronym LINE:

  • Linear association: The relationship between \(X\) and \(Y\) needs to be linear

  • Independent: The rows in the data need to be independent

    • The cats can’t be related.
  • Normal errors: The residuals should appear to be approximately Normal

  • Equal Spread: We need constant variance so we can estimate a single variance, \(\sigma^2\)

    • Occasionally called homoscedasticity

We can check the LIE part of our acronym with a Residual Plot and the Normality assumption with a Q-Q Plot

Diagnostic Plot 2: Histogram and Q-Q Plot

Creating the histogram

If we want to look at if a variable is Normal, we can look at a histogram. The difference is that instead of looking at a histogram of \(Y\), we create a histogram for the residuals: \(e_i = y_i - \hat{y}_i\)

First, we need to get our residuals. We can do it by hand with cats\$res <- cats\$heart - cats_lm\$fit or simpler cats\$res <- cats_lm\$res. Alternatively, as seen below, we can use the augment() function from the broom package:

cats2 <- 
  augment(
    x = cats_lm,  # The linear model we'll get the residuals from
    data = cats   # The data to calculate the fitted and residuals
  )

slice_sample(cats2, n = 10)
## # A tibble: 10 × 10
##    name     sex    body heart .fitted  .resid    .hat .sigma  .cooksd .std.resid
##    <chr>    <chr> <dbl> <dbl>   <dbl>   <dbl>   <dbl>  <dbl>    <dbl>      <dbl>
##  1 Pounce   M       3.5  15.6   13.8   1.84   0.0248    1.45  2.09e-2     1.28  
##  2 Marigold F       2.3   9.7    8.92  0.778  0.0123    1.46  1.81e-3     0.539 
##  3 Dash     M       3.2  11.6   12.6  -0.952  0.0137    1.46  3.02e-3    -0.660 
##  4 Rosie    F       2.3   9      8.92  0.0783 0.0123    1.46  1.83e-5     0.0543
##  5 Tiger    M       2.2   7.6    8.52 -0.918  0.0151    1.46  3.11e-3    -0.637 
##  6 Angel    F       2.2   8.7    8.52  0.182  0.0151    1.46  1.22e-4     0.126 
##  7 Merlin   M       2.9  11.3   11.3  -0.0421 0.00787   1.46  3.36e-6    -0.0291
##  8 Lynx     M       3.4  12.8   13.4  -0.559  0.0205    1.46  1.59e-3    -0.389 
##  9 Felix    M       2.3   9.6    8.92  0.678  0.0123    1.46  1.37e-3     0.470 
## 10 Chloe    F       2     9.5    7.71  1.79   0.0225    1.45  1.78e-2     1.25

All the columns augment() added have a . before their name to make it easier to ID which columns it added.

Now we can use cats2 to create our histogram:

gg_cat_reshist <- 
  ggplot(
    data = cats2,
    mapping = aes(x = .resid)
  ) + 
  geom_histogram(
    breaks = seq(min(cats2$.resid), max(cats2$.resid)+0.2, by = 0.4),
    fill = 'steelblue',
    color = 'white'
  ) +
  theme_bw() + 
  labs(
    x = 'Heart Weight Residuals',
    title = 'Histogram of Residuals of cats heart weight by body weight'
  ) +
  scale_y_continuous(expand = c(0, 0, 0.05, 0))

gg_cat_reshist

The histogram looks okay. Has a rough bellshape to it.

Histograms can do well in identifying the shape a variable takes, but if have a specific distribution we want to check, we can use a more specific tool: the Q-Q Plot

Graph 3: Q-Q Plot

Creating a Q-Q plot by hand is complicated, but pretty straight forward in R.

If we’re using the ggplot2 framework, we can create our Q-Q plot similar to how we made the histogram with a couple of important changes:

  • x = .resid becomes sample = .resid

  • geom_histogram() becomes geom_qq()

  • add a geom_qq_line() to add the line the points should follow

ggplot(
  data = cats2,
  mapping = aes(sample = .resid)
) + 
  # Distribution defaults to normal (qnorm), but included as a demo
  geom_qq_line(
    distribution = qnorm,
    color = 'red'
  ) + 
  geom_qq(distribution = qnorm) +

  theme_bw() + 
  labs(
    x = 'Heart Weight Residuals',
    title = 'Q-Q Plot of Residuals of cats heart weight by body weight'
  ) 

It has a bit of a wiggle to it, especially in the middle. So maybe the residuals aren’t Normal :(

While the Q-Q plot is typically better for helping assess Normality, there is still subjectivity and grey area that can happen.

So what do we do?

Look for part 3!

LS0tDQp0aXRsZTogIkRpYWdub3N0aWMgUGxvdHM6IFEtUSBQbG90Ig0KYXV0aG9yOiAiQ2hhcHRlciAzIg0KZGF0ZTogJ1NUQSA0MjEwJw0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGZpZ193aWR0aDogMTANCiAgICBmaWdfaGVpZ2h0OiA2DQogICAgZmlnX2NhcHRpb246IHRydWUNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHNtb290aF9zY3JvbGw6IHRydWUNCiAgICB0aGVtZTogbHVtZW4NCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEYsDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEYsDQogICAgICAgICAgICAgICAgICAgICAgZmlnLmFsaWduID0gJ2NlbnRlcicpDQpgYGANCg0KIyMgQ2F0cyBEYXRhDQoNCkxpa2Ugd2UgaGF2ZSB3aXRoIHRoZSBwcmV2aW91cyBSIGV4YW1wbGVzLCB3ZSdsbCBzdGFydCBieSBsb2FkaW5nIHRoZSBwYWNrYWdlcyBhbmQgZ2V0dGluZyBvdXIgZGF0YToNCg0KYGBge3IgcGFja2FnZXNfZGF0YX0NCmxpYnJhcnkodGlkeXZlcnNlKTsgbGlicmFyeShicm9vbSkNCmNhdHMgPC0gcmVhZC5jc3YoJ2h0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9TaGFtbWFsYW1hbGEvU1RBMjQxMC9yZWZzL2hlYWRzL21haW4vZGF0YS9DaDMvY2F0c193aXRoX25hbWVzLmNzdicpDQoNCnNsaWNlX3NhbXBsZShjYXRzLCBuID0gMTApDQpgYGANCg0KDQojIyBGaXR0aW5nIHRoZSBsaW5lYXIgbW9kZWwNCg0KV2UnbGwgZml0IG91ciBsaW5lYXIgbW9kZWwgdXNpbmcgdGhlIGJ1aWx0LWluIGZ1bmN0aW9uLCBgbG0oKWAgYW5kIHNhdmUgdGhlIHJlc3VsdHMgYXMgYGNhdHNfbG1gLg0KDQpgYGB7ciBjYXRzX2xtfQ0KY2F0c19sbSA8LSBsbShoZWFydCB+IGJvZHksIGNhdHMpDQoNCiMgVmlldyB0aGUgcmVzdWx0aW5nIGNvZWZmaWNpZW50cyB1c2luZyB0aWR5KCkgZnJvbSB0aGUgYnJvb20gcGFja2FnZToNCnRpZHkoY2F0c19sbSkgDQpgYGANCg0KDQpUaGUgcC12YWx1ZSBpbiB0aGUgdGFibGUgYWJvdmUgaXMgb25seSByZWxpYWJsZSAqKklGKiogdGhlIG90aGVyIHBpZWNlcyBvZiBvdXIgbW9kZWwgYXJlIHRydWUuIFRoZSBjb21wbGV0ZSBtb2RlbCBmb3IgbGluZWFyIHJlZ3Jlc3Npb24gaXM6DQoNCiQkWV9pIFxzaW0gTihcYmV0YV8wICsgXGJldGFfMSBYX2ksIFxzaWdtYV4yKSQkDQoNCklmIGFueSBwYXJ0IG9mIHRoZSBtb2RlbCBpcyBub3QgdHJ1ZSwgdGhlIHAtdmFsdWUgY2FuIGJlIHNtYWxsLCBldmVuIHdoZW4gJFxiZXRhXzEgPSAwJC4gU28gd2UgbmVlZCB0byBjaGVjayB0aGUgb3RoZXIgYXNzdW1wdGlvbnMgYXJlIGNvcnJlY3QuDQoNCiMjIEFzc3VtcHRpb25zOg0KDQpUaGUgYXNzdW1wdGlvbnMgZm9yIGxpbmVhciByZWdyZXNzaW9uIGNhbiBiZSByZW1lbWJlcmVkIHdpdGggdGhlIGFjcm9ueW0gTElORToNCg0KLSAqKkwqKmluZWFyIGFzc29jaWF0aW9uOiBUaGUgcmVsYXRpb25zaGlwIGJldHdlZW4gJFgkIGFuZCAkWSQgbmVlZHMgdG8gYmUgbGluZWFyDQoNCi0gKipJKipuZGVwZW5kZW50OiBUaGUgcm93cyBpbiB0aGUgZGF0YSBuZWVkIHRvIGJlIGluZGVwZW5kZW50DQogICAgLSBUaGUgY2F0cyBjYW4ndCBiZSByZWxhdGVkLg0KICAgIA0KLSAqKk4qKm9ybWFsIGVycm9yczogVGhlIHJlc2lkdWFscyBzaG91bGQgYXBwZWFyIHRvIGJlIGFwcHJveGltYXRlbHkgTm9ybWFsDQoNCi0gKipFKipxdWFsIFNwcmVhZDogV2UgbmVlZCBjb25zdGFudCB2YXJpYW5jZSBzbyB3ZSBjYW4gZXN0aW1hdGUgYSBzaW5nbGUgdmFyaWFuY2UsICRcc2lnbWFeMiQNCiAgICAtIE9jY2FzaW9uYWxseSBjYWxsZWQgKmhvbW9zY2VkYXN0aWNpdHkqDQogICAgDQoNCldlIGNhbiBjaGVjayB0aGUgTElFIHBhcnQgb2Ygb3VyIGFjcm9ueW0gd2l0aCBhICoqUmVzaWR1YWwgUGxvdCoqIGFuZCB0aGUgTm9ybWFsaXR5IGFzc3VtcHRpb24gd2l0aCBhICoqUS1RIFBsb3QqKg0KDQoNCg0KDQojIyBEaWFnbm9zdGljIFBsb3QgMjogSGlzdG9ncmFtIGFuZCBRLVEgUGxvdA0KDQojIyMgQ3JlYXRpbmcgdGhlIGhpc3RvZ3JhbQ0KDQpJZiB3ZSB3YW50IHRvIGxvb2sgYXQgaWYgYSB2YXJpYWJsZSBpcyBOb3JtYWwsIHdlIGNhbiBsb29rIGF0IGEgaGlzdG9ncmFtLiBUaGUgZGlmZmVyZW5jZSBpcyB0aGF0IGluc3RlYWQgb2YgbG9va2luZyBhdCBhIGhpc3RvZ3JhbSBvZiAkWSQsIHdlIGNyZWF0ZSBhIGhpc3RvZ3JhbSBmb3IgdGhlIHJlc2lkdWFsczogJGVfaSA9IHlfaSAtIFxoYXR7eX1faSQNCg0KDQpGaXJzdCwgd2UgbmVlZCB0byBnZXQgb3VyIHJlc2lkdWFscy4gV2UgY2FuIGRvIGl0IGJ5IGhhbmQgd2l0aCBgY2F0c1wkcmVzIDwtIGNhdHNcJGhlYXJ0IC0gY2F0c19sbVwkZml0YCBvciBzaW1wbGVyIGBjYXRzXCRyZXMgPC0gY2F0c19sbVwkcmVzYC4gQWx0ZXJuYXRpdmVseSwgYXMgc2VlbiBiZWxvdywgd2UgY2FuIHVzZSB0aGUgYGF1Z21lbnQoKWAgZnVuY3Rpb24gZnJvbSB0aGUgYGJyb29tYCBwYWNrYWdlOg0KDQpgYGB7ciBhdWdtZW50fQ0KY2F0czIgPC0gDQogIGF1Z21lbnQoDQogICAgeCA9IGNhdHNfbG0sICAjIFRoZSBsaW5lYXIgbW9kZWwgd2UnbGwgZ2V0IHRoZSByZXNpZHVhbHMgZnJvbQ0KICAgIGRhdGEgPSBjYXRzICAgIyBUaGUgZGF0YSB0byBjYWxjdWxhdGUgdGhlIGZpdHRlZCBhbmQgcmVzaWR1YWxzDQogICkNCg0Kc2xpY2Vfc2FtcGxlKGNhdHMyLCBuID0gMTApDQpgYGANCg0KQWxsIHRoZSBjb2x1bW5zIGBhdWdtZW50KClgIGFkZGVkIGhhdmUgYSBgLmAgYmVmb3JlIHRoZWlyIG5hbWUgdG8gbWFrZSBpdCBlYXNpZXIgdG8gSUQgd2hpY2ggY29sdW1ucyBpdCBhZGRlZC4NCg0KTm93IHdlIGNhbiB1c2UgYGNhdHMyYCB0byBjcmVhdGUgb3VyIGhpc3RvZ3JhbToNCg0KYGBge3IgcmVzX3Bsb3R9DQpnZ19jYXRfcmVzaGlzdCA8LSANCiAgZ2dwbG90KA0KICAgIGRhdGEgPSBjYXRzMiwNCiAgICBtYXBwaW5nID0gYWVzKHggPSAucmVzaWQpDQogICkgKyANCiAgZ2VvbV9oaXN0b2dyYW0oDQogICAgYnJlYWtzID0gc2VxKG1pbihjYXRzMiQucmVzaWQpLCBtYXgoY2F0czIkLnJlc2lkKSswLjIsIGJ5ID0gMC40KSwNCiAgICBmaWxsID0gJ3N0ZWVsYmx1ZScsDQogICAgY29sb3IgPSAnd2hpdGUnDQogICkgKw0KICB0aGVtZV9idygpICsgDQogIGxhYnMoDQogICAgeCA9ICdIZWFydCBXZWlnaHQgUmVzaWR1YWxzJywNCiAgICB0aXRsZSA9ICdIaXN0b2dyYW0gb2YgUmVzaWR1YWxzIG9mIGNhdHMgaGVhcnQgd2VpZ2h0IGJ5IGJvZHkgd2VpZ2h0Jw0KICApICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCwgMC4wNSwgMCkpDQoNCmdnX2NhdF9yZXNoaXN0DQpgYGANCg0KVGhlIGhpc3RvZ3JhbSBsb29rcyAqb2theSouIEhhcyBhIHJvdWdoIGJlbGxzaGFwZSB0byBpdC4NCg0KSGlzdG9ncmFtcyBjYW4gZG8gd2VsbCBpbiBpZGVudGlmeWluZyB0aGUgc2hhcGUgYSB2YXJpYWJsZSB0YWtlcywgYnV0IGlmIGhhdmUgYSBzcGVjaWZpYyBkaXN0cmlidXRpb24gd2Ugd2FudCB0byBjaGVjaywgd2UgY2FuIHVzZSBhIG1vcmUgc3BlY2lmaWMgdG9vbDogdGhlICoqUS1RIFBsb3QqKg0KDQojIyMgR3JhcGggMzogUS1RIFBsb3QNCg0KQ3JlYXRpbmcgYSBRLVEgcGxvdCBieSBoYW5kIGlzIGNvbXBsaWNhdGVkLCBidXQgcHJldHR5IHN0cmFpZ2h0IGZvcndhcmQgaW4gUi4gDQoNCklmIHdlJ3JlIHVzaW5nIHRoZSBgZ2dwbG90MmAgZnJhbWV3b3JrLCB3ZSBjYW4gY3JlYXRlIG91ciBRLVEgcGxvdCBzaW1pbGFyIHRvIGhvdyB3ZSBtYWRlIHRoZSBoaXN0b2dyYW0gd2l0aCBhIGNvdXBsZSBvZiBpbXBvcnRhbnQgY2hhbmdlczoNCg0KLSBgeCA9IC5yZXNpZGAgYmVjb21lcyBgc2FtcGxlID0gLnJlc2lkYA0KDQotIGBnZW9tX2hpc3RvZ3JhbSgpYCBiZWNvbWVzIGBnZW9tX3FxKClgDQoNCi0gYWRkIGEgYGdlb21fcXFfbGluZSgpYCB0byBhZGQgdGhlIGxpbmUgdGhlIHBvaW50cyBzaG91bGQgZm9sbG93DQoNCmBgYHtyIFFRX3Bsb3R9DQpnZ3Bsb3QoDQogIGRhdGEgPSBjYXRzMiwNCiAgbWFwcGluZyA9IGFlcyhzYW1wbGUgPSAucmVzaWQpDQopICsgDQogICMgRGlzdHJpYnV0aW9uIGRlZmF1bHRzIHRvIG5vcm1hbCAocW5vcm0pLCBidXQgaW5jbHVkZWQgYXMgYSBkZW1vDQogIGdlb21fcXFfbGluZSgNCiAgICBkaXN0cmlidXRpb24gPSBxbm9ybSwNCiAgICBjb2xvciA9ICdyZWQnDQogICkgKyANCiAgZ2VvbV9xcShkaXN0cmlidXRpb24gPSBxbm9ybSkgKw0KDQogIHRoZW1lX2J3KCkgKyANCiAgbGFicygNCiAgICB4ID0gJ0hlYXJ0IFdlaWdodCBSZXNpZHVhbHMnLA0KICAgIHRpdGxlID0gJ1EtUSBQbG90IG9mIFJlc2lkdWFscyBvZiBjYXRzIGhlYXJ0IHdlaWdodCBieSBib2R5IHdlaWdodCcNCiAgKSANCmBgYA0KDQpJdCBoYXMgYSBiaXQgb2YgYSB3aWdnbGUgdG8gaXQsIGVzcGVjaWFsbHkgaW4gdGhlIG1pZGRsZS4gU28gbWF5YmUgdGhlIHJlc2lkdWFscyBhcmVuJ3QgTm9ybWFsIDooDQoNCldoaWxlIHRoZSBRLVEgcGxvdCBpcyB0eXBpY2FsbHkgYmV0dGVyIGZvciBoZWxwaW5nIGFzc2VzcyBOb3JtYWxpdHksIHRoZXJlIGlzIHN0aWxsIHN1YmplY3Rpdml0eSBhbmQgZ3JleSBhcmVhIHRoYXQgY2FuIGhhcHBlbi4NCg0KU28gd2hhdCBkbyB3ZSBkbz8NCg0KTG9vayBmb3IgcGFydCAzIQ0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=