Xem tên trước: names() và colnames()
Tên cột nằm trong một thuộc tính (attribute) mà bạn có thể đọc như mọi vector. Với data frame, cả names() lẫn colnames() đều trả về thuộc tính này:
Cùng một kết quả. Khác biệt thực sự duy nhất: colnames() còn dùng được với ma trận, names() thì không, nên code tổng quát dùng colnames(). Với data frame thường ngày, hai hàm thay thế được cho nhau.
Điểm mấu chốt cho mọi thứ bên dưới: đổi tên cột chính là gán vào vector này. Không có hàm base nào chuyên cho việc đó; bạn sửa names(df) bằng đúng cách đánh chỉ số bạn dùng với mọi vector.
Đổi tên theo vị trí (chạy được nhưng dễ gãy)
Đánh chỉ số vector tên theo vị trí rồi gán:
Cột 2 giờ là price. Ổn khi sửa nhanh trong phiên tương tác, nhưng là cái bẫy trong script. Vị trí là một lời hứa về thứ tự cột, mà thứ tự cột lại là thứ kém ổn định nhất của dữ liệu nhập vào: vào ngày tệp CSV bạn đọc có thêm một cột, names(df)[2] sẽ âm thầm đổi tên nhầm cột. Nếu script đổi tên theo vị trí, một thay đổi cấu trúc dữ liệu sẽ làm nó hỏng mà không có thông báo lỗi nào.
Đổi tên theo tên (cách viết chắc chắn trong base R)
Khớp theo tên cũ thay vì tin vào vị trí:
Đọc phần bên trong trước: names(sales) == "prc" tạo ra FALSE TRUE FALSE, một mặt nạ logic trên vector tên. Đánh chỉ số bằng nó sẽ chọn phần tử khớp, và phép gán thay thế phần tử đó. Nếu không có "prc", không gì khớp và không gì thay đổi (không báo lỗi, nên hãy kiểm tra kết quả). Cách viết này vẫn đúng khi thứ tự cột thay đổi, khi có thêm cột, và khi chép sang script khác. Hãy nhớ nó; đây là câu trả lời của base R cho câu hỏi "đổi tên một cột".
Đổi nhiều cột cùng lúc
Thay cả vector khi bạn đặt tên cho mọi cột (thường gặp ngay sau khi nhập dữ liệu), hoặc dùng match() để đổi tên một nhóm cột được chọn:
match(old, names(df)) trả về vị trí của các tên cũ, nên tên mới rơi đúng chỗ của tên cũ, an toàn với thứ tự như cách viết cho một tên. Nếu thiếu tên cũ nào, match() trả về NA và phép gán báo lỗi, đúng kiểu thất bại bạn muốn: thật rõ ràng.
setNames() cho pipeline
Mọi cách viết ở trên đều sửa một biến qua hai bước. setNames(object, names) trả về một bản sao đã đổi tên trong một biểu thức, đúng thứ mà pipeline cần:
Rất tiện khi một hàm trả về data frame không có tên hoặc tên xấu và bạn muốn sửa ngay tại chỗ, read_something() |> setNames(c("id", "value")), mà không cần biến tạm.
Cách của dplyr: rename() và rename_with()
rename() của dplyr nhận các cặp new = old, tên mới đứng trước, ai cũng từng viết ngược một lần (code tĩnh; môi trường chạy thử chỉ có base R):
library(dplyr)
sales |> rename(price = prc, qty = q)
Các tên cũ không được nhắc tới trong lệnh gọi được giữ nguyên, và một tên cũ viết sai là lỗi thật sự chứ không lặng lẽ bỏ qua, một cải tiến đáng kể so với cách viết base. Để đổi tên theo quy tắc thay vì theo cặp, rename_with() áp dụng một hàm lên các tên:
sales |> rename_with(toupper) # every column
sales |> rename_with(toupper, starts_with("p")) # just some
Xem giới thiệu dplyr để biết các hàm này nằm ở đâu trong họ động từ.
Làm sạch tiêu đề lộn xộn khi nhập dữ liệu
Tiêu đề CSV thực tế đến dưới dạng "Order ID", "unit.price ($)", "2024 Total": những tên có dấu cách và ký hiệu buộc bạn phải bọc dấu backtick ở mọi nơi. make.names() của base R chuyển bất kỳ vector character nào thành tên R hợp lệ:
Hợp lệ, nhưng xấu theo kiểu riêng (X2024.Total). Đó là lý do nhiều nhóm thống nhất dùng clean_names() của package janitor, hàm này tạo ra tên snake_case gọn gàng (order_id, unit_price, x2024_total) chỉ trong một lệnh: df |> janitor::clean_names(). Nếu tuần nào bạn cũng nhập tệp lộn xộn, nó đáng giá ngay lập tức.
Điều cần nhớ
- Tên cột chỉ là một vector: đọc bằng
names()/colnames(), đổi tên bằng cách gán vào nó. names(df)[2] <- "new"dễ gãy;names(df)[names(df) == "old"] <- "new"là cách viết cần nhớ.match()đổi tên nhiều cột theo tên;setNames()đổi tên ngay trong pipeline.rename(new = old)của dplyr báo lỗi khi thiếu tên (tốt) vàrename_with()đổi tên theo quy tắc.- Với tiêu đề lộn xộn khi nhập:
make.names()trong base,janitor::clean_names()cho kết quả đẹp.
Tiếp theo: sắp xếp, gồm sort() cho vector, mẹo order() cho data frame và arrange().
Câu hỏi thường gặp
Làm sao để đổi tên một cột trong R?
Cách viết chắc chắn nhất trong base R là khớp theo tên: names(df)[names(df) == "old"] <- "new". Lệnh này tìm cột có tên old ở bất cứ vị trí nào và đổi tên nó. Trong dplyr là rename(df, new = old), chú ý thứ tự: tên mới đứng trước.
names() và colnames() khác nhau thế nào?
Với data frame thì trên thực tế không khác gì: cả hai đều đọc và đặt tên cột. colnames() còn dùng được với ma trận (còn names() thì không), nên code cần xử lý cả hai loại thường dùng colnames(). Với data frame thông thường, bạn thích cái nào thì dùng cái đó.
Làm sao để đổi tên cột theo vị trí trong R?
Gán vào vector tên tại vị trí đó: names(df)[2] <- "price" đổi tên cột thứ hai. Cách này chạy được nhưng dễ gãy: vào ngày có thêm một cột hoặc tệp CSV đổi thứ tự cột, vị trí 2 sẽ là một cột khác. Nên đổi tên theo tên.
Làm sao để đổi tên nhiều cột cùng lúc trong R?
Gán cả một vector: names(df) <- c("id", "name", "price") thay mọi tên theo thứ tự. Để đổi tên vài cột được chọn theo tên, dùng match(): names(df)[match(c("old1", "old2"), names(df))] <- c("new1", "new2"). Trong dplyr: rename(df, new1 = old1, new2 = old2).