בשביל מה?

אחד הכלים החשובים שעומדים לרשותנו כחוקרים הוא ויז’ואליזציה, אבל למה הוא כל כך חשוב?

בקורס זה נשים דגש על חבילת הגרפיקה ggplot2 ולא על גרפיקה בסיסית (Base)

למה ggplot2?

ב-R ישנם כלי ויז’ואליזציה רבים: סטטיים, אינטראקטיביים, ממגוון צורות ואפשרויות. הכלים הבסיסיים ביותר מגיעים עם ההתקנה הבסיסית ביותר (“Base R”), אבל אחד מהכלים השימושיים והנפוצים הוא דווקא חבילה הנקראת ggplot2.

מה הופך את חבילת ggplot2 לכל כך מוצלחת ופופולרית?

בשל עובדות אלו, נדלג על גרפיקה של Base R ונקפוץ ישר ללמוד על ggplot2.

התיאוריה: דקדוק גרפי (the grammer of graphics)

יסודות התיאוריה עליהם מבוססת החבילה זמינים במאמר:

התיאוריה אומרת שכל גרף ניתן לתאר באמצעות פירוקו לגורמים, שכבות שונות. בכל שכבה מיוצגת משמעות סטטיסטית מסוימת, וכל שכבה בנוייה מאסטטיקות שונות:

  • נתונים (data set).
  • מערכת צירים (קרטיזית xy, פולרית,…).
  • גיאומטריות (geoms) - אמצעים ויזואליים המתרגמים גדלים לגובה, גודל, צבע, צורה, וכדומה.

נחזור לאחד הגרפים הקודמים שבהם עסקנו בפרק הקודם, כדי להמחיש באמצעות דוגמה.

השכבה הראשונה בגרף היא שכבת הנתונים. היא לא “מציירת” כלום על המסך מעבר לקנווס ריק, אבל היא מהווה את הבסיס ליתר השכבות.

ggplot(kaggle_workday_survey)

בשכבה הבאה אנחנו מוסיפים את העמודות, כאשר לעמודות אלו נפרט “אסטטיקה” של הצירים. עבור ציר ה-x האסטטיקה תהא סוג הפעילות, ובעבור צריך ה-y, האסטטיקה תהיה אחוז ממוצע. כמו כן, נבקש שהעמודות כולן יצבעו בכחול בהיר, עם מסגרת שחורה. הצירים שנחשבים כ“אסטטיקה” מופיעים בתוך הפקודהaes(), וכדי לציין שאנחנו מעוניינים בעמודות משתמשים בפקודה geom_col().

שימו לב שבין השכבה הקודמת לשכבה הנוכחית אנחנו משתמשים בסימן החיבור “+”, וכמו כן, על הקנווס התווספו אוטומטית צירים, שמות לצירים, כותרות לפעילויות המופיעות בציר x, ומספרים בציר ה-y. טווח הצירים נקבע אוטומטית על ידי הפקודה אך ניתן לשינוי.

ggplot(kaggle_workday_survey) + 
  geom_col(fill = "lightblue", 
           color = "black", 
           aes(x = activity,
               y = mean_percent)) 

כעת נוסיף לגרף שכבה נוספת של תוויות שימוקמו בראש העמודות, כדי להקל על הקריאה של הגרף

ggplot(kaggle_workday_survey) + 
  geom_col(fill = "lightblue", 
           color = "black", 
           aes(x = activity,
               y = mean_percent)) + 
  geom_label(aes(x = activity,
                 y = mean_percent,
                 label = paste0(round(mean_percent, 0), "%")))

הדבר האחרון שנותר הוא קצת לייפות את הגרף. נשנה את שמות הצירים ונוסיף כותרת לגרף, באמצעות פקודות השכבות המתאימות.

כמו כן, כדי לקצר קצת את הפקודה, דחפנו את כל האסטטיקות לפקודה המרכזית, היות שהצירים משותפים בין שכבת העמודות לשכבת התווית. הפקודות של השכבות נותרו רק geom_col(fill = ..., color = ...) + geom_label().

ggplot(kaggle_workday_survey,
       aes(x = activity,
           y = mean_percent,
           label = paste0(round(mean_percent, 0), "%"))) + 
  geom_col(fill = "lightblue", 
           color = "black") + geom_label() + 
  ylab("Average percent [%]") + 
  xlab("Activity type") + 
  ggtitle("Activities and their respective proportion of time (average)",
          subtitle = "Based on kaggle's 2017 survey")

בקצרה ניתן לסכם שכל גרף של ggplot2 יראה בתבנית דומה מאוד לתבנית הבאה:

ggplot(data = <DATA>, mapping = aes(<MAPPINGS>)) +
   geom_TYPE(mapping = aes(<MAPPINGS>), stat = <STAT>, position = <POSITION>) +
   <COORDINATE_FUNCTION> + 
   <FACE_FUNCTION> + 
   <SCALE_FUNCTION> + 
   <THEME_FUNCTION>

כאשר בתבנית לעיל ישנם אלמנטים מסוימים שטרם הסברנו, אך נסביר בהמשך הפרק.


תרגול - קובץ Google Play

בקובץ googleplaystore.csv ישנן כ-11,000 רשומות של אפליקציות מתוך חנות האפליקציות של google. הקובץ נוצר באמצעות scraping, טכניקה שבאמצעותה סורקים אתרים ומחלצים מתוכם נתונים לתוך מבנה מסודר וטבלאי, כפי שנמצא בקובץ.

להורדת הקובץ ניתן להיכנס לקישור הבא: https://github.com/adisarid/learnr_R_hebrew_book/blob/master/02-data/googleplaystore.csv

אנו נשתמש בקובץ זה כדי לתרגל צורות שונות של ויז’ואליזציה. כדי להקל על ההתחלה של השימוש ב-R, חלק מהפקודות כבר ישנן, ועליכם להשלים את הפרמטרים המתאימים במקומות שבהם ישנו XXX. ככל שנתקדם בתרגילים, יותר חלקים יהיו חסרים (עד לשלב שבו לא יהיו פקודות, ותצטרכו לכתוב הכל בעצמכם).

  1. קראו את הקובץ לתוך אובייקט ששמו google_play, הציצו בנתונים באמצעות פונקציית glimpse
    1. איזה משתנים עשויים לייצג את הקטגוריה (סיווג) של האפליקציה?
library(tidyverse)
google_play <- read_csv(file = XXX)
glimpse(XXX)
  1. בנו תרשים שיציג את מספר האפליקציות בכל קטגוריה. סדרו את תוויות ציר x שבתרשים בזווית של 45 מעלות כדי שיהיה קל לקרוא אותם.
    1. לפי איזה סדר מסודרות העמודות שבציר x? (מה הסדר משמאל לימין?)
    2. מהן שלושת הקטגוריות בהן ישנן הכי הרבה אפליקציות?
    3. האם אתם מזהים שגיאה בסיווג הקטגוריות? ממה נובעת השגיאה? (רמז: הסתכלו על הקטגוריה הראשונה)
    4. עדכנו את האובייקט google_play על ידי סינון התצפית החריגה.
ggplot(data = XXX, aes(x = Category)) + 
  geom_bar(stat = "count") + 
  theme(axis.title.x = element_text(angle = XXX))
  
google_play <- google_play %>%
   filter(Category != "XXX")
  1. בנו תרשים שיציג באילו מערכות הפעלה תומכות האפליקציות.
    1. מה לדעתכם גרסת האנדרואיד המעודכנת ביותר הקיימת בשוק, נכון למועד הכנת הקובץ?
    2. בתרשים שהכנתם ישנן 3-4 עמודות בולטות (גבוהות) במיוחד. מהן, ולמה?
ggplot(XXX, aes(x = `Android Ver`)) + 
  XXX + 
  theme(axis.text.x = XXX)

הערה: שימו לב לשימוש במרכאות סביב המשתנה Android Ver. בקובץ המקור שורת ה-header מכילה שם עם רווח. היות שמשתנה לא יכול להכיל רווחים ב-R, כדי להשתמש באותו המשתנה משתמשים בתו המרכאות כדי להקיף את שם המשתנה, וכך R יודע לפרש אותו.

  1. האם יש קשר בין דירוג של אפליקציה לבין מספר המדרגים שלה? בנה תרשים שיסייע לבחון השערה זו. על התרשים להציג את הפיזור של התצפיות (geom_point), כאשר בציר x מספר הביקורות ובציר y הדירוג הממוצע שניתן. כדי לבנות את התרשים באפשרותך להיעזר בגיליון עזר “שליף”. ב-RStudio לך לתפריט Help -> Cheatsheets -> Data Visualization with ggplot2.
    1. האם קיבלתם הודעת אזהרה במהלך הרצת הפקודה? מה משמעות הודעת האזהרה, למה הופיעה?
    2. באמצעות הפקודה stat_smooth. התנסו עם שיטות החלקה מסוגים שונים. באיזו שיטת החלקה הייתם בוחרים? למה?
    3. בונוס: האם ישנה המרה (טרנספורמציה) מתמטית שניתן להפעיל על ציר ה-x שתאפשר לזהות קשרים חזקים יותר?
ggplot(XXX, aes(XXX, XXX)) + 
   geom_XXX() + 
   stat_smooth(method = "XXX")
  1. כעת, חלקו את מספר הביקורות שניתנו ל-5 קבוצות שונות, באמצעות הפונקציה cut. הציגו גרף פיזור של דירוג למול מספר הביקורות, שבו כל קבוצה נצבעת בצבע שונה, על פי הקבוצה אליה היא מתייחסת.
    1. האם ניתן לזהות קשרים חזקים יותר בטווחים מסוימים?
google_play_groups <- google_play %>%
  mutate(reviews_groups = cut(XXX, breaks = c(10^(0:6), max(Reviews) + 1)))

rating_reviews_grouped_chart <- 
  ggplot(google_play_groups, aes(x = XXX, y = XXX, color = XXX)) + 
  geom_point() + 
  stat_smooth(method = "lm") +
  scale_x_log10()
  1. הציגו תרשים boxplot המשוואה את התפלגות מספר הביקורות בין אפליקציות חינמיות לאפליקציות בתשלום.
    1. האם ניתן ללמוד משהו מהשוואה זו?
    2. השתמשו בטרנספורמציית log על מספר הביקורות. האם כעת ההשוואה ברורה יותר?
    3. מה ההבדל בין שימוש ב-log(Reviews) לעומת שימוש ב-scale_y_log()?
    4. בצעו השוואה של הדירוג הממוצע למול סוג האפליקציה (חינמית/בתשלום). מה ניתן ללמוד מהשוואה זו?
# now you're on your own... use `geom_boxplot()` and the cheatsheet.
# use log() for the log transformation in question 6b, or add scale_y_log10() to the ggplot sequence
  1. דרך חלופית להסתכל על התפלגויות היא באמצעות גרף עמודות הנקרא היסטוגרמה. בסעיף זה נבחן את התפלגות גודל האפליקציה.
    1. השתמש בפונקציה glimpse בשנית כדי להבין מהו המשתנה המתאר את גודל האפליקציה ומה הסוג שלו (מספרי? מחרוזת - טקסט?)
    2. השתמש בפונקציה str_replace ובפונקציה as.numeric כדי להפוך אותו למספרי.
    3. בפקודות geom_histogram ו-geom_freqpoly ישנו פרמטר bins. ערך ברירת המחדל שלו הוא 30. נסו לשנות אותו במספרים שונים. על מה הוא משפיע? מה קורה כאשר בוחרים את אותו ערך לשניהם? מה קורה כאשר בוחרים ערך שונה? למה?
    4. איפה נמצאת “עיקר המסה” מבחינת מרבית האפליקציות - האם הן בעלות נפח גדול או קטן? האם להתפלגות יש זנב “כבד”?
google_play <- google_play %>%
   mutate(size_app_numeric = as.numeric(str_replace(XXX, "M", ""))
   
ggplot(XXX, aes(XXX)) + 
   geom_histogram(fill = "lightblue", bins = XXX) + 
   geom_freqpoly(size = 1.5, bins = XXX)
   

יכולת נוספת בבניית גרפיקה עם ggplot2 היא יצירת פאות (facets), היא מאפשרת לנו להגדיר גרף מסוים ואז לפצל אותו ללא מאמץ לפי משתנה כלשהו. בנו תרשים המציג את הדירוג הממוצע (Rating) בציר y, ואת הלוגריתם של מספר הביקורות בציר x. הוסיפו בסוף הפקודה של התרשים את הפונקציה הבאה:

+ facet_wrap(~ Category)
  1. התרשים שהתקבל מציג “מיני תרשימים”, המתייחסים לקטגוריות שונות של האפליקציה.
    1. האם המגמה שבין מספר הביקורות לבין הדירוג הממוצע שונה בין קטגוריות שונות?
    2. השוו בין קטגורית EDUCATION לקטגוריית TOOLS. הביטו בצורה בה מתפזרות הנקודות בכל אחד מהגרפים. על מה מעיד הבדל זה?

בפרק זה למדנו והתנסנו בסוגים שונים של גרפים, ובדרכים לבנות אותם, אבל למעשה רק נגענו ב“קצה הקרחון” של היכולות של ggplot2. במהלך הקורס תראו עוד הרבה גרפים שילוו את ההסברים בפרקים השונים, ותוכלו להתנסות עוד בבניית גרפים.

איך לבנות את הגרף “הנכון”?

כדי לבחור את הגרף שעליכם לבנות לבעיה או שאלת מחקר מסוימת, ענו על השאלות הבאות:

  1. כמה משתנים מעורבים בשאלת המחקר (משתנה אחד? שני משתנים? שלושה? יותר?)
  2. מהם המאפיינים של כל אחד מהמשתנים:
    1. האם המשתנים הם בעלי ערכים רציפים?
    2. ערכים בדידים (לדוגמה קטגוריות)
    3. ערכים אורדינליים (בדידים עם סדר יחס)
  3. בחירה באופן המיפוי של משתני קטגוריות
    1. האם לפי ציר x או
    2. לפי גיאומטריות אחרות כגון: צבע, וצורה
    3. לפי פאות (facets)
  4. בחירה באופן המיפוי של משתנים רציפים
    1. האם לפי ציר x
    2. ציר y
    3. לפי גודל או צבע האובייקט
  5. סידור הצירים והמקרא
    1. האם הצירים ברורים, האם הם מכילים את כל הנתונים אבל לא “מעלימים” מידע חשוב?
    2. האם הכותרות ברורות?

בכל מקרה שבו אתם מתלבטים איך נכון לבנות גרף מסוים, או לתאר את הקשר שבין משתנים, או רק מחפשים “שליף”, נוח מאוד להשתמש ב-Cheetsheets הנמצאים בתפריט ב-Rstudio תחת:

Help -> Cheetsheets -> Data Visualization with ggplot2

קובץ תרגול Telco - נטישה ולקוחות

קראו את הקובץ של נטישת לקוחות Telco, הקובץ הורד מהאתר של IBM בכתובת: https://www.ibm.com/communities/analytics/watson-analytics-blog/guide-to-sample-datasets/

אגב ניתן לקרוא את הקובץ ישירות על ידי שימוש בפקודה:

telco_churn <- read_csv("https://community.watsonanalytics.com/wp-content/uploads/2015/03/WA_Fn-UseC_-Telco-Customer-Churn.csv")
## Parsed with column specification:
## cols(
##   .default = col_character(),
##   SeniorCitizen = col_integer(),
##   tenure = col_integer(),
##   MonthlyCharges = col_double(),
##   TotalCharges = col_double()
## )
## See spec(...) for full column specifications.
glimpse(telco_churn)
## Observations: 7,043
## Variables: 21
## $ customerID       <chr> "7590-VHVEG", "5575-GNVDE", "3668-QPYBK", "77...
## $ gender           <chr> "Female", "Male", "Male", "Male", "Female", "...
## $ SeniorCitizen    <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
## $ Partner          <chr> "Yes", "No", "No", "No", "No", "No", "No", "N...
## $ Dependents       <chr> "No", "No", "No", "No", "No", "No", "Yes", "N...
## $ tenure           <int> 1, 34, 2, 45, 2, 8, 22, 10, 28, 62, 13, 16, 5...
## $ PhoneService     <chr> "No", "Yes", "Yes", "No", "Yes", "Yes", "Yes"...
## $ MultipleLines    <chr> "No phone service", "No", "No", "No phone ser...
## $ InternetService  <chr> "DSL", "DSL", "DSL", "DSL", "Fiber optic", "F...
## $ OnlineSecurity   <chr> "No", "Yes", "Yes", "Yes", "No", "No", "No", ...
## $ OnlineBackup     <chr> "Yes", "No", "Yes", "No", "No", "No", "Yes", ...
## $ DeviceProtection <chr> "No", "Yes", "No", "Yes", "No", "Yes", "No", ...
## $ TechSupport      <chr> "No", "No", "No", "Yes", "No", "No", "No", "N...
## $ StreamingTV      <chr> "No", "No", "No", "No", "No", "Yes", "Yes", "...
## $ StreamingMovies  <chr> "No", "No", "No", "No", "No", "Yes", "No", "N...
## $ Contract         <chr> "Month-to-month", "One year", "Month-to-month...
## $ PaperlessBilling <chr> "Yes", "No", "Yes", "No", "Yes", "Yes", "Yes"...
## $ PaymentMethod    <chr> "Electronic check", "Mailed check", "Mailed c...
## $ MonthlyCharges   <dbl> 29.85, 56.95, 53.85, 42.30, 70.70, 99.65, 89....
## $ TotalCharges     <dbl> 29.85, 1889.50, 108.15, 1840.75, 151.65, 820....
## $ Churn            <chr> "No", "No", "Yes", "No", "Yes", "Yes", "No", ...
  1. כעת, השתמש בתרשים כדי להציג את הפיזור של כלל ההוצאות (TotalCharges) למול וותק הלקוח (tenure).
    1. איזה צורה יוצר התרשים? מדוע?
    2. השתמש בפאות (facets) כדי לפצל את התרשים לפי סוג החוזה (משתנה Contract), והוסף החלקה (stat_smooth). מה אתה למד מהתוצאה?
  2. בחן את ההתפלגות של ההוצאות החודשיות (MonthlyCharges) למול נטישה (Churn)
    1. האם יש קשר בין נטישה לבין התשלום החודשי? האם משתמשים נוטשים שילמו יותר או פחות?
    2. כעת, בנה תרשים נוסף הבוחן את ההוצאות החודשיות למול נטישה, אך גם מפריד בין סוגי החיבור לאינטרנט (InternetService). האם הקביעה שלך בסעיף הקודם משתנה באיזשהו אופן? מדוע זה כך?
    3. בנו תרשים המציג את כמות הלקוחות שנטשו ולא נטשו, למול סוג השירות שקיבלו. האם תרשים זה עוזר להסביר את התופעה שבסעיף הקודם?

הפרק הבא

בפרק הבא “נחזור ליסודות” עם עקרונות הקידוד ב-R, סוגי משתנים, פונקציות, לולאות, אופרטורים, ועוד.