Tags:
Node Thumbnail

สำหรับคนทำงานสาย IT ช่วงหลายปีหลังมานี้น่าจะไม่มีใครไม่เคยได้ยินคำว่าข้อมูลขนาดใหญ่ (big data) แม้หลายคนจะคิดว่าคำดังกล่าวเป็นเพียงแค่คำแฟชั่นเท่ๆ (buzzword) เท่านั้น แต่ก็ปฏิเสธไม่ได้ว่าโลก IT ก้าวมาถึงขั้นที่ให้ความสำคัญกับการขุดหาความรู้ (data mining) จากข้อมูลที่มีอยู่กันซักพักแล้ว

ส่วนก้าวต่อไปในโลก IT คงหนีไม่พ้นเครื่องจักรที่เรียนรู้ได้ (machine learning) และปัญญาประดิษฐ์ (artificial intelligence) การจะเข้าใจในศาสตร์เหล่านี้ได้ แค่เขียนโปรแกรมเป็นอย่างเดียวนั้นไม่เพียงพออีกต่อไป แต่ยังต้องรู้จักและจัดการกับข้อมูลเป็นอีกด้วย

บทความนี้จะพาไปสัมผัสกับภาษา R ที่แม้จะมีรากฐานมาจากการใช้งานทางสถิติ แต่ภายหลังก็ปรับตัวมารองรับการคำนวณข้อมูลที่ซับซ้อนยิ่งขึ้น จนกลายเป็นหนึ่งในภาษาที่มาแรงไม่ควรมองข้าม

No Description

ประวัติโดยย่อ

R เป็นภาษาแบบเปิดซอร์ซที่ได้รับแรงบันดาลใจมาจากภาษา S โดยมีจุดประสงค์สำหรับการคำนวณและนำเสนอกราฟิกทางสถิติ มันถูกสร้างและเผยแพร่เป็นครั้งแรกในปี 1993 โดย Ross Ihaka และ Robert Gentleman ปัจจุบันตัวภาษาเดินทางมาถึงรุ่นที่ 3.4 แล้ว

ด้วยความเฉพาะทางของมัน R จึงแตกต่างจากภาษาโปรแกรมสำหรับงานทั่วไป (general-purpose programming language) อย่างเช่น C, Python ถึงกระนั้น การจะเรียนรู้ R ก็ไม่จำเป็นต้องทิ้งความรู้จากภาษาอื่นๆ ไปเสียทั้งหมด เพียงแค่ให้ระวังความแตกต่างอันเป็นเอกลักษณ์จากภาษาใหม่นี้ก็พอแล้ว

ซึ่งเอกลักษณ์สำคัญอย่างหนึ่งของภาษา R ก็คือการเป็นภาษาโปรแกรมเชิงอาเรย์ (array language) ที่เน้นการคำนวณข้อมูลพร้อมกันเป็นกลุ่ม แนวคิดนี้อาจฟังดูแปลกและไกลตัว แต่อันที่จริงแล้วมันอาจเป็นเรื่องใกล้ตัวที่หลายคนเคยผ่านหูผ่านตาจนชินด้วยซ้ำ นั่นก็คือ การคำนวณเซลล์หลายเซลล์ด้วยสูตรเดียวกันบนโปรแกรม Excel นั่นเอง

เริ่มต้นใช้งานผ่าน REPL

สำหรับใครที่คุ้นเคยแต่กับการเขียนภาษาที่ต้องคอมไพล์ก่อน เช่น C, Java จะพบว่าการทดลองคำนวณเล็กน้อยๆ ก่อนการเขียนโปรแกรมจริงนั้นเป็นเรื่องที่ยุ่งยากพอสมควร ในภาษาแบบสคริปต์หลายๆ ภาษาจึงมักมีเครื่องมือคำนวณแบบโต้ตอบที่เรียกว่า REPL (read-eval-print loop) แถมมาให้ โดยเครื่องมือนี้มีหลักการทำงานง่ายๆ 3 ขั้นตอนตามชื่อของมัน คือ อ่านคำสั่ง คำนวณค่าตามคำสั่งนั้นๆ แล้วแสดงผลลัพธ์กลับมาให้ผู้ใช้งานเห็นทันที ก่อนจะวนกลับไปรออ่านคำสั่งถัดไปซ้ำเรื่อยๆ นั่นเอง

เครื่องมือดังกล่าวนอกจากจะช่วยให้ผู้ใช้สามารถเรียนรู้ภาษานั้นๆ ได้อย่างรวดเร็วแล้ว มันยังมีค่าเปรียบได้ดั่งกระดาษทด ที่เราสามารถจดบันทึกแนวคิดต่างๆ พร้อมคำนวณผลลัพธ์ออกมาดูความถูกต้องได้ทันทีทันใดอีกด้วย

เราสามารถทดลองใช้ REPL ของภาษา R ได้ผ่านเว็บ R-Fiddle หรือ Jupyter ได้ทันที หรือถ้าใครติดตั้งโปรแกรมไว้ในเครื่องแล้ว ก็เพียงแค่เปิดโปรแกรม terminal/cmd ขึ้นมา พิมพ์ R (ใช้ตัวอักษรตัวใหญ่ ต่างจากคำสั่งโดยทั่วไปที่มักใช้ตัวเล็กทั้งหมด) แล้วกดปุ่ม <Enter>

เมื่อเข้ามาแล้วเราจะพบกับข้อความต้อนรับที่บอกรายละเอียดว่ากำลังใช้โปรแกรมเวอร์ชันใด พร้อมทั้งแสดงคำสั่งพื้นฐานสำหรับช่วยเหลืออีกเล็กน้อย เลื่อนลงมาดูที่ด้านล่างสุดของหน้าจอ จะพบกับเครื่องหมาย > ที่ตามด้วยเคอร์เซอร์กะพริบรอรับคำสั่งที่เราพิมพ์เข้าไป เราอาจทดลองคำนวณการบวกเลขง่ายๆ เช่น 1+1 ได้ดังนี้

> 1+1
[1] 2

สังเกตว่าที่ด้านหน้าของผลลัพธ์ (เลข 2) จะมีตัวเลขในวงเล็บปีกแข็ง ([1]) แสดงอยู่ นี่เป็นตัวเลขที่จะแสดงเฉพาะในโหมด REPL เท่านั้น มันมีไว้เพื่อช่วยให้เรานับได้อย่างสะดวกว่า ค่าผลลัพธ์ที่เห็นเป็นตัวแรกในแถวนี้เป็นผลลัพธ์ตัวที่เท่าไรจากผลลัพธ์ทั้งหมด

เราอาจทดลองคำนวณค่าอื่นๆ ด้วยตัวดำเนินการพื้นฐานทางคณิตศาสตร์ที่เราคุ้นเคยในภาษาทั่วไป อันได้แก่ บวก (+) ลบ (-) คูณ (*) หาร (/) หรือบางตัวก็แปลกกว่าภาษาอื่นบ้าง เช่น ยกกำลัง (^) หารเอาเศษ (%%) และหารปัดเศษทิ้ง (%/%) สังเกตว่า R จะมองว่าตัวเลขต่างๆ เป็นตัวเลขแบบจุดทศนิยมลอยตัว (floating point) โดยปริยาย ดังนั้นการคำนวณตัวเลขแปลกๆ มักจะไม่ส่งผลให้โปรแกรมต้องหยุดทำงานเนื่องจากมีข้อผิดพลาดเกิดขึ้น เช่น

> 1/0
[1] Inf

แต่นั่นก็ไม่ได้หมายความภาษา R จะเป็นภาษาที่เขียนโปรแกรมได้โดยไม่มีข้อผิดพลาดแต่อย่างใด เช่น ข้อผิดพลาดนี้ที่เกิดจากการพิมพ์คำสั่งผิด (syntax error)

> (1+)*3
Error: unexpected ')' in "(1+)"

เมื่อเจอข้อผิดพลาดเหล่านี้ก็ไม่ต้องตกใจ ขอเพียงแค่ค่อยๆ ทำความเข้าใจว่าข้อความดังกล่าวบอกว่าเราผิดพลาดตรงไหน แล้วก็แก้ไขข้อผิดพลาดนั้น โปรแกรมก็จะทำงานต่อไปได้แล้ว

ส่วนในการทำงานจริง เราอาจลืมไปว่าฟังก์ชันที่ต้องการใช้นั้นเรียกใช้งานอย่างไร หรือประเภทข้อมูลที่ใช้มีสมบัติใดบ้าง เราสามารถเรียกเอกสารขึ้นมาอ่านได้ทันทีผ่านฟังก์ชัน help() (หรือจะใช้ทางลัดเป็นเครื่องหมาย ? เพียงตัวเดียวนำหน้าหัวข้อที่ต้องการก็ได้)

help()           # หน้าแรกเอกสาร
help("+")        # เอกสารเกี่ยวกับการดำเนินการตัวเลขพื้นฐาน
help(numeric)    # เอกสารเกี่ยวกับข้อมูลประเภทตัวเลข

เมื่อต้องการออกจาก REPL สามารถพิมพ์ฟังก์ชัน q() จากที่ได้เกริ่นไปข้างต้นว่าการใช้งานโปรแกรม REPL นี้เปรียบได้ดั่งกระดาษทด โปรแกรมจะถามว่าเราต้องการเก็บบันทึกตัวแปรและคำสั่งต่างๆ บนกระดาษทดครั้งนี้ไว้เผื่อใช้ซ้ำในครั้งถัดไปหรือไม่ หากตอบว่าใช่ (y) ทุกครั้งนับจากนี้ที่เปิดโปรแกรม REPL จากตำแหน่งแฟ้มเดิมขึ้นมา เราจะสามารถเรียกใช้ตัวแปรที่เคยสร้างและย้อนดูคำสั่งที่เคยเรียกจากครั้งนี้ได้เสมอ

ตัวแปรและประเภทข้อมูล

แม้ภาษา R จะรับแนวคิดการเขียนโปรแกรมเชิงวัตถุ (object-oriented programming) ที่ทำให้เราสามารถดัดแปลงและสร้างประเภทข้อมูลชนิดใหม่ๆ ขึ้นมาได้ แต่โดยส่วนใหญ่เราจะวนเวียนอยู่กับข้อมูลพื้นฐานเพียงไม่กี่ประเภทเท่านั้น

การทดสอบว่าข้อมูลดังกล่าวถูกจัดอยู่ในประเภทใด สามารถใช้ฟังก์ชัน class() เพื่อตรวจสอบประเภทของข้อมูลนั้นได้ เช่น

> class("hello")
[1] "character"

แต่ก่อนที่เราจะไปดูว่าภาษา R มีประเภทข้อมูลที่น่าสนใจใดบ้าง จะขอกล่าวถึงเรื่องพื้นฐานที่น่าสนใจไม่แพ้กันอย่างการประกาศตัวแปรเสียก่อน

ประกาศตัวแปร

การประกาศตัวแปรในภาษา R จะใช้สัญลักษณ์ที่แตกต่างจากภาษาโปรแกรมโดยทั่วไป แต่ดูคุ้นเคยเมื่อเขียนเป็นสมการคณิตศาสตร์ ซึ่งก็คือสัญลักษณ์ลูกศรซ้าย (<-) นั่นเอง ดังเช่นตัวอย่างการประกาศตัวแปรเหล่านี้

> x <- 42
> y <- "We \u2764 R-lang"

อย่างไรก็ตาม R ยังยอมให้ใช้สัญลักษณ์เท่ากับ (=) เพื่อประกาศตัวแปรได้เช่นกัน (แม้หลายที่เช่น Google จะไม่แนะนำวิธีนี้) ความแตกต่างคือสัญลักษณ์เท่ากับถูกออกแบบมาเพื่อใช้ประกาศพารามิเตอร์/อาร์กิวเมนต์ของฟังก์ชันเป็นหลัก การใช้สัญลักษณ์ลูกศรซ้ายเพื่อประกาศพารามิเตอร์/อาร์กิวเมนต์ฟังก์ชันอาจทำให้โปรแกรมทำงานผิดพลาดได้

อนึ่ง R ยังยินยอมให้ใช้สัญลักษณ์จุด . เป็นส่วนผสมในชื่อตัวแปรได้ สำหรับใครที่คุ้นชินกับภาษาอื่นที่ใช้จุดเพื่อคั่นแอตทรีบิวต์/เมธอด ให้ระวังว่าเมื่อพบตัวแปรมีจุดในภาษา R ไม่ได้หมายความว่าตัวแปรนั้นจะเป็นแอตทรีบิวต์ของวัตถุด้านหน้าจุดแต่อย่างใด

เราสามารถขอดูตัวแปรทั้งหมดที่เราสร้างขึ้นมาได้ผ่านฟังก์ชัน ls() และทำลายตัวแปร ได้ผ่านฟังก์ชัน rm()

ประเภทข้อมูลเดี่ยว

ข้อมูลเดี่ยวคือข้อมูลขนาดเล็กที่สุดที่ไม่สามารถแบ่งย่อยลงไปได้อีก และไม่สามารถนำไปใช้คำนวณทางสถิติได้

ข้อมูลตัวเลข

เนื่องจาก R เป็นภาษาที่เน้นการนำไปใช้งานทางสถิติเป็นหลัก การเก็บตัวเลขแบบจุดทศนิยมลอยตัว (ในภาษา R เรียกข้อมูลชนิดนี้ว่า numeric) จึงเป็นท่ามาตรฐานที่ R เลือกใช้ เพราะนำไปคำนวณต่อได้ง่าย อย่างไรก็ตาม R ยังมีข้อมูลตัวเลขอีกสองประเภทให้เลือกใช้เพิ่มเติมตามความเหมาะสมเช่นกัน ซึ่งก็คือ

  1. จำนวนเต็ม (integer) สร้างได้โดยเขียนห้อยตัว L ไว้ข้างหลังตัวเลข เช่น 100L จำนวนเต็มมีไว้สำหรับใช้ในงานที่ต้องการให้แน่ใจว่าการคำนวณต่างๆ เป็นแบบวิยุตคณิต (discrete mathematics) หรือใช้เพื่อบ่งบอกจำนวนการวนซ้ำ
  2. จำนวนเชิงซ้อน (complex) สร้างได้โดยเขียนห้อยตัว i ไว้ข้างหลังตัวเลข เช่น 1i, 3+4i จำนวนเชิงซ้อนมีไว้สำหรับงานทางด้านวิทยาศาสตร์-วิศวกรรมบางแขนง เช่น การคำนวณด้านแม่เหล็กไฟฟ้า พลศาสตร์ของไหล

ลำดับของตัวเลขในภาษา R เรียงจากต่ำไปสูงได้แก่ จำนวนเต็ม จำนวนทศนิยม และจำนวนเชิงซ้อน หากมีการใช้งานตัวเลขคละประเภทกัน R จะแปลงตัวเลขที่อยู่ในชั้นต่ำกว่าขึ้นไปเป็นชั้นที่สูงกว่าเสมอ

ข้อมูลตัวอักษร

ภาษา R ไม่แบ่งแย่งข้อมูลตัวอักษรเป็นหลายระดับ (แบบภาษา C) แต่จะมองว่าตัวอักษรชุดหนึ่งไม่ว่าจะยาวเท่าใดคือหนึ่งข้อความเสมอ (แบบภาษา Python) และเรียกข้อมูลชนิดนี้ว่า character การประกาศข้อความสามารถใช้ได้ทั้งอัญประกาศเดี่ยว (') หรืออัญประกาศคู่ (") ล้อมรอบข้อความที่ต้องการโดยไม่มีความแตกต่างกันแต่อย่างใด

ข้อมูลตรรกะ

การแสดงข้อมูลตรรกะ (logical) ว่าถูกหรือผิด จะใช้คำว่า TRUE กับ FALSE ตามลำดับ ไม่สามารถใช้ตัวเลขทดแทนได้ อย่างไรก็ตาม หากต้องการประหยัดเวลาพิมพ์ในส่วนนี้ ก็สามารถป้อนแค่ตัวอักษรตัวแรก (T,F) เข้าไปได้เช่นกัน

R ยังมีข้อมูลตรรกะแบบพิเศษอีกตัวหนึ่งเพิ่มเข้ามา คือ ข้อมูลว่าง (NA) ซึ่งจะเกิดขึ้นเมื่อพยายามเข้าถึงข้อมูลในตำแหน่งที่ไม่มีข้อมูล หรือพยายามคำนวณค่าทั้งที่ไม่มีข้อมูลอยู่ หมายเหตุว่า R จะไม่ถือว่าเกิดข้อผิดพลาดขึ้นจนต้องหยุดการคำนวณ แต่อาจขึ้นคำเตือนบางครั้งเมื่อพยายามทำงานบนข้อมูลที่ไม่สมบูรณ์นี้

ประเภทข้อมูลชุด

ข้อมูลชุดคือข้อมูลที่เก็บรวบรวมข้อมูลเดี่ยวหลายๆ ตัวมาไว้ในที่เดียวกัน จึงทำให้สามารถนำไปวิเคราะห์เชิงสถิติต่อได้

เวกเตอร์

อย่างที่หลายคนน่าจะสังเกตเห็นตอนที่ทดลองคำสั่งบน REPL ไปแล้ว ว่าการคำนวณใดๆ ในภาษา R จะมีตัวเลขตำแหน่งกำกับที่ผลลัพธ์เสมอ นั่นหมายความว่า R ให้ความสำคัญกับการคำนวณตัวเลขหลายตัวพร้อมกันเป็นอย่างมาก อันที่จริงแล้วข้อมูลเดี่ยวทุกชนิดที่สร้างขึ้นในภาษา R จะถูกห่อหุ้มด้วยสิ่งที่เรียกว่าเวกเตอร์ (vector) ซึ่งเทียบในภาษาอื่นได้ว่าเป็นอาเรย์ที่มีความยาวไม่จำกัด แม้ว่าข้อมูลชุดนั้นจะมีความยาวเพียงแค่หนึ่งหน่วยก็ตาม

การนำเวกเตอร์หลายชุดมารวมกัน จะเป็นการนำเวกเตอร์มาต่อกันเสมอ ซึ่งก็คือจะได้ผลลัพธ์เป็นเวกเตอร์เพียงชุดเดียวที่ไล่สมาชิกจากเวกเตอร์ตัวแรกไปจนจบ แล้วจึงไล่สมาชิกจากเวกเตอร์ตัวถัดไปเรื่อยๆ (ไม่ใช่ได้ผลลัพธ์เป็นเวกเตอร์ซ้อนเวกเตอร์แต่อย่างใด) โดยฟังก์ชันที่ทำหน้าที่ประกาศเวกเตอร์หรือนำเวกเตอร์มาต่อกันคือ c() ดังตัวอย่างต่อไปนี้

> x <- 1
> y <- c(2,3,4)
> z <- c(x,y)
> z
[1] 1 2 3 4

การถามประเภทข้อมูลของเวกเตอร์ด้วยฟังก์ชัน class() จะไม่รับคำตอบกลับมาว่ามันคือเวกเตอร์ แต่จะได้รับคำตอบว่าสมาชิกทุกตัวในเวกเตอร์นี้เป็นข้อมูลเดี่ยวประเภทใด นี่ทำให้เมื่อนำเวกเตอร์ของข้อมูลคละชนิดกันมารวมกัน สมาชิกบางตัวจะถูกแปลงประเภทข้อมูลขึ้นไปเป็นแบบที่สามัญกว่าทันที

> class(z)
[1] "numeric"
> mix <- c(1,"a",NA,T)
> mix
[1] "1"    "a"    NA     "TRUE"
> class(mix)
[1] "character"

การเข้าถึงสมาชิกในเวกเตอร์ สามารถใช้สัญลักษณ์วงเล็บปีกแข็งล้อมรอบตัวเลขตำแหน่งที่ต้องการ โดย R จะเริ่มนับสมาชิกตัวแรกด้วยเลขหนึ่ง (one-based indexing) และการเข้าถึงสมาชิกตัวที่สูงเกินกว่าขนาดของเวกเตอร์จะได้ผลลัพธ์เป็นข้อมูลว่าง เช่นนี้

> z[3]
[1] 3
> z[10]
[1] NA
> length(z)
[1] 4

นอกจากนี้ เรายังสามารถเข้าถึงข้อมูลย่อยพร้อมๆ กันหลายตัวได้ โดยส่งเวกเตอร์ของตำแหน่งทั้งหมดที่ต้องการเข้าไป ซึ่งสามารถเขียนย่อช่วงตำแหน่งที่ติดกันโดยใช้สัญลักษณ์โคลอน (:) หรือจะส่งเวกเตอร์ตรรกะเพื่อบอกว่าจะเลือกสมาชิกตัวไหนบ้างก็ได้ ตามตัวอย่างต่อไปนี้

> z[c(2,4)]
[1] 2 4
> z[1:3]
[1] 1 2 3
> z[4:2]
[1] 4 3 2
> z[c(T,F,F,T)]
[1] 1 4
> z[c(T,F)]
[1] 1 3

การคำนวณทางคณิตศาสตร์ระหว่างเวกเตอร์ จะเป็นการจับคู่สมาชิกแต่ละตัวในเวกเตอร์แล้วคำนวณตามเครื่องหมายคณิตศาสตร์ที่ให้ไว้ ถ้าเวกเตอร์มีขนาดไม่เท่ากัน เวกเตอร์ตัวที่สั้นกว่าจะถูกนำมาใช้จับคู่ซ้ำเรื่อยๆ นั่นเอง

> z - 1
[1] 0 1 2 3
> z * c(1,-1)
[1]  1 -2  3 -4
> z + c(4,5,6)
[1] 5 7 9 8
Warning message:
In z + c(4, 5, 6) :
  longer object length is not a multiple of shorter object length

ข้อมูลชุดสำหรับคำนวณทางคณิตศาสตร์

หากเราต้องการเก็บข้อมูลที่เทียบเท่ากับอาเรย์สองมิติในภาษาอื่น ภาษา R จะเรียกข้อมูลชนิดนั้นว่าเมทริกซ์ (matrix) การจะสร้างข้อมูลชนิดนี้ได้ต้องเตรียมข้อมูลให้อยู่ในรูปของเวกเตอร์เสียก่อน แล้วจึงค่อยเปลี่ยนประเภทข้อมูลเป็นเมทริกซ์ ดังนี้

> A <- matrix(z, nrow=2, ncol=2)
     [,1] [,2]
[1,]    1    3
[2,]    2    4
> B <- matrix(z, nrow=2, ncol=2, byrow=TRUE)
     [,1] [,2]
[1,]    1    2
[2,]    3    4

สังเกตว่าที่ตัวเลขกำกับตำแหน่งผลลัพธ์ จะมีสัญลักษณ์ลูกน้ำ (,) เพิ่มเข้ามาด้วย นั่นบ่งบอกว่าการเข้าถึงสมาชิกในข้อมูลประเภทเมทริกซ์ จะต้องเขียนสัญลักษณ์ลูกน้ำกำกับด้วยเช่นกัน

> A[1,2]
[1] 3
> B[,2]
[1] 2 4
> A[2:1,2:1]
     [,1] [,2]
[1,]    4    2
[2,]    3    1

หากเรายังต้องการเก็บอาเรย์ที่มีมิติสูงขึ้นไปกว่านี้ ก็สามารถสร้างประเภทข้อมูลอาเรย์ได้ผ่านฟังก์ชัน array() โดยระบุขนาดในแต่ละมิติเข้าไป

> array(1:24, c(4,3,2,1))
, , 1, 1
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12

, , 2, 1
     [,1] [,2] [,3]
[1,]   13   17   21
[2,]   14   18   22
[3,]   15   19   23
[4,]   16   20   24

สังเกตว่าการดำเนินการทางคณิตศาสตร์โดยใช้เครื่องหมายธรรมดาทั่วไป (+,-,*,/) จะเป็นการนำสมาชิกในข้อมูลทั้งสองชุดมาจับคู่กันตัวต่อตัวแล้วดำเนินการทางคณิตศาสตร์แบบธรรมดาเท่านั้น หากต้องการดำเนินการทางคณิตศาสตร์แบบเมทริกซ์ ต้องใช้เครื่องหมายคณิตศาสตร์สำหรับเมทริกซ์โดยเฉพาะ (เช่น การคูณเมทริกซ์ %*%) ดังตัวอย่างต่อไปนี้

> A * B
     [,1] [,2]
[1,]    1    6
[2,]    6   16
> A %*% B
     [,1] [,2]
[1,]   10   14
[2,]   14   20

กรอบข้อมูล

กรอบข้อมูล (data.frame) คือข้อมูลชุดแบบตารางเช่นเดียวกับตารางในฐานข้อมูล กล่าวคือ แต่ละหลักของตารางจะบอกว่าหลักนี้ใช้เก็บข้อมูลเรื่องใด ส่วนแต่ละแถวของตารางก็คือข้อมูลหนึ่งระเบียน (record) นั่นเอง

วิธีสร้างกรอบข้อมูล เริ่มจากการเตรียมข้อมูลแต่ละหลักด้วยเวกเตอร์ โดยตรวจสอบให้แน่ใจว่าเวกเตอร์ทุกตัวมีความยาวเท่ากัน แล้วจึงนำมารวมกันด้วยฟังก์ชัน data.frame() ดังนี้

> handle <- c("Sheldon","Leonard","Penny")
> year <- c(1980,1980,1985)
> iq <- c(187,173,NA)
> big.bang <- data.frame(handle, yob=year, iq)
> big.bang
   handle  yob  iq
1 Sheldon 1980 187
2 Leonard 1980 173
3   Penny 1985  NA

เนื่องจากรูปแบบการเก็บข้อมูลนี้ คล้ายคลึงกับการเก็บโดยเมทริกซ์ การเข้าถึงข้อมูลแต่ละแถว สามารถทำได้ในทำนองเดียวกันกับที่ทำบนเมทริกซ์ คือ

> big.bang[3,]
  handle  yob iq
3  Penny 1985 NA

ส่วนการเข้าถึงข้อมูลแต่ละหลัก สามารถใช้เครื่องหมาย $ แล้วตามด้วยชื่อของเขตข้อมูลที่ต้องการได้เลย ซึ่งก็คือ

> big.bang$yob
[1] 1980 1980 1985

อนึ่ง หากข้อมูลในหลักซ้ายสุดของตารางมีค่าไม่ซ้ำกัน และเราต้องการค้นหาข้อมูลในแถวนั้นโดยใช้ค่าในหลักซ้ายสุดเป็นคำค้น (เทียบได้กับกุญแจหลักในฐานข้อมูล) ตอนสร้างกรอบข้อมูลให้กำหนดอาร์กิวเมนต์เพิ่มเข้าไปเล็กน้อย ดังนี้

> new.big.bang <- data.frame(row.names=handle, yob=year, iq)
> new.big.bang
         yob  iq
Sheldon 1980 187
Leonard 1980 173
Penny   1985  NA
> new.big.bang["Sheldon",]
         yob  iq
Sheldon 1980 187

นอกจากนี้ อย่าลืมว่าเราสามารถใช้เวกเตอร์ตรรกะซึ่งอาจได้มาจากการคำนวณ เพื่อกรองว่าต้องการใช้ข้อมูลแถวได้บ้างในกรอบข้อมูลนี้ ดังที่จะเห็นได้จากตัวอย่างถัดไป

> use <- new.big.bang$yob == 1980
> use
[1]  TRUE  TRUE FALSE
> new.big.bang[use,]
         yob  iq
Sheldon 1980 187
Leonard 1980 173

สถิติและความน่าจะเป็นอย่างรวบรัด

พูดอย่างหยาบๆ แล้ว สถิติคือวิชาที่เล่นกับข้อมูลจำนวนมาก ส่วนความน่าจะเป็นนั้นนำค่าทางสถิติคาดมาเดาอนาคต หลายต่อหลายครั้งสถิติและความน่าจะเป็นมักมาพร้อมกับการสุ่ม เช่น เมื่อเราเจอเหรียญที่ไม่สมดุลเหรียญหนึ่งจึงทดลองโยนดูค่า 100 ครั้ง พบว่าออกหัวถึง 80 ครั้ง เราอาจทำนายได้ว่าเมื่อโยนครั้งต่อไปควรคาดหวังว่าผลลัพธ์จะออกด้านหัวมากกว่าก้อย

สิ่งสำคัญที่เรียกได้ว่าเป็นพระเอกของงานนี้ก็คือ "ข้อมูล" นั่นเอง หากขาดซึ่งข้อมูลแล้ว ไม่ว่าจะสร้างโมเดลทำนายความน่าจะเป็นมาดีขนาดไหนก็คงไร้ประโยชน์

ในหัวข้อนี้ เราอาจสร้างข้อมูลอย่างสุ่มขึ้นมาเพื่อทดลองหาค่าทางสถิติก่อนก็ได้ แต่จะขอแสดงวิธีนำเข้าข้อมูลจากภายนอก ซึ่งแม้บางขั้นตอนจะซับซ้อนอยู่บ้าง แต่ก็ดีกว่าค่อยๆ ป้อนตัวเลขเข้าไปทีละตัวเป็นแน่แท้

นำเข้าข้อมูล

เนื่องจากการเข้าถึงไฟล์ข้อมูลทางสถิติต่างๆ ของประเทศไทยยังเป็นไปได้ยาก ตัวอย่างต่อไปนี้จะขอยืมข้อมูลจากต่างประเทศมาใช้ก่อน ซึ่งก็คือข้อมูลคะแนนเฉลี่ยของการสอบ SAT จากโรงเรียนระดับมัธยมในเมืองนิวยอร์ก (แนวคิดข้อสอบเทียบได้กับการสอบแอดมิชชั่นของไทย) โดยมีขั้นตอนดังนี้

เริ่มจากเข้าไปดาวน์โหลดข้อมูลดังกล่าวผ่าน Data.gov เลือกรูปแบบไฟล์ที่จะดาวน์โหลดเป็นแบบ CSV (comma separated values) เพื่อความสะดวกในการนำไปใช้ต่อ

เมื่อดาวน์โหลดไฟล์ดังกล่าวเสร็จสิ้น อาจทดสอบความถูกต้องของข้อมูลด้วยการเปิดไฟล์ดังกล่าวผ่านโปรแกรมจำพวกสเปรดชีต เช่น Microsoft Excel, Google Sheets, LibreOffice Calc หรือจะเปิดผ่านโปรแกรมแก้ไขข้อความทั่วไปก็ได้

ต่อไปจะอ่านข้อมูลเข้ามาใช้ใน R โดยคร่าวๆ แล้วสามารถทำได้ดังนี้ (สำหรับ Windows ใช้เครื่องหมาย / เพื่อคั่นระหว่างโฟลเดอร์เช่นเดียวกัน)

> data <- read.csv("PATH/TO/DOWNLOAD/SAT_Results.csv")

อย่างไรก็ตาม หากเราลองดูข้อมูลตัวเลขที่อ่านเข้ามา จะพบว่าในหลักเดียวกันนั้น บางแถวก็ยังมีตัวอักษร "s" ปนอยู่ด้วย หากยังพอจำกันได้ การมีข้อมูลตัวอักษรปนกับข้อมูลตัวเลขนั้น จะทำให้ข้อมูลตัวเลขถูกแปลงไปเป็นตัวอักษรทันที ซึ่งอาจส่งผลให้วิเคราะห์ผิดพลาดในภายหลัง นี่เป็นสิ่งที่เราสามารถหลีกเลี่ยงได้โดยระบุว่าตัวอักษร "s" หมายถึงข้อมูลในช่องนั้นหายไป (ข้อมูลชุดอื่นๆ อาจใช้ตัวอักษรระบุค่าที่หายไปแตกต่างกัน เช่น "-", "n/a" เป็นต้น)

> data <- read.csv("PATH/TO/DOWNLOAD/SAT_Results.csv",
                   na.strings="s")

นอกจากนี้แล้ว เรายังสามารถระบุเพิ่มเติมว่า ให้ใช้หลักแรกสุดเป็นกุญแจในการเข้าถึงข้อมูลในแถวนั้นๆ โดยเพิ่มอาร์กิวเมนต์ row.names เข้าไป

> data <- read.csv("PATH/TO/DOWNLOAD/SAT_Results.csv",
                   na.strings="s", row.names=1)

สำหรับข้อมูลชุดนี้ การระบุอาร์กิวเมนต์ตามข้างต้นก็ถือว่าเพียงพอแล้ว เพราะเมื่อไม่สนใจค่า NA ทั้งหมดที่พบ R จะพยายามแปลงข้อมูลแต่ละแถวให้เป็นตัวเลข เพื่อให้นำไปใช้งานต่อในเชิงสถิติได้ทันที อย่างไรก็ตาม หากเราไม่มั่นใจว่า R จะแปลงประเภทข้อมูลได้ถูกต้อง เรายังสามารถบังคับประเภทของตัวแปรในแต่ละแถวได้เช่นกัน

> data <- read.csv("PATH/TO/DOWNLOAD/SAT_Results.csv",
                   na.strings="s", row.names=1,
                   colClasses=c(rep("character",2), rep("numeric",4)))

ท้ายสุด เราอาจเลือกลบแถวที่ข้อมูลขาดหาย (มีข้อมูลเป็น NA) ทิ้งไป เพื่อความสะดวกในการคำนวณข้อมูลในภายหลัง ซึ่งทำได้โดย

> data <- na.omit(data)

ภาพรวมข้อมูลทางสถิติ

เมื่อต้องการเริ่มวิเคราะห์ข้อมูลชุดหนึ่งๆ การไล่ค่าทุกค่าในข้อมูลชุดนั้นออกมาแล้วทำความเข้าใจกับมันมักเป็นการกระทำที่เสียเวลาและไม่สมเหตุสมผล วิธีการที่มีประสิทธิภาพกว่าคือการดูข้อสรุปชิ้นเล็กๆ ที่ถูกสรุปออกมาจากข้อมูลชุดนั้น โดยหลักๆ แล้วเราอาจสนใจค่าสรุปอยู่ 2 แบบ ได้แก่ ตำแหน่งข้อมูล และการกระจายข้อมูล

ตำแหน่งข้อมูล

ตำแหน่งข้อมูลจะแสดงว่าข้อมูลดังกล่าวมีตำแหน่งสำคัญๆ ตรงไหนบ้าง เช่น ค่ามากสุด/น้อยสุด ค่ากลางข้อมูล และควอร์ไทล์

ค่ากลางข้อมูลคือค่าที่แสดงว่าข้อมูลชุดนั้นมีค่าลู่เข้าสู่ตรงกลางที่เท่าใด ซึ่งอาจแบ่งย่อยได้เป็นอีก 3 ประเภท คือ

  1. มัชฌิม (mean) หรือบางที่ก็เรียกว่าค่าเฉลี่ย (average) หาได้จากการนำทุกค่าในข้อมูลมาบวกรวมกัน แล้วจึงหารด้วยจำนวนข้อมูลที่ใช้
  2. มัธยฐาน (median) หาได้จากการเรียงข้อมูลจากน้อยไปมากตามลำดับ แล้วดูว่าที่ตำแหน่งตรงกลางของข้อมูลมีค่าเป็นเท่าใด
  3. ฐานนิยม (mode) หาได้จากการนับว่าค่าใดปรากฏซ้ำมากที่สุดในข้อมูล

ค่าทั้งสามนี้ไม่จำเป็นต้องมีค่าเท่ากัน ซึ่งก็ถือเป็นเรื่องปรกติเนื่องจากข้อมูลมักมีการกระจายตัวไม่สม่ำเสมอ

ส่วนควอร์ไทล์จะใช้แนวคิดเดียวกันกับการหามัธยฐาน เพียงแต่แบ่งข้อมูลออกเป็นกลุ่มที่มีขนาดเท่ากันเพิ่มเป็น 4 กลุ่ม และสนใจค่าที่อยู่ตรงจุดเปลี่ยนระหว่างแต่ละกลุ่ม ดังนี้

  1. ควอร์ไทล์ที่ 1 (1st quartile) คือค่าที่มีตำแหน่งอยู่ตรงกลางระหว่างค่าน้อยสุดและค่ามัธยฐาน
  2. ควอร์ไทล์ที่ 2 (2nd quartile) คือมัธยฐานนั่นเอง (และเรามักไม่ค่อยเรียกว่า "ควอร์ไทล์ที่ 2" เท่าใดนักเพราะสามารถเรียกชื่อที่พื้นฐานกว่าได้)
  3. ควอร์ไทล์ที่ 3 (3rd quartile) คือค่าที่มีตำแหน่งอยู่ตรงกลางระหว่างค่ามากที่สุดและค่ามัธยฐาน

ใน R เราสามารถเรียกฟังก์ชัน summary() เพื่อดูภาพรวมตำแหน่งข้อมูลต่างๆ เหล่านี้ได้ทันที เช่น

> summary(data$SAT.Math.Avg..Score)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  312.0   371.0   395.0   413.4   437.0   735.0 

สังเกตว่าผลลัพธ์ดังกล่าว ไม่ได้นำข้อมูลฐานนิยมมาแสดงด้วย เพราะโดยทั่วไปข้อมูลมักมีความแม่นยำของตำแหน่งทศนิยมสูงจนไม่มีข้อมูลชิ้นใดซ้ำกัน การหาฐานนิยมด้วยวิธีพื้นฐานมักไม่ให้ข้อสรุปที่เกิดประโยชน์เท่าไร

เราอาจเลี่ยงไปหาฐานนิยมด้วยการแบ่งข้อมูลเป็นช่วงๆ ผ่านฮิสโตแกรม ซึ่งจะกล่าวถึงในหัวข้อถัดไป

การกระจายข้อมูล

ข้อสรุปเดี่ยวๆ จากการวัดตำแหน่งตัวแทนข้อมูล อาจพอบอกแนวโน้มของข้อมูลส่วนใหญ่ได้บ้าง แต่สิ่งที่มันบอกไม่ได้เลยคือลักษณะการกระจายตัวของข้อมูล ลองพิจารณาตัวอย่างว่ามีร้านค้าอยู่สองร้าน ร้านทั้งสองมีจำนวนลูกค้าเข้าร้านเท่าๆ กันและมีรายได้จากการขายของใกล้เคียงกัน ดังนั้นร้านทั้งสองจึงขายของได้เฉลี่ยต่อลูกค้าเป็นจำนวนเงินไม่แตกต่างกันเท่าใดนัก มองผ่านๆ เราอาจคิดว่าร้านทั้งสองขายสินค้าคล้ายกันและตั้งราคาไม่หนีกัน แต่อันที่จริงแล้วข้อมูลเพียงเท่านี้ไม่สามารถบอกลักษณะความหลากหลายในช่วงราคาที่ลูกค้าจับจ่ายได้เลย เพราะร้านหนึ่งอาจขายของเฉพาะทางไม่กี่อย่างในช่วงราคาแคบๆ ส่วนอีกร้านอาจขายตั้งแต่สากกะเบือยันเรือดำน้ำก็เป็นได้

แต่การวัดตำแหน่งข้อมูลก็ไม่ได้ไร้ประโยชน์เสียทีเดียว เพราะเมื่อนำเอาข้อสรุปหลายๆ ชิ้นมารวมกัน ก็อาจพอบอกถึงการกระจายข้อมูลได้บ้าง เช่น

  • พิสัย (range) บอกถึงขนาดของช่วงที่เล็กที่สุดที่สามารถครอบคลุมข้อมูลได้ทั้งหมด คำนวณจากการนำค่ามากที่สุดลบน้อยที่สุดได้เลย
  • พิสัยระหว่างควอร์ไทล์ (interquartile range) คำนวณจากการนำค่าของควอร์ไทล์ที่ 3 ลบด้วยค่าของควอร์ไทล์ที่ 1 หรือกล่าวอีกนัยหนึ่งได้ว่า วิธีนี้คือเป็นหาพิสัยที่ครอบคลุมข้อมูลจำนวนครึ่งหนึ่งที่กระจายรอบมัธยฐานนั่นเอง

นอกจากนี้ ยังมีการวัดการกระจายข้อมูลอีกวิธีหนึ่งที่เป็นที่นิยม ซึ่งก็คือส่วนเบี่ยงเบนมาตรฐาน (standard deviation, สัญลักษณ์ σ) จุดเด่นของวิธีนี้คือการใช้ข้อมูลทุกตัวมาคำนวณ แทนที่จะอ้างอิงจากค่าสรุปทางสถิติเพียงไม่กี่ค่า (ขอละการอธิบายสมการเนื่องจากมีรายละเอียดค่อนข้างมาก) การวัดการกระจายด้วยวิธีนี้จะใช้ได้ผลดีที่สุดเมื่อข้อมูลมีรูปร่างเป็นการแจกแจงปรกติ (normal distribution) ซึ่งบอกเป็นนัยว่ามีข้อมูลที่อยู่ห่างจากค่าเฉลี่ยไม่เกิน ±1σ อยู่ถึง 68% อย่างไรก็ตาม ข้อมูลที่มีการแจกแจงรูปแบบอื่นจะไม่สามารถสรุปขนาดของการแจกแจงเช่นนี้ได้

ฟังก์ชันสำหรับคำนวณการกระจายข้อมูลในภาษา R ได้แก่

> range(data$SAT.Math.Avg..Score)
[1] 312 735
> IQR(data$SAT.Math.Avg..Score)
[1] 66
> sd(data$SAT.Math.Avg..Score)
[1] 64.68466

พล็อตข้อมูล

จุดแข็งสำคัญของภาษาที่เน้นการใช้งานเชิงสถิติอย่าง R คงหนีไม่พ้นความเรียบง่ายแต่ก็ทรงพลังในการพล็อตข้อมูลขึ้นมาดู ยิ่งเมื่อคำนึงว่ามนุษย์เราสามารถรับรู้รูปทรงได้รวดเร็วกว่าการดูตัวเลขเป็นจำนวนมหาศาลแล้ว การพล็อตข้อมูลนอกจากจะช่วยให้เห็นภาพว่าข้อมูลโดยรวมมีหน้าตาเป็นยังไงแล้ว มันยังช่วยให้เราไม่เข้าใจข้อมูลผิดไปเพียงเพราะเลือกดูแค่ค่าเฉลี่ยที่สรุปมาจากข้อมูลทั้งหมดเท่านั้น ดังเช่นทวีตต่อไปนี้ที่แสดงให้เห็นว่าข้อมูลจริงสามารถแตกต่างกันได้มากแม้จะมีข้อสรุปทางสถิติไม่ต่างกัน

เพื่อความรวดเร็วและถูกต้องของข้อมูล ในหัวข้อนี้เราจะใช้ข้อมูลตัวอย่างที่ภาษา R แถมมา สำหรับสร้างภาพการพล็อตทั้งหมด

พล็อตประชันทุกเขตข้อมูล

เมื่อเราเริ่มทำงานกับข้อมูลชุดใหม่ที่ยังไม่เคยเห็นภาพมาก่อน เราอาจไม่รู้เลยว่าความสัมพันธ์ของแต่ละเขตข้อมูลเป็นอย่างไรบ้าง ดังนั้นสิ่งที่เราควรเริ่มทำเป็นอย่างแรก คือการมองภาพความสัมพันธ์ทั้งหมดอย่างหยาบๆ ก่อน

การพล็อตประชันทุกเขตข้อมูล คือการจับเอาข้อมูลทุกๆ คู่ในแต่ละหลักของกรอบข้อมูลมาประชันกัน แล้วพล็อตแต่ละคู่ของข้อมูลแบบกระจาย (scatter plot) การพล็อตนี้สามารถทำได้ผ่านฟังก์ชันง่ายๆ คือ plot() ตัวอย่างต่อไปนี้จะยืมข้อมูลดอกไอริสของ Edgar Anderson มาใช้ โดยข้อมูลดังกล่าวสามารถเรียกใช้งานได้ทันทีเลยในภาษา R โดยไม่ต้องโหลดไลบรารีใดเพิ่ม

> iris
    Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
1            5.1         3.5          1.4         0.2     setosa
2            4.9         3.0          1.4         0.2     setosa
...
150          5.9         3.0          5.1         1.8  virginica
> plot(iris)

No Description

ภาพที่ 1: พล็อตประชันทุกเขตข้อมูลของข้อมูลดอกไอริส

พล็อตกระจาย (Scatter Plot)

จากภาพที่ 1 เราอาจเริ่มต้นให้ความสนใจที่ความยาวกลีบดอก (Petal.Length) ต่อความกว้างกลีบดอก (Petal.Width) เนื่องจากเมื่อดูอย่างหยาบๆ แล้วน่าจะสร้างโมเดลได้ง่ายที่สุด เราจะเลือกพล็อตกระจายโดยใช้เขตข้อมูลเพียง 2 เขตนี้เท่านั้น เพื่อความสะดวกเราจะบอก R ก่อนว่ากำลังใช้ข้อมูลชุดใดเป็นหลักผ่านฟังก์ชัน attach() แล้วจึงสั่งพล็อตแบบเฉพาะเจาะจงด้วยสมการดังนี้

> attach(iris)
> plot(Petal.Width ~ Petal.Length)

No Description

ภาพที่ 2: พล็อตกระจายข้อมูลดอกไอริส โดยสนใจความกว้างต่อความยาวกลีบดอก

หมายเหตุว่า Petal.Width ~ Petal.Length ไม่ใช่การนำตัวแปรสองตัวมาดำเนินการกันแต่อย่างใด แต่มันคือการประกาศตัวแปรแบบสมการในภาษา R ที่มีความมหัศจรรย์ตรงที่ไม่ต้องใช้เครื่องหมายอัญประกาศมาครอบเพื่อบอกขอบเขตของสมการเฉกเช่นการประกาศข้อความ

อนึ่ง ในอนาคตเมื่อไม่ต้องการใช้ข้อมูลชุดนี้แล้ว (เช่น หลังจบหัวข้อนี้) ควรเรียกฟังก์ชัน detach() ในทำนองเดียวกันกับที่เรียก attach() ทุกครั้ง เพื่อไม่ให้เกิดความสับสนจากการที่มีข้อมูลจำนวนหลายชุดมากเกินกว่าที่ต้องการใช้งาน และเพื่อป้องกันกรณีที่มีข้อมูลต่างชุดกันแต่ใช้ชื่อเขตข้อมูลซ้ำกัน

ลากโมเดลเชิงเส้น

จากภาพที่ 2 เราอาจต้องการหาโมเดลเชิงเส้นของข้อมูลดังกล่าว เพื่อทำนายแนวโน้มว่าหากมีข้อมูลใหม่ๆ เพิ่มขึ้นมา ควรคาดหวังว่าข้อมูลเหล่านั้นจะเกิดบริเวณไหน การคำนวณดังกล่าวสามารถทำได้ผ่านฟังก์ชัน lm() ดังนี้

> lm(Petal.Width ~ Petal.Length)
Call:
lm(formula = Petal.Width ~ Petal.Length, data = iris)

Coefficients:
 (Intercept)  Petal.Length  
     -0.3631        0.4158  

หากต้องการลากเส้นดังกล่าวลงไปบนพล็อต ขอให้แน่ใจก่อนว่ามีภาพของพล็อตเดิมอยู่ก่อนแล้ว หลังจากนั้นจึงค่อยเรียกฟังก์ชัน abline() เพื่อลากเส้นตรงจากสัมประสิทธิ์ที่คำนวณได้ ดังนี้

> plot(Petal.Width ~ Petal.Length)
> abline(lm(Petal.Width ~ Petal.Length))

No Description

ภาพที่ 3: โมเดลเชิงเส้นของความกว้างต่อความยาวกลีบดอก

ลากโมเดลไม่เชิงเส้น

อย่างไรก็ตาม หากข้อมูลที่สนใจมีแนวโน้มว่าจะไม่เป็นเชิงเส้น ภาษา R ก็มีวิธีการคำนวณโมเดลแบบต่างๆ ให้เลือกใช้ได้หลายวิธี สำหรับวิธีที่ง่ายต่อการเรียกใช้งานที่สุด คือการประมาณค่าถดถอยแบบท้องถิ่น (local regression หรือ LOESS) ซึ่งจะสร้างโมเดลสมการพหุนามโดยอาศัยข้อมูลแต่ละส่วนในบริเวณใกล้เคียงมาปรับค่า เราสามารถเรียกดูผลลัพธ์จุดตัดต่างๆ บนเส้นโค้งของโมเดลที่คำนวณได้ดังนี้

> loess.smooth(Petal.Length, Petal.Width)
$x
 [1] 1.000000 1.120408 1.240816 1.361224 1.481633
...
$y
 [1] 0.06892099 0.11545407 0.16200259 0.20869061 0.25544166
...

ส่วนการพล็อตเส้นโค้งดังกล่าว จะเปลี่ยนมาเรียกผ่านฟังก์ชัน lines() ที่จะลากเส้นตรงเชื่อมระหว่างจุดตัดต่างๆ ตามที่ระบุ แต่เนื่องจากเรามีจุดตัดที่คำนวณไว้เป็นจำนวนมาก จึงได้ผลลัพธ์ออกมาเปรียบเสมือนเส้นโค้งนั่นเอง

> plot(Petal.Width ~ Petal.Length)
> lines(loess.smooth(Petal.Length, Petal.Width))

No Description

ภาพที่ 4: โมเดลไม่เชิงเส้นของความกว้างต่อความยาวกลีบดอก

ขอให้สังเกตการสลับที่ของตัวแปร สำหรับบรรทัดบนที่ระบุสมการผ่านเครื่องหมาย ~ นั้น จะมีความหมายว่าฝั่งซ้ายของ ~ คือข้อมูลในแกนตั้ง หรืออาจมองเป็นสมการคณิตศาสตร์ที่คุ้นตาโดยเปลี่ยนเครื่องหมาย ~ เป็น = ในทำนองเดียวกันกับ y=x นั่นเอง ส่วนในบรรทัดล่างเนื่องจากเราไม่สามารถระบุสมการด้วยเครื่องหมาย ~ ได้ จึงต้องส่งค่าเข้าไปเป็นข้อมูลในแกนนอน (x) แล้วจึงตามด้วยแกนตั้ง (y) ตามลำดับ

ลากโมเดลอิสระจากสมการที่กำหนดเอง

หากว่าเรายังไม่พอใจกับโมเดลแบบต่างๆ ที่ภาษา R มีให้เลือก เราอาจคำนวณหาสมการด้วยตนเองแล้วนำไปพล็อตทดสอบกับข้อมูลจริงได้ดังนี้

> plot(Petal.Width ~ Petal.Length)
> curve(exp(x-4/3*pi), add=TRUE)

No Description

ภาพที่ 5: โมเดลจากสมการ ex-(4/3)π

จุดที่น่าสนใจคือเราสามารถเขียนสมการที่ประกอบขึ้นมาจากพจน์ x แล้วส่งเป็นอาร์กิวเมนต์แรกของฟังก์ชัน curve() ได้ตรงๆ โดยที่เราไม่จำเป็นต้องมีตัวแปร x มาก่อน (หรือหากมีตัวแปรนี้อยู่ มันจะไม่ถูกใช้ในการคำนวณนี้แต่อย่างใด) นี่เป็นอีกหนึ่งในความมหัศจรรย์ของภาษา R ที่พยายามเอาใจผู้ใช้ที่มีพื้นฐานมาจากฝั่งคณิตศาสตร์/สถิติ ซึ่งอาจจะดูผิดปรกติไปบ้างเมื่อเทียบกับภาษาคอมพิวเตอร์อื่นๆ

แต้มสีแยกกลุ่มข้อมูล

อนึ่ง จากข้อมูลตั้งต้นของดอกไอริส นอกจากจะมีข้อมูลเชิงตัวเลขที่บ่งบอกลักษณะดอกไม้แล้ว ยังมีข้อมูลการแบ่งกลุ่มดอกไม้ตามสปีชีส์อีกด้วย ซึ่งข้อมูลการแบ่งกลุ่มนี้อาจไม่เหมาะสมในการพล็อตเทียบกับข้อมูลตัวเลขซักเท่าไร (ดังจะเห็นได้จากแถวล่างสุด/แถบขวาสุดในภาพที่ 1) แต่จะเหมาะสมกว่าหากพล็อตข้อมูลตัวเลขเทียบกันเช่นเดิม แล้วเพิ่มการแบ่งกลุ่มเข้าไปเป็นอีกแกนหนึ่ง เช่น แต้มสีจุดของแต่ละกลุ่มให้แตกต่างกัน ซึ่งสามารถสั่งได้ดังนี้

> plot(Petal.Width ~ Petal.Length, col=Species)

No Description

ภาพที่ 6: ข้อมูลดอกไอริสโดยแยกสีตามสปีชีส์

ทั้งนี้ทั้งนั้น หากข้อมูลตั้งต้นไม่ได้ถูกแบ่งกลุ่มไว้ก่อน แต่เรายังต้องการจัดกลุ่มข้อมูลอยู่ อาจเลือกใช้วิธี k-means ในการแบ่งกลุ่มข้อมูล โดยมีหลักการคร่าวๆ ดังนี้

  1. สุ่มเลือกจุดศูนย์กลางของแต่ละกลุ่มมาทั้งหมด k จุด
  2. กำหนดว่าจุดข้อมูลต่างๆ อยู่ในกลุ่มใด โดยพิจารณาจากจุดข้อมูลนั้นอยู่ใกล้จุดศูนย์กลางใดมากที่สุด
  3. คำนวณย้ายค่าจุดศูนย์กลางของแต่ละกลุ่มให้เป็นค่าเฉลี่ยของทุกจุดข้อมูลในกลุ่ม
  4. ทำซ้ำขั้นตอนที่ 2-3 จนกว่าการจัดกลุ่มจะเสถียร

เนื่องจาก R เตรียมฟังก์ชันดังกล่าวไว้ให้แล้ว วิธีเรียกใช้จึงไม่ยุ่งยากอะไร ขอเพียงแค่เตรียมข้อมูลสำหรับการจัดกลุ่ม และจำนวนกลุ่มที่ต้องการจัดไว้ให้เรียบร้อย แล้วจึงป้อนคำสั่งเหล่านี้ลงไป

> group <- kmeans(iris[3:4], 3)
> plot(Petal.Width ~ Petal.Length, col=group$cluster)

No Description

ภาพที่ 7: ข้อมูลดอกไอริสที่จัดกลุ่มแยกสีด้วยวิธี k-means เป็นจำนวน 3 กลุ่ม

หมายเหตุ เนื่องจากอัลกอริทึม k-means เริ่มต้นด้วยการสุ่ม อีกทั้งข้อมูลบางชุดก็ไม่สามารถแบ่งกลุ่มได้อย่างชัดเจน การแบ่งกลุ่มแต่ละครั้งด้วยวิธีดังกล่าวอาจให้ผลลัพธ์ที่แตกต่างกัน

พล็อตข้อสรุปการแจกแจงข้อมูล

เราได้เห็นการพล็อตกระจายที่พล็อตทุกๆ จุดจากข้อมูลที่มีไปแล้ว แต่บางครั้งเราก็ต้องการพล็อตแค่ภาพรวมของข้อมูลเท่านั้น วิธีการพล็อตที่ตอบโจทย์ดังกล่าวและเป็นที่นิยม คือ ฮิสโตแกรมและพล็อตกล่อง

ฮิสโตแกรม (histogram)

ฮิสโตแกรมเป็นหนึ่งในวิธีพื้นฐานสำหรับแสดงการแจกแจงข้อมูล โดยมันจะแสดงแท่งสี่เหลี่ยมที่ความสูงต่างกัน แต่ละแท่งบอกว่ามีข้อมูลจำนวนเท่าใดที่เกิดขึ้นในช่วงนั้นๆ

> hist(Sepal.Width)

No Description

ภาพที่ 8: ฮิสโตแกรมความกว้างกลีบเลี้ยงดอกไอริส

จากภาพที่ 8 จะเห็นว่าแท่งที่สูงที่สุดคือแท่งที่ 5 จากทางซ้าย หรือเมื่อดูเป็นช่วงจะได้ว่าคือช่วงที่ข้อมูลมีค่ามากกว่า 2.8 แต่ไม่เกิน 3.0 ซึ่งจะได้ว่าช่วงนี้คือฐานนิยมนั่นเอง

เราสามารถเปลี่ยนจำนวนรอยต่อระหว่างช่วงที่ต้องการได้ ด้วยการระบุอาร์กิวเมนต์ breaks เพิ่มเข้าไปดังนี้

> hist(Sepal.Width, breaks=4)

No Description

ภาพที่ 9: ฮิสโตแกรมความกว้างกลีบเลี้ยงดอกไอริส โดยขีดเส้นแบ่งช่วง 4 ครั้ง

หรือเราอาจระบุตำแหน่งทั้งหมดของรอยต่อระหว่างช่วงที่ต้องการแบ่งก็ได้ โดยเรามีอิสระที่ไม่จำเป็นต้องเลือกให้แต่ละช่วงมีขนาดเท่ากัน

> hist(Sepal.Width, breaks=c(2,2.1,2.4,2.7,3.0,3.3,3.6,3.9,4.4))

No Description

ภาพที่ 10: ฮิสโตแกรมความกว้างกลีบเลี้ยงดอกไอริส โดยแบ่งขนาดช่วงไม่เท่ากัน

หากแต่ละแท่งในฮิสโตแกรมมีความกว้างไม่เท่ากัน (เช่น ในภาพที่ 10) แกนแนวตั้งในพล็อตจะไม่ได้แสดงจำนวนการเกิดของข้อมูลอีกต่อไปแล้ว แต่มันคือค่าความหนาแน่นของข้อมูลในแต่ละช่วงแทน โดยใช้กฎว่าพื้นที่ของสี่เหลี่ยมทุกแท่ง (ขนาดช่วงคูณความหนาแน่น) รวมกันจะต้องได้เท่ากับหนึ่งหน่วยเสมอ ดังนั้นหากเราขยายขนาดของช่วงโดยที่มันยังครอบคลุมข้อมูลได้เป็นจำนวนเท่าเดิม แท่งข้อมูลแทนช่วงนั้นก็จะมีความสูงลดลงไปนั่นเอง (เช่น ขยายขนาดของช่วงสุดท้ายจาก 3.9-4.4 ไปเป็น 3.9-5.0)

พล็อตกล่อง (box plot)

พล็อตกล่องนั้นไม่ได้แสดงจำนวนการเกิดของข้อมูลเป็นช่วงๆ เหมือนอย่างฮิสโตแกรมแล้ว แต่จะแสดงข้อมูลด้วยค่าสรุปจากควอร์ไทล์แทน ลองพิจารณาตัวอย่างต่อไปนี้

> boxplot(Sepal.Length)

No Description

ภาพที่ 11: พล็อตกล่องความกว้างกลีบเลี้ยงดอกไอริส

ผลลัพธ์ที่ได้จากพล็อตกล่องในภาพที่ 11 มีลักษณะเป็นกล่องสี่เหลี่ยมที่ลอยอยู่และมีขายื่นออกมาข้างบนและล่าง เส้นหนาที่ลากผ่านกลางกล่องคือมัธยฐาน ขอบล่างของกล่องคือควอร์ไทล์ที่ 1 และขอบบนของกล่องคือควอร์ไทล์ที่ 3 สำหรับขาแต่ละข้างที่ยื่นออกมาจากตัวกล่องนั้น สุดปลายขาล่างคือค่าต่ำสุด และสุดปลายขาบนคือค่าสูงสุดนั่นเอง

แม้ว่าพล็อตกล่องจะแสดงการกระจายได้ไม่ละเอียดเท่าฮิสโตแกรม แต่ข้อได้เปรียบของพล็อตกล่องก็คือมันสามารถแสดงข้อมูลหลายๆ ชุดเปรียบเทียบกันได้ง่ายกว่า ดังจะเห็นได้ในภาพถัดไป

> boxplot(iris[1:4])

No Description

ภาพที่ 12: พล็อตกล่องข้อมูลตัวเลขทั้งหมดของดอกไอริส

การเปรียบเทียบการกระจายในภาพที่ 12 แสดงให้เห็นชัดว่า ความกว้างกลีบเลี้ยง (Sepal.Width) มีการกระจายตัวต่ำที่สุด และในทางกลับกัน ความยาวกลีบดอก (Petal.Length) ก็มีการกระจายตัวมากที่สุด นอกจากนี้มันยังบอกตำแหน่งข้อมูลได้อีกด้วยว่า ความยาวกลีบเลี้ยง (Sepal.Length) มีค่ามากกว่าความกว้างกลีบดอก (Petal.Width) เสมอ

จุดเด่นอีกอย่างของพล็อตกล่อง คือมันสามารถแยกให้เห็นค่าผิดปรกติ (outlier) ได้ง่าย โดยแสดงเป็นจุดที่อยู่ไกลเลยขากล่องไปนั่นเอง หลักเกณฑ์สำหรับคัดแยกค่าผิดปรกติ คือ ค่านั้นมีค่าน้อยกว่า/มากกว่าค่าจากควอร์ไทล์ที่ 1/ควอร์ไทล์ที่ 3 อยู่ 1.5 เท่าของพิสัยระหว่างควอร์ไทล์ตามลำดับ ซึ่งอาจตีความหมายได้ว่า ค่านั้นเกิดจากการจดบันทึกข้อมูลผิดเพี้ยน และควรลบมันทิ้งก่อนนำข้อมูลไปประมวลผลต่อไป

เขียนโปรแกรมด้วย R

จากหัวข้อก่อนๆ เราอาจสัมผัสได้ถึงพลังของ R ในการใช้งานคำนวณโต้ตอบแบบกระดาษทด (REPL) ไปแล้ว แต่ในการใช้งานที่ซับซ้อนขึ้นไปนั้น การพิมพ์คำสั่งใหม่ทุกครั้งที่ต้องการคำนวณอาจไม่สะดวกเท่าไรนัก การจัดเรียบเรียงคำสั่งต่างๆ เหล่านั้นลงในไฟล์ แล้วเรียกใช้งานเป็นโปรแกรมแยกที่ทำงานทุกอย่างด้วยตัวเอง หรือนำเข้าฟังก์ชันสำคัญจากไฟล์นั้นมาใช้งานต่อ จึงเป็นทางเลือกที่มีประสิทธิภาพกว่าการพิมพ์คำสั่งใหม่ทุกครั้งเสมอ

เราจะแสดงการเขียนโปรแกรมในภาษา R โดยยกตัวอย่างผ่านปัญหานักสะสมคูปอง ที่ถามคำถามง่ายๆ แต่น่าสนใจว่า นักสะสมคูปองต้องเปิดซองเสี่ยงโชคกี่ครั้งถึงจะเก็บคูปองที่สุ่มแจกได้ครบทุกแบบ

หมายเหตุว่าสำหรับหัวข้อนี้ โค้ดที่แสดงจะไม่มีสัญลักษณ์ > นำหน้า ซึ่งก็คือทุกคำสั่งต่อไปนี้ไม่ได้สั่งงานผ่าน REPL และรอดูคำตอบแล้ว แต่เป็นการเขียนคำสั่งต่างๆ เหล่านี้ลงไปในไฟล์สคริปต์แทน ใครที่ต้องการทดลองตามขอให้สร้างไฟล์ใหม่ชื่อ coupon.R แล้วคัดลอกโค้ดต่างๆ ที่กำลังจะปรากฏในหัวข้อนี้ลงไปยังไฟล์ดังกล่าว

ประกาศฟังก์ชัน

ฟังก์ชันในภาษา R นั้น เมื่อมองอย่างผิวเผินแล้วอาจดูไม่แตกต่างจากฟังก์ชันในภาษาอื่น การประกาศฟังก์ชันทำได้โดยใช้คำสำคัญว่า function ตามด้วยพารามิเตอร์ แล้วจบด้วยเนื้อหาในฟังก์ชันนั้นๆ สิ่งสำคัญที่ต่างออกไปจากภาษาอื่น คือการตั้งชื่อให้ฟังก์ชันจะต้องทำด้วยวิธีเดียวกันกับการประกาศตัวแปรเท่านั้น

random.int <- function(n) { sample.int(n, 1) }

โค้ดข้างต้นสร้างฟังก์ชัน random.int สำหรับสุ่มเลขจำนวนเต็มระหว่าง 1 ถึง n ขึ้นมาหนึ่งตัว

ข้อสังเกตสำหรับการเขียนเนื้อหาฟังก์ชัน นั่นก็คือตอนจบฟังก์ชันไม่จำเป็นต้องสั่ง return เพื่อบอกว่าต้องการคืนค่าใดจากฟังก์ชัน แต่คำสั่งสุดท้ายในฟังก์ชันจะถูกส่งคืนค่าโดยอัตโนมัติ

วนซ้ำจนกว่าจะผิดเงื่อนไข (while loop)

การวนซ้ำจนกว่าจะผิดเงื่อนไข ประกาศโดยคำสำคัญ while และมีโครงสร้างเฉกเช่นภาษาทั่วไป

random.coupon <- function(...) {
  count <- 0
  have.coupon <- logical(...)
  while (!all(have.coupon)) {
    have.coupon[random.int(...)] <- TRUE
    count <- count + 1
  }
  count
}

โค้ดข้างต้นสร้างฟังก์ชัน random.coupon สำหรับนับว่าต้องเปิดซองชิงโชคกี่ครั้ง ถึงจะได้คูปองครบ n แบบ โดยใช้เทคนิคการวนซ้ำแบบ while ในบรรทัดที่ 4 ถึง 7

ข้อสังเกตที่มาคู่กับการวนซ้ำเช่นนี้ คงหนีไม่พ้นการเพิ่มค่าตัวนับในแต่ละรอบที่วน ในภาษาทั่วไปอาจใช้เครื่องหมาย ++ หรือ += เพื่อเพิ่มค่านั้นก็ได้ แต่สำหรับภาษา R ที่ไม่มีตัวดำเนินการทั้งสองให้ใช้แล้ว จึงจำเป็นต้องเขียนเต็มยศ ดังเช่นที่เห็นได้จากบรรทัดที่ 6 ในโค้ดข้างต้น

ข้อสังเกตอื่นๆ อยู่ตรงการประกาศฟังก์ชันที่ระบุพารามิเตอร์ว่า ... ซึ่งมีความหมายว่าฟังก์ชันนี้สามารถรับอาร์กิวเมนต์ได้ไม่จำกัด และจะส่งต่อค่าในอาร์กิวเมนต์ที่รับเข้ามาไปยังฟังก์ชันอื่นๆ ในเนื้อหาฟังก์ชันนี้ที่เรียกใช้ ... เป็นอาร์กิวเมนต์ (ตัวอย่างเช่น ถ้าเรียกใช้ฟังก์ชัน random.coupon(42) ค่า 42 นี้จะถูกส่งไปเป็นเรียกใช้งานฟังก์ชัน logical(42) ต่อไป)

วนซ้ำสำหรับของแต่ละชิ้น (for loop)

การวนซ้ำสำหรับของแต่ละชิ้น ประกาศโดยใช้คำสำคัญ for ตามด้วยตัวแปรสำหรับรับสิ่งของในแต่ละรอบ แล้วตามด้วยเวกเตอร์ของสิ่งของทั้งหมดที่ต้องการวนซ้ำ (เช่นเดียวกับ foreach ในหลายภาษา)

sample.coupon <- function(n, size=10*n) {
  result <- NULL
  for (i in 1:size) {
    result <- c(result, random.coupon(n))
  }
  result
}

โค้ดข้างต้นสร้างฟังก์ชัน sample.coupon สำหรับทดลองหลายๆ ครั้งว่าแต่ละครั้งต้องสุ่มเปิดซองชิงโชคกี่ครั้ง โดยใช้เทคนิคการวนซ้ำแบบ for ในบรรทัดที่ 3 ถึง 5

จุดที่น่าสนใจอยู่ที่การประกาศพารามิเตอร์โดยกำหนดอาร์กิวเมนต์ปริยาย (default argument) ที่สามารถอ้างอิงไปยังอาร์กิวเมนต์อื่นของฟังก์ชันนี้ได้ทันที

ทำหรือไม่ตามแต่เงื่อนไขจะกำหนด (if-else)

การตรวจสอบถ้า-แล้ว สามารถใช้คำสำคัญได้เช่นเดียวกันกับภาษาทั่วไป คือ if else if และ else

if (!interactive()) {
  args <- commandArgs(TRUE)
  if (length(args) == 1) {
    filename <- "sample-coupon.png"
  } else if (length(args) == 2) {
    filename <- args[2]
  } else {
    stop("wrong number of argument!")
  }
  png(filename, width=800, height=800)
  arg.list <- list(x=as.numeric(args[1]))
  do.call(hist, list(substitute(sample.coupon(x), arg.list)))
  dev.off()
}

โค้ดข้างต้นจะตรวจสอบว่าโปรแกรมนี้ถูกเรียกใช้งานผ่าน REPL หรือไม่ ถ้าไม่ใช่แล้วจึงตรวจสอบต่อว่าการเรียกใช้งานมีอาร์กิวเมนต์ใดบ้าง หากอาร์กิวเมนต์ถูกต้องจะสร้างไฟล์ภาพฮิสโตแกรมของการทดลองหาคำตอบของคำถามนักสะสมคูปองขึ้นมา

เรียกใช้โปรแกรม

ถึงตอนนี้หากใครพิมพ์ตามโค้ดข้างต้นทั้งหมด ก็จะได้ไฟล์สคริปต์ภาษา R มาหนึ่งโปรแกรมแล้ว (ส่วนใครไม่ได้พิมพ์ตาม แต่ยังอยากทดลองต่อ สามารถดาวน์โหลดโค้ดข้างต้นได้ที่นี่) เราสามารถเรียกใช้โปรแกรมดังกล่าวผ่าน terminal/cmd ได้ดังนี้

$ Rscript coupon.R 30

หรือจะนำเข้าฟังก์ชันจากไฟล์ดังกล่าวเข้ามายัง REPL เพื่อประยุกต์ใช้แบบโต้ตอบก็ย่อมได้

> source("PATH/TO/SCRIPT/coupon.R")
> hist(sample.coupon(30))

ไม่ว่าจะเรียกด้วยวิธีไหน ผลลัพธ์ที่ได้จะมีหน้าตาหนีไม่พ้นภาพที่ 13 ไปเท่าใดนัก (แม้ว่าอัลกอริทึมดังกล่าวจะอาศัยการสุ่มก็ตาม)

No Description

ภาพที่ 13: ฮิสโตแกรมคำตอบจากการทดลองปัญหานักสะสมคูปอง เมื่อมีคูปองแตกต่างกัน 30 แบบ

ดาวน์โหลดแพ็กเกจเสริมความสามารถ

จากหัวข้อที่ผ่านๆ มา หลายคนอาจมองเห็นแนวทางอันหลากหลายที่จะประยุกต์ใช้ภาษา R เพื่อช่วยแก้ปัญหาที่ต้องข้องเกี่ยวกับข้อมูลกันไปแล้ว แต่ความสามารถเท่านั้นจากตัวภาษา R เพียงอย่างเดียว ก็ยังนับว่าน้อยนิดเมื่อเทียบกับแพ็กเกจเพิ่มความสามารถที่ชุมชนอุทิศให้

โดยสถานที่จัดเก็บแพ็กเกจหลักอย่างเป็นทางการของภาษาดังกล่าวมีชื่อว่า CRAN (Comprehensive R Archive Network) ซึ่งปัจจุบันได้โฮสต์แพ็กเกจเสริมความสามารถกว่าหนึ่งหมื่นแพ็กเกจให้เลือกใช้งาน

วิธีการดาวน์โหลดแพ็กเกจเหล่านี้ก็ไม่ยาก เพียงแค่มีชื่อแพ็กเกจเหล่านั้นไว้ในใจ แล้วเปิด REPL ขึ้นมาเรียกฟังก์ชันต่อไปนี้

> install.pacakges("packageName")

โดยเปลี่ยน "packageName" เป็นชื่อแพ็กเกจที่ต้องการ หลังจากนั้นจะมีตัวเลือก mirror ของประเทศต่างๆ ขึ้นมา เลือกประเทศที่อยู่ใกล้ที่สุดเพื่อความเร็วในการดาวน์โหลด แล้วรอโปรแกรมดาวน์โหลดและติดตั้งแพ็กเกจเหล่านั้น

หลังจากติดตั้งเสร็จสิ้น เมื่อต้องการนำเข้าแพ็คเกจเหล่านั้นมาใช้งาน ก็ทำได้โดย

> library(packageName)

ต่อไปนี้จะขอแนะนำแพ็กเกจที่น่าสนใจจำนวนหนึ่ง (หากสั่ง library() แล้วพบข้อความแสดงความผิดพลาด อย่าลืมว่าต้องดาวน์โหลดแพ็กเกจเหล่านั้นก่อน)

กราฟเครือข่าย

กราฟเครือข่ายสามารถบ่งบอกความสัมพันธ์ในข้อมูลผ่านจุด (node) และเส้นเชื่อม (edge) ตัวอย่างข้อมูลที่เราคุ้นเคยกันดีคงหนีไม่พ้นความสัมพันธ์บนเครือข่ายสังคมออนไลน์ แต่ข้อมูลชนิดอื่นๆ ก็อาจถูกแปลงให้เป็นกราฟเครือข่ายได้ เช่น แผนที่ทางหลวง

> library(igraph)
> plot(make_graph("petersen"))

No Description

ภาพที่ 14: กราฟเครือข่ายที่มีการเชื่อมโยงแบบกราฟปีเตอร์เซน

ทำนายอนาคต

สำหรับข้อมูลที่มีแกนหนึ่งเป็นแกนเวลา เช่น ข้อมูลดินฟ้าอากาศ จำนวนประชากร หรือกระทั่งราคาหุ้น หากข้อมูลเหล่านั้นมีรูปแบบที่เกิดซ้ำมากพอ เราก็อาจทำนายอนาคตของข้อมูลนั้นได้

> library(forecast)
> plot(forecast(nottem))

No Description

ภาพที่ 15: พยากรณ์อุณหภูมิของเมืองนอตทิงแฮม (เส้นสีน้ำเงิน)

แผนที่โลก

นอกจากการพล็อตข้อมูลเชิงสถิติแล้ว R ยังสามารถพล็อตแผนที่โลกได้อีกด้วย

> library(maps)
> library(mapdata)
> map("world2Hires", xlim=c(110,190), ylim=c(-50,0))
> points(quakes[2:1], pch=".", col="red")

No Description

ภาพที่ 16: แผนที่โลกพร้อมตำแหน่งเกิดแผ่นดินไหว (จุดสีแดง) ใกล้ฟีจี

สรุป

บทความตอนนี้แสดงให้เห็นความสามารถสำคัญๆ ในภาษา R โดยไล่ตั้งแต่เรื่องพื้นฐานอย่างชนิดตัวแปร ไปจนถึงการดาวน์โหลดแพ็กเกจเพิ่มความสามารถและเขียนโปรแกรมมาแก้ไขปัญหาจริง ใครที่อดทนอ่านและทำความเข้าใจได้จนถึงบรรทัดนี้ ก็คงพูดไม่ผิดว่าคุณรู้จักและสามารถเขียนโค้ดในภาษา R ได้แล้ว

อย่างไรก็ตาม บทความนี้ก็ไม่ได้กล่าวถึงทุกเรื่องที่ควรรู้ในภาษา R และตัวอย่างการคำนวณทางสถิติก็ยังอยู่ในระดับขั้นพื้นฐานเป็นอย่างมาก ใครที่ต้องการนำภาษา R ไปประยุกต์ใช้งานอย่างจริงจัง หรือวาดฝันไว้ว่าต้องการเป็นนักวิทยาศาสตร์ข้อมูล (data scientist) ก็คงต้องบอกว่าจงตั้งใจศึกษาตัวภาษาและวิชาสถิติเพิ่มเติม

เพราะการเรียนรู้ไม่มีวันสิ้นสุด และการเรียนรู้จะบรรลุผลสูงสุดเมื่อสามารถประยุกต์ใช้ความรู้นั้นเพื่อสร้างสรรค์ประโยชน์ได้

ภาคผนวก

ติดตั้งโปรแกรมบนเครื่อง

การติดตั้ง R นั้นสามารถทำได้อย่างง่ายดาย เพียงแค่เข้าไปยังเว็บไซต์หลักของภาษา R แล้วตามลิงก์ที่เขียนคำว่า "download R" จะพบกับรายชื่อ mirror ต่างๆ ทั่วโลกที่ทำสำเนาโครงการภาษา R ไว้ เลือกตัวเลือกที่อยู่ใกล้เคียงตำแหน่งปัจจุบันมากที่สุดเพื่อความเร็วในการดาวน์โหลด (สำหรับประเทศไทย มี mirror ของมหาวิทยาลัยสงขลานครินทร์สำหรับสำเนาดังกล่าว) เมื่อเข้าไปแล้ว จะพบกับตัวเลือกให้ดาวน์โหลดโปรแกรมได้ทั้งบน Windows, OS X และ Linux โดยมีรายละเอียดต่างกันเล็กน้อย ดังนี้

  • Windows เลือกดาวน์โหลดไฟล์ติดตั้ง .exe จากแฟ้ม "base" มาติดตั้งก็เพียงพอแล้วสำหรับบทความนี้
  • OS X ดาวน์โหลดไฟล์ติดตั้ง .pkg รุ่นใหม่ล่าสุดจากหน้าดาวน์โหลดของ OS X มาติดตั้งได้เลย
  • Linux เนื่องจากความหลากหลายทางดิสโทร การหาไฟล์ติดตั้งที่ถูกต้องจากหน้านี้อาจเป็นเรื่องยุ่งยากเกินจำเป็น เราอาจเลือกสั่งเพียงคำสั่งต่อไปนี้ผ่าน terminal แทน
    • apt install r-base สำหรับดิสโทร Debian/Ubuntu
    • yum install R สำหรับดิสโทร Red Hat/Fedora

หลังจากติดตั้งโปรแกรมสำเร็จแล้ว ผู้ใช้ OS X/Linux ไม่ต้องทำอะไรต่อ ส่วนผู้ใช้ Windows ควรแก้ไขตัวแปร PATH โดยเพิ่มตำแหน่งที่ติดตั้งโปรแกรม R (เช่น C:\Program Files\R\R-3.4.1\bin\x64) ต่อท้ายตัวแปรนั้น เพื่อให้สามารถเรียกโปรแกรม R ผ่าน cmd ได้ในภายหลัง

Get latest news from Blognone

Comments

By: panurat2000
ContributorSymbianUbuntuIn Love
on 28 July 2017 - 14:39 #1000178
panurat2000's picture

เพียงแค่ให้ระวังความแตกแตกอันเป็นเอกลักษณ์

ความแตกแตก ?

 ที่ตามด้วยเคอร์เซอร์กระพริบรอรับคำสั่ง

กระพริบ => กะพริบ

By: neizod
ContributorTraineeIn Love
on 28 July 2017 - 15:07 #1000181 Reply to:1000178
neizod's picture

เรียบร้อยครับ

By: topty
Contributor
on 28 July 2017 - 22:09 #1000224 Reply to:1000178

เราจะพบกับข้อความต้อนรับที่บอกรายระเอียด

รายระเอียด => รายละเอียด

ค่าผลลัพธ์ที่เห็นเป็นตัวแรกในแถวนี้เป็นผลลัพธ์ตัวที่เท่าไหร่จากผลลัพธ์ทั้งหมด

การหาฐานนิยมด้วยวิธีพื้นฐานมักไม่ให้ข้อสรุปที่เกิดประโยชน์เท่าไหร่

เท่าไหร่ => เท่าไร

ส่วนอีกร้านอาจขายตั้งแต่สากกระเบือยันเรือดำน้ำก็เป็นได้

สากกระเบือ => สากกะเบือ

By: neizod
ContributorTraineeIn Love
on 29 July 2017 - 14:58 #1000282 Reply to:1000224
neizod's picture

เรียบร้อยครับ

By: Jedt3D
iPhoneAndroidUbuntuWindows
on 28 July 2017 - 19:03 #1000212
Jedt3D's picture

login เข้ามาเพื่อของคุณโดยเฉพาะเลย ขอบคุณมากนะครับที่เขียนบทความสรุปเรื่องภาษา R มาเสียยาวเลยครับ เดี๋ยวมาอ่านต่อครับผม ตอนนี้เอาไปแชร์ก่อน :D

By: itpcc
ContributoriPhoneRed HatUbuntu
on 28 July 2017 - 21:18 #1000221
itpcc's picture

ขอบคุณครับ
เคยใช้ตอนเรียนวิชา TextMining ใช้งานสะดวกดีครับ ติดแต่ R ภาษามันออกประหลาดๆ ไปหน่อย แต่โดยรวมก็ถือว่าใช้สะดวกดีครับ Lib โหลดง่ายมาก


บล็อกส่วนตัวที่อัพเดตตามอารมณ์และความขยัน :P

By: MrThursday
ContributorRed HatUbuntuWindows
on 29 July 2017 - 01:11 #1000229

Ubuntu add repo เองโลดครับ ไม่งั้นจะได้ version ไม่ล่าสุดมา

By: Architec
ContributorWindows PhoneAndroidWindows
on 29 July 2017 - 06:18 #1000234

ใครอยากเรียนแบบกึ่งละเอียดก็ตามลิงค์นี้ครับ(สนุกมาก) http://tryr.codeschool.com/

By: LagSeeN
ContributorWindows PhoneWindows
on 29 July 2017 - 07:12 #1000235
LagSeeN's picture

น่าลองครับ ขอบคุณครับ

By: horakung
iPhoneAndroidWindows
on 29 July 2017 - 08:36 #1000238
horakung's picture

นั่งลองที่ datacamp อยู่พอดีเลย

By: Jirawat
Android
on 29 July 2017 - 18:50 #1000303
Jirawat's picture

นึกว่าอ่านงานวิจัย เดี๋ยวมาต่อครับ เขียนได้สุดยอด

By: nario
AndroidIn Love
on 29 July 2017 - 21:23 #1000314
nario's picture

ขอบคุณที่แบ่งปันความรู้ครับผม

By: TaishoEra on 30 July 2017 - 01:42 #1000332

ดีกว่า Python อย่างไรบ้าง

By: MrThursday
ContributorRed HatUbuntuWindows
on 30 July 2017 - 21:06 #1000378 Reply to:1000332

ทำ stat ละเมียดกว่าครับ

By: khao_lek on 2 August 2017 - 15:54 #1000860 Reply to:1000332

R = เกิดมาเพื่อสถิติ
Python = เกิดมาเพื่อโปรแกรมมิ่ง

By: koalaz
ContributorAndroid
on 31 July 2017 - 00:28 #1000388
koalaz's picture

ปีที่แล้ว Google รับพนักงานมี requirement เรื่อง R โดยเฉพาะเลยล็อตนึง

วิทยากรของกูเกิ้ลเองก็ยังบอกว่าเขาใช้ R ในขั้นสุดท้ายของ Analytic อยู่ประมาณว่า Automate tool ยังไงยังต้องจบขั้นสุดท้ายใน R

หรือจะดีจะร้ายยังไง สุดท้ายความจริงที่ว่าเวลาเจอข้อมูลก้อนใหญ่ๆ มันไม่มีปัญหาเหมือน Spss แค่นี้ก็น่าสนใจแล้ว


Shut up and ヽ༼ຈل͜ຈ༽ノ raise your dongers ヽ༼ຈل͜ຈ༽ノ

By: Jirawat
Android
on 31 July 2017 - 06:21 #1000395
Jirawat's picture

เห็นstyle แล้วนึกถึง mathlab

By: btoy
ContributorAndroidWindows
on 31 July 2017 - 08:38 #1000400
btoy's picture

ขอบคุณมากๆเลยครับ เป็นประโยชน์กับผู้ที่ไม่รู้จักภาษานี้มากๆเลย เยี่ยมมาก


..: เรื่อยไป

By: gift099
Windows PhoneAndroidWindowsIn Love
on 31 July 2017 - 11:40 #1000455

ขอบคุณครับ เพิ่งรู้จักภาษา R ก็วันนี้แหล

อ่านช่วงแรกๆ นึกว่า กำลังเขียน matlab

By: loptar on 31 July 2017 - 12:25 #1000465
loptar's picture

เป็นประโยชน์มากครับ ขอบคุณ

By: ukozaa
Windows
on 31 July 2017 - 14:20 #1000498
ukozaa's picture

ใช้ได้หลากหลายดีครับ
ที่สำคัญมันฟรี

By: เพชร
iPhoneWindows PhoneAndroidUbuntu
on 1 August 2017 - 07:37 #1000609
เพชร's picture

ขอบคุณมากครับ อ่านอยู่นานเลยครับ :)

By: gab
Windows PhoneAndroidWindows
on 1 August 2017 - 20:06 #1000750
gab's picture

ขอบคุณบทความมาก เขียนดีครับ
ใช้เวลาอ่านอยู่นานเหมือนกัน นึกไม่ออกเลยว่าใช้เวลาและพลังในการเขียนขนาดไหน

By: neizod
ContributorTraineeIn Love
on 3 August 2017 - 05:22 #1000950 Reply to:1000750
neizod's picture

เดือนนิดๆ ครับ

By: prowin
iPhoneWindows PhoneAndroidWindows
on 2 August 2017 - 12:38 #1000833
prowin's picture

ขอบคุณมากครับ