เพิ่ม Data Munging ด้วย R
เผยแพร่แล้ว: 2022-03-11ภาษา R มักถูกมองว่าเป็นภาษาสำหรับนักสถิติและนักวิทยาศาสตร์ข้อมูล เมื่อนานมาแล้ว เรื่องนี้ส่วนใหญ่เป็นความจริง อย่างไรก็ตาม ในช่วงหลายปีที่ผ่านมาความยืดหยุ่นของ R ผ่านแพ็คเกจทำให้ R เป็นภาษาที่ใช้งานทั่วไปมากขึ้น R เป็นโอเพ่นซอร์สในปี 1995 และตั้งแต่นั้นมา ที่เก็บของแพ็คเกจ R ก็เติบโตอย่างต่อเนื่อง เมื่อเปรียบเทียบกับภาษาอย่าง Python แล้ว R นั้นอิงจากข้อมูลเป็นหลัก
เมื่อพูดถึงข้อมูล ข้อมูลแบบตารางควรได้รับความสนใจเป็นพิเศษ เนื่องจากเป็นข้อมูลประเภทหนึ่งที่ใช้บ่อยที่สุด เป็นประเภทข้อมูลที่สอดคล้องกับโครงสร้างตารางที่รู้จักในฐานข้อมูล โดยที่แต่ละคอลัมน์สามารถเป็นประเภทที่แตกต่างกันได้ และประสิทธิภาพการประมวลผลของประเภทข้อมูลเฉพาะนั้นเป็นปัจจัยสำคัญสำหรับแอปพลิเคชันจำนวนมาก
ในบทความนี้ เราจะนำเสนอวิธีการบรรลุการแปลงข้อมูลแบบตารางอย่างมีประสิทธิภาพ หลายคนที่ใช้ R อยู่แล้วสำหรับการเรียนรู้ของเครื่องไม่ทราบว่าการรวบรวมข้อมูลสามารถทำได้เร็วขึ้นใน R และพวกเขาไม่จำเป็นต้องใช้เครื่องมืออื่นสำหรับมัน
โซลูชันประสิทธิภาพสูงใน R
Base R เปิดตัวคลาส data.frame ในปี 1997 ซึ่งใช้ S-PLUS มาก่อน R data.frame แตกต่างจากฐานข้อมูลที่ใช้ทั่วไปซึ่งเก็บข้อมูลทีละแถว R data.frame เก็บข้อมูลในหน่วยความจำเป็นโครงสร้างเชิงคอลัมน์ จึงทำให้มีประสิทธิภาพแคชมากขึ้นสำหรับการดำเนินการของคอลัมน์ซึ่งเป็นเรื่องปกติในการวิเคราะห์ นอกจากนี้ แม้ว่า R จะเป็นภาษาโปรแกรมที่ใช้งานได้จริง แต่ก็ไม่ได้บังคับใช้ภาษานั้นกับนักพัฒนา โอกาสทั้งสองได้รับการแก้ไขอย่างดีด้วย data.table R ซึ่งมีอยู่ในที่เก็บ CRAN มันทำงานค่อนข้างเร็วเมื่อจัดกลุ่ม และโดยเฉพาะอย่างยิ่งหน่วยความจำมีประสิทธิภาพโดยระมัดระวังเกี่ยวกับการสร้างชุดย่อยของข้อมูลระดับกลาง เช่น การสร้างเฉพาะคอลัมน์ที่จำเป็นสำหรับงานบางอย่าง นอกจากนี้ยังหลีกเลี่ยงการคัดลอกที่ไม่จำเป็นผ่านความหมายอ้างอิงในขณะที่เพิ่มหรืออัปเดตคอลัมน์ แพ็คเกจรุ่นแรกได้รับการเผยแพร่ในเดือนเมษายน 2549 ซึ่งช่วยปรับปรุงประสิทธิภาพของ data.frame ในขณะนั้นอย่างมาก คำอธิบายแพ็คเกจเริ่มต้นคือ:
แพ็คเกจนี้ทำน้อยมาก เหตุผลเดียวสำหรับการมีอยู่คือสมุดปกขาวระบุว่า data.frame ต้องมีชื่อแถว แพ็คเกจนี้กำหนดคลาส data.table ใหม่ซึ่งทำงานเหมือนกับ data.frame แต่ใช้หน่วยความจำน้อยกว่าถึง 10 เท่า และสร้าง (และคัดลอก) ได้เร็วกว่าถึง 10 เท่า นอกจากนี้ยังใช้โอกาสในการอนุญาต subset() และ with() เช่นนิพจน์ภายใน [] โค้ดส่วนใหญ่ถูกคัดลอกมาจากฟังก์ชันพื้นฐานโดยที่โค้ดที่จัดการ row.names ถูกลบ
ตั้งแต่นั้นมา ทั้งการใช้งาน data.frame และ data.table ก็ได้รับการปรับปรุง แต่ data.table ยังคงเร็วกว่า base R อย่างไม่น่าเชื่อ อันที่จริง data.table ไม่ใช่แค่เร็วกว่า base R แต่ดูเหมือนว่าจะเป็นหนึ่งเดียว ของเครื่องมือโต้แย้งข้อมูลโอเพนซอร์ซที่เร็วที่สุดที่มีอยู่ แข่งขันกับเครื่องมืออย่าง Python Pandas และฐานข้อมูลการจัดเก็บแบบเสาหรือแอป Big Data เช่น Spark ประสิทธิภาพเหนือโครงสร้างพื้นฐานที่ใช้ร่วมกันแบบกระจายยังไม่ได้รับการเปรียบเทียบ แต่ความสามารถในการมีแถวได้ถึงสองพันล้านแถวในอินสแตนซ์เดียวจะช่วยให้มีแนวโน้มที่ดี ประสิทธิภาพที่โดดเด่นควบคู่ไปกับฟังก์ชันต่างๆ นอกจากนี้ ด้วยความพยายามล่าสุดในการขนานชิ้นส่วนที่ต้องใช้เวลามากเพื่อให้ได้ประสิทธิภาพที่เพิ่มขึ้น ทิศทางเดียวในการผลักดันขีดจำกัดประสิทธิภาพจึงค่อนข้างชัดเจน
ตัวอย่างการแปลงข้อมูล
การเรียนรู้ R นั้นง่ายขึ้นเล็กน้อยเนื่องจากการทำงานแบบโต้ตอบ เราจึงสามารถทำตามตัวอย่างทีละขั้นตอนและดูผลลัพธ์ของแต่ละขั้นตอนได้ตลอดเวลา ก่อนที่เราจะเริ่ม เรามาติดตั้งแพ็คเกจ data.table จากที่เก็บ CRAN กันก่อน
install.packages("data.table") คำแนะนำ ที่เป็นประโยชน์ : เราสามารถเปิดคู่มือของฟังก์ชันใดๆ ก็ได้ เพียงแค่พิมพ์ชื่อโดยใช้เครื่องหมายคำถามนำหน้า นั่นคือ ?install.packages
กำลังโหลดข้อมูลลงใน R
มีแพ็คเกจมากมายสำหรับการดึงข้อมูลจากรูปแบบและฐานข้อมูลที่หลากหลาย ซึ่งมักจะรวมถึงไดรเวอร์ดั้งเดิม เราจะโหลดข้อมูลจาก ไฟล์ CSV ซึ่งเป็นรูปแบบที่พบบ่อยที่สุดสำหรับข้อมูลตารางดิบ ไฟล์ที่ใช้ในตัวอย่างต่อไปนี้สามารถพบได้ที่นี่ เราไม่ต้องกังวลเกี่ยวกับประสิทธิภาพการอ่าน CSV เนื่องจากฟังก์ชัน fread ได้รับการปรับให้เหมาะสมอย่างมาก
ในการใช้งานฟังก์ชั่นใดๆ จากแพ็คเกจ เราจำเป็นต้องโหลดมันด้วยการเรียกไล library รี่
library(data.table) DT <- fread("flights14.csv") print(DT) ## year month day dep_delay arr_delay carrier origin dest air_time ## 1: 2014 1 1 14 13 AA JFK LAX 359 ## 2: 2014 1 1 -3 13 AA JFK LAX 363 ## 3: 2014 1 1 2 9 AA JFK LAX 351 ## 4: 2014 1 1 -8 -26 AA LGA PBI 157 ## 5: 2014 1 1 2 1 AA JFK LAX 350 ## --- ## 253312: 2014 10 31 1 -30 UA LGA IAH 201 ## 253313: 2014 10 31 -5 -14 UA EWR IAH 189 ## 253314: 2014 10 31 -8 16 MQ LGA RDU 83 ## 253315: 2014 10 31 -4 15 MQ LGA DTW 75 ## 253316: 2014 10 31 -5 1 MQ LGA SDF 110 ## distance hour ## 1: 2475 9 ## 2: 2475 11 ## 3: 2475 19 ## 4: 1035 7 ## 5: 2475 13 ## --- ## 253312: 1416 14 ## 253313: 1400 8 ## 253314: 431 11 ## 253315: 502 11 ## 253316: 659 8 หากข้อมูลของเราไม่มีแบบจำลองที่ดีสำหรับการประมวลผลต่อไป เนื่องจากจำเป็นต้องเปลี่ยนรูปแบบจากรูปแบบยาวไปกว้างหรือกว้างไปยาว (เรียกอีกอย่างว่า pivot และ unpivot ) เราอาจดูที่ ?dcast และ ?melt รู้จักจากแพ็คเกจ reshape2 อย่างไรก็ตาม data.table ใช้วิธีที่รวดเร็วและมีประสิทธิภาพหน่วยความจำสำหรับคลาส data.table/data.frame
การสืบค้นด้วย data.table Syntax
หากคุณคุ้นเคยกับ data.frame
แบบสอบถาม data.table คล้ายกับการสืบค้น data.frame ในขณะที่กรองในอาร์กิวเมนต์ i เราสามารถใช้ชื่อคอลัมน์ได้โดยตรงโดยไม่จำเป็นต้องเข้าถึงด้วยเครื่องหมาย $ เช่น df[df$col > 1, ] เมื่อระบุอาร์กิวเมนต์ถัดไป j เราจัดเตรียมนิพจน์เพื่อประเมินในขอบเขตของ data.table ของเรา ในการส่งผ่านอาร์กิวเมนต์ j ที่ไม่ใช่นิพจน์ ให้ใช้ with=FALSE อาร์กิวเมนต์ที่สาม ไม่มีอยู่ในเมธอด data.frame กำหนดกลุ่ม ทำให้นิพจน์ใน j ถูกประเมินโดยกลุ่ม
# data.frame DF[DF$col1 > 1L, c("col2", "col3")] # data.table DT[col1 > 1L, .(col2, col3), ...] # by group using: `by = col4`หากคุณคุ้นเคยกับฐานข้อมูล
แบบสอบถาม data.table ในหลาย ๆ ด้านสอดคล้องกับการสืบค้น SQL ที่ผู้คนจำนวนมากอาจคุ้นเคย DT ด้านล่างแสดงถึงวัตถุ data.table และสอดคล้องกับคำสั่ง SQLs FROM
DT[ i = where, j = select | update, by = group by] [ having, ... ] [ order by, ... ] [ ... ] ... [ ... ] การเรียงลำดับแถวและการจัดลำดับคอลัมน์ใหม่
การเรียงลำดับข้อมูลเป็นการเปลี่ยนแปลงที่สำคัญสำหรับอนุกรมเวลา และยังนำเข้าสำหรับการดึงข้อมูลและการนำเสนออีกด้วย การเรียงลำดับสามารถทำได้โดยระบุเวกเตอร์จำนวนเต็มของลำดับแถวให้กับอาร์กิวเมนต์ i เช่นเดียวกับ data.frame อาร์กิวเมนต์แรกใน order(carrier, -dep_delay) จะเลือกข้อมูลในลำดับจากน้อยไปมากในฟิลด์ carrier และลำดับจากมากไปน้อยในการวัด dep_delay อาร์กิวเมนต์ที่สอง j ตามที่อธิบายไว้ในส่วนก่อนหน้า กำหนดคอลัมน์ (หรือนิพจน์) ที่จะส่งคืนและลำดับของคอลัมน์
ans <- DT[order(carrier, -dep_delay), .(carrier, origin, dest, dep_delay)] head(ans) ## carrier origin dest dep_delay ## 1: AA EWR DFW 1498 ## 2: AA JFK BOS 1241 ## 3: AA EWR DFW 1071 ## 4: AA EWR DFW 1056 ## 5: AA EWR DFW 1022 ## 6: AA EWR DFW 989 ในการเรียงลำดับข้อมูลใหม่โดยการอ้างอิง แทนที่จะทำการสืบค้นข้อมูลในลำดับเฉพาะ เราใช้ฟังก์ชัน set*
setorder(DT, carrier, -dep_delay) leading.cols <- c("carrier","dep_delay") setcolorder(DT, c(leading.cols, setdiff(names(DT), leading.cols))) print(DT) ## carrier dep_delay year month day arr_delay origin dest air_time ## 1: AA 1498 2014 10 4 1494 EWR DFW 200 ## 2: AA 1241 2014 4 15 1223 JFK BOS 39 ## 3: AA 1071 2014 6 13 1064 EWR DFW 175 ## 4: AA 1056 2014 9 12 1115 EWR DFW 198 ## 5: AA 1022 2014 6 16 1073 EWR DFW 178 ## --- ## 253312: WN -12 2014 3 9 -21 LGA BNA 115 ## 253313: WN -13 2014 3 10 -18 EWR MDW 112 ## 253314: WN -13 2014 5 17 -30 LGA HOU 202 ## 253315: WN -13 2014 6 15 10 LGA MKE 101 ## 253316: WN -13 2014 8 19 -30 LGA CAK 63 ## distance hour ## 1: 1372 7 ## 2: 187 13 ## 3: 1372 10 ## 4: 1372 6 ## 5: 1372 7 ## --- ## 253312: 764 16 ## 253313: 711 20 ## 253314: 1428 17 ## 253315: 738 20 ## 253316: 397 16ส่วนใหญ่แล้ว เราไม่ต้องการ ทั้ง ชุดข้อมูลดั้งเดิมและชุดข้อมูลที่เรียงลำดับ/จัดเรียง โดยค่าเริ่มต้น ภาษา R ซึ่งคล้ายกับภาษาการเขียนโปรแกรมเชิงฟังก์ชันอื่นๆ จะส่งคืนข้อมูลที่จัดเรียงเป็นออบเจกต์ใหม่ ดังนั้นจึงต้องใช้หน่วยความจำมากเป็นสองเท่าของการจัดเรียงตามข้อมูลอ้างอิง
แบบสอบถามชุดย่อย
มาสร้างชุดข้อมูลย่อยสำหรับต้นทางของเที่ยวบิน “JFK” และเดือนตั้งแต่ 6 ถึง 9 ในอาร์กิวเมนต์ที่สอง เราจะย่อยผลลัพธ์เป็นคอลัมน์ที่แสดง โดยเพิ่มตัวแปรที่คำนวณได้หนึ่ง sum_delay
ans <- DT[origin == "JFK" & month %in% 6:9, .(origin, month, arr_delay, dep_delay, sum_delay = arr_delay + dep_delay)] head(ans) ## origin month arr_delay dep_delay sum_delay ## 1: JFK 7 925 926 1851 ## 2: JFK 8 727 772 1499 ## 3: JFK 6 466 451 917 ## 4: JFK 7 414 450 864 ## 5: JFK 6 411 442 853 ## 6: JFK 6 333 343 676 โดยค่าเริ่มต้น เมื่อตั้งค่าย่อยชุดข้อมูลบน data.table คอลัมน์เดียว จะสร้างดัชนีสำหรับคอลัมน์นั้นโดยอัตโนมัติ ซึ่งส่งผลให้ได้รับคำตอบตามเวลาจริงสำหรับการเรียกการกรองเพิ่มเติมในคอลัมน์นั้น
อัปเดตชุดข้อมูล
การเพิ่มคอลัมน์ใหม่โดยการอ้างอิงจะดำเนินการโดยใช้ตัวดำเนินการ := ซึ่งจะกำหนดตัวแปรให้กับชุดข้อมูลที่มีอยู่ ซึ่งจะหลีกเลี่ยงการคัดลอกชุดข้อมูลในหน่วยความจำ ดังนั้นเราจึงไม่จำเป็นต้องกำหนดผลลัพธ์ให้กับตัวแปรใหม่แต่ละตัว
DT[, sum_delay := arr_delay + dep_delay] head(DT) ## carrier dep_delay year month day arr_delay origin dest air_time ## 1: AA 1498 2014 10 4 1494 EWR DFW 200 ## 2: AA 1241 2014 4 15 1223 JFK BOS 39 ## 3: AA 1071 2014 6 13 1064 EWR DFW 175 ## 4: AA 1056 2014 9 12 1115 EWR DFW 198 ## 5: AA 1022 2014 6 16 1073 EWR DFW 178 ## 6: AA 989 2014 6 11 991 EWR DFW 194 ## distance hour sum_delay ## 1: 1372 7 2992 ## 2: 187 13 2464 ## 3: 1372 10 2135 ## 4: 1372 6 2171 ## 5: 1372 7 2095 ## 6: 1372 11 1980 ในการเพิ่มตัวแปรพร้อมกัน เราสามารถใช้ DT[, := (sum_delay = arr_delay + dep_delay)] ไวยากรณ์ คล้ายกับ .(sum_delay = arr_delay + dep_delay) เมื่อทำการสืบค้นจากชุดข้อมูล
เป็นไปได้ที่จะกำหนดย่อยโดยการอ้างอิง อัปเดตเฉพาะแถวที่อยู่ในตำแหน่ง เพียงแค่รวมกับอาร์กิวเมนต์ i
DT[origin=="JFK", distance := NA] head(DT) ## carrier dep_delay year month day arr_delay origin dest air_time ## 1: AA 1498 2014 10 4 1494 EWR DFW 200 ## 2: AA 1241 2014 4 15 1223 JFK BOS 39 ## 3: AA 1071 2014 6 13 1064 EWR DFW 175 ## 4: AA 1056 2014 9 12 1115 EWR DFW 198 ## 5: AA 1022 2014 6 16 1073 EWR DFW 178 ## 6: AA 989 2014 6 11 991 EWR DFW 194 ## distance hour sum_delay ## 1: 1372 7 2992 ## 2: NA 13 2464 ## 3: 1372 10 2135 ## 4: 1372 6 2171 ## 5: 1372 7 2095 ## 6: 1372 11 1980ข้อมูลรวม
ในการรวมข้อมูล เราจัดเตรียมอาร์กิวเมนต์ที่สาม by วงเล็บเหลี่ยม จากนั้นใน j เราจำเป็นต้องจัดเตรียมการเรียกฟังก์ชันรวม เพื่อให้สามารถรวมข้อมูลได้จริง สัญลักษณ์ .N ที่ใช้ในอาร์กิวเมนต์ j สอดคล้องกับจำนวนการสังเกตทั้งหมดในแต่ละกลุ่ม ตามที่กล่าวไว้ก่อนหน้านี้ การรวมสามารถรวมกับชุดย่อยในแถวและการเลือกคอลัมน์
ans <- DT[, .(m_arr_delay = mean(arr_delay), m_dep_delay = mean(dep_delay), count = .N), .(carrier, month)] head(ans) ## carrier month m_arr_delay m_dep_delay count ## 1: AA 10 5.541959 7.591497 2705 ## 2: AA 4 1.903324 3.987008 2617 ## 3: AA 6 8.690067 11.476475 2678 ## 4: AA 9 -1.235160 3.307078 2628 ## 5: AA 8 4.027474 8.914054 2839 ## 6: AA 7 9.159886 11.665953 2802 บ่อยครั้ง เราอาจจำเป็นต้องเปรียบเทียบค่าของแถวหนึ่งกับผลรวมของแถวนั้นในกลุ่ม ใน SQL เราใช้ การรวมบนพาร์ติชันโดย : AVG(arr_delay) OVER (PARTITION BY carrier, month)
ans <- DT[, .(arr_delay, carrierm_mean_arr = mean(arr_delay), dep_delay, carrierm_mean_dep = mean(dep_delay)), .(carrier, month)] head(ans) ## carrier month arr_delay carrierm_mean_arr dep_delay carrierm_mean_dep ## 1: AA 10 1494 5.541959 1498 7.591497 ## 2: AA 10 840 5.541959 848 7.591497 ## 3: AA 10 317 5.541959 338 7.591497 ## 4: AA 10 292 5.541959 331 7.591497 ## 5: AA 10 322 5.541959 304 7.591497 ## 6: AA 10 306 5.541959 299 7.591497 หากเราไม่ต้องการสืบค้นข้อมูลด้วยการรวมเหล่านั้น และเพียงแค่ใส่ลงในตารางจริงที่อัปเดตโดยการอ้างอิง เราก็สามารถทำได้ด้วยตัวดำเนินการ := ซึ่งจะหลีกเลี่ยงการคัดลอกชุดข้อมูลในหน่วยความจำ ดังนั้นเราจึงไม่จำเป็นต้องกำหนดผลลัพธ์ให้กับตัวแปรใหม่

DT[, `:=`(carrierm_mean_arr = mean(arr_delay), carrierm_mean_dep = mean(dep_delay)), .(carrier, month)] head(DT) ## carrier dep_delay year month day arr_delay origin dest air_time ## 1: AA 1498 2014 10 4 1494 EWR DFW 200 ## 2: AA 1241 2014 4 15 1223 JFK BOS 39 ## 3: AA 1071 2014 6 13 1064 EWR DFW 175 ## 4: AA 1056 2014 9 12 1115 EWR DFW 198 ## 5: AA 1022 2014 6 16 1073 EWR DFW 178 ## 6: AA 989 2014 6 11 991 EWR DFW 194 ## distance hour sum_delay carrierm_mean_arr carrierm_mean_dep ## 1: 1372 7 2992 5.541959 7.591497 ## 2: NA 13 2464 1.903324 3.987008 ## 3: 1372 10 2135 8.690067 11.476475 ## 4: 1372 6 2171 -1.235160 3.307078 ## 5: 1372 7 2095 8.690067 11.476475 ## 6: 1372 11 1980 8.690067 11.476475เข้าร่วมชุดข้อมูล
การรวม Base R และการรวมชุดข้อมูลถือเป็นการดำเนินการ ชุดย่อย ชนิดพิเศษ เราจัดเตรียมชุดข้อมูลที่เราต้องการเข้าร่วมในอาร์กิวเมนต์วงเล็บเหลี่ยมแรก i สำหรับแต่ละแถวในชุดข้อมูลที่ให้กับ i เราจับคู่แถวจากชุดข้อมูลที่เราใช้ [ . หากเราต้องการเก็บเฉพาะแถวที่ตรงกัน ( inner join ) เราจะส่งอาร์กิวเมนต์พิเศษ nomatch = 0L เราใช้ on อาร์กิวเมนต์เพื่อระบุคอลัมน์ที่เราต้องการรวมชุดข้อมูลทั้งสอง
# create reference subset carrierdest <- DT[, .(count=.N), .(carrier, dest) # count by carrier and dest ][1:10 # just 10 first groups ] # chaining `[...][...]` as subqueries print(carrierdest) ## carrier dest count ## 1: AA DFW 5877 ## 2: AA BOS 1173 ## 3: AA ORD 4798 ## 4: AA SEA 298 ## 5: AA EGE 85 ## 6: AA LAX 3449 ## 7: AA MIA 6058 ## 8: AA SFO 1312 ## 9: AA AUS 297 ## 10: AA DCA 172 # outer join ans <- carrierdest[DT, on = c("carrier","dest")] print(ans) ## carrier dest count dep_delay year month day arr_delay origin ## 1: AA DFW 5877 1498 2014 10 4 1494 EWR ## 2: AA BOS 1173 1241 2014 4 15 1223 JFK ## 3: AA DFW 5877 1071 2014 6 13 1064 EWR ## 4: AA DFW 5877 1056 2014 9 12 1115 EWR ## 5: AA DFW 5877 1022 2014 6 16 1073 EWR ## --- ## 253312: WN BNA NA -12 2014 3 9 -21 LGA ## 253313: WN MDW NA -13 2014 3 10 -18 EWR ## 253314: WN HOU NA -13 2014 5 17 -30 LGA ## 253315: WN MKE NA -13 2014 6 15 10 LGA ## 253316: WN CAK NA -13 2014 8 19 -30 LGA ## air_time distance hour sum_delay carrierm_mean_arr ## 1: 200 1372 7 2992 5.541959 ## 2: 39 NA 13 2464 1.903324 ## 3: 175 1372 10 2135 8.690067 ## 4: 198 1372 6 2171 -1.235160 ## 5: 178 1372 7 2095 8.690067 ## --- ## 253312: 115 764 16 -33 6.921642 ## 253313: 112 711 20 -31 6.921642 ## 253314: 202 1428 17 -43 22.875845 ## 253315: 101 738 20 -3 14.888889 ## 253316: 63 397 16 -43 7.219670 ## carrierm_mean_dep ## 1: 7.591497 ## 2: 3.987008 ## 3: 11.476475 ## 4: 3.307078 ## 5: 11.476475 ## --- ## 253312: 11.295709 ## 253313: 11.295709 ## 253314: 30.546453 ## 253315: 24.217560 ## 253316: 17.038047 # inner join ans <- DT[carrierdest, # for each row in carrierdest nomatch = 0L, # return only matching rows from both tables on = c("carrier","dest")] # joining on columns carrier and dest print(ans) ## carrier dep_delay year month day arr_delay origin dest air_time ## 1: AA 1498 2014 10 4 1494 EWR DFW 200 ## 2: AA 1071 2014 6 13 1064 EWR DFW 175 ## 3: AA 1056 2014 9 12 1115 EWR DFW 198 ## 4: AA 1022 2014 6 16 1073 EWR DFW 178 ## 5: AA 989 2014 6 11 991 EWR DFW 194 ## --- ## 23515: AA -8 2014 10 11 -13 JFK DCA 53 ## 23516: AA -9 2014 5 21 -12 JFK DCA 52 ## 23517: AA -9 2014 6 5 -6 JFK DCA 53 ## 23518: AA -9 2014 10 2 -21 JFK DCA 51 ## 23519: AA -11 2014 5 27 10 JFK DCA 55 ## distance hour sum_delay carrierm_mean_arr carrierm_mean_dep count ## 1: 1372 7 2992 5.541959 7.591497 5877 ## 2: 1372 10 2135 8.690067 11.476475 5877 ## 3: 1372 6 2171 -1.235160 3.307078 5877 ## 4: 1372 7 2095 8.690067 11.476475 5877 ## 5: 1372 11 1980 8.690067 11.476475 5877 ## --- ## 23515: NA 15 -21 5.541959 7.591497 172 ## 23516: NA 15 -21 4.150172 8.733665 172 ## 23517: NA 15 -15 8.690067 11.476475 172 ## 23518: NA 15 -30 5.541959 7.591497 172 ## 23519: NA 15 -1 4.150172 8.733665 172 โปรดทราบว่าเนื่องจากความสอดคล้องของการตั้งค่าย่อยฐาน R การรวมภายนอกจึงเป็นค่าเริ่มต้น RIGHT OUTER หากเรากำลังมองหา LEFT OUTER เราจำเป็นต้องสลับตารางดังตัวอย่างด้านบน พฤติกรรมที่แน่นอนยังสามารถควบคุมได้อย่างง่ายดายในวิธีการ merge data.table โดยใช้ API เดียวกันกับฐานข้อมูล R merge data.frame
หากเราต้องการเพียงแค่ค้นหาคอลัมน์ในชุดข้อมูลของเรา เราสามารถทำได้อย่างมีประสิทธิภาพด้วยตัวดำเนินการ := ในอาร์กิวเมนต์ j ขณะเข้าร่วม เช่นเดียวกับที่เรามอบหมายย่อยโดยการอ้างอิง ตามที่อธิบายไว้ในส่วน อัปเดตชุดข้อมูล ตอนนี้เราเพิ่งเพิ่มคอลัมน์โดยการอ้างอิงจากชุดข้อมูลที่เราเข้าร่วม ซึ่งจะหลีกเลี่ยงการคัดลอกข้อมูลในหน่วยความจำ ดังนั้นเราจึงไม่จำเป็นต้องกำหนดผลลัพธ์ให้กับตัวแปรใหม่
DT[carrierdest, # data.table to join with lkp.count := count, # lookup `count` column from `carrierdest` on = c("carrier","dest")] # join by columns head(DT) ## carrier dep_delay year month day arr_delay origin dest air_time ## 1: AA 1498 2014 10 4 1494 EWR DFW 200 ## 2: AA 1241 2014 4 15 1223 JFK BOS 39 ## 3: AA 1071 2014 6 13 1064 EWR DFW 175 ## 4: AA 1056 2014 9 12 1115 EWR DFW 198 ## 5: AA 1022 2014 6 16 1073 EWR DFW 178 ## 6: AA 989 2014 6 11 991 EWR DFW 194 ## distance hour sum_delay carrierm_mean_arr carrierm_mean_dep lkp.count ## 1: 1372 7 2992 5.541959 7.591497 5877 ## 2: NA 13 2464 1.903324 3.987008 1173 ## 3: 1372 10 2135 8.690067 11.476475 5877 ## 4: 1372 6 2171 -1.235160 3.307078 5877 ## 5: 1372 7 2095 8.690067 11.476475 5877 ## 6: 1372 11 1980 8.690067 11.476475 5877 สำหรับ การรวมในขณะที่เข้าร่วม ให้ใช้ by = .EACHI ดำเนินการเข้าร่วมที่ไม่ก่อให้เกิดผลลัพธ์การรวมระดับกลางและจะใช้การรวมทันที ทำให้หน่วยความจำมีประสิทธิภาพ
Rolling join เป็นคุณสมบัติที่ไม่ธรรมดา ออกแบบมาเพื่อจัดการกับข้อมูลที่สั่ง เหมาะอย่างยิ่งสำหรับการประมวลผลข้อมูลชั่วคราวและอนุกรมเวลาโดยทั่วไป โดยทั่วไปจะหมุนการจับคู่ในเงื่อนไขการรวมเป็นค่าที่ตรงกันถัดไป ใช้โดยระบุอาร์กิวเมนต์ roll เมื่อเข้าร่วม
Fast overlap join รวมชุดข้อมูลตามช่วงเวลาและการจัดการที่ทับซ้อนกันโดยใช้ตัวดำเนินการที่ทับซ้อนกันต่างๆ: any , within , start , end
ขณะนี้กำลังพัฒนาคุณลักษณะการรวม แบบ non-equi เพื่อเข้าร่วมชุดข้อมูลโดยใช้เงื่อนไขที่ไม่เท่ากัน
ข้อมูลโปรไฟล์
เมื่อสำรวจชุดข้อมูลของเรา บางครั้งเราอาจต้องการรวบรวมข้อมูลทางเทคนิคเกี่ยวกับเรื่องดังกล่าว เพื่อให้เข้าใจถึงคุณภาพของข้อมูลได้ดียิ่งขึ้น
สถิติเชิงพรรณนา
summary(DT) ## carrier dep_delay year month ## Length:253316 Min. :-112.00 Min. :2014 Min. : 1.000 ## Class :character 1st Qu.: -5.00 1st Qu.:2014 1st Qu.: 3.000 ## Mode :character Median : -1.00 Median :2014 Median : 6.000 ## Mean : 12.47 Mean :2014 Mean : 5.639 ## 3rd Qu.: 11.00 3rd Qu.:2014 3rd Qu.: 8.000 ## Max. :1498.00 Max. :2014 Max. :10.000 ## ## day arr_delay origin dest ## Min. : 1.00 Min. :-112.000 Length:253316 Length:253316 ## 1st Qu.: 8.00 1st Qu.: -15.000 Class :character Class :character ## Median :16.00 Median : -4.000 Mode :character Mode :character ## Mean :15.89 Mean : 8.147 ## 3rd Qu.:23.00 3rd Qu.: 15.000 ## Max. :31.00 Max. :1494.000 ## ## air_time distance hour sum_delay ## Min. : 20.0 Min. : 80.0 Min. : 0.00 Min. :-224.00 ## 1st Qu.: 86.0 1st Qu.: 529.0 1st Qu.: 9.00 1st Qu.: -19.00 ## Median :134.0 Median : 762.0 Median :13.00 Median : -5.00 ## Mean :156.7 Mean : 950.4 Mean :13.06 Mean : 20.61 ## 3rd Qu.:199.0 3rd Qu.:1096.0 3rd Qu.:17.00 3rd Qu.: 23.00 ## Max. :706.0 Max. :4963.0 Max. :24.00 Max. :2992.00 ## NA's :81483 ## carrierm_mean_arr carrierm_mean_dep lkp.count ## Min. :-22.403 Min. :-4.500 Min. : 85 ## 1st Qu.: 2.676 1st Qu.: 7.815 1st Qu.:3449 ## Median : 6.404 Median :11.354 Median :5877 ## Mean : 8.147 Mean :12.465 Mean :4654 ## 3rd Qu.: 11.554 3rd Qu.:17.564 3rd Qu.:6058 ## Max. : 86.182 Max. :52.864 Max. :6058 ## NA's :229797คาร์ดินัลลิตี้
เราสามารถตรวจสอบเอกลักษณ์ของข้อมูลโดยใช้ฟังก์ชัน uniqueN และนำไปใช้กับทุกคอลัมน์ Object .SD ในแบบสอบถามด้านล่างสอดคล้องกับ S ย่อยของ D ata.table:
DT[, lapply(.SD, uniqueN)] ## carrier dep_delay year month day arr_delay origin dest air_time ## 1: 14 570 1 10 31 616 3 109 509 ## distance hour sum_delay carrierm_mean_arr carrierm_mean_dep lkp.count ## 1: 152 25 1021 134 134 11อัตราส่วน NA
ในการคำนวณอัตราส่วนของค่าที่ไม่รู้จัก ( NA ใน R และ NULL ใน SQL) สำหรับแต่ละคอลัมน์ เราได้จัดเตรียมฟังก์ชันที่ต้องการเพื่อใช้กับทุกคอลัมน์
DT[, lapply(.SD, function(x) sum(is.na(x))/.N)] ## carrier dep_delay year month day arr_delay origin dest air_time ## 1: 0 0 0 0 0 0 0 0 0 ## distance hour sum_delay carrierm_mean_arr carrierm_mean_dep lkp.count ## 1: 0.3216654 0 0 0 0 0.9071555การส่งออกข้อมูล
ข้อมูลตารางการส่งออกอย่างรวดเร็วไปยังรูปแบบ CSV นั้นมีให้โดยแพ็คเกจ data.table
tmp.csv <- tempfile(fileext=".csv") fwrite(DT, tmp.csv) # preview exported data cat(system(paste("head -3",tmp.csv), intern=TRUE), sep="\n") ## carrier,dep_delay,year,month,day,arr_delay,origin,dest,air_time,distance,hour,sum_delay,carrierm_mean_arr,carrierm_mean_dep,lkp.count ## AA,1498,2014,10,4,1494,EWR,DFW,200,1372,7,2992,5.54195933456561,7.59149722735674,5877 ## AA,1241,2014,4,15,1223,JFK,BOS,39,,13,2464,1.90332441727168,3.98700802445548,1173 ในขณะที่เขียนสิ่งนี้ ฟังก์ชัน fwrite ยังไม่ได้เผยแพร่ไปยังที่เก็บ CRAN ในการใช้งาน เราจำเป็นต้องติดตั้งเวอร์ชันพัฒนา data.table มิฉะนั้น เราสามารถใช้ฟังก์ชัน base R write.csv ได้ แต่อย่าคาดหวังว่ามันจะเร็ว
ทรัพยากร
มีทรัพยากรมากมาย นอกจากคู่มือที่มีให้สำหรับแต่ละฟังก์ชันแล้ว ยังมีขอบมืดของแพ็คเกจ ซึ่งเป็นบทช่วยสอนที่เน้นไปที่หัวข้อนั้นๆ สามารถพบได้ในหน้าเริ่มต้นใช้งาน นอกจากนี้ หน้าการนำเสนอยังแสดงรายการเอกสารมากกว่า 30 รายการ (สไลด์ วิดีโอ ฯลฯ) จากการนำเสนอ data.table ทั่วโลก นอกจากนี้ การสนับสนุนจากชุมชนได้เติบโตขึ้นในช่วงหลายปีที่ผ่านมา โดยล่าสุดได้เข้าถึงคำถามที่ 4000 ในแท็ก data.table ของ Stack Overflow ซึ่งยังคงมีอัตราส่วนของคำถามที่ตอบสูง (91.9%) พล็อตด้านล่างแสดงจำนวนคำถามที่ติดแท็ก data.table บน Stack Overflow เมื่อเวลาผ่านไป
สรุป
บทความนี้แสดงตัวอย่างที่เลือกไว้สำหรับการแปลงข้อมูลแบบตารางอย่างมีประสิทธิภาพใน R โดยใช้แพ็คเกจ data.table สามารถตรวจสอบตัวเลขที่แท้จริงเกี่ยวกับประสิทธิภาพได้โดยการมองหาเกณฑ์มาตรฐานที่ทำซ้ำได้ ฉันเผยแพร่บล็อกโพสต์โดยสรุปเกี่ยวกับโซลูชัน data.table สำหรับคำถาม StackOverflow 50 อันดับแรกสำหรับภาษา R ที่เรียกว่า แก้ปัญหา R ทั่วไปอย่างมีประสิทธิภาพด้วย data.table ซึ่งคุณสามารถหาตัวเลขจำนวนมากและโค้ดที่ทำซ้ำได้ แพ็คเกจ data.table ใช้การนำการจัดกลุ่ม Radix แบบเนทีฟไปใช้งานจริงสำหรับการดำเนินการจัดกลุ่ม และการค้นหาไบนารีสำหรับชุดย่อย/การรวมที่รวดเร็ว การจัดลำดับฐานนี้รวมอยู่ในฐาน R จากเวอร์ชัน 3.3.0 แล้ว นอกจากนี้ อัลกอริทึมเพิ่งนำมาใช้ในแพลตฟอร์มการเรียนรู้ของเครื่อง H2O และขนานกันบนคลัสเตอร์ H2O ทำให้สามารถเข้าร่วมขนาดใหญ่ได้อย่างมีประสิทธิภาพในแถว 10B x 10B
