Menu

Thư mục làm việc trong R: setwd(), getwd() và dọn workspace

R tìm tệp ở đâu và cách điều khiển việc đó: getwd(), setwd(), list.files(), dọn environment bằng rm(list = ls()), và vì sao tự động lưu .RData là một cái bẫy.

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.

Thư mục làm việc là gì

Mỗi phiên R chạy "bên trong" đúng một thư mục: thư mục làm việc (working directory). Mỗi khi bạn dùng đường dẫn tương đối, tức đường dẫn không bắt đầu từ gốc của hệ thống tệp, R sẽ giải nó dựa trên thư mục đó. read.csv("data.csv") không có nghĩa là "tìm data.csv ở đâu đó trên máy tôi"; nó có nghĩa là "mở tệp tên data.csv trong thư mục làm việc".

Chỉ một điều này thôi đã giải thích lỗi phổ biến nhất của người mới học R:

read.csv("data.csv")
# Error in file(file, "rt") : cannot open the connection
# In addition: Warning message: cannot open file 'data.csv': No such file or directory

Tệp vẫn tồn tại, chỉ là không nằm trong thư mục R đang nhìn vào. Lệnh gọi read.csv của bạn không sai gì cả; thư mục làm việc của phiên và vị trí của tệp không khớp nhau. Hai hàm dưới đây biến bí ẩn đó thành một lần chẩn đoán mất mười giây.

getwd() và setwd()

getwd() (get working directory) cho bạn biết R đang tìm ở đâu ngay lúc này:

Chạy đoạn này, bạn sẽ thấy một đường dẫn tuyệt đối, chính là thư mục mà phiên này đang gắn vào. setwd() (set working directory) thay đổi nó:

setwd("C:/Users/rosa/projects/sales-analysis")   # Windows: use / not \
setwd("/Users/rosa/projects/sales-analysis")     # macOS / Linux
getwd()                                          # confirm it took

Hai lưu ý kỹ thuật. Trên Windows, hãy viết đường dẫn bằng / (hoặc \\ gấp đôi): một dấu \ là ký tự escape trong chuỗi R, nên "C:\Users" là lỗi cú pháp. Ngoài ra setwd() báo lỗi ngay nếu thư mục không tồn tại, và đó là kiểu thất bại dễ chịu nhất bạn sẽ gặp trong ngày: nó không thể để bạn âm thầm trỏ nhầm chỗ.

Vì vậy, quy trình gỡ lỗi cho mọi lỗi "cannot open file" là: chạy getwd() để xem R đang tìm ở đâu, rồi so sánh với nơi tệp thực sự nằm.

list.files(): xem những gì R nhìn thấy

Công cụ thứ ba trong bộ này liệt kê nội dung của thư mục làm việc, đúng nghĩa là tập hợp các tên mà một đường dẫn tương đối có thể với tới:

Nếu tệp bạn muốn đọc xuất hiện trong đầu ra đó, read.csv("thatfile.csv") sẽ tìm thấy nó. Nếu không, gõ lại dòng read.csv bao nhiêu lần cũng vô ích: thứ cần sửa là thư mục làm việc. list.files("data") nhìn vào một thư mục con, còn list.files(pattern = "\\.csv$") chỉ lọc ra các tệp CSV. Khi tên tệp trông đúng mà vẫn không tìm thấy, list.files() cũng vạch ra những thủ phạm kinh điển: phần mở rộng .txt vô hình do trình soạn thảo văn bản tự thêm, hoặc Data.csv và data.csv trên một hệ thống phân biệt chữ hoa chữ thường.

Vì sao setwd() viết cứng lại hỏng

Giờ đến phần mang tính quan điểm. Bạn sẽ gặp những script mở đầu bằng:

setwd("C:/Users/me/Desktop/stuff/project3/final_FINAL")

Dòng đó chỉ chạy được trên đúng một máy: máy của tác giả. Trên laptop của bạn, máy Mac của đồng nghiệp, máy chủ của phòng ban, hay chính máy của bạn sau khi sắp xếp lại thư mục, nó báo lỗi ngay dòng 1. Viết cứng một đường dẫn tuyệt đối là nhét cách sắp xếp thư mục của một người vào code vốn phải sống lâu hơn cách sắp xếp đó.

Cách sửa là đảo ngược trách nhiệm: đừng bắt script tự đi tìm thư mục, hãy khởi động R ở đúng thư mục và dùng đường dẫn tương đối bên trong script. Cụ thể:

  • Dùng RStudio Project. Mở một tệp .Rproj sẽ tự động đặt thư mục làm việc thành thư mục project, trên bất kỳ máy nào. Khi đó, bên trong script, read.csv("data/sales.csv") chạy được với mọi người có project đó.
  • Chạy từ terminal cũng làm được việc tương tự: Rscript analysis.R lấy thư mục nơi bạn chạy lệnh làm thư mục làm việc (xem chạy script R).
  • Package here còn tiến thêm một bước cho ai cần: here::here("data", "sales.csv") dựng đường dẫn từ gốc project, bất kể thư mục làm việc đã trôi đi đâu. Nhưng Project cộng với đường dẫn tương đối đã đủ cho hầu hết nhu cầu mà không cần tới nó.

Bản thân setwd() không xấu: dùng tương tác khi đang khám phá dữ liệu thì hoàn toàn ổn. Thứ đáng ngại là một lệnh setwd() tuyệt đối, gắn với một máy cụ thể, được commit ở đầu một script dùng chung.

Workspace: ls() và rm()

Bên cạnh câu hỏi "mình đang ở thư mục nào" là phần trạng thái thứ hai của phiên: workspace (global environment), tức mọi đối tượng bạn đã tạo trong phiên này. ls() liệt kê chúng, rm() xóa chúng:

Lệnh ls() cuối cùng in ra character(0), tức một workspace trống. Câu thần chú rm(list = ls()) đọc có vẻ lạ cho tới khi bạn phân tích nó: ls() trả về vector character chứa tên mọi đối tượng, và rm(list = ...) xóa từng tên trong vector đó. Đây là phiên bản bằng code của biểu tượng cây chổi trong khung Environment của RStudio.

Hãy biết lệnh này không làm gì: các package nạp bằng library() vẫn được nạp, các tùy chọn vẫn giữ nguyên và thư mục làm việc vẫn ở chỗ cũ. Nó chỉ xóa đối tượng, không hơn, nên không thể thay thế việc khởi động lại R khi bạn muốn bắt đầu thật sự từ đầu.

Xóa console và cái bẫy .RData

Xóa console, tức phần lịch sử lệnh và đầu ra đã cuộn qua, chỉ là chuyện hiển thị và tách biệt với việc xóa workspace. Trong RStudio, bạn nhấn Ctrl+L; trong code, lệnh cat("\014") khá kỳ quặc sẽ gửi ký tự form-feed, thứ mà hầu hết console hiểu là "xóa màn hình". Cả hai cách đều không đụng tới đối tượng của bạn.

Cuối cùng là cái bẫy. Khi bạn thoát R, nó hỏi "save workspace image?", tức ghi mọi đối tượng vào một tệp .RData ẩn, tệp này sẽ âm thầm được nạp lại lần sau bạn khởi động R trong thư mục đó. Hãy từ chối, và tắt hẳn tính năng này (RStudio: Tools → Global Options → bỏ chọn "Restore .RData", đặt "Save workspace" thành Never). Một workspace được khôi phục nghĩa là phiên của bạn bắt đầu với đầy đối tượng từ nhiều ngày trước, được tạo bởi code mà có thể bạn đã sửa hoặc xóa từ lâu. Script của bạn trông như vẫn chạy, vì nó đang dựa vào một model_v2 cũ mà không dòng nào trong tệp còn tạo ra nữa, cho tới khi nó chạy trên một máy sạch và sụp đổ.

Thói quen chuyên nghiệp thì ngược lại: bắt đầu từ trạng thái sạch và chạy lại script. Nếu phân tích của bạn là một script chạy từ đầu đến cuối trong một phiên mới, nó tự khắc tái lập được (reproducible); workspace là đầu ra có thể vứt bỏ, không bao giờ là trạng thái quý giá. Hãy khởi động lại R thường xuyên (RStudio: Session → Restart R) chính là để chứng minh script của bạn vẫn tự đứng vững.

Những điều cần nhớ

  • Đường dẫn tương đối được giải dựa trên thư mục làm việc; getwd() hiển thị nó, setwd() thay đổi nó, list.files() cho thấy những gì có thể với tới.
  • "Cannot open file" gần như luôn có nghĩa là thư mục làm việc và thư mục chứa tệp không khớp nhau; chẩn đoán bằng getwd() + list.files().
  • Đừng bao giờ commit một lệnh setwd() tuyệt đối vào script dùng chung; hãy dùng RStudio Project (hoặc chạy script từ thư mục của nó) và đường dẫn tương đối.
  • ls() liệt kê các đối tượng trong workspace; rm(x) xóa một đối tượng; rm(list = ls()) xóa tất cả, còn package và tùy chọn vẫn giữ nguyên.
  • Ctrl+L (hoặc cat("\014")) chỉ xóa chữ trên console.
  • Đừng tự động lưu và khôi phục .RData: hãy bắt đầu từ trạng thái sạch và chạy lại script, đó là điều giúp công việc của bạn tái lập được.

Tiếp theo: đọc dữ liệu thật với read.csv() và các hàm tương tự, giờ khi R đã tìm đúng thư mục.

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

Làm sao để đặt thư mục làm việc trong R?

Gọi setwd("path/to/folder") để đổi thư mục và getwd() để xem thư mục hiện tại. Trong RStudio bạn cũng có thể dùng menu: Session → Set Working Directory. Nhưng với code bạn sẽ chạy lại hoặc chia sẻ, hãy ưu tiên RStudio Project (tự động đặt thư mục làm việc) thay vì một dòng setwd() viết cứng, vì đường dẫn tuyệt đối sẽ hỏng trên mọi máy khác.

Làm sao để xóa environment trong R?

rm(list = ls()) xóa mọi đối tượng khỏi global environment: ls() liệt kê tên tất cả đối tượng và rm() xóa chúng. Để xóa một đối tượng duy nhất, dùng rm(x). Lưu ý lệnh này chỉ xóa đối tượng: các package đã nạp vẫn được nạp, còn chữ trên console không bị đụng tới (muốn xóa console thì nhấn Ctrl+L trong RStudio).

Vì sao read.csv báo 'cannot open file' dù tệp vẫn tồn tại?

Vì R giải đường dẫn tương đối dựa trên thư mục làm việc, và thư mục làm việc của bạn đang trỏ tới một nơi khác chứ không phải thư mục chứa tệp. Chạy getwd() để xem R đang tìm ở đâu và list.files() để xem R thấy gì ở đó. Hãy sửa bằng cách mở project ở đúng thư mục (hoặc dùng setwd()), đừng dán đường dẫn tuyệt đối vào script.

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