2012-01-06 59 views
19

Tôi đang chọn lọc thông qua gói và tập lệnh sử dụng gói và muốn xác định các phụ thuộc bên ngoài. Mục đích là sửa đổi các tập lệnh để chỉ định library(pkgName) và sửa đổi các chức năng trong gói để sử dụng require(pkgName), để các phụ thuộc này sẽ rõ ràng hơn sau này.Xác định các phụ thuộc của chức năng và tập lệnh R

Tôi đang sửa đổi mã để giải thích cho mỗi gói phụ thuộc bên ngoài. Ví dụ, mặc dù nó không có nghĩa là dứt khoát, bây giờ tôi thấy khó xác định mã phụ thuộc vào data.table. Tôi có thể thay thế data.table bằng Matrix, ggplot2, bigmemory, plyr hoặc nhiều gói khác, vì vậy hãy trả lời bằng các ví dụ dựa trên các gói khác.

Tìm kiếm này không dễ dàng. Các cách tiếp cận tôi đã cố gắng cho đến nay bao gồm:

  • Tìm kiếm mã cho libraryrequire báo cáo
  • Tìm kiếm đề cập đến data.table (ví dụ library(data.table))
  • Thử chạy codetools::checkUsage để xác định nơi có thể có một số vấn đề. Đối với các tập lệnh, chương trình của tôi chèn tập lệnh vào một hàm cục bộ và áp dụng checkUsage cho hàm đó. Nếu không, tôi sử dụng checkUsagePackage cho gói.
  • Tìm kiếm các câu lệnh có phần duy nhất với data.table, chẳng hạn như :=.
  • Hãy tìm nơi lớp đối tượng có thể được xác định thông qua ký hiệu Hungarian, chẳng hạn như DT

Bản chất của việc tìm kiếm của tôi là tìm:

  • tải data.table,
  • đối tượng với tên cho biết chúng là các đối tượng data.table,
  • các phương pháp có vẻ là data.table -specific

Phần dễ dàng duy nhất của điều này dường như là tìm nơi gói được tải. Thật không may, không phải tất cả các chức năng có thể tải hoặc yêu cầu gói bên ngoài một cách rõ ràng - chúng có thể giả định rằng nó đã được tải. Đây là một thực hành tồi, và tôi đang cố sửa nó. Tuy nhiên, việc tìm kiếm các đối tượng và phương pháp có vẻ khó khăn.

Điều này (data.table) chỉ là một gói và một với những gì dường như bị hạn chế và sử dụng một phần duy nhất. Giả sử tôi muốn tìm cách sử dụng các hàm ggplot, nơi các tùy chọn mở rộng hơn và văn bản cú pháp không phải là kiểu riêng (ví dụ: sử dụng thường xuyên + không phải là kiểu riêng, trong khi := có vẻ như).

Tôi không nghĩ rằng phân tích tĩnh sẽ đưa ra câu trả lời hoàn hảo, ví dụ: người ta có thể vượt qua một đối số cho một hàm, trong đó xác định một gói được nạp. Tuy nhiên: có bất kỳ công cụ hoặc gói cốt lõi nào có thể cải thiện phương pháp tiếp cận vũ phu này, hoặc thông qua phân tích tĩnh hoặc động?

Đối với giá trị của nó, tools::pkgDepends chỉ địa chỉ phụ thuộc ở cấp gói, chứ không phải hàm hoặc cấp tập lệnh, là cấp mà tôi đang làm việc.


Cập nhật 1: Ví dụ về công cụ phân tích động sẽ hoạt động là báo cáo gói nào được tải trong khi thực thi mã. Tôi không biết nếu một khả năng như vậy tồn tại trong R, mặc dù - nó sẽ giống như Rprof báo cáo đầu ra của search() thay vì ngăn xếp mã.

+3

Bạn có thể thử 'foodweb' trong gói' mvbutils' không? Tôi không có kinh nghiệm với nó bản thân mình nhưng nó có vẻ hứa hẹn cho tôi (ngoại trừ tôi không biết làm thế nào sâu nó tìm kiếm). Một cái gì đó như 'foodweb (nơi = 'gói: data.table', prune = 'function_youre_examining')'? –

+0

@ mathematics.coffee Điều đó rất hấp dẫn. Có vẻ như nó sẽ khá hữu ích trong gói; Tôi chưa rõ ràng về những gì nó có thể làm liên-gói, nhưng tôi sẽ cung cấp cho nó một whirl, cảm ơn! – Iterator

+0

@ mathematics.coffee Điều này rất thú vị. Tôi đang cắm nó đi xa hơn một chút. Bạn có thể đăng bình luận của bạn như là một câu trả lời? Tôi có thể giúp chỉnh sửa nó thành một giải pháp, giả sử tôi có thể làm cho nó hoạt động. Trước đây tôi đã nhận xét rằng nó dường như không hoạt động trên các gói, nhưng điều đó không đúng. Đối số 'where' dường như làm thủ thuật để quản lý không gian tìm kiếm. Btw, sự giúp đỡ cho changes.funs đọc gần như tôi sẽ viết về một số kinh nghiệm gần đây của tôi. :) – Iterator

Trả lời

13

Đầu tiên, nhờ @ mathematics.coffee đưa tôi vào con đường sử dụng gói mvbutils của Mark Bravington. Chức năng foodweb là thỏa đáng hơn.

Để tóm tắt lại, tôi muốn biết về việc kiểm tra một gói, giả sử myPackage so với một gói khác, nói externalPackage và về cách kiểm tra tập lệnh theo số externalPackage. Tôi sẽ chứng minh làm thế nào để làm mỗi. Trong trường hợp này, gói bên ngoài là data.table.

1: Đối với myPackage so data.table, các lệnh sau đây là đủ:

library(mvbutils) 
library(myPackage) 
library(data.table) 
ixWhere <- match(c("myPackage","data.table"), search()) 
foodweb(where = ixWhere, prune = ls("package:data.table"), descendents = FALSE) 

Điều này tạo ra một biểu đồ hiển thị tuyệt vời mà các chức năng phụ thuộc vào chức năng trong data.table. Mặc dù biểu đồ bao gồm các phụ thuộc trong phạm vi data.table, nhưng nó không quá nặng nề: tôi có thể dễ dàng thấy chức năng của mình phụ thuộc vào data.table và các chức năng nào họ sử dụng, chẳng hạn như as.data.table, data.table, :=, key, v.v. Tại thời điểm này, người ta có thể nói rằng vấn đề phụ thuộc gói được giải quyết, nhưng foodweb cung cấp nhiều hơn nữa, vì vậy chúng ta hãy nhìn vào đó. Phần thú vị là ma trận phụ thuộc.

depMat <- foodweb(where = ixWhere, prune = ls("package:data.table"), descendents = FALSE, plotting = FALSE) 
ix_sel <- grep("^myPackage.",rownames(depMat)) 
depMat <- depMat[ix_sel,] 
depMat <- depMat[,-ix_sel] 
ix_drop <- which(colSums(depMat) == 0) 
depMat <- depMat[,-ix_drop] 
ix_drop <- which(rowSums(depMat) == 0) 
depMat <- depMat[-ix_drop,] 

Điều này thật tuyệt vời: bây giờ nó hiển thị các phụ thuộc của các hàm trong gói của tôi, nơi tôi đang sử dụng tên tiết, ví dụ: myPackage.cleanData, về chức năng không trong gói của tôi, cụ thể là chức năng trong data.table và loại bỏ hàng và cột không có phụ thuộc. Điều này là ngắn gọn, cho phép tôi khảo sát phụ thuộc một cách nhanh chóng, và tôi có thể tìm thấy bộ bổ sung cho các chức năng của tôi khá dễ dàng, quá, bằng cách xử lý rownames(depMat).

NB: plotting = FALSE dường như không ngăn thiết bị vẽ sơ đồ được tạo, ít nhất là lần đầu tiên foodweb được gọi trong một chuỗi các cuộc gọi. Đó là gây phiền nhiễu, nhưng không khủng khiếp. Có lẽ tôi đang làm điều gì sai.

2: Đối với tập lệnh so với data.table, điều này sẽ thú vị hơn một chút. Đối với mỗi tập lệnh, tôi cần tạo một hàm tạm thời và sau đó kiểm tra các phụ thuộc. Tôi có một chức năng nhỏ bên dưới làm chính xác điều đó.

listFiles <- dir(pattern = "myScript*.r") 
checkScriptDependencies <- function(fname){ 
    require(mvbutils) 
    rawCode <- readLines(fname) 
    toParse <- paste("localFunc <- function(){", paste(rawCode, sep = "\n", collapse = "\n"), "}", sep = "\n", collapse = "") 
    newFunc <- eval(parse(text = toParse)) 
    ix  <- match("data.table",search()) 
    vecPrune <- c("localFunc", ls("package:data.table")) 
    tmpRes <- foodweb(where = c(environment(),ix), prune = vecPrune, plotting = FALSE) 
    tmpMat <- tmpRes$funmat 
    tmpVec <- tmpMat["localFunc",] 
    return(tmpVec) 
} 

listDeps <- list() 
for(selFile in listFiles){ 
    listDeps[[selFile]] <- checkScriptDependencies(selFile) 
} 

Bây giờ, tôi chỉ cần nhìn vào listDeps, và tôi có cùng một loại hiểu biết chút tuyệt vời mà tôi có từ depMat trên. Tôi đã sửa đổi checkScriptDependencies từ mã khác mà tôi đã viết gửi các tập lệnh được phân tích theo codetools::checkUsage; thật tốt khi có một chức năng nhỏ như thế này để phân tích mã độc lập. Kudo đến @Spacedman@Tommy để có thông tin chi tiết giúp cải thiện cuộc gọi thành foodweb, sử dụng environment().

(True hungaRians sẽ thông báo rằng tôi không nhất quán với thứ tự tên và loại - tooBad. :) Có lý do dài hơn cho điều này, nhưng điều này không chính xác là mã tôi đang sử dụng.)


Mặc dù tôi đã không được đăng hình ảnh của đồ thị được tạo ra bởi foodweb cho mã của tôi, bạn có thể thấy một số ví dụ đẹp tại http://web.archive.org/web/20120413190726/http://www.sigmafield.org/2010/09/21/r-function-of-the-day-foodweb. Trong trường hợp của tôi, đầu ra của nó chắc chắn nắm bắt việc sử dụng của data.table là :=J, cùng với các hàm được đặt tên chuẩn, như keyas.data.table. Dường như để xóa các tìm kiếm văn bản của tôi và là một cải tiến theo nhiều cách (ví dụ: tìm các hàm mà tôi đã bỏ qua).

Tất cả trong tất cả, foodweb là một công cụ tuyệt vời và tôi khuyến khích người khác khám phá gói mvbutils và một số gói đẹp khác của Mark Bravington, chẳng hạn như debug. Nếu bạn cài đặt mvbutils, chỉ cần kiểm tra ?changed.funs nếu bạn nghĩ rằng chỉ có bạn đấu tranh với việc quản lý mã R đang phát triển. :)

Các vấn đề liên quan