R for Data Analysis

สวัสดีทู๊กคนน จบไปแล้วกับคอร์สเรียน R for Data Analysis (R4DA) ครั้งแรกของเพจเราที่สอนแบบสองวัน 17 & 24 มีนาคม 2561 ที่ผ่านมา บล๊อกนี้เรามาสรุปโค้ดและเนื้อหาสำคัญๆให้เพื่อนๆได้ทบทวนอีกทีฮะ

สำหรับเพื่อนๆที่ยังไม่เคยเขียน R มาก่อน เรามีสอน R programming สำหรับผู้เริ่มต้น เรียนฟรี 2 ชั่วโมง (ก็ทำเป็นล๊าว!) ได้ที่คอร์สออนไลน์ของเราเลยฮะ สมัครเรียนฟรีที่ https://datarockie.teachable.com


Installation R, RStudio, Package

cats
หน้าตาของ RStudio: popular code editor (IDE) ของภาษา R

สำหรับเพื่อนๆที่อยากจะลองทำตาม tutorial ในบล๊อกนี้ด้วยต้องลงโปรแกรม R และ RStudio ก่อนนะฮะ สามารถดาวน์โหลดทั้งสองโปรแกรมได้ที่ลิ้งนี้เลย

พอลงโปรแกรมทั้งสองตัวเสร็จแล้ว ให้เราเปิด RStudio ขึ้นมา แล้ว copy โค้ดด้านล่างนี้แปะลงไปใน console ได้เลยฮะ

# install and load package
install.packages("tidyverse")
library(tidyverse)

package ที่เราใช้เรียนกันในคลาส R4DA ชื่อว่า tidyverse ซึ่งเป็น package ที่สำคัญมากในการทำงานด้าน data analysis | data science สำหรับ R programmers ถ้าอยากรู้ว่า tidyverse ทำอะไรได้บ้าง? ลองอ่านที่เว็บเค้าเลยฮะ (optional นะ)

สำหรับการ install packages ใหม่ๆใน RStudio ทำได้ง่ายมากๆ แค่พิมพ์ install.packages("package_name") ลงไปใน console แล้วเด๋ว RStudio จะต่อกับ server และจัดการทุกอย่างให้เราโดยอัตโนมัติเลย

ปัจจุบัน (28 มีนาคม 2561) R community มีมากกว่า 14,xxx packages และมีฟังชั่นให้เลือกใช้งานมากกว่า 2 ล้านฟังชั่น เยอะอะไรเบอร์นี้ !! ส่วน tidyverse ก็ติดอันดับ top 10 ที่มีการใช้งานมากที่สุดเลย อ้างอิงจากสถิติการใช้งานบนเว็บ R Documentation

TIP: RStudio คือ IDE ย่อมาจาก “Integrated Development Environment” เป็นโปรแกรมคล้ายๆ advanced text editor สำหรับเขียนภาษา R โดยเฉพาะเลย


Data Types in R

สำหรับ data types ใน R หลักๆจะมีอยู่ 5 แบบที่เราใช้กัน ประกอบด้วย

  • numeric | double | integer
  • logical (boolean)
  • character (string)
  • factor
  • date & time

เราสามารถตรวจสอบ class หรือ type ของข้อมูลเราได้ด้วยฟังชั่น is.numeric(), is.logical(), is.character() ใน R ยังมีอีกหลายฟังชั่นเลยที่ใช้ในการตรวจสอบ data types ให้ลองพิมพ์ is. ลงไปใน console แล้วเด๋ว R จะแนะนำฟังชั่นทั้งหมดที่ขึ้นต้นด้วย is. ขึ้นมาให้เราเลือกใช้งาน

เราสามารถเปลี่ยน class หรือ type ด้วยฟังชั่น as.numeric(), as.logical(), as.character() ฯลฯ ตามโค้ดตัวอย่างนี้เลย

# integer
x <- c(1:10)
class(x) 

# logical
y <- c(TRUE, FALSE)
class(y) 

# character
z <- c("Hello", "Hi", "Ni Hao")
class(z) 

# convert logical to character
y <- c(TRUE, FALSE)
y <- as.character(y)
class(y)

สำหรับ factor ใน R คือตัวแปร categorical ในทางสถิตินั่นเอง เช่น ชาย|หญิง, ซื้อ|ไม่ซื้อ ฯลฯ ซึ่ง factor แตกย่อยได้สองแบบตามหลักสถิติคือ nominal vs. ordinal ต่างกันนิดเดียวตรงที่ ordinal สามารถเรียงสูงกลางต่ำได้ เช่น รายได้ต่ำ|ปานกลาง|สูง ฯลฯ

# create factor (nominal)
animals <- c("dog", "cat", "cat", "cat", "dog")
factor(animals)

# create factor (ordinal)
household.income <- c("high", "medium", "low", "low", "medium")
factor(household.income, levels = c("low", "medium", "high"), ordered = T)

ส่วนข้อมูลแบบ datetime เราสามารถใช้ package lubridate ในการ extract ข้อมูลพวกวันที่ เดือน ปีออกมาจาก date object ได้ง่ายๆด้วยฟังชั่นตัวอย่างด้านล่าง

# download lubridate package
install.packages("lubridate")
library(lubridate)

# lubridate key functions
today <- Sys.time()
year(today)
month(today, label = T)
day(today)
wday(today, label = T)

TIP: เวลาเจอ function ใหม่ๆที่ไม่เคยใช้มาก่อน เราสามารถเรียก help file ขึ้นมาอ่านได้แค่กดปุ่ม F1 ที่ชื่อ function นั้นๆ หรือพิมพ์ ?function.name หรือ help(function.name) ใน console


DataFrame vs. Tibble

tabluar
Dataframe and the power of data

dataframe หรือที่เราเรียกกันว่า tabular table ถือว่าเป็นหัวใจสำคัญของการวิเคราะห์ข้อมูลด้วย R programming เลย เราสามารถ import ข้อมูลเข้า RStudio ได้ง่ายๆด้วยฟังชั่น read_csv และใช้ฟังชั่น write_csv ในการ export ข้อมูลออกจาก RStudio

โค้ดด้านล่าง แสดงตัวอย่างการนำเข้าข้อมูล iris.csv จากเว็บไซต์ UCI machine learning repository อ่านรายละเอียดเพิ่มเติมเกี่ยวกับ iris dataset ได้ที่นี่

# import dataset from UCI ML website
url <- "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
iris <- read_csv(url, col_names = FALSE)

# export dataset as .csv file
getwd() # check working directory
write_csv(iris, "iris.csv")

ใน R จะมี dataframe พิเศษอีกรูปแบบหนึ่งที่เรียกว่า tibble (พัฒนาโดยทีมงาน RStudio และ Hadley Wickham คนเดิม สาย R ไม่มีใครไม่รู้จักคนนี้) ซึ่งเป็น enhanced dataframe ที่มีประสิทธิภาพมากขึ้น เช่นการแสดง data type ของแต่ละคอลั่ม และการปริ้นผลใน console จะมีการจัดหน้าตาที่เป็นระเบียบเรียบร้อยมากขึ้น เช่น ปริ้นแค่ 10 แถวบนสุด (by default)

# change from tibble back to normal dataframe
iris <- as.data.frame(iris)
class(iris)

# change from normal dataframe to tibble
iris <- as_tibble(iris)
class(iris)

เราสามารถ subset ข้อมูลใน dataframe เบื้องต้นด้วย [ ]

# basic subsetting
iris[ ,1:2] # sub set all rows, columns 1-2
iris[1:10, ] # subset rows 1-10, all columns
iris[1:10, 1:2] # subset rows 1-10, columns 1-2

TIP: เราสามารถใช้ฟังชั่น subset() ของ base R ในการเลือก columns และ rows ที่เราต้องการได้ง่ายๆ เช่น subset(mtcars, hp > 200, select = c("wt", "hp", "mpg")) หรือใช้ package ยอดนิยม dplyr ที่เรากำลังจะสอนต่อไปในบล๊อกวันนี้


Load Built-in Dataset

R มาพร้อมกับ built-in dataset ให้เราได้ลองศึกษา พิมพ์ฟังชั่น data() ลงไปใน console เพื่อเรียกดู data ทั้งหมดที่มีอยู่ใน base R ได้เลย เราสามารถโหลด dataset เข้ามาใน environment ของเราด้วยฟังชั่น data(dataframe) ตามตัวอย่างด้านล่าง

# load data set
data(mtcars)

# change class to tibble
mtcars <- as_tibble(mtcars)

# review data structure & basic summary
glimpse(mtcars)
head(mtcars) # print first 6 rows
tail(mtcars) # print last 6 rows
summary(mtcars) # compute basic statistics e.g. min, max, mean, median

TIP: ทุกครั้งที่เราโหลด dataset ใหม่เข้าไปใน R เราควรจะต้องเช็คก่อนว่าข้อมูลของเรามีกี่ columns มีกี่ rows และพรีวิวข้อมูลเบื้องต้นด้วยฟังชั่น glimpse()


Basic Statistics

R เนี่ยเกิดมาเพื่อทำงานด้าน statistics โดยเฉพาะเลยรู้ไหม! แปลว่าฟังชั่นที่เราสามารถใช้ในการคำนวณค่าสถิติต่างๆ R มีเยอะมากๆ ด้านล่างเป็นแค่ส่วนหนึ่งที่เราใช้กันบ่อย ตั้งแต่หาค่า mean, median, mode, sd, var รวมถึงสรุปค่าสถิติแบบเทพๆด้วยแพ็คเกต psych

  • mean()
  • median()
  • modeest::mfv()
  • sd()
  • var()
  • mad()
  • max()
  • min()
  • quantile()
  • fivenum()
  • summary()
  • psych::describe()

สำหรับการใช้งานฟังชั่นพวกนี้ก็ตรงๆเลย ส่วนใหญ่มันจะ take dataframe หรือว่า vector ตัวเลขเป็น input แล้วก็ return ค่าสถิติต่างๆออกมา ตัวอย่างโค้ดด้านล่างเลย

# compute mean, sd, summary stats of column hp in mtcars
mean(mtcars$hp)
sd(mtcars$hp)
summary(mtcars$hp)

ทวนความจำนิดนึง เราใช้ $ ในการเลือก column ที่เราต้องการจาก dataframe ส่วนโค้ดด้านล่างเราใช้ psych::describe ในการคำนวณค่าสถิติของทุก columns ใน mtcars ทีเดียวเลย

# use package psych
install.packages("psych")
psych::describe(mtcars)

Data Transformation

bumblebee
Source: https://bit.ly/2DRZ5JW

มาถึง 5 verbs ที่สำคัญมากในการทำ data transformation ใน R (จริง dplyr คล้ายๆกับการใช้งาน SQL ที่หลายคนอาจจะเคยลองทำมาบ้างแล้ว)

  • select()
  • filter()
  • arrange()
  • mutate()
  • summarise()

สำหรับ select ใช้สำหรับเลือก columns ที่เราต้องการ การเลือก columns ถือว่าเป็นการ subset ข้อมูลรูปแบบหนึ่งใน R

ปกติเราจะใช้ %>% pipe operator ในการเชื่อมโค้ดต่างๆเข้าด้วยกันตามตัวอย่างด้านล่าง

# select columns
mtcars %>%
    select(1:3)
mtcars %>%
    select(wt, mpg, hp)
mtcars %>%
    select(starts_with("m"))

filter ใช้ในการสร้างเงื่อนไข (conditions) เพื่อเลือก rows ที่เราต้องการ (หรือ filter rows ที่ไม่ต้องการทิ้งไป) การสร้างเงื่อนไขสามารถสร้างได้มากกว่าหนึ่งข้อ โดยใช้ & (AND) และ | (OR) operators มาช่วย เช่น wt > 2 & gear == 4

# filter rows with conditions
mtcars %>%
    filter(wt < 2) mtcars %>%
    filter(wt > 2 & gear == 4)
mtcars %>%
    filter(hp %in% 100:150)

arrange ใช้ในการ sort ข้อมูลจากค่าน้อยไปมาก (ascending order) หรือแบบมากไปน้อย (descending order) เราสามารถ sort ตัวแปรได้มากกว่าหนึ่งตัวพร้อมกัน

# arrange data (sorting)
mtcars %>%
    arrange(hp)
mtcars %>%
    arrange(desc(hp))

mutate ใช้ในการสร้างตัวแปรใหม่ใน dataframe ของเรา

# create new variables
mtcars %>%
    mutate(new.var = hp/100)
mtcars %>%
    mutate(new.var = ifelse(hp >= 200, "High", "Low"))

summarise ใช้ในการสรุปค่าสถิติต่างๆใน dataframe ของเรา ปกติเราจะใช้คู่กับ adverb function อีกหนึ่งตัวที่มีประโยชน์มากของ dplyr ชื่อว่า group_by ใช้ในการจับกลุ่มข้อมูลตามตัวแปร factor ที่อยู่ใน dataframe ของเราก่อนจะคำนวณค่าสถิติ

# summarise basic statistics
mtcars %>%
    summarise(avg_hp = mean(hp),
              max_hp = max(hp),
              min_hp = min(hp),
              stdev_hp = sd(hp),
              n = n())

# group summary statistics by am variable (Auto|Manual)
mtcars %>%
    group_by(factor(am)) %>%
    summarise(avg_hp = mean(hp))

TIP: การสร้างเงื่อนไขแบบ equality conditions ใน R ต้องใช้เครื่องหมาย == (เท่ากับ สองตัวติดกัน) เช่น 1+1 == 2 .. R จะตอบกลับมาใน console ว่า TRUE ส่วนเครื่องหมาย != อ่านว่าไม่เท่ากัน


Tidy Data

tidy data
Source: https://unsplash.com/

หลักการของ tidy data ถูกนำเสนอโดย Hadley Wickham (อีกแล้ว !!) ในปี 2014 โดยนิยามหลักๆของมันคือ “a variable forms a column” และ “an observation forms a row”

tidyr เป็นอีกหนึ่ง package หลักใน tidyverse ที่ใช้ในการ transform data ให้อยู่ในรูปของ tidy format เช่น การเปลี่ยนจาก wide → long format ด้วยคำสั่ง gather หรือเปลี่ยนจาก long → wide format ด้วยคำสั่ง spread (อ่านความแตกต่างระหว่าง wide vs. long ได้ที่นี่)

ส่วน separate และ unite อย่างที่ชื่อมันบอกเป็นนัยๆคือการแยกและ รวมคอลั่มตามลำดับ ลองดูตัวอย่างการใช้งาน และการเขียนโค้ดเบื้องต้นด้านล่างเลยฮะ

  • gather()
  • spread()
  • separate()
  • unite()
# prepare data for our tutorial
data(WorldPhones)
df <- as.data.frame(WorldPhones)
df$Year <- rownames(df)
rownames(df) <- NULL
glimpse(df)
# transformation: wide to long format
df.long <- df %>%
    gather(1:7, key = Region, value = Sales)
print(df.long)

# transformation: long to wide format
df.long %>%
    spread(key = Region, value = Sales)

# separate() and unite() columns
table5 %>%
    separate(rate, into = c("infected", "population"), sep = "/") %>%
    unite(new.year, century, year, sep = "")

ถ้าเข้าใจฟังชั่นและทำได้ครบทั้ง dplyr และ tidyr ก็สุดยอดแล้วฮะ เรา transform data ได้หลายรูปแบบเลย ถ้าอยากเข้าใจเชิงลึกเรื่อง tidy data เราแนะนำให้อ่านเปเปอร์ของ Hadley Wickham ที่ลิ้งนี้เลย https://www.jstatsoft.org/article/view/v059i10/v59i10.pdf (2014)

TIP: R ชอบข้อมูลแบบ long format มากกว่าสำหรับการทำ exploratory data analysis ชีวิตเราจะง่ายมว๊าก ถ้า “a variable forms a column” และ “an observation forms a row” ตาม tidy data concept


Missing Values

tidy data ที่ดีควรจะมี data ที่สมบูรณ์เช่นกัน i.e. ไม่มี missing values หรือ NA มากวนใจเราตอนวิเคราะห์ผล ใน R เราสามารถตรวจสอบว่า dataframe ของเรามีค่า NA หรือเปล่าง่ายๆด้วยฟังชั่น complete.cases(dataframe) โดยฟังชั่นนี้จะ return ค่า FALSE ถ้าเกิด row ไหนใน dataframe มีข้อมูลไม่ครบ (i.e NA presence)

เราสามารถ drop rows ที่มี missing value ทิ้งได้เลยด้วยคำสั่ง na.omit

# load data for this tutorial
install.package("MASS")
data(biopsy, package = "MASS")

# check if dataframe has any missing values (row by row)
complete.cases(biopsy)
mean(complete.cases(biopsy)) # 97.7% complete, 2.3% missing

# drop rows that have missing values
biopsy <- na.omit(biopsy)

ถ้าเราตัดสินใจไม่ drop rows เนื่องจาก sample size (n) เราน้อย ให้เราทำ imputation ค่า missing value ในคอลั่มนั้นๆก็ได้ เช่น แทนที่ NA ด้วยค่า mean, median, หรือค่า mode ของคอลั่มนั้น

ตัวอย่างโค้ดด้านล่าง เราใช้ filter เพื่อกรองเอา rows ที่มีค่า NA ออกมาดู แล้วค่อย impute NA ในคอลั่มนั้นด้วยค่า mode ของตัวแปร V6

# filter rows with missing value (NAs)
biopsy %>%
    filter(!complete.cases(.)) # all NAs identified in column V6

# NA imputation
table(biopsy$V6) # find mode in this column
biopsy$V6[is.na(biopsy$V6)] <- 1

TIP: ฟังชั่นที่มีประโยชน์มากในการ impute NA คือ tidyr::replace_na() ในตัวอย่าง biopsy ด้านบน เราสามารถพิมพ์ replace_na(biopsy$V6, 1) เพื่อแทนที่ NA ด้วยเลข 1 (ค่า mode) ได้เลยง่ายๆ


Data Visualization

Rplot3
ตัวอย่างการใช้งาน ggplot() สร้าง plots เบื้องต้น

ggplot2 เป็นอีกหนึ่ง package ที่รวมอยู่ใน tidyverse แล้วฮะ เราสามารถเรียกใช้งานฟังชั่น ggplot() เพื่อสร้าง plots เบื้องต้นได้เลย

  • geom_point() ใช้สร้าง scatter plot
  • geom_smooth() ใช้สร้าง smooth line หรือ linear ก็ได้
  • geom_histogram() ใช้สร้าง histogram
  • geom_bar() ใช้สร้างกราฟแท่ง
  • geom_line() ใช้สร้างไลน์ชาร์ทเพื่อดูเทรนด์ (พวก time series)
  • geom_boxplot() ใช้สร้าง boxplot เพื่อดู outliers
# scatter plot
ggplot(data = mtcars, mapping = aes(x = wt, y = mpg)) +
    geom_point()

# scatter plot + smoother
ggplot(data = mtcars, mapping = aes(x = wt, y = mpg)) +
    geom_point() +
    geom_smooth()

# histogram
ggplot(data = mtcars, mapping = aes(x = mpg)) +
    geom_histogram(bins = 15)

# bar plot
data(diamonds)
ggplot(data = diamonds, mapping = aes(x = clarity)) +
    geom_bar()

# line plot
data(economics)
ggplot(data = economics, mapping = aes(x = date, y = pop)) +
    geom_line()

# boxplot
ggplot(data = diamonds, mapping = aes(x = cut, y = price)) +
    geom_boxplot()

อีกหนึ่งเทคนิคที่ทำให้เราชอบใช้ ggplot2 มากๆคือการทำ facet แบ่ง plot ของเราเป็นกรุ๊ปตามตัวแปร factor | category ที่อยู่ใน dataframe ของเราตามตัวอย่างด้านล่าง

Rplot2
ตัวอย่างการใช้งาน facet_wrap ใน ggplot2
# facet plots by factor variable in dataframe
ggplot(df, aes(carat, price)) +
    geom_point(alpha = 0.3) +
    geom_smooth() + 
    theme_minimal() +
    facet_wrap(~ cut, ncol = 5)

TIP: สำหรับ ggplot2 เป็น package ที่ทำอะไรได้อีกเยอะมากๆ ที่เราสอนกันในคลาส R4DA แค่ประมาณ 20-30% ของฟังชั่นทั้งหมดใน ggplot2 เอง สำหรับเพื่อนๆที่สนใจอยากศึกษาเพิ่มเติม ลองดาวน์โหลด Cheat Sheet ggplot2 ได้ที่นี่เลย


Machine Learning in R

ML
Source: https://www.entrepreneur.com/article/304945

อีกหนึ่งสุดยอด package ขวัญใจโปรแกรมเมอร์สาย R สำหรับการทำ Machine Learning คือ caret นั่นเองฮะ (พัฒนาและดูแลโค้ดทั้งหมดโดย Max Kuhn)

# install package caret
install.packages("caret")
library(caret)

สำหรับการสร้าง machine learning models เบื้องต้น กระบวนการทำงานจะมี 4 ขั้นตอน ประกอบด้วย

  1. Data preparation
  2. Train models
  3. Tune models
  4. Evaluate results

ตอนนี้สมมติว่าข้อมูลของเรา clean & prep มาเรียบร้อยแล้ว i.e. ไม่มี NA (missing values) หรือทำพวก feature engineering หรือ transformation มาเสร็จแล้ว เราจะข้ามไปสอนที่ step 2-4 เลย

โค้ดตัวอย่างด้านล่าง เราโหลดข้อมูล Sonar สำหรับปัญหา binary classification i.e. Class = R or M เพื่อที่จะใช้ predict ว่าวัตถุนั้นเป็นหินหรือว่าแร่ธาตุ

# load dataset SONAR
data(Sonar)
glimpse(Sonar)

# split data into train | test sets
set.seed(1)
id <- sample(nrow(Sonar), 0.8*nrow(Sonar))
train <- Sonar[id, ]
test <- Sonar[-id, ]

ลองโยน train data ของเราให้กับ Random Forest algorithm เรียนรู้และใช้ k-fold cross validation (k = 5) ในการเทรนโมเดลเพื่อให้ได้ Accuracy สูงที่สุด i.e. metric = “Accuracy” เสร็จแล้วก็ evaluate results ของ fit.rf ด้วย confusion matrix

# train RandomForest algorithm
fit.rf <- train(Class ~ ., 
                data = train, 
                method = "rf", 
                metric = "Accuracy",
                trControl = trainControl(method = "cv", number = 5))

# evaluate results
print(fit.rf)
predictions <- predict(fit.rf, test)
tab1 <- table(predictions, test$Class) # confusion matrix
sum(diag(tab1)) / nrow(test) # accuracy of test data

และนี่คือโค้ด template เบื้องต้นในการเทรนโมเดลของเราด้วย caret package นั้นเอง ง่ายอะไรเบอร์นี้! ปกติเราจะเทรน algorithms หลายๆตัว เพื่อหาว่าตัวไหนดีที่สุด (เช่นได้ Accuracy สูงสุด) สำหรับปัญหานั้นๆของเรา

TIP: ถ้าเราต้องการเทรน algorithms แบบอื่นด้วย template ด้านบน แค่เปลี่ยน method = "rf" เป็นชื่อ algorithms ตัวอื่นๆ เช่น naive bayes "nb" หรือ decision tree "rpart" เป็นต้น อ่านคู่มือการใช้งาน caret ได้ทั้งหมดที่ link นี้เลย


Thank You for Joining R4DA Class

teamwork
อั๋น ท๊อป ทอย มาร์ช เหลียง – ทีมงาน DataRockie Not a Hotdog

ขอบคุณเพื่อนๆทุกคนมาเรียน R4DA กับเราทั้งสองอาทิตย์ที่ผ่านมาครับ ขอบคุณเพื่อนๆที่ติดตามอ่านบล๊อกนี้ของเราจนจบเช่นกัน หากมีคำถามเกี่ยวกับบล๊อกวันนี้ ทักมาคุยเล่นกับเราได้ที่ m.me/datarockie

สำหรับเพื่อนๆที่สนใจอยากเรียน basic statistics | data science | R & Python Programming เบื้องต้น สามารถติดตามคอร์สเรียนออนไลน์ใหม่ๆของเพจเราได้ที่ https://datarockie.teachable.com

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.