2012-02-10 55 views
28

Tôi có một data.table với các cột từ 2 đến 20 là các chuỗi có dấu cách (ví dụ: "Tên loài"). Tôi muốn chạy đồng thời str_replace() trên tất cả các cột đó để tất cả "Tên loài" trở thành "Species_Name". Tôi có thể hoặc là làm:Làm cách nào để chạy ứng dụng trên data.table?

data.table(apply(as.data.frame(dt[,2:dim(dt)[2], with=F]), 2, 
           function(x){ str_replace(x," ","_") })) 

hoặc nếu tôi giữ nó như là một đối tượng data.table, sau đó tôi có thể làm một cột này tại một thời điểm:

dt[,SpeciesName := str_replace(SpeciesName, " ", "_") 

Làm thế nào để làm điều này cho tất cả các cột từ 2 đến sự kết thúc tương tự như một trong những điều trên?

Trả lời

30

Hoàn toàn viết lại trên 2015/11/24, để sửa chữa một lỗi trong phiên bản trước.

Bạn có một vài tùy chọn.

  1. Process tất cả các cột mục tiêu với một cuộc gọi nhúng để lapply(), sử dụng := để gán các giá trị sửa đổi tại chỗ. Điều này dựa trên sự hỗ trợ rất tiện dụng của := để phân công đồng thời cho một số cột có tên trên LHS của nó.

  2. Sử dụng vòng lặp for để chạy qua từng cột mục tiêu một lần, sử dụng set() để sửa đổi giá trị của mỗi lần lượt.

  3. Sử dụng một vòng lặp for để lặp qua nhiều "ngây thơ" gọi để [.data.table(), mỗi một trong số đó sẽ thay đổi một cột duy nhất.

Tất cả các phương pháp này đều có vẻ nhanh như nhau, do đó, phương pháp bạn sử dụng sẽ là chủ yếu là vấn đề về hương vị. (1) nhỏ gọn và độc đáo biểu cảm. Đó là những gì tôi thường xuyên sử dụng nhất, mặc dù bạn có thể tìm thấy (2) dễ đọc hơn. Vì chúng xử lý và sửa đổi từng cột một, (2) hoặc (3) sẽ có lợi thế trong trường hợp hiếm hoi trong đó data.table của bạn quá lớn nên bạn có nguy cơ gặp phải các giới hạn do Bộ nhớ có sẵn của phiên R.

library(data.table) 

## Create three identical 1000000-by-20 data.tables 
DT1 <- data.table(1:1e6, 
      as.data.table(replicate(1e6, paste(sample(letters, nr, TRUE), 
              sample(letters, nr, TRUE))))) 
cnames <- c("ID", paste0("X", 1:19)) 
setnames(DT1, cnames) 
DT2 <- copy(DT1); DT3 <- copy(DT1) 

## Method 1 
system.time({ 
DT1[, cnames[-1] := lapply(DT1[,cnames[-1],with=FALSE], 
         function(x) gsub(" ", "_", x))] 
}) 
## user system elapsed 
## 10.90 0.11 11.06 

## Method 2 
system.time({ 
    for(cname in cnames[-1]) { 
     set(DT2, j=cname, value=gsub(" ", "_", DT2[[cname]])) 
    } 
}) 
## user system elapsed 
## 10.65 0.05 10.70 

## Method 3 
system.time({ 
    for(cname in cnames[-1]) { 
     DT3[ , cname := gsub(" ", "_", DT3[[cname]]), with=FALSE] 
    } 
}) 
## user system elapsed 
## 10.33 0.03 10.37 

Để biết thêm chi tiết về set():=, đọc trang trợ giúp của họ, nhận được bằng cách gõ ?set hoặc ?":=".

+0

Đây là một trường hợp thú vị. Ở đây, 19 cột trong số 20 cột đang được thay thế; RHS của ': =' gần như là tất cả các bảng. Lợi thế của ': =' lớn hơn khi, giả sử, một hoặc hai cột được thêm vào 20, hoặc một hoặc hai cột 20 được sửa đổi. Trong những trường hợp đó, hầu hết các cột được đặt đúng vị trí và ': =' nhanh hơn nhiều so với việc sao chép toàn bộ bảng. –

+0

Ngoài ra, 'set()' là một hàm mới trong v1.8.0 (chưa có trên CRAN), cung cấp trực tiếp hàm ': ='. 'set()' có thể nhanh hơn ': =' khi gán trong vòng lặp (để dễ lập trình tự nhiên hơn). Có một ví dụ trong NEWS mới nhất trên trang chủ. –

+1

@MatthewDowle - Cảm ơn nhận xét của bạn.Họ nhắc tôi rằng, cuối tuần qua, tôi đã có một cảm giác dai dẳng về câu trả lời tôi đã đưa ra ở đây, và thúc giục tôi xem lại điều này. Rõ ràng là tôi có lý do chính đáng để cảm thấy bị nản lòng. Xin hãy xem câu trả lời đã sửa đổi của tôi. Ngoài ra, hãy thêm bất kỳ gợi ý nào bạn có trong nhận xét của mình vào nội dung câu trả lời của tôi **, nơi bạn cho rằng chúng có thể hữu ích. Tôi sẽ có một cái nhìn tại 'set()', nhưng chưa cảm thấy đủ điều kiện để thảo luận về nó. Và, một lần nữa, cảm ơn tất cả các công việc bạn đưa vào phát triển liên tục của gói data.table! –

6

Bạn có thể làm điều này:

library("stringr") 
dt[, -1] <- lapply(dt[, -1], function(x) str_replace(x," ","_")) 
Các vấn đề liên quan