read.csv() biến tệp CSV thành data frame
Đọc tệp CSV trong R chỉ cần một lệnh gọi hàm có sẵn trong ngôn ngữ, không cần package nào:
sales <- read.csv("sales.csv")
read.csv() đọc tệp, dùng dòng đầu tiên làm tên cột, đoán kiểu dữ liệu cho từng cột và trả về một data frame. Với một tệp gọn gàng, chỉ có vậy thôi.
Bạn có thể xem hàm này hoạt động mà không cần tệp nào, vì read.csv() cũng nhận văn bản CSV thô qua đối số text: vẫn là bộ phân tích đó, chỉ là nhận một chuỗi thay vì tên tệp:
str() là thứ đầu tiên nên chạy trên mọi dữ liệu bạn nhập vào: hàm này hiện từng cột kèm kiểu dữ liệu đã đoán. Ở đây name được đọc thành character, score thành integer, passed thành logical, tất cả đều đoán từ giá trị. Để xem nhanh bằng mắt, head(students) hiện các hàng đầu tiên và nrow(students) đếm số hàng. Nếu một cột bạn tưởng là số lại hiện thành chr, nghĩa là trong cột đó có thứ không phải số (một dòng chú thích lạc, một "N/A", dấu phân cách hàng nghìn), và đó là lúc cần sửa, chứ không phải ba biểu đồ sau.
Những đối số quan trọng
read.csv() có hàng chục tham số; bạn sẽ dùng khoảng năm cái.
header: dòng đầu tiên có chứa tên cột hay không. Mặc định là TRUE. Nếu tệp bắt đầu ngay bằng dữ liệu, truyền header = FALSE và R sẽ đặt tên cột là V1, V2, ...
sep: ký tự phân cách các trường, mặc định là ",". Phần lớn châu Âu viết CSV với dấu chấm phẩy làm dấu phân cách, vì ở đó dấu phẩy là dấu thập phân. R có sẵn một biến thể cấu hình cho đúng trường hợp này: read.csv2() dùng sep = ";" và dec = ",":
Chiều cao được đọc ra thành số đúng nghĩa: read.csv2 hiểu 1,63 là một phẩy sáu ba. Nếu dùng read.csv thông thường cho tệp này, bạn sẽ nhận một cột lộn xộn duy nhất, vì không gì có thể tách đúng theo một dấu phẩy vốn là dấu thập phân.
na.strings: những chuỗi nào được coi là giá trị thiếu. Mặc định chỉ có "NA" thành giá trị thiếu. Dữ liệu xuất từ hệ thống thật ghi giá trị thiếu thành ô trống, "missing", "-", "NULL", và nếu bạn không khai báo, chúng sẽ được đọc thành văn bản và kéo cả cột thành character:
Với na.strings đúng, các ô trống trở thành NA thật và score vẫn là số. Không có nó, "missing" chỉ là một từ, và cả cột thành văn bản.
skip: số dòng bỏ qua trước khi dữ liệu bắt đầu. File xuất hay đặt một hai dòng tiêu đề phía trên bảng dữ liệu thật; skip = 2 sẽ nhảy qua chúng.
colClasses: ép kiểu cho cột thay vì để R tự đoán. Nạn nhân kinh điển là mọi thứ có số 0 ở đầu, như mã bưu chính, số điện thoại, mã sản phẩm, bị bộ đoán của R đọc thành số và làm mất các số 0:
00501 biến thành 501. Dữ liệu đúng trong tệp nhưng sai trong R, mà không có cảnh báo nào. colClasses = c("integer", "character") cho bộ phân tích biết kiểu của từng cột theo thứ tự, và các số 0 được giữ nguyên.
Một đối số bạn không cần nữa: stringsAsFactors. Trong phần lớn lịch sử của R, read.csv() chuyển mọi cột văn bản thành factor trừ khi bạn yêu cầu khác, và điều đó gây ra vô số nhầm lẫn. Từ R 4.0, giá trị mặc định là FALSE: văn bản vẫn là văn bản. Bạn vẫn sẽ thấy stringsAsFactors = FALSE rải rác trong các hướng dẫn cũ; trên R hiện tại nó vô hại nhưng thừa.
Đường dẫn tệp: lý do số một khiến read.csv thất bại
Lỗi mà người học R nào cũng gặp trong tuần đầu tiên:
Error in file(file, "rt") : cannot open the connection
In addition: Warning message:
cannot open file 'sales.csv': No such file or directory
Tệp có tồn tại, bạn đang nhìn thấy nó trong trình quản lý tệp. Nhưng R không tìm ở đó. Một tên tệp trần như "sales.csv" được hiểu theo đường dẫn tương đối với thư mục làm việc (working directory) của R, và thư mục đó thường không phải thư mục chứa tệp của bạn. Hai dòng là đủ để chẩn đoán:
getwd() # where R is actually looking
file.exists("sales.csv") # FALSE means the path is wrong, full stop
Nếu file.exists() trả về FALSE, chạy lại bao nhiêu lần cũng vô ích: hãy sửa đường dẫn. Hoặc dùng đường dẫn đầy đủ (read.csv("C:/Users/ada/data/sales.csv"); dấu gạch chéo xuôi cũng chạy được trên Windows và an toàn hơn dấu gạch chéo ngược), hoặc chuyển thư mục làm việc của R sang thư mục dữ liệu bằng setwd(). Thư mục làm việc là gì, các project quản lý nó ra sao và vì sao không nên dùng setwd() trong script được giải thích trong bài working directory.
Hãy tập thói quen gọi file.exists(). Chỉ trong một giây, nó biến "lỗi nhập dữ liệu bí ẩn" thành "gõ sai đường dẫn".
Đọc CSV trực tiếp từ URL
read.csv() nhận URL ở bất cứ chỗ nào nhận tên tệp, và tự tải tệp về cho bạn:
url <- "https://raw.githubusercontent.com/mwaskom/seaborn-data/master/penguins.csv"
penguins <- read.csv(url)
str(penguins)
Cách này tiện cho các bài hướng dẫn và bộ dữ liệu công khai: không cần bước tải về, không có vấn đề đường dẫn. Đổi lại, script của bạn giờ phụ thuộc vào mạng và vào việc URL đó còn hoạt động; với thứ bạn sẽ chạy lại thường xuyên, hãy tải về một lần rồi đọc bản sao trên máy.
readr::read_csv: lựa chọn nhanh hơn và báo nhiều thông tin hơn
Tidyverse có bộ đọc CSV riêng trong package readr: read_csv() (dấu gạch dưới, không phải dấu chấm). Hàm này đáng biết vì phần lớn code R hiện đại bạn sẽ đọc đều dùng nó:
install.packages("readr") # once
library(readr) # every session
flights <- read_csv("flights.csv")
Những khác biệt thực tế so với read.csv():
- Tốc độ: nhanh hơn rõ rệt với tệp có hàng trăm nghìn hàng.
- Trả về tibble: một data frame hiện đại hơn, in ra bản xem trước gọn gàng thay vì tràn ngập console.
- Báo lại kiểu đã đoán: sau khi đọc, hàm in ra kiểu được phát hiện của từng cột, nên một cột bị đọc thành character trong khi bạn chờ số sẽ lộ ra ngay, thay vì thành bất ngờ về sau.
- Không bao giờ làm hỏng tên cột:
read.csv()đổi một tiêu đề nhưfirst namethànhfirst.name;read_csv()giữ nguyên.
Với tệp nhỏ, hàm nào cũng được. Hãy chọn read_csv() khi tệp lớn dần hoặc khi phần còn lại của script đã dùng tidyverse. Mọi điều ở trên về đường dẫn, dấu phân cách (cũng có read_csv2()) và chuỗi giá trị thiếu (na =) đều áp dụng được, chỉ khác tên đối số một chút.
Khi phân tích xong, chiều ngược lại, tức xuất kết quả ra tệp, có trong bài write.csv.
Điều cần nhớ
df <- read.csv("file.csv")đọc CSV thành data frame; kiểm tra ngay bằngstr()vàhead().read.csv(text = "...")phân tích trực tiếp một chuỗi CSV, rất hợp để thử nghiệm và làm ví dụ nhỏ.- Những đối số đáng dùng nhất:
header,sep(hoặcread.csv2cho dấu chấm phẩy),na.strings,skip,colClasses. - "Cannot open file" nghĩa là thư mục làm việc không nằm ở nơi bạn nghĩ;
getwd()vàfile.exists()giải quyết ngay. readr::read_csv()là phiên bản tidyverse nhanh hơn: trả về tibble, báo kiểu cột, giữ nguyên tên cột.
Tiếp theo: những tệp không phải văn bản thuần, tức đọc bảng tính Excel bằng package readxl.
Câu hỏi thường gặp
Làm sao để đọc tệp CSV trong R?
Dùng read.csv("file.csv"): hàm có sẵn, không cần package nào. Hàm trả về một data frame, mỗi cột CSV thành một cột, dòng đầu tiên được dùng làm tên cột. Gán kết quả cho một biến: df <- read.csv("sales.csv"), rồi kiểm tra bằng str(df) và head(df).
Vì sao read.csv báo 'cannot open file: No such file or directory'?
R tìm tệp theo đường dẫn tương đối với thư mục làm việc (working directory) của nó, và thư mục đó nhiều khả năng không phải thư mục chứa tệp của bạn. Chạy getwd() để xem R đang tìm ở đâu và file.exists("file.csv") để xác nhận là không tìm thấy. Sửa bằng đường dẫn đầy đủ, hoặc đặt thư mục làm việc về thư mục chứa tệp.
read.csv và read_csv trong R khác nhau thế nào?
read.csv() thuộc base R: luôn có sẵn, trả về một data frame thông thường. read_csv() đến từ package readr: nhanh hơn với tệp lớn, trả về tibble, không bao giờ đụng tới tên cột và in ra kiểu dữ liệu nó đoán cho từng cột, nhờ vậy bạn phát hiện ngay lỗi phân tích. Với tệp nhỏ, hàm nào cũng được; với tệp lớn hoặc pipeline tidyverse, hãy dùng read_csv().
Làm sao để đọc tệp CSV phân cách bằng dấu chấm phẩy trong R?
Dùng read.csv2("file.csv"): đây là read.csv đã cấu hình sẵn theo quy ước châu Âu, dấu chấm phẩy làm dấu phân cách và dấu phẩy làm dấu thập phân. Hoặc truyền sep = ";" (và dec = "," nếu cần) cho read.csv() thông thường.