dostępne na: github

Użyte biblioteki

require("dplyr")
require("ggplot2")
require("GGally")
require("MASS")
require("cowplot")
require("car")
require("liver")

Informacje wstępne

Poniższe dane pochodzą z serwisu Kaggle i przedstawiają dane dla małej próbki amerykańskiej populacji w zakresie kosztów ubezpieczenia zdrowotnego na podstawie kilku atrybutów opisanych w sekcji Zawartość.

Zawartość

zmienna opis
age wiek opłacającego ubezpieczenie
sex płeć opłacającego ubezpieczenie
bmi indeks masy ciała (Body Mass Index, BMI) ubezpieczonego
children liczba dzieci objętych tym samym ubezpieczeniem/liczba zależnych beneficjentów
smoker czy ubezpieczony jest palaczem?
region okręg zamieszkania ubezpieczonego w USA: northeast, southeast, southwest, northwest
charges koszty ubezpieczenia

źródło: github

Problem

Celem było stworzenie najlepszego modelu, który na podstawie podanych zmiennych umożliwiłby wymodelowanie kosztu ubezpieczenia. Następnie należało sprawdzić, które zmienne mają największy wpływ na model i uprościć go, koncentrując się głównie na tych zmiennych.

Analiza

Import i wstępna wizualizacja danych

df <- read.csv("data/mcp.txt", stringsAsFactors = T)
head(df)
##   age    sex    bmi children smoker    region   charges
## 1  19 female 27.900        0    yes southwest 16884.924
## 2  18   male 33.770        1     no southeast  1725.552
## 3  28   male 33.000        3     no southeast  4449.462
## 4  33   male 22.705        0     no northwest 21984.471
## 5  32   male 28.880        0     no northwest  3866.855
## 6  31 female 25.740        0     no southeast  3756.622
n <- length(df$charges)

df %>%
  ggpairs(aes(color = sex, alpha = 0.7),
    upper = list(continuous = wrap("cor", size = 2.5))
  ) +
  theme(
    axis.ticks.x = element_blank(),
    axis.text.x = element_blank(),
    axis.ticks.y = element_blank(),
    axis.text.y = element_blank()
  )

str(df)
## 'data.frame':    1338 obs. of  7 variables:
##  $ age     : int  19 18 28 33 32 31 46 37 37 60 ...
##  $ sex     : Factor w/ 2 levels "female","male": 1 2 2 2 2 1 1 1 2 1 ...
##  $ bmi     : num  27.9 33.8 33 22.7 28.9 ...
##  $ children: int  0 1 3 0 0 0 1 3 2 0 ...
##  $ smoker  : Factor w/ 2 levels "no","yes": 2 1 1 1 1 1 1 1 1 1 ...
##  $ region  : Factor w/ 4 levels "northeast","northwest",..: 4 3 3 2 2 3 3 2 1 2 ...
##  $ charges : num  16885 1726 4449 21984 3867 ...
summary(df)
##       age            sex           bmi           children     smoker    
##  Min.   :18.00   female:662   Min.   :15.96   Min.   :0.000   no :1064  
##  1st Qu.:27.00   male  :676   1st Qu.:26.30   1st Qu.:0.000   yes: 274  
##  Median :39.00                Median :30.40   Median :1.000             
##  Mean   :39.21                Mean   :30.66   Mean   :1.095             
##  3rd Qu.:51.00                3rd Qu.:34.69   3rd Qu.:2.000             
##  Max.   :64.00                Max.   :53.13   Max.   :5.000             
##        region       charges     
##  northeast:324   Min.   : 1122  
##  northwest:325   1st Qu.: 4740  
##  southeast:364   Median : 9382  
##  southwest:325   Mean   :13270  
##                  3rd Qu.:16640  
##                  Max.   :63770

Wśród danych znajdują się zmienne kategorialne: sex, smoker i region, które posiadają odpowiednio 2, 2 i 4 kategorie. Pozostałe zmienne są to zmienne numeryczne. Powyżej przedstawione są wykresy, które pokazują zależności między każdą zmienną a pozostałymi zmiennymi. W zależności od typu danych wykorzystuje się różne rodzaje wykresów. Jeśli mamy do czynienia z dwoma zmiennymi numerycznymi, stosuje się wykres korelacji, natomiast w przypadku zmiennej numerycznej kontra kategorialna korzysta się z wykresu pudełkowego. Histogramy pokazują rozkład danej cechy. Podstawowe statystyki dla danych kategorialnych pokazują, że w przypadku zmiennej smoker dane są niezbalansowane, ale są zrównoważone w przypadku pozostałych zmiennych kategorialnych. Zmienne numeryczne nie wykazują znaczących odchyleń w statystykach podstawowych. Średnia i mediana są na podobnym poziomie, a kwantyle są równomiernie rozmieszczone. Jedynie zmienna charges wykazuje skośność i przesunięcie w lewo, co oznacza, że ma tendencję do przyjmowania niższych wartości, ale jednocześnie posiada ciężki ogon dla wartości wysokich.

Histogramy i wykresy pudełkowe cech numerycznych

hist.age <- ggplot(data = df, aes(age)) +
  geom_histogram()

hist.bmi <- ggplot(data = df, aes(bmi)) +
  geom_histogram()

hist.children <- ggplot(data = df, aes(children)) +
  geom_histogram(bins = 5)

hist.charges <- ggplot(data = df, aes(charges)) +
  geom_histogram()

plot_grid(hist.age, hist.bmi, hist.children, hist.charges, labels = "auto", align = "hv")

box.age <- ggplot(data = df, aes(age)) +
  geom_boxplot(outlier.colour = "red", outlier.alpha = 0.5) +
  coord_flip() +
  theme(
    axis.title.x = element_blank(),
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank()
  )

box.bmi <- ggplot(data = df, aes(bmi)) +
  geom_boxplot(outlier.colour = "red", outlier.alpha = 0.5) +
  coord_flip() +
  theme(
    axis.title.x = element_blank(),
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank()
  )

box.children <- ggplot(data = df, aes(children)) +
  geom_boxplot(outlier.colour = "red", outlier.alpha = 0.5) +
  coord_flip() +
  theme(
    axis.title.x = element_blank(),
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank()
  )

box.charges <- ggplot(data = df, aes(charges)) +
  geom_boxplot(outlier.colour = "red", outlier.alpha = 0.5) +
  coord_flip() +
  theme(
    axis.title.x = element_blank(),
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank()
  )

plot_grid(box.age, box.bmi, box.children, box.charges, labels = "auto", align = "hv")

Zarówno histogramy, jak i wykresy pudełkowe pozwalają nam zobaczyć rozkład danych numerycznych. Na podstawie tych wykresów można wnioskować, że zmienne children i charges prawdopodobnie mają rozkład Poissona, z kolei zmienna age wykazuje cechy rozkładu jednostajnego. Natomiast zmienna BMI prawdopodobnie ma rozkład normalny, choć na wykresie pudełkowym można zauważyć wydłużony ogon w kierunku wyższych wartości, co może wpływać na percepcję danych.

Korelacja między zmiennymi numerycznymi

age.v.bmi <- ggplot(data = df, aes(age, bmi, color = sex)) +
  geom_point(size = 2.5, alpha = 0.7) +
  geom_smooth(color = "red", fill = "blue") +
  ggtitle(sprintf("r = %.4f", cor(df$age, df$bmi))) +
  theme(legend.position = "none")

age.v.children <- ggplot(data = df, aes(age, children, color = sex)) +
  geom_point(size = 2.5, alpha = 0.7) +
  ggtitle(sprintf("r = %.4f", cor(df$age, df$children))) +
  theme(legend.position = "none")

age.v.charges <- ggplot(data = df, aes(age, charges, color = sex)) +
  geom_point(size = 2.5, alpha = 0.7) +
  ggtitle(sprintf("r = %.4f", cor(df$age, df$charges))) +
  theme(legend.position = "none")

children.v.bmi <- ggplot(data = df, aes(children, bmi, color = sex)) +
  geom_point(size = 2.5, alpha = 0.7) +
  ggtitle(sprintf("r = %.4f", cor(df$children, df$bmi))) +
  theme(legend.position = "none")

children.v.charges <- ggplot(data = df, aes(children, charges, color = sex)) +
  geom_point(size = 2.5, alpha = 0.7) +
  ggtitle(sprintf("r = %.4f", cor(df$children, df$charges))) +
  theme(legend.position = "none")

charges.v.bmi <- ggplot(data = df, aes(charges, bmi, color = sex)) +
  geom_point(size = 2.5, alpha = 0.7) +
  ggtitle(sprintf("r = %.4f", cor(df$charges, df$bmi)))

plot_grid(age.v.bmi, age.v.children, age.v.charges, children.v.bmi,
  align = "hv",
  children.v.charges, charges.v.bmi, labels = "auto", ncol = 2
)

Zbadano korelację między danymi numerycznymi w celu sprawdzenia, czy istnieje jakakolwiek zależność między nimi. Jak można zauważyć na powyższych wykresach, żadne z danych numerycznych nie wykazuje istotnej korelacji. Jednak na niektórych wykresach można dostrzec wyodrębnione grupy. Na przykład na wykresie charges w zależności od age można zaobserwować trzy linie, co sugeruje istnienie trzech populacji osób, które, niezależnie od wieku, płacą określone poziomy składek. Obserwuje się również pewną korelację, gdzie wraz z wiekiem wzrasta wysokość składek, ale słaby współczynnik korelacji może wynikać z obecności dwóch populacji powyżej pierwszej. Również na wykresie bmi w zależności od charges można zauważyć wyodrębnienie dwóch populacji, gdzie dla wyższych składek populacja ma wyższe wartości bmi. To sugeruje, że zachowanie się danych różni się dla wyższych i niższych składek, co może utrudniać określenie jednego trendu dla wszystkich danych.

Model liniowy

# liniowy zgeneralizowany
df.lm1 <- lm(charges ~ ., data = df)
summary(df.lm1)
## 
## Call:
## lm(formula = charges ~ ., data = df)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -11304.9  -2848.1   -982.1   1393.9  29992.8 
## 
## Coefficients:
##                 Estimate Std. Error t value Pr(>|t|)    
## (Intercept)     -11938.5      987.8 -12.086  < 2e-16 ***
## age                256.9       11.9  21.587  < 2e-16 ***
## sexmale           -131.3      332.9  -0.394 0.693348    
## bmi                339.2       28.6  11.860  < 2e-16 ***
## children           475.5      137.8   3.451 0.000577 ***
## smokeryes        23848.5      413.1  57.723  < 2e-16 ***
## regionnorthwest   -353.0      476.3  -0.741 0.458769    
## regionsoutheast  -1035.0      478.7  -2.162 0.030782 *  
## regionsouthwest   -960.0      477.9  -2.009 0.044765 *  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 6062 on 1329 degrees of freedom
## Multiple R-squared:  0.7509, Adjusted R-squared:  0.7494 
## F-statistic: 500.8 on 8 and 1329 DF,  p-value: < 2.2e-16
# wykresy diagnostyczne
par(mfrow = c(2, 2))
plot(df.lm1, pch = 16)

Następnie przystąpiono do utworzenia modelu liniowego, uwzględniającego wszystkie dostępne dane. Wartość współczynnika determinacji \(R2\) nie jest wystarczająca, ale nie jest również najgorsza.

Wykres Residuals vs Fitted, przedstawiający na osi \(X\) wartości dopasowane, a na osi \(Y\) różnice między wartościami dopasowanymi a przewidywanymi, ukazuje istnienie trzech populacji. Prawdopodobnie istnieje czynnik nieuwzględniony w danych, który odpowiada za ten stan rzeczy. Dopasowanie danych dla niższych wartości charges jest znacznie lepsze niż dla wyższych, gdzie obserwuje się przeszacowanie lub niedoszacowanie.

Wykres Normal Q-Q wyraźnie odbiega od rozkładu normalnego, ponieważ dane w wąskim zakresie osadzone są wzdłuż linii. Krzywa odchylona ku górze po prawej stronie wykresu wskazuje na wysoką liczbę dużych wartości skrajnych lub prawoskośność.

Wykres skali i lokalizacji ponownie ujawnia obecność dwóch populacji. Dla niższych wartości charges dane są rozproszone wokół linii, ale można również zauważyć wiele punktów odstających. Natomiast druga populacja znajduje się wyżej na wykresie, co wskazuje na nieodpowiednie dopasowanie modelu dla wyższych wartości, gdzie przewidywane wartości znacznie różnią się od rzeczywistych.

Ostatni z czterech wykresów ponownie ukazuje trzy populacje, choć nie jest to oczywiste i występuje wiele wartości odstających.

# obserwacje wplywowe i odstajace
par(mfrow = c(1, 2))
cutoff <- 4 / (n - length(df.lm1$coefficients) - 2)
influencePlot(df.lm1, fill.alpha = 1, fill.col = "red")
##        StudRes         Hat       CookD
## 439  -1.001958 0.015980803 0.001811548
## 544   3.827591 0.012173584 0.019856755
## 578   4.219800 0.009354004 0.018448566
## 1086 -1.754152 0.018123011 0.006300674
## 1301  5.009599 0.006920097 0.019084806
plot(df.lm1, which = 4, cook.levels = cutoff, lwd = 2)
abline(h = cutoff, col = "red", lty = 2, lwd = 2)

W celu wykrycia obserwacji odstających i wpływowych zastosowano następujące parametry:

  • Studentized residuals: służy do oceny, czy dana obserwacja jest wartością odstającą pod względem reszt. Im większa wartość bezwzględna studentized residuals, tym bardziej odstająca jest dana obserwacja.

  • Hat-values: określają wpływ poszczególnych obserwacji na dopasowanie modelu. Przyjmują wartości między 0 a 1, gdzie wartości bliższe 1 oznaczają większy wpływ.

  • Cook’s D: miara wpływu obserwacji na dopasowanie modelu regresji oraz na wartości estymowanych parametrów. Wartość progu krytycznego dla Cook’s D, oznaczanego jako cutoff, jest ustalana na podstawie wzoru \(\mathrm{cutoff} = \frac{4}{(n - p - 2)}\), gdzie \(n\) to liczba obserwacji w danych, a \(p\) to liczba parametrów (współczynników) w modelu regresji, wliczając w to wyraz wolny.

Analiza wskazuje na istnienie wielu obserwacji, które potencjalnie mogą być uznane za odstające, co jest zgodne z wcześniejszymi wykresami.

df.lm1.AIC <- AIC(df.lm1)
df.lm1.AIC
## [1] 27115.51

\(AIC\) uwzględnia zarówno dopasowanie modelu do danych, jak i złożoność modelu, dążąc do znalezienia równowagi między tymi dwoma czynnikami. Jego celem jest wybór modelu, który dobrze uogólnia dane, minimalizując jednocześnie złożoność modelu. Dlatego właśnie wybraliśmy \(AIC\) jako miarę oceny naszego modelu. Im mniejsza wartość \(AIC\), tym lepsze dopasowanie modelu, stąd widać, że powyższy model nie jest najlepszym dopasowaniem.

Następnie skonstruowano nowy model, uwzględniając liczbę predyktorów równą liczbie współczynników (coefficients) o najniższych wartościach \(p\) (ozn. ***).

df.lm2 <- lm(charges ~ age + bmi + children + smoker, data = df)
summary(df.lm2)
## 
## Call:
## lm(formula = charges ~ age + bmi + children + smoker, data = df)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -11897.9  -2920.8   -986.6   1392.2  29509.6 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -12102.77     941.98 -12.848  < 2e-16 ***
## age            257.85      11.90  21.675  < 2e-16 ***
## bmi            321.85      27.38  11.756  < 2e-16 ***
## children       473.50     137.79   3.436 0.000608 ***
## smokeryes    23811.40     411.22  57.904  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 6068 on 1333 degrees of freedom
## Multiple R-squared:  0.7497, Adjusted R-squared:  0.7489 
## F-statistic: 998.1 on 4 and 1333 DF,  p-value: < 2.2e-16
par(mfrow = c(2, 2))
plot(df.lm2, pch = 16)

par(mfrow = c(1, 2))
cutoff <- 4 / (n - length(df.lm2$coefficients) - 2)
influencePlot(df.lm2, fill.alpha = 1, fill.col = "red")
##        StudRes         Hat       CookD
## 544   3.809648 0.010485091 0.030448715
## 578   4.347011 0.005089296 0.019076254
## 1048  1.615743 0.015136582 0.008014970
## 1086 -1.827707 0.014660714 0.009923179
## 1301  4.916443 0.004483168 0.021398531
plot(df.lm2, which = 4, cook.levels = cutoff, lwd = 2)
abline(h = cutoff, col = "red", lty = 2, lwd = 2)

# czy jest roznica miedzy tymi modelami?
df.lm2.AIC <- AIC(df.lm2)
df.lm2.AIC
## [1] 27114.04
anova(df.lm1, df.lm2)
## Analysis of Variance Table
## 
## Model 1: charges ~ age + sex + bmi + children + smoker + region
## Model 2: charges ~ age + bmi + children + smoker
##   Res.Df        RSS Df  Sum of Sq      F Pr(>F)
## 1   1329 4.8840e+10                            
## 2   1333 4.9078e+10 -4 -238917273 1.6253 0.1654

Korelacje, wykresy, wyniki oraz ocena modelu praktycznie nie uległy zmianie. Ponieważ nie było istotnej różnicy między modelami, pozostano przy modelu prostszym (df.lm2).

Transformacje zmiennych

Zdecydowano się więc przeprowadzić transformację zmiennych. W tym celu najpierw sprawdzono, czy dane numeryczne, które uwzględniliśmy w modelu (z wyjątkiem zmiennej smoker), mają rozkład normalny.

shapiro.test(df$age)
## 
##  Shapiro-Wilk normality test
## 
## data:  df$age
## W = 0.9447, p-value < 2.2e-16
shapiro.test(df$bmi)
## 
##  Shapiro-Wilk normality test
## 
## data:  df$bmi
## W = 0.99389, p-value = 2.605e-05
shapiro.test(df$children)
## 
##  Shapiro-Wilk normality test
## 
## data:  df$children
## W = 0.82318, p-value < 2.2e-16

Okazało się, że żadna z tych zmiennych nie spełniała tego warunku, co potwierdziła również wcześniejsza analiza histogramów oraz wykresów pudełkowych.

Przeprowadzono transformację Boxa-Coxa w celu znalezienia optymalnej wartości \(\lambda\), która pozwoliłaby przekształcić dane tak, aby przybliżyć je do rozkładu normalnego.

age.fit <- boxCox(lm(age ~ 1, data = df))

age.lambda <- age.fit$x[which.max(age.fit$y)]
age.lambda
## [1] 0.6262626
df$age.boxcox <- (df$age^age.lambda - 1) / age.lambda

bmi.fit <- boxCox(lm(bmi ~ 1, data = df))

bmi.lambda <- bmi.fit$x[which.max(bmi.fit$y)]
bmi.lambda
## [1] 0.4646465
df$bmi.boxcox <- (df$bmi^bmi.lambda - 1) / bmi.lambda

children.fit <- boxCox(lm(children + 1 ~ 1, data = df))

children.lambda <- children.fit$x[which.max(children.fit$y)]
children.lambda
## [1] -0.3838384
df$children.boxcox <- ((df$children + 1)^children.lambda - 1) / children.lambda

Następnie sprawdzono, czy dopasowane \(\lambda\) wpłynęło na zmianę rozkładu danych.

shapiro.test(df$age.boxcox)
## 
##  Shapiro-Wilk normality test
## 
## data:  df$age.boxcox
## W = 0.9439, p-value < 2.2e-16
shapiro.test(df$bmi.boxcox)
## 
##  Shapiro-Wilk normality test
## 
## data:  df$bmi.boxcox
## W = 0.99858, p-value = 0.3492
shapiro.test(df$children.boxcox)
## 
##  Shapiro-Wilk normality test
## 
## data:  df$children.boxcox
## W = 0.81148, p-value < 2.2e-16
hist.age.boxcox <- ggplot(data = df, aes(age.boxcox)) +
  geom_histogram()

hist.bmi.boxcox <- ggplot(data = df, aes(bmi.boxcox)) +
  geom_histogram()

hist.children.boxcox <- ggplot(data = df, aes(children.boxcox)) +
  geom_histogram(bins = 5)

plot_grid(hist.age.boxcox, hist.bmi.boxcox, hist.children.boxcox, labels = "auto", align = "hv")

Rezultaty pokazały, że transformacja była możliwa jedynie dla zmiennej bmi, zatem zbudowano kolejny model uwzględniający transformowane bmi.

df.lm3 <- lm(charges ~ age + bmi.boxcox + children + smoker, data = df)
summary(df.lm3)
## 
## Call:
## lm(formula = charges ~ age + bmi.boxcox + children + smoker, 
##     data = df)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -11533  -2956  -1072   1504  29402 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -19116.15    1465.19 -13.047  < 2e-16 ***
## age            257.17      11.89  21.627  < 2e-16 ***
## bmi.boxcox    2023.70     170.57  11.865  < 2e-16 ***
## children       473.36     137.67   3.438 0.000603 ***
## smokeryes    23819.56     410.86  57.975  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 6063 on 1333 degrees of freedom
## Multiple R-squared:  0.7501, Adjusted R-squared:  0.7494 
## F-statistic:  1000 on 4 and 1333 DF,  p-value: < 2.2e-16
par(mfrow = c(2, 2))
plot(df.lm3, pch = 16)

par(mfrow = c(1, 2))
cutoff <- 4 / (n - length(df.lm3$coefficients) - 2)
influencePlot(df.lm3, fill.alpha = 1, fill.col = "red")
##         StudRes         Hat       CookD
## 439  -0.9414585 0.013121191 0.002357104
## 544   3.8938507 0.009465927 0.028674285
## 578   4.3519654 0.005062440 0.019017752
## 1086 -1.7523178 0.015378722 0.009577071
## 1301  4.9025126 0.004477112 0.021250713
plot(df.lm3, which = 4, cook.levels = cutoff, lwd = 2)
abline(h = cutoff, col = "red", lty = 2, lwd = 2)

df.lm3.AIC <- AIC(df.lm3)
df.lm3.AIC
## [1] 27111.7
anova(df.lm2, df.lm3)
## Analysis of Variance Table
## 
## Model 1: charges ~ age + bmi + children + smoker
## Model 2: charges ~ age + bmi.boxcox + children + smoker
##   Res.Df        RSS Df Sum of Sq F Pr(>F)
## 1   1333 4.9078e+10                      
## 2   1333 4.8993e+10  0  85440586

Ten model nie różni się od poprzednich.

Obserwacje odstające i wpływowe

We wszystkich 3 modelach widać było wiele obserwacji przekraczających dystans Cooka.

outlierTest(df.lm3)
##      rstudent unadjusted p-value Bonferroni p
## 1301 4.902513         1.0625e-06    0.0014216
## 578  4.351965         1.4523e-05    0.0194320
infl <- influence.measures(df.lm3)$is.inf
data.frame(infl) %>%
  mutate(isINF = if_any()) %>%
  filter(isINF == T) %>%
  length()
## [1] 10

Wykazano, że w ostatnim modelu mniej jest obserwacji odstających, jednak aż 10 wpływowych.

Porównanie modeli

model <- c("df.lm1", "df.lm2", "df.lm3")
AIC.scores <- c(df.lm1.AIC, df.lm2.AIC, df.lm3.AIC)
ggplot() +
  geom_col(aes(x = model, y = AIC.scores))

Powyższy wykres pokazuje, że nie zaobserwowano różnicy między ocenami modeli, dlatego wybrano model najprostszy, z najmniejszą liczbą zmiennych oraz brakiem transformowanych danych.

Wnioski i dyskusja

Przeprowadzona analiza wykazała, że istnieje istotny związek pomiędzy zmiennymi age, bmi children oraz smoker a wysokością opłacanej składki ubezpieczeniowej. Zmienna region nie wpływa na wysokość składki. Wydaje się, że zmienna sex wpływa na wysokość składki, ale nie jest istotna statystycznie. W danych widoczne są wyraźne subpopulacje (np. 3 na wykresie age vs charges), co sugeruje, że istnieje pewna zależność od zmiennej, która nie została uwzględniona w analizie, być może związana z historią klienta. W każdym przypadku, precyzyjne modelowanie składki na podstawie dostępnych danych jest trudne, chyba że podzielimy klientów na konkretne grupy, co pozwoliłoby na bardziej dokładne przewidywanie składek dla poszczególnych klientów w każdej z tych grup. Jednak nie jest jasne, jak dokładnie podzielić klientów na grupy, oprócz samego wyniku, który jest obecnie modelowany. Przewidywania są dokładne tylko dla niższych składek, czyli dla większej populacji klientów.