בפרק הקודם הזכרנו שבחיזוי סטטיסטי אנחנו מחפשים את הפונקציה \(f\) שמביאה לתוצאת החיזוי הטובה ביותר:
\[Y=f(X)+\epsilon\]
רגרסיה לינארית היא שיטה סטטיסטית המניחה מבנה פשטני מאוד, אך שימושי להפליא. גם אם רוב התופעות הנצפות אינן “לינאריות”, בהרבה מהמקרים המעשיים רגרסיה לינארית תביא תוצאות טובות, או לפחות תעזור לנו לחפש בכיוונים טובים, ולהבין אילו משתנים משפיעים על משתנה המטרה שלנו ובאיזה כיוון.
מודל הרגרסיה הלינארית מניח שהפונקציה \(f\) היא לינארית, כלומר:
\[Y = \beta_0 + \beta_1x_1+\ldots+\beta_px_p\ + \epsilon\]
כדי לחשב את המקדמים \(\{\beta_i\}_{i=0}^p\), אנחנו פותרים בעיית מינימום - המקדמים שמביאים את השגיאה הריבועית למינימום.
library(tidyverse)
iris_setosa <- iris %>%
filter(Species == "setosa") %>%
mutate(Sepal.Length = jitter(Sepal.Length))
setosa_lm = lm(data = iris_setosa,
formula = Sepal.Width ~ Sepal.Length)
iris_lm_errors <- iris_setosa %>%
mutate(Sepal.Width.pred = predict(setosa_lm,
newdata = iris %>%
filter(Species == "setosa")))
ggplot(iris_lm_errors,
aes(x=Sepal.Length, y=Sepal.Width)) +
geom_point() + stat_smooth(method = "lm", se = FALSE) +
geom_segment(aes(x = Sepal.Length, xend = Sepal.Length, y = Sepal.Width, yend = Sepal.Width.pred))
פתרון הרגרסיה הלינארית מביא לקו רגרסיה הממזער את המרחקים שבין הנקודות (התצפיות) לבינו (ריבועי המרחקים שבתרשים).
כל מודל רגרסיה מכיל סיכום כך לדוגמה מודל רגרסיה מורכב יותר עבור אותה בעיה (רק המכיל את כל המשתנים) יראה כך:
iris_lm_complete <- lm(data = iris,
formula = Sepal.Width ~ Sepal.Length + Petal.Width + Petal.Length)
summary(iris_lm_complete)
##
## Call:
## lm(formula = Sepal.Width ~ Sepal.Length + Petal.Width + Petal.Length,
## data = iris)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.88045 -0.20945 0.01426 0.17942 0.78125
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.04309 0.27058 3.855 0.000173 ***
## Sepal.Length 0.60707 0.06217 9.765 < 2e-16 ***
## Petal.Width 0.55803 0.12256 4.553 1.1e-05 ***
## Petal.Length -0.58603 0.06214 -9.431 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.3038 on 146 degrees of freedom
## Multiple R-squared: 0.524, Adjusted R-squared: 0.5142
## F-statistic: 53.58 on 3 and 146 DF, p-value: < 2.2e-16
שימו לב לשימוש בפרמטר formula שבפקודה lm. פרמטר זה מתאר את המודל לפיו הפקודה צריכה לבנות את הרגרסיה. התוצר של הפקודה summary מפרט את המודל, בעמודה estimate ניתן לראות את המקדמים למשתנים. עמודה Std error מציגה את סטיית התקן, ושתי העמודות האחרונות מתייחסות למבחן סטטיסטי לגבי המקדם הרלוונטי (האם המקדם שונה מ-0 באופן מובהק סטטיסטי). הכוכביות שבימין כל שורה מציגות את רמת המובהקות על פי מדרגות. במדעי החברה מקובל להתייחס לרמות מובהקות מתחת ל-0.05 כמשמעותיות. המודל בדוגמה יתורגם כך:
\[\text{Sepal.Width} \approx 1.04 + 0.61\cdot\text{Sepal.Length} + 0.56\cdot\text{Petal.Width} -0.58\cdot\text{Petal.Length}\]
בתחתית הדיווח מופיע המדד Multiple R-squared ו-Adjusted R-sqaured. שניהם מדדים הנוגעים לטיב הרגרסיה. אלו מדדים שנעים בין 0-1, וכאשר הם קרובים ל-1 המשמעות היא שהרגרסיה מצליחה להסביר חלק משמעותי מהשונות שבמשתנה התלוי.
מדד נוסף למידת הדיוק של הרגרסיה הוא ה-RSE (או Residual standard error), שגם הוא נמצא בתחתית סיכום מודל הרגרסיה. ה-RSE מחושב על ידי הנוסחה הבאה:
\[\text{RSE} = \sqrt{\frac{\text{RSS}}{n-2}} = \sqrt{\frac{1}{n-2}\sum_{i=1}^n\left(y_i-\hat{y}_i\right)^2}\]
הנוסחה עבור \(R^2\) נתונה על ידי:
\[R^2 = 1 - \frac{\sum_{i=1}^n\left(y_i-\hat{y}_i\right)^2}{\sum_{i=1}^n\left(y_i-\bar{y}_i\right)^2} = 1 - \frac{\text{RSS}}{\text{TSS}}\]
לא ניכנס לעומק התיאוריה של מודל הרגרסיה הלינארית, אך לקורא המעוניין ניתן להעמיק בספר הבא (פרק 3):
במישור המעשי, מודל של רגרסיה לינארית מתאים כדי לחזות משתנה מטרה רציף (שמקבל ערכים בטווח ערכים מסוים), אך במקרים מסוימים עשוי להתאים גם לבעיות סיווג או למשתנה מטרה אורדינלי (בדיד עם סט ערכים סופי שיש ביניהם יחס סדר).
בדוגמה לעיל עסקנו במשתנים רציפים בלבד. אך לעיתים ישנם משתנים קטגוריאליים שאנו רוצים להשתמש בהם במסגרת מודל החיזוי, למשל השפעת מגדר על גובה ההכנסה. מגדר הוא משתנה המקבל אחת משתי קטגוריות. כיצד ניתן להשתמש בו במודל רגרסיה?
במודל הרגרסיה הוא יבוא לידי ביטוי כתוספת בעבור ערך מסוים.
\[\text{Salary} = \beta_0 + \beta_{\text{f}}\cdot X_{\text{female}}\]
אם התצפית מתייחסת לנקבה אז ערך המשכורת ישתנה בתוספת של \(\beta_{\text{f}}\). הרמה הנומינלית תהיה (עבור גברים) \(\beta_0\).
בפונקציה lm ניתן להכניס יחסית בקלות הכללות למודל הלינארי, על ידי שינוי מתאים במשתנים. לדוגמה, הפקודה הבאה תכניס משתנים חדשים עם יחסים של מכפלות או ריבוע. עם זאת יש להיזהר מ“קללת המימד” עליה הסברנו בפרק קודם. בדוגמה הבאה הכנסנו גם את משתנה הפקטור Species, את האינטראקציה בין אורך ורוחב של עלי כותרת, ואת האורך של העלים בריבוע.
iris_nonlm <- lm(data = iris %>% mutate(sqaured.Length = Sepal.Length^2),
formula =
Sepal.Width ~
Sepal.Length + Petal.Length + Petal.Width +
sqaured.Length + Petal.Length*Petal.Width +
factor(Species))
summary(iris_nonlm)
##
## Call:
## lm(formula = Sepal.Width ~ Sepal.Length + Petal.Length + Petal.Width +
## sqaured.Length + Petal.Length * Petal.Width + factor(Species),
## data = iris %>% mutate(sqaured.Length = Sepal.Length^2))
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.85025 -0.15336 -0.00016 0.15714 0.77920
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -2.55343 1.27616 -2.001 0.047312 *
## Sepal.Length 1.82593 0.45677 3.997 0.000103 ***
## Petal.Length -0.12474 0.12068 -1.034 0.303026
## Petal.Width 0.39122 0.34471 1.135 0.258321
## sqaured.Length -0.12249 0.03834 -3.194 0.001727 **
## factor(Species)versicolor -1.33290 0.30149 -4.421 1.94e-05 ***
## factor(Species)virginica -1.58903 0.34753 -4.572 1.04e-05 ***
## Petal.Length:Petal.Width 0.03115 0.06563 0.475 0.635800
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2589 on 142 degrees of freedom
## Multiple R-squared: 0.6638, Adjusted R-squared: 0.6473
## F-statistic: 40.06 on 7 and 142 DF, p-value: < 2.2e-16
בתרגיל זה תתאימו מודל לחיזוי מחירם של יהלומים.
glimpse(diamonds)
## Observations: 53,940
## Variables: 10
## $ carat <dbl> 0.23, 0.21, 0.23, 0.29, 0.31, 0.24, 0.24, 0.26, 0.22, ...
## $ cut <ord> Ideal, Premium, Good, Premium, Good, Very Good, Very G...
## $ color <ord> E, E, E, I, J, J, I, H, E, H, J, J, F, J, E, E, I, J, ...
## $ clarity <ord> SI2, SI1, VS1, VS2, SI2, VVS2, VVS1, SI1, VS2, VS1, SI...
## $ depth <dbl> 61.5, 59.8, 56.9, 62.4, 63.3, 62.8, 62.3, 61.9, 65.1, ...
## $ table <dbl> 55, 61, 65, 58, 58, 57, 57, 55, 61, 61, 55, 56, 61, 54...
## $ price <int> 326, 326, 327, 334, 335, 336, 336, 337, 337, 338, 339,...
## $ x <dbl> 3.95, 3.89, 4.05, 4.20, 4.34, 3.94, 3.95, 4.07, 3.87, ...
## $ y <dbl> 3.98, 3.84, 4.07, 4.23, 4.35, 3.96, 3.98, 4.11, 3.78, ...
## $ z <dbl> 2.43, 2.31, 2.31, 2.63, 2.75, 2.48, 2.47, 2.53, 2.49, ...
# Use ?diamonds to see the documentation of the database
# or click F1 on diamonds
# Set a consistent test set among the groups - USE THIS SET AS A TRAIN SET OF 80%
train_set_idx <- round(seq(1, NROW(diamonds), length.out = (NROW(diamonds)*0.8)))
predict באופן הבא:predict(linear_model, newdata,...)
הקבוצה המנצחת היא זו שהגיעה לשגיאה הנמוכה ביותר.
חלק חשוב בבניית מודלים (ולמעשה אף בהכנת ה-data לפני בניית המודלים), הוא צמצום מימדים, ובחירת משתנים (Variable selection, dimension reduction). באמצעות תהליך “צעדים” ניתן להנחות את המחשב לבחור רגרסיה המכילה תת-קבוצה של המשתנים. נמחיש פקודה העוזרת לצמצם את מודל הרגרסיה למשתנים המשמעותיים ביותר:
step_wise_model <- MASS::stepAIC(iris_nonlm, direction = "both", trace = TRUE)
## Start: AIC=-397.66
## Sepal.Width ~ Sepal.Length + Petal.Length + Petal.Width + sqaured.Length +
## Petal.Length * Petal.Width + factor(Species)
##
## Df Sum of Sq RSS AIC
## - Petal.Length:Petal.Width 1 0.01509 9.5305 -399.42
## <none> 9.5154 -397.66
## - sqaured.Length 1 0.68375 10.1991 -389.25
## - Sepal.Length 1 1.07079 10.5862 -383.66
## - factor(Species) 2 1.40177 10.9172 -381.05
##
## Step: AIC=-399.42
## Sepal.Width ~ Sepal.Length + Petal.Length + Petal.Width + sqaured.Length +
## factor(Species)
##
## Df Sum of Sq RSS AIC
## - Petal.Length 1 0.0648 9.5952 -400.41
## <none> 9.5305 -399.42
## + Petal.Length:Petal.Width 1 0.0151 9.5154 -397.66
## - sqaured.Length 1 0.7970 10.3275 -389.37
## - Sepal.Length 1 1.2766 10.8071 -382.56
## - Petal.Width 1 1.3433 10.8738 -381.64
## - factor(Species) 2 3.9365 13.4669 -351.56
##
## Step: AIC=-400.41
## Sepal.Width ~ Sepal.Length + Petal.Width + sqaured.Length + factor(Species)
##
## Df Sum of Sq RSS AIC
## <none> 9.5952 -400.41
## + Petal.Length 1 0.0648 9.5305 -399.42
## - sqaured.Length 1 1.0942 10.6894 -386.21
## - Petal.Width 1 1.3204 10.9156 -383.07
## - Sepal.Length 1 1.4978 11.0931 -380.65
## - factor(Species) 2 12.0842 21.6794 -282.14
summary(step_wise_model)
##
## Call:
## lm(formula = Sepal.Width ~ Sepal.Length + Petal.Width + sqaured.Length +
## factor(Species), data = iris %>% mutate(sqaured.Length = Sepal.Length^2))
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.85363 -0.13206 0.00619 0.17297 0.78127
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -2.60830 1.14098 -2.286 0.0237 *
## Sepal.Length 1.80619 0.38096 4.741 5.05e-06 ***
## Petal.Width 0.49857 0.11200 4.451 1.70e-05 ***
## sqaured.Length -0.12422 0.03065 -4.052 8.26e-05 ***
## factor(Species)versicolor -1.59481 0.12661 -12.596 < 2e-16 ***
## factor(Species)virginica -1.88631 0.19275 -9.786 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2581 on 144 degrees of freedom
## Multiple R-squared: 0.661, Adjusted R-squared: 0.6493
## F-statistic: 56.16 on 5 and 144 DF, p-value: < 2.2e-16
דרך נוספת לצמצם מימדים (כאשר מדובר במשתנים רציפים) היא באמצעות ניתוח גורמים עיקריים Principle Component Analysis. ככלל, לפני שאנחנו מתאימים מודל, נרצה לוודא שהמשתנים אינם בעלי קורלציה אחד לשני (כדי לא להכניס משתנים מיותרים, וכדי לא ליצור אפקטים מטעים במודלים). האלגוריתם של PCA מחלץ מהנתונים “וקטורים” לפי סדר יורד שבו בכל וקטור השונות היא “הגבוהה ביותר האפשרית”.
כך הנתונים מתפרקים לגורמים המסבירים את השונות, וניתן לבחור תת קבוצה של וקטורים. החיסרון הוא שלא תמיד הפירוק ניתן להסבר בצורה מיידית.
no_show_appointments <- read_csv("data-files/Medical_Appointments_No_Shows_KaggleV2-May-2016.csv") %>%
select(Age, Scholarship:SMS_received) %>%
mutate(is_train = runif(NROW(Age)) <= 0.8)
pca1 <- prcomp(no_show_appointments %>% select(-Age) %>% filter(is_train))
pca1
## Standard deviations (1, .., p=7):
## [1] 0.4669771 0.4208393 0.2977788 0.2207158 0.1710981 0.1601232 0.0000000
##
## Rotation (n x k) = (7 x 7):
## PC1 PC2 PC3 PC4
## Scholarship 0.003011206 -0.03113490 0.998673335 -0.024782941
## Hipertension -0.041397640 0.92393746 0.036044597 0.371269025
## Diabetes -0.023561666 0.37436968 -0.009283136 -0.924876661
## Alcoholism -0.012544195 0.04025337 0.035454172 0.077392799
## Handcap -0.010354381 0.03582672 -0.002728225 -0.011330244
## SMS_received 0.998727914 0.04810046 -0.001318959 -0.005500832
## is_train 0.000000000 0.00000000 0.000000000 0.000000000
## PC5 PC6 PC7
## Scholarship -0.03237822 -0.003252380 0
## Hipertension -0.06773584 0.029962750 0
## Diabetes 0.05703655 0.023579897 0
## Alcoholism 0.99542853 -0.009885082 0
## Handcap -0.01052352 -0.999181000 0
## SMS_received 0.01102918 -0.008675174 0
## is_train 0.00000000 0.000000000 1
summary(pca1)
## Importance of components%s:
## PC1 PC2 PC3 PC4 PC5 PC6 PC7
## Standard deviation 0.4670 0.4208 0.2978 0.22072 0.17110 0.16012 0
## Proportion of Variance 0.3712 0.3015 0.1509 0.08292 0.04983 0.04364 0
## Cumulative Proportion 0.3712 0.6727 0.8236 0.90653 0.95636 1.00000 1
# to retreive the variables use
pca1_use <- predict(pca1, newdata = no_show_appointments %>% select(-Age) %>% filter(!is_train))
glimpse(pca1_use)
## num [1:22182, 1:7] -0.31 -0.31 -0.307 0.691 0.688 ...
## - attr(*, "dimnames")=List of 2
## ..$ : NULL
## ..$ : chr [1:7] "PC1" "PC2" "PC3" "PC4" ...
# now you can continue to work with pca1_use as your data set
# If you add age, you will see that it becomes the most significant element (PC1)
pca_with_age <- prcomp(no_show_appointments)
pca_with_age
## Standard deviations (1, .., p=8):
## [1] 23.1112273 0.4670066 0.4005191 0.3614747 0.2962222 0.2205927
## [7] 0.1701503 0.1607010
##
## Rotation (n x k) = (8 x 8):
## PC1 PC2 PC3 PC4
## Age -9.999558e-01 -8.358323e-05 5.260250e-05 -9.197616e-03
## Scholarship 1.190997e-03 -1.563966e-03 -1.375456e-03 7.138690e-02
## Hipertension -8.689894e-03 2.975722e-02 -3.878626e-05 9.182012e-01
## Diabetes -3.268288e-03 1.961402e-02 7.182586e-04 3.862034e-01
## Alcoholism -7.118241e-04 1.196221e-02 -1.516572e-03 2.367262e-02
## Handcap -5.455131e-04 1.041083e-02 3.869627e-04 2.757424e-02
## SMS_received -2.554582e-04 -9.992362e-01 -1.716065e-03 3.538461e-02
## is_train -5.494215e-05 1.715729e-03 -9.999961e-01 5.715387e-05
## PC5 PC6 PC7 PC8
## Age -0.001821892 0.0004455456 -0.0005156437 -0.0003121143
## Scholarship -0.996303953 0.0317144543 -0.0356426698 0.0006169277
## Hipertension 0.055056584 -0.3875661066 -0.0467941464 -0.0227411302
## Diabetes 0.054750263 0.9184427537 0.0589565384 -0.0209869959
## Alcoholism -0.036164364 -0.0709342017 0.9954667963 0.0447218185
## Handcap 0.004600799 0.0135786560 -0.0445027052 0.9984712268
## SMS_received 0.003891307 0.0057302939 0.0112704837 0.0098486073
## is_train 0.001457420 0.0007341138 -0.0014531053 0.0002865905
summary(pca_with_age)
## Importance of components%s:
## PC1 PC2 PC3 PC4 PC5 PC6
## Standard deviation 23.1112 0.46701 0.4005 0.36147 0.29622 0.22059
## Proportion of Variance 0.9987 0.00041 0.0003 0.00024 0.00016 0.00009
## Cumulative Proportion 0.9987 0.99910 0.9994 0.99964 0.99981 0.99990
## PC7 PC8
## Standard deviation 0.17015 0.16070
## Proportion of Variance 0.00005 0.00005
## Cumulative Proportion 0.99995 1.00000
לרגרסיה הלינארית ישנן מספר חולשות שכדאי להיות מודעים אליהם:
iris_setosa_outlier <- iris_setosa %>%
add_row(Sepal.Length = 5.1, Sepal.Width = 35, Petal.Length = 1.4, Petal.Width = 0.2, Species = "setosa")
ggplot(iris_setosa_outlier, aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point() + stat_smooth(method = "lm", se = FALSE,
linetype = "dashed") +
stat_smooth(inherit.aes = FALSE,
method = "lm",
data = iris_setosa, aes(x = Sepal.Length, y = Sepal.Width),
se = FALSE) +
coord_cartesian(ylim = c(2, 4.5)) +
ggtitle("The influence of a single outlier on the regression model\nChart is zoomed-in, i.e., the outlier (5.1, 35) is not visible in the chart")
הוספה של תצפית אחת חריגה (5.1, 35) “זרקה” את מודל הרגרסיה בצורה קיצונית. לכן, לפני הרצת מודל רגרסיה (ובאופן כללי מודלים של חיזוי) מומלץ לבדוק ערכים חסרים ולטפל בהם או להחריג אותם מהניתוח.
בפרק הקודם עסקנו באלגוריתם KNN. המחשנו את השימוש ב-KNN לצורך בעיות סיווג, אך ניתן להפעיל אותו גם לצורך חיזוי משתנים רציפים, מעין “רגרסיה מבוססת שכנים” כאשר הערך שינתן לתצפית חדשה יבוסס על הערך הממוצע של השכנים שלה.
\[\hat{y}_X=\sum_{i\in\mathcal{N}_k(X)}y_i\]
הקובץ NYC_payroll_sample.csv הוא מדגם מתוך נתוני המשכורות של עובדי עיריית ניו-יורק. https://data.cityofnewyork.us/widgets/k397-673e. לצורך התרגיל, ניתן לעבוד עם המדגם עצמו (כ-70,000 שורות נתונים מתוך 2.7 מיליון). קובץ המדגם זמין בתיקייה וגם בעמוד ה-github תחת שם הקובץ NYC_payroll_sample.csv
תיאור הקובץ והמשתנים שבו נמצא בקובץ: Open-Data-Dictionary-Citywide_Payroll.FINAL.xlsx
עליכם לטעון את הקובץ ולהשתמש במשתנים שבו לצורך חיזוי רמת השכר של עובד חדש המתקבל לעבודה בעיריית ניו-יורק. להלן כמה נקודות למחשבה:
predictבפרק הבא נדון בסוג אחר של רגרסיה - רגרסיה לוגיסטית. בהרבה מובנים רגרסיה לוגיסטית דומה לרגרסיה לינארית, אך היא מתאימה הרבה יותר לסיווג לקטגוריות (או לאבחון כישלון/הצלחה) בהשוואה לרגרסיה לינארית.