Menu

Sắp xếp trong R: sort(), order() và arrange()

Sắp xếp trong R thực sự hoạt động thế nào: sort() cho vector, vì sao data frame cần mẹo chỉ số của order(), sắp xếp giảm dần, theo nhiều cột và arrange() của dplyr.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

sort() sắp xếp giá trị của một vector

Với một vector thông thường, sort() làm đúng điều bạn mong đợi: trả về các giá trị theo thứ tự tăng dần (vector gốc giữ nguyên):

Có hai tùy chọn đáng biết. decreasing = TRUE đảo chiều sắp xếp. Và theo mặc định, sort() lặng lẽ bỏ đi các giá trị NA: sort(c(3, NA, 1)) chỉ trả về 1 3. Nếu giá trị thiếu phải được giữ lại sau khi sắp xếp, hãy nói rõ chúng nằm ở đâu: sort(x, na.last = TRUE) giữ chúng lại, đặt sau các giá trị đã sắp xếp.

Đến đây thì mọi thứ còn hiển nhiên. Phần thú vị là vì sao sort() là công cụ sai cho data frame.

order() trả về vị trí: thay đổi cách nghĩ

sort() trả lời câu hỏi "các giá trị, theo thứ tự, là gì?". order() trả lời một câu hỏi khác: "tôi phải đi qua những vị trí nào để thấy các giá trị theo thứ tự?"

Hãy đọc order(times) như một chỉ dẫn: giá trị nhỏ nhất nằm ở vị trí 2, giá trị kế tiếp ở vị trí 4, rồi 1, rồi 3. Đánh chỉ số theo chỉ dẫn đó, times[order(times)], cho ra đúng sort(times).

Vì sao điều đó quan trọng? Vì một hàng của data frame là một đội giá trị phải di chuyển cùng nhau. Chỉ sắp xếp riêng cột time bằng sort() sẽ sắp xếp lại một cột và bỏ rơi đồng đội của nó: mọi hàng bị xáo trộn. order() cho bạn vị trí các hàng, và đặt các vị trí đó vào chỗ của hàng trong ngoặc vuông sẽ di chuyển cả hàng:

Giá trị 9.8 của Ben đến cùng với tên Ben. Đó là toàn bộ mẹo, và đó là cách viết chuẩn để sắp xếp data frame trong base R: df[order(df$column), ]. (Nhãn hàng được in ra, 2 4 1 3, là số hàng gốc đi theo; vô hại.)

Sắp xếp giảm dần và theo nhiều cột

Để sắp xếp số giảm dần, hãy đổi dấu cột: sắp xếp -time tăng dần chính là sắp xếp time giảm dần. Và order() nhận nhiều cột, cột đứng trước được ưu tiên, các cột sau phân định khi bằng nhau:

Lệnh gọi thứ hai sắp xếp phòng ban theo bảng chữ cái và, trong từng phòng ban, lương từ cao đến thấp. Mẹo đổi dấu chỉ dùng được với số: -df$name trên cột character sẽ báo lỗi. Để sắp xếp văn bản giảm dần, hãy dùng order(df$name, decreasing = TRUE) (lệnh này đảo chiều mọi khóa sắp xếp cùng lúc).

rank() là người anh em thứ ba

Nhân tiện: rank(x) trả lời thêm một câu hỏi khác, "mỗi giá trị đứng hạng mấy?", mà không di chuyển gì cả:

sort() cho giá trị đã sắp xếp, order() cho các vị trí cần đi qua, rank() cho thứ hạng của từng giá trị tại chỗ. Người ta liên tục nhầm order() với rank(); hãy để ý rằng chúng là hoán vị ngược của nhau, và chỉ dùng rank() khi bạn thật sự cần xếp hạng (bảng xếp hạng, phân vị).

Cách của dplyr: arrange()

arrange() của dplyr gói toàn bộ thao tác với order() vào một động từ: các cột là khóa sắp xếp, desc() đảo chiều một cột (code tĩnh; môi trường chạy thử chỉ có base R):

library(dplyr)

runners |> arrange(time)
staff   |> arrange(dept, desc(salary))

Không ngoặc vuông, không $, không mẹo đổi dấu: desc() dùng được cả với cột character. Một khác biệt về hành vi đáng biết: arrange() đặt các giá trị NA ở cuối bất kể chiều sắp xếp, còn order() của base cũng mặc định na.last = TRUE. Xem giới thiệu dplyr để biết arrange() nối với filter() và các hàm khác ra sao.

Sắp xếp chuỗi có những góc cạnh nguy hiểm

Hai lưu ý khi sắp xếp văn bản. Thứ nhất, chữ số lưu dưới dạng văn bản sẽ được sắp xếp như văn bản: phép so sánh ký tự đi từng ký hiệu một:

"10" đứng trước "2" vì phép so sánh dừng ở ký hiệu đầu tiên: "1" < "2". Nếu một cột số sắp xếp kỳ lạ, gần như chắc chắn nó đang được lưu dưới dạng character: hãy chuyển bằng as.numeric() trước (hậu quả thường gặp sau một lần nhập CSV lộn xộn).

Thứ hai, thứ tự văn bản phụ thuộc vào locale của hệ thống: dấu, chữ hoa chữ thường và chữ viết không phải Latin có thể được sắp xếp khác nhau trên laptop của bạn và trên một máy chủ dùng locale C. Kết quả sắp xếp cần giống hệt nhau giữa các máy nên đặt locale rõ ràng hoặc sắp xếp theo một khóa đã chuẩn hóa.

Điều cần nhớ

  • sort(x) sắp xếp giá trị của vector; decreasing = TRUE đảo chiều; các NA bị bỏ đi trừ khi có na.last = TRUE.
  • order(x) trả về vị trí, và df[order(df$x), ] là cách viết chuẩn của base để sắp xếp data frame.
  • Nhiều cột: order(df$a, -df$b); đổi dấu để giảm dần chỉ dùng được với số.
  • rank() cho thứ hạng mà không sắp xếp lại, không thay thế được order().
  • arrange(dept, desc(salary)) của dplyr là cùng phép sắp xếp với ít dấu câu hơn.
  • Trong văn bản, "10" đứng trước "2": hãy kiểm tra kiểu dữ liệu của cột.

Tiếp theo: gom các hàng thành bản tóm tắt theo nhóm với table(), tapply(), aggregate() và group_by() của dplyr.

Câu hỏi thường gặp

Làm sao để sắp xếp data frame theo một cột trong R?

Dùng order() bên trong ngoặc vuông ở vị trí hàng: df[order(df$price), ]. order() trả về vị trí các hàng theo thứ tự đã sắp xếp, và đánh chỉ số bằng chúng sẽ sắp xếp lại cả data frame. sort() không dùng được ở đây: nó sắp xếp giá trị của một vector và làm mất liên kết với các cột khác.

sort() và order() trong R khác nhau thế nào?

sort(x) trả về các giá trị của x đã được sắp xếp lại theo thứ tự. order(x) trả về các vị trí (chỉ số) giúp đưa x về đúng thứ tự, đó chính là thứ bạn cần để sắp xếp cả data frame theo một cột: df[order(df$x), ].

Làm sao để sắp xếp giảm dần trong R?

Với vector: sort(x, decreasing = TRUE). Với data frame: df[order(-df$x), ] (đổi dấu một cột số) hoặc df[order(df$x, decreasing = TRUE), ], cách này dùng được cả với cột character, nơi phép đổi dấu không dùng được. Trong dplyr: arrange(df, desc(x)).

Làm sao để sắp xếp theo nhiều cột trong R?

Truyền tất cả cho order(): df[order(df$dept, -df$salary), ] sắp xếp theo phòng ban, rồi theo lương giảm dần trong từng phòng ban. Các đối số đứng trước là khóa chính; các đối số sau phân định khi bằng nhau. Trong dplyr: arrange(df, dept, desc(salary)).

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu