Cách tạo một ví dụ R có thể tái tạo tuyệt vời
Khi thảo luận về hiệu suất với đồng nghiệp, giảng dạy, gửi báo cáo lỗi hoặc tìm kiếm hướng dẫn về danh sách gửi thư và tại đây trên Stack Overflow, một ví dụ có thể tái tạo thường được hỏi và luôn hữu ích.
Mẹo của bạn để tạo một ví dụ xuất sắc là gì? Làm cách nào để bạn dán các cấu trúc dữ liệu từ r vào một định dạng văn bản? Bạn nên bao gồm những thông tin nào khác?
Có thủ thuật nào khác ngoài việc sử dụng dput()
, dump()
hoặc structure()
? Khi nào bạn nên bao gồm library()
hoặc require()
tuyên bố? Mà từ dành riêng nên một tránh, ngoài việc c
, df
, data
, vv?
Làm thế nào để tạo ra một ví dụ có thể tái tạo r tuyệt vời ?
Trả lời
Một ví dụ có thể tái tạo tối thiểu bao gồm các mục sau:
- một tập dữ liệu tối thiểu, cần thiết để chứng minh vấn đề
- mã chạy được tối thiểu cần thiết để tái tạo lỗi, có thể chạy trên tập dữ liệu đã cho
- thông tin cần thiết về các gói đã sử dụng, phiên bản R và hệ thống mà nó đang chạy.
- trong trường hợp các quá trình ngẫu nhiên, một hạt giống (được thiết lập bởi
set.seed()
) cho độ tái lập 1
Để có các ví dụ về các ví dụ có thể tái tạo tối thiểu tốt , hãy xem các tệp trợ giúp của hàm bạn đang sử dụng. Nói chung, tất cả mã được cung cấp ở đó đáp ứng các yêu cầu của một ví dụ có thể tái tạo tối thiểu: dữ liệu được cung cấp, mã tối thiểu được cung cấp và mọi thứ đều có thể chạy được. Ngoài ra, hãy xem các câu hỏi trên Stack Overflow với nhiều lượt ủng hộ.
Tạo tập dữ liệu tối thiểu
Đối với hầu hết các trường hợp, điều này có thể dễ dàng thực hiện bằng cách chỉ cung cấp một khung vector / dữ liệu với một số giá trị. Hoặc bạn có thể sử dụng một trong các bộ dữ liệu tích hợp, được cung cấp với hầu hết các gói.
Có thể xem danh sách toàn diện các bộ dữ liệu tích hợp sẵn library(help = "datasets")
. Có một mô tả ngắn cho mọi tập dữ liệu và có thể lấy thêm thông tin, ví dụ như ?mtcars
trong đó 'mtcars' là một trong những tập dữ liệu trong danh sách. Các gói khác có thể chứa các tập dữ liệu bổ sung.
Tạo một vector rất dễ dàng. Đôi khi cần phải thêm một số ngẫu nhiên vào nó, và có một số chức năng để thực hiện điều đó. sample()
có thể ngẫu nhiên hóa một vectơ hoặc cho một vectơ ngẫu nhiên chỉ có một vài giá trị. letters
là một vectơ hữu ích chứa bảng chữ cái. Điều này có thể được sử dụng để tạo ra các yếu tố.
Một vài ví dụ :
- giá trị ngẫu nhiên:
x <- rnorm(10)
cho phân phối chuẩn,x <- runif(10)
cho phân phối đồng đều, ... - hoán vị của một số giá trị:
x <- sample(1:10)
cho vectơ 1:10 theo thứ tự ngẫu nhiên. - một yếu tố ngẫu nhiên:
x <- sample(letters[1:4], 20, replace = TRUE)
Đối với ma trận, người ta có thể sử dụng matrix()
, ví dụ:
matrix(1:10, ncol = 2)
Tạo khung dữ liệu có thể được thực hiện bằng cách sử dụng data.frame()
. Người ta nên chú ý đặt tên cho các mục trong khung dữ liệu và không làm cho nó quá phức tạp.
Một ví dụ :
set.seed(1)
Data <- data.frame(
X = sample(1:10),
Y = sample(c("yes", "no"), 10, replace = TRUE)
)
Đối với một số câu hỏi, có thể cần các định dạng cụ thể. Đối với những, người ta có thể sử dụng bất kỳ cung cấp as.someType
các chức năng: as.factor
, as.Date
, as.xts
, ... Những kết hợp với vector và / hoặc khung dữ liệu thủ đoạn.
Sao chép dữ liệu của bạn
Nếu bạn có một số dữ liệu đó sẽ là quá khó khăn để xây dựng sử dụng những lời khuyên này, sau đó bạn luôn có thể làm cho một tập hợp con của dữ liệu ban đầu của bạn, sử dụng head()
, subset()
hoặc các chỉ số. Sau đó, sử dụng dput()
để cung cấp cho chúng tôi một cái gì đó có thể được đưa vào R ngay lập tức:
> dput(iris[1:4, ]) # first four rows of the iris data set
structure(list(Sepal.Length = c(5.1, 4.9, 4.7, 4.6), Sepal.Width = c(3.5,
3, 3.2, 3.1), Petal.Length = c(1.4, 1.4, 1.3, 1.5), Petal.Width = c(0.2,
0.2, 0.2, 0.2), Species = structure(c(1L, 1L, 1L, 1L), .Label = c("setosa",
"versicolor", "virginica"), class = "factor")), .Names = c("Sepal.Length",
"Sepal.Width", "Petal.Length", "Petal.Width", "Species"), row.names = c(NA,
4L), class = "data.frame")
Nếu khung dữ liệu của bạn có một yếu tố với nhiều mức, dput
đầu ra có thể khó sử dụng vì nó vẫn sẽ liệt kê tất cả các mức yếu tố có thể có ngay cả khi chúng không có trong tập con dữ liệu của bạn. Để giải quyết vấn đề này, bạn có thể sử dụng droplevels()
hàm. Lưu ý bên dưới cách loài là một nhân tố chỉ có một cấp độ:
> dput(droplevels(iris[1:4, ]))
structure(list(Sepal.Length = c(5.1, 4.9, 4.7, 4.6), Sepal.Width = c(3.5,
3, 3.2, 3.1), Petal.Length = c(1.4, 1.4, 1.3, 1.5), Petal.Width = c(0.2,
0.2, 0.2, 0.2), Species = structure(c(1L, 1L, 1L, 1L), .Label = "setosa",
class = "factor")), .Names = c("Sepal.Length", "Sepal.Width",
"Petal.Length", "Petal.Width", "Species"), row.names = c(NA,
4L), class = "data.frame")
Khi sử dụng dput
, bạn cũng có thể chỉ muốn bao gồm các cột có liên quan:
> dput(mtcars[1:3, c(2, 5, 6)]) # first three rows of columns 2, 5, and 6
structure(list(cyl = c(6, 6, 4), drat = c(3.9, 3.9, 3.85), wt = c(2.62,
2.875, 2.32)), row.names = c("Mazda RX4", "Mazda RX4 Wag", "Datsun 710"
), class = "data.frame")
Một lưu ý khác dput
là nó sẽ không hoạt động đối với data.table
các đối tượng được khóa hoặc đối với tbl_df
(lớp grouped_df
) được nhóm từ dplyr
. Trong những trường hợp này, bạn có thể chuyển đổi trở lại khung dữ liệu thông thường trước khi chia sẻ , dput(as.data.frame(my_data))
.
Trường hợp xấu nhất, bạn có thể đưa ra một biểu diễn văn bản có thể đọc được bằng cách sử dụng text
tham số read.table
:
zz <- "Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa"
Data <- read.table(text=zz, header = TRUE)
Sản xuất mã tối thiểu
Đây là phần dễ dàng nhưng thường là không. Điều bạn không nên làm, là:
- thêm tất cả các loại chuyển đổi dữ liệu. Đảm bảo dữ liệu được cung cấp đã ở đúng định dạng (tất nhiên trừ khi đó là vấn đề)
- sao chép-dán toàn bộ hàm / đoạn mã gây ra lỗi. Đầu tiên, hãy cố gắng xác định chính xác dòng nào gây ra lỗi. Thường xuyên hơn không, bạn sẽ tự mình tìm ra vấn đề.
Điều bạn nên làm là:
- thêm gói nào nên được sử dụng nếu bạn sử dụng bất kỳ (sử dụng
library()
) - nếu bạn mở kết nối hoặc tạo tệp, hãy thêm một số mã để đóng chúng hoặc xóa tệp (sử dụng
unlink()
) - nếu bạn thay đổi các tùy chọn, hãy đảm bảo rằng mã có chứa một câu lệnh để hoàn nguyên chúng trở lại các tùy chọn ban đầu. (vd
op <- par(mfrow=c(1,2)) ...some code... par(op)
) - chạy thử mã của bạn trong phiên R mới, trống để đảm bảo mã có thể chạy được. Mọi người có thể chỉ cần sao chép-dán dữ liệu và mã của bạn trong bảng điều khiển và nhận được chính xác như những gì bạn có.
Cung cấp thêm thông tin
Trong hầu hết các trường hợp, chỉ cần phiên bản R và hệ điều hành là đủ. Khi xung đột phát sinh với các gói, việc đưa ra đầu ra của sessionInfo()
có thể thực sự hữu ích. Khi nói về các kết nối với các ứng dụng khác (có thể là thông qua ODBC hoặc bất kỳ thứ gì khác), người ta cũng nên cung cấp số phiên bản cho những ứng dụng đó và nếu có thể thì cung cấp thông tin cần thiết về thiết lập.
Nếu bạn đang chạy R trong R Studio bằng cách sử dụng rstudioapi::versionInfo()
có thể hữu ích để báo cáo phiên bản RStudio của bạn.
Nếu bạn gặp sự cố với một gói cụ thể, bạn có thể muốn cung cấp phiên bản của gói đó bằng cách đưa ra kết quả packageVersion("name of the package")
.
1 Lưu ý: Đầu ra của set.seed()
khác nhau giữa R> 3.6.0 và các phiên bản trước. Hãy chỉ định phiên bản R mà bạn đã sử dụng cho quy trình ngẫu nhiên và đừng ngạc nhiên nếu bạn nhận được kết quả hơi khác khi làm theo các câu hỏi cũ. Để có được kết quả tương tự trong những trường hợp như vậy, bạn có thể sử dụng- RNGversion()
function trước set.seed()
(ví dụ RNGversion("3.5.2")
:).
(Đây là lời khuyên của tôi từ Cách viết một ví dụ có thể tái tạo được . Tôi đã cố gắng làm cho nó ngắn gọn nhưng ngọt ngào)
Làm thế nào để viết một ví dụ có thể tái tạo.
Bạn có nhiều khả năng nhận được trợ giúp tốt cho vấn đề R của mình nếu bạn cung cấp một ví dụ có thể tái tạo. Một ví dụ có thể tái tạo cho phép người khác tạo lại sự cố của bạn chỉ bằng cách sao chép và dán mã R.
Có bốn điều bạn cần bao gồm để làm cho ví dụ của bạn có thể tái tạo: các gói bắt buộc, dữ liệu, mã và mô tả về môi trường R của bạn.
Các gói phải được tải ở đầu tập lệnh, vì vậy, thật dễ dàng để xem ví dụ cần những gói nào.
Cách dễ nhất để đưa dữ liệu vào email hoặc câu hỏi về Stack Overflow là sử dụng
dput()
để tạo mã R để tạo lại nó. Ví dụ, để tạo lạimtcars
tập dữ liệu trong R, tôi sẽ thực hiện các bước sau:- Chạy
dput(mtcars)
trong R - Sao chép đầu ra
- Trong tập lệnh có thể tái tạo của tôi, hãy nhập
mtcars <-
rồi dán.
- Chạy
Dành một chút thời gian để đảm bảo rằng mã của bạn dễ dàng cho người khác đọc:
đảm bảo rằng bạn đã sử dụng khoảng trắng và tên biến của bạn ngắn gọn nhưng đầy đủ thông tin
sử dụng nhận xét để chỉ ra vấn đề của bạn nằm ở đâu
cố gắng hết sức để loại bỏ tất cả mọi thứ không liên quan đến vấn đề.
Mã của bạn càng ngắn thì càng dễ hiểu.
Bao gồm đầu ra của
sessionInfo()
một nhận xét trong mã của bạn. Điều này tóm tắt môi trường R của bạn và giúp dễ dàng kiểm tra xem bạn có đang sử dụng gói lỗi thời hay không.
Bạn có thể kiểm tra xem bạn đã thực sự tạo một ví dụ có thể tái tạo bằng cách bắt đầu một phiên R mới và dán tập lệnh của bạn vào.
Trước khi đưa tất cả mã của bạn vào email, hãy cân nhắc đưa nó lên Gist github . Nó sẽ cung cấp cho mã của bạn làm nổi bật cú pháp đẹp mắt và bạn không phải lo lắng về bất cứ điều gì bị làm sai lệch bởi hệ thống email.
Cá nhân tôi thích lót "một" hơn. Một cái gì đó dọc theo dòng:
my.df <- data.frame(col1 = sample(c(1,2), 10, replace = TRUE),
col2 = as.factor(sample(10)), col3 = letters[1:10],
col4 = sample(c(TRUE, FALSE), 10, replace = TRUE))
my.list <- list(list1 = my.df, list2 = my.df[3], list3 = letters)
Cấu trúc dữ liệu nên bắt chước ý tưởng về vấn đề của người viết chứ không phải cấu trúc nguyên văn chính xác. Tôi thực sự đánh giá cao nó khi các biến không ghi đè lên các biến của riêng tôi hoặc thần thánh cấm, các hàm (như df
).
Ngoài ra, người ta có thể cắt một vài góc và trỏ đến tập dữ liệu đã có từ trước, giống như:
library(vegan)
data(varespec)
ord <- metaMDS(varespec)
Đừng quên đề cập đến bất kỳ gói đặc biệt nào bạn có thể đang sử dụng.
Nếu bạn đang cố gắng thể hiện điều gì đó trên các vật thể lớn hơn, bạn có thể thử
my.df2 <- data.frame(a = sample(10e6), b = sample(letters, 10e6, replace = TRUE))
Nếu bạn đang làm việc với dữ liệu không gian thông qua raster
gói, bạn có thể tạo một số dữ liệu ngẫu nhiên. Có thể tìm thấy rất nhiều ví dụ trong họa tiết gói, nhưng đây là một mục nhỏ.
library(raster)
r1 <- r2 <- r3 <- raster(nrow=10, ncol=10)
values(r1) <- runif(ncell(r1))
values(r2) <- runif(ncell(r2))
values(r3) <- runif(ncell(r3))
s <- stack(r1, r2, r3)
Nếu bạn đang cần một số đối tượng không gian như được triển khai trong sp
, bạn có thể lấy một số bộ dữ liệu thông qua các tệp bên ngoài (như tệp hình dạng ESRI) trong các gói "không gian" (xem dạng xem Không gian trong Dạng xem Tác vụ).
library(rgdal)
ogrDrivers()
dsn <- system.file("vectors", package = "rgdal")[1]
ogrListLayers(dsn)
ogrInfo(dsn=dsn, layer="cities")
cities <- readOGR(dsn=dsn, layer="cities")
Lấy cảm hứng từ chính bài đăng này, bây giờ tôi sử dụng một chức năng tiện dụng
reproduce(<mydata>)
khi tôi cần đăng lên StackOverflow.
HƯỚNG DẪN NHANH
Nếu myData
là tên đối tượng của bạn để tái tạo, hãy chạy như sau trong R:
install.packages("devtools")
library(devtools)
source_url("https://raw.github.com/rsaporta/pubR/gitbranch/reproduce.R")
reproduce(myData)
Chi tiết:
Hàm này là một trình bao bọc thông minh để dput
và thực hiện những việc sau:
- tự động lấy mẫu một tập dữ liệu lớn (dựa trên kích thước và lớp. Kích thước mẫu có thể được điều chỉnh)
- tạo ra một
dput
đầu ra - cho phép bạn chỉ định mà các cột để xuất khẩu
- nối vào mặt trước của nó
objName <- ...
để có thể dễ dàng sao chép + dán, nhưng ... - Nếu làm việc trên mac, đầu ra được sao chép tự động vào khay nhớ tạm, để bạn có thể chỉ cần chạy nó và sau đó dán vào câu hỏi của mình.
Nguồn có sẵn tại đây:
- Github - pubR / tái tạo.R
Thí dụ:
# sample data
DF <- data.frame(id=rep(LETTERS, each=4)[1:100], replicate(100, sample(1001, 100)), Class=sample(c("Yes", "No"), 100, TRUE))
DF khoảng 100 x 102. Tôi muốn lấy mẫu 10 hàng và một vài cột cụ thể
reproduce(DF, cols=c("id", "X1", "X73", "Class")) # I could also specify the column number.
Cung cấp đầu ra sau:
This is what the sample looks like:
id X1 X73 Class
1 A 266 960 Yes
2 A 373 315 No Notice the selection split
3 A 573 208 No (which can be turned off)
4 A 907 850 Yes
5 B 202 46 Yes
6 B 895 969 Yes <~~~ 70 % of selection is from the top rows
7 B 940 928 No
98 Y 371 171 Yes
99 Y 733 364 Yes <~~~ 30 % of selection is from the bottom rows.
100 Y 546 641 No
==X==============================================================X==
Copy+Paste this part. (If on a Mac, it is already copied!)
==X==============================================================X==
DF <- structure(list(id = structure(c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 25L, 25L, 25L), .Label = c("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y"), class = "factor"), X1 = c(266L, 373L, 573L, 907L, 202L, 895L, 940L, 371L, 733L, 546L), X73 = c(960L, 315L, 208L, 850L, 46L, 969L, 928L, 171L, 364L, 641L), Class = structure(c(2L, 1L, 1L, 2L, 2L, 2L, 1L, 2L, 2L, 1L), .Label = c("No", "Yes"), class = "factor")), .Names = c("id", "X1", "X73", "Class"), class = "data.frame", row.names = c(1L, 2L, 3L, 4L, 5L, 6L, 7L, 98L, 99L, 100L))
==X==============================================================X==
Cũng lưu ý rằng toàn bộ đầu ra là một dòng dài, đẹp, không phải là một đoạn cao với các dòng bị cắt nhỏ. Điều này giúp bạn đọc các bài đăng câu hỏi SO dễ dàng hơn và sao chép + dán cũng dễ dàng hơn.
Cập nhật tháng 10 năm 2013:
Bây giờ bạn có thể chỉ định bao nhiêu dòng đầu ra văn bản sẽ chiếm (tức là những gì bạn sẽ dán vào StackOverflow). Sử dụng lines.out=n
đối số cho điều này. Thí dụ:
reproduce(DF, cols=c(1:3, 17, 23), lines.out=7)
sản lượng:
==X==============================================================X==
Copy+Paste this part. (If on a Mac, it is already copied!)
==X==============================================================X==
DF <- structure(list(id = structure(c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 25L,25L, 25L), .Label
= c("A", "B", "C", "D", "E", "F", "G", "H","I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U","V", "W", "X", "Y"), class = "factor"),
X1 = c(809L, 81L, 862L,747L, 224L, 721L, 310L, 53L, 853L, 642L),
X2 = c(926L, 409L,825L, 702L, 803L, 63L, 319L, 941L, 598L, 830L),
X16 = c(447L,164L, 8L, 775L, 471L, 196L, 30L, 420L, 47L, 327L),
X22 = c(335L,164L, 503L, 407L, 662L, 139L, 111L, 721L, 340L, 178L)), .Names = c("id","X1",
"X2", "X16", "X22"), class = "data.frame", row.names = c(1L,2L, 3L, 4L, 5L, 6L, 7L, 98L, 99L, 100L))
==X==============================================================X==
Đây là một hướng dẫn tốt .
Điểm quan trọng nhất là: Chỉ cần đảm bảo rằng bạn tạo một đoạn mã nhỏ mà chúng tôi có thể chạy để xem vấn đề là gì . Một chức năng hữu ích cho việc này là dput()
, nhưng nếu bạn có dữ liệu rất lớn, bạn có thể muốn tạo một tập dữ liệu mẫu nhỏ hoặc chỉ sử dụng 10 dòng đầu tiên hoặc lâu hơn.
BIÊN TẬP:
Ngoài ra, hãy chắc chắn rằng bạn đã xác định được vấn đề là do đâu. Ví dụ không được là toàn bộ tập lệnh R với "Trên dòng 200 có lỗi". Nếu bạn sử dụng các công cụ gỡ lỗi trong R (Tôi yêu thích browser()
) và Google, bạn sẽ có thể thực sự xác định được vấn đề ở đâu và tạo lại một ví dụ nhỏ trong đó điều tương tự cũng xảy ra.
Danh sách gửi thư trợ giúp R có một hướng dẫn đăng bài bao gồm cả việc hỏi và trả lời câu hỏi, bao gồm một ví dụ về cách tạo dữ liệu:
Ví dụ: Đôi khi việc cung cấp một ví dụ nhỏ mà ai đó thực sự có thể thực hiện sẽ hữu ích. Ví dụ:
Nếu tôi có một ma trận x như sau:
> x <- matrix(1:8, nrow=4, ncol=2,
dimnames=list(c("A","B","C","D"), c("x","y"))
> x
x y
A 1 5
B 2 6
C 3 7
D 4 8
>
làm cách nào để biến nó thành một khung dữ liệu có 8 hàng và ba cột có tên là 'row', 'col' và 'value', có tên thứ nguyên là giá trị của 'row' và 'col', như thế này:
> x.df
row col value
1 A x 1
...
(Câu trả lời có thể là:
> x.df <- reshape(data.frame(row=rownames(x), x), direction="long",
varying=list(colnames(x)), times=colnames(x),
v.names="value", timevar="col", idvar="row")
)
Từ nhỏ đặc biệt quan trọng. Bạn nên hướng tới một ví dụ có thể tái tạo tối thiểu , có nghĩa là dữ liệu và mã phải càng đơn giản càng tốt để giải thích vấn đề.
CHỈNH SỬA: Mã đẹp dễ đọc hơn mã xấu. Sử dụng một hướng dẫn phong cách .
Kể từ R.2.14 (tôi đoán) bạn có thể cung cấp trực tiếp biểu diễn văn bản dữ liệu của mình tới read.table
:
df <- read.table(header=TRUE,
text="Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
")
Đôi khi vấn đề thực sự không thể tái tạo được với một phần dữ liệu nhỏ hơn, cho dù bạn có cố gắng đến đâu và không xảy ra với dữ liệu tổng hợp (mặc dù rất hữu ích khi chỉ ra cách bạn tạo tập dữ liệu tổng hợp không tái tạo vấn đề, bởi vì nó đưa ra một số giả thuyết).
- Đăng dữ liệu lên web ở đâu đó và cung cấp URL có thể là cần thiết.
- Nếu dữ liệu không thể được công bố rộng rãi nhưng hoàn toàn có thể được chia sẻ, thì bạn có thể đề nghị gửi nó qua e-mail cho các bên quan tâm (mặc dù điều này sẽ làm giảm số lượng người sẽ làm việc trên đó).
- Tôi thực sự chưa thấy điều này được thực hiện, bởi vì những người không thể phát hành dữ liệu của họ nhạy cảm về việc phát hành nó dưới bất kỳ hình thức nào, nhưng có vẻ hợp lý khi trong một số trường hợp, người ta vẫn có thể đăng dữ liệu nếu nó đủ ẩn danh / xáo trộn / bị hỏng nhẹ một cách nào đó.
Nếu bạn không thể làm một trong hai điều này thì có lẽ bạn cần thuê một nhà tư vấn để giải quyết vấn đề của mình ...
chỉnh sửa : Hai câu hỏi SO hữu ích để ẩn danh / xáo trộn:
- Làm cách nào để tạo tập dữ liệu mẫu từ dữ liệu riêng tư (thay thế các cấp và tên biến bằng các phần giữ địa điểm không thông tin)?
- Cho một tập hợp các số ngẫu nhiên được rút ra từ một phân phối đơn biến liên tục, hãy tìm phân phối
Các câu trả lời cho đến nay rõ ràng là tuyệt vời cho phần tái tạo. Điều này chỉ để làm rõ rằng một ví dụ có thể tái tạo không thể và không nên là thành phần duy nhất của một câu hỏi. Đừng quên giải thích bạn muốn nó trông như thế nào và các đường nét của vấn đề của bạn, chứ không chỉ là cách bạn đã cố gắng đạt được điều đó cho đến nay. Mã là không đủ; bạn cũng cần từ.
Dưới đây là một ví dụ có thể lặp lại về những việc cần tránh làm (lấy từ một ví dụ thực tế, tên được thay đổi để bảo vệ người vô tội):
Sau đây là dữ liệu mẫu và một phần của chức năng tôi gặp sự cố.
code
code
code
code
code (40 or so lines of it)
Làm thế nào tôi có thể đạt được điều này ?
Tôi có một cách rất dễ dàng và hiệu quả để tạo một ví dụ R mà chưa được đề cập ở trên. Bạn có thể xác định cấu trúc của mình trước tiên. Ví dụ,
mydata <- data.frame(a=character(0), b=numeric(0), c=numeric(0), d=numeric(0))
>fix(mydata)
Sau đó, bạn có thể nhập dữ liệu của mình theo cách thủ công. Điều này hiệu quả đối với các ví dụ nhỏ hơn là những ví dụ lớn.
Để nhanh chóng tạo một dput
dữ liệu của mình, bạn chỉ cần sao chép (một phần) dữ liệu vào khay nhớ tạm và chạy như sau trong R:
cho dữ liệu trong Excel:
dput(read.table("clipboard",sep="\t",header=TRUE))
cho dữ liệu trong tệp txt:
dput(read.table("clipboard",sep="",header=TRUE))
Bạn có thể thay đổi sep
cái sau nếu cần. Tất nhiên, điều này sẽ chỉ hoạt động nếu dữ liệu của bạn nằm trong khay nhớ tạm.
Nguyên tắc:
Mục tiêu chính của bạn khi soạn thảo các câu hỏi của bạn phải là làm cho người đọc hiểu và tái tạo vấn đề của bạn trên hệ thống của họ dễ dàng nhất có thể. Làm như vậy:
- Cung cấp dữ liệu đầu vào
- Cung cấp sản lượng mong đợi
- Giải thích vấn đề của bạn một cách ngắn gọn
- nếu bạn có hơn 20 dòng văn bản + mã, bạn có thể quay lại và đơn giản hóa
- đơn giản hóa mã của bạn nhiều nhất có thể trong khi bảo vệ sự cố / lỗi
Điều này có mất một số công việc nhưng có vẻ như là một sự đánh đổi công bằng vì bạn đang yêu cầu người khác làm công việc cho bạn.
Cung cấp dữ liệu:
Tập dữ liệu tích hợp
Lựa chọn tốt nhất cho đến nay là dựa vào bộ dữ liệu tích hợp sẵn. Điều này rất dễ khiến người khác giải quyết vấn đề của bạn. Nhập data()
vào dấu nhắc R để xem dữ liệu nào có sẵn cho bạn. Một số ví dụ cổ điển:
iris
mtcars
ggplot2::diamonds
(gói bên ngoài, nhưng hầu như mọi người đều có nó)
Kiểm tra các tập dữ liệu tích hợp để tìm một tập dữ liệu phù hợp với sự cố của bạn.
Nếu bạn có thể diễn đạt lại vấn đề của mình để sử dụng tập dữ liệu tích hợp, bạn sẽ có nhiều khả năng nhận được câu trả lời tốt (và số phiếu tán thành).
Dữ liệu tự tạo
Nếu sự cố của bạn rất cụ thể đối với một loại dữ liệu không được đại diện trong các tập dữ liệu hiện có, thì hãy cung cấp mã R tạo ra tập dữ liệu nhỏ nhất có thể mà sự cố của bạn thể hiện trên đó. Ví dụ
set.seed(1) # important to make random data reproducible
myData <- data.frame(a=sample(letters[1:5], 20, rep=T), b=runif(20))
Bây giờ ai đó đang cố gắng trả lời câu hỏi của tôi có thể sao chép / dán hai dòng đó và bắt đầu giải quyết vấn đề ngay lập tức.
dput
Phương án cuối cùng , bạn có thể sử dụng dput
để chuyển đổi một đối tượng dữ liệu sang mã R (ví dụ dput(myData)
:). Tôi nói như một "phương sách cuối cùng" vì đầu ra của dput
thường khá khó sử dụng, gây khó chịu khi sao chép-dán và che khuất phần còn lại của câu hỏi của bạn.
Cung cấp đầu ra mong đợi:
Ai đó đã từng nói:
Một bức tranh về sản lượng mong đợi có giá trị 1000 từ
- một người rất khôn ngoan
Nếu bạn có thể thêm một cái gì đó như "Tôi mong đợi nhận được kết quả này":
cyl mean.hp
1: 6 122.28571
2: 4 82.63636
3: 8 209.21429
đối với câu hỏi của bạn, mọi người có nhiều khả năng hiểu nhanh những gì bạn đang cố gắng làm. Nếu kết quả mong đợi của bạn lớn và khó sử dụng, thì có thể bạn chưa nghĩ đủ về cách đơn giản hóa vấn đề của mình (xem tiếp theo).
Giải thích vấn đề của bạn một cách ngắn gọn
Điều chính cần làm là đơn giản hóa vấn đề của bạn càng nhiều càng tốt trước khi bạn đặt câu hỏi. Việc đóng khung lại vấn đề để làm việc với các bộ dữ liệu có sẵn sẽ giúp ích rất nhiều trong vấn đề này. Bạn cũng sẽ thường thấy rằng chỉ cần trải qua quá trình đơn giản hóa, bạn sẽ trả lời được vấn đề của chính mình.
Dưới đây là một số ví dụ về các câu hỏi hay:
- với tập dữ liệu tích hợp
- với dữ liệu do người dùng tạo
Trong cả hai trường hợp, vấn đề của người dùng gần như chắc chắn không xảy ra với các ví dụ đơn giản mà họ cung cấp. Thay vào đó, họ trừu tượng hóa bản chất của vấn đề và áp dụng nó vào một tập dữ liệu đơn giản để đặt câu hỏi.
Tại sao lại có câu trả lời khác cho câu hỏi này?
Câu trả lời này tập trung vào những gì tôi nghĩ là phương pháp hay nhất: sử dụng các tập dữ liệu tích hợp sẵn và cung cấp những gì bạn mong đợi ở dạng tối thiểu. Các câu trả lời nổi bật nhất tập trung vào các khía cạnh khác. Tôi không mong đợi câu trả lời này sẽ trở nên nổi bật; đây chỉ là ở đây để tôi có thể liên kết đến nó trong phần bình luận cho các câu hỏi dành cho người mới.
Mã có thể tái tạo là chìa khóa để nhận trợ giúp. Tuy nhiên, có nhiều người dùng có thể nghi ngờ về việc dán ngay cả một phần dữ liệu của họ. Ví dụ: họ có thể đang làm việc với dữ liệu nhạy cảm hoặc dữ liệu gốc được thu thập để sử dụng trong một bài báo nghiên cứu. Vì bất kỳ lý do gì, tôi nghĩ sẽ rất tuyệt nếu có một chức năng tiện dụng để "làm biến dạng" dữ liệu của tôi trước khi dán nó công khai. Các anonymize
chức năng từ gói SciencesPo
là rất ngớ ngẩn, nhưng đối với tôi nó hoạt động độc đáo với dput
chức năng.
install.packages("SciencesPo")
dt <- data.frame(
Z = sample(LETTERS,10),
X = sample(1:10),
Y = sample(c("yes", "no"), 10, replace = TRUE)
)
> dt
Z X Y
1 D 8 no
2 T 1 yes
3 J 7 no
4 K 6 no
5 U 2 no
6 A 10 yes
7 Y 5 no
8 M 9 yes
9 X 4 yes
10 Z 3 no
Sau đó, tôi ẩn danh nó:
> anonymize(dt)
Z X Y
1 b2 2.5 c1
2 b6 -4.5 c2
3 b3 1.5 c1
4 b4 0.5 c1
5 b7 -3.5 c1
6 b1 4.5 c2
7 b9 -0.5 c1
8 b5 3.5 c2
9 b8 -1.5 c2
10 b10 -2.5 c1
Người ta cũng có thể muốn lấy mẫu một vài biến thay vì toàn bộ dữ liệu trước khi áp dụng lệnh ẩn danh và dput.
# sample two variables without replacement
> anonymize(sample.df(dt,5,vars=c("Y","X")))
Y X
1 a1 -0.4
2 a1 0.6
3 a2 -2.4
4 a1 -1.4
5 a2 3.6
Thông thường, bạn cần một số dữ liệu để làm ví dụ, tuy nhiên, bạn không muốn đăng dữ liệu chính xác của mình. Để sử dụng một số data.frame hiện có trong thư viện đã thiết lập, hãy sử dụng lệnh data để nhập nó.
ví dụ,
data(mtcars)
và sau đó giải quyết vấn đề
names(mtcars)
your problem demostrated on the mtcars data set
Nếu bạn có tập dữ liệu lớn không thể dễ dàng đưa vào tập lệnh bằng cách sử dụng dput()
, hãy đăng dữ liệu của bạn lên pastebin và tải chúng bằng cách sử dụng read.table
:
d <- read.table("http://pastebin.com/raw.php?i=m1ZJuKLH")
Lấy cảm hứng từ @Henrik .
Tôi đang phát triển gói awfield để giải quyết nhu cầu này để nhanh chóng chia sẻ dữ liệu có thể tái tạo, đôi khi dput
hoạt động tốt với các tập dữ liệu nhỏ hơn nhưng nhiều vấn đề mà chúng tôi giải quyết lớn hơn nhiều, chia sẻ một tập dữ liệu lớn như vậy dput
là không thực tế.
Trong khoảng:
awfield cho phép người dùng chia sẻ mã tối thiểu để tái tạo dữ liệu. Người dùng đặtn
(số hàng) và chỉ định bất kỳ số lượng hàm biến đặt trước nào (hiện có 70) bắt chước dữ liệu nếu thực (những thứ như giới tính, tuổi, thu nhập, v.v.)
Cài đặt:
Hiện nay (2015/06/11), Wakefield là một gói phần mềm GitHub nhưng sẽ đi đến cran cuối cùng sau khi kiểm tra đơn vị được viết. Để cài đặt nhanh chóng, hãy sử dụng:
if (!require("pacman")) install.packages("pacman")
pacman::p_load_gh("trinker/wakefield")
Thí dụ:
Đây là một ví dụ:
r_data_frame(
n = 500,
id,
race,
age,
sex,
hour,
iq,
height,
died
)
Điều này tạo ra:
ID Race Age Sex Hour IQ Height Died
1 001 White 33 Male 00:00:00 104 74 TRUE
2 002 White 24 Male 00:00:00 78 69 FALSE
3 003 Asian 34 Female 00:00:00 113 66 TRUE
4 004 White 22 Male 00:00:00 124 73 TRUE
5 005 White 25 Female 00:00:00 95 72 TRUE
6 006 White 26 Female 00:00:00 104 69 TRUE
7 007 Black 30 Female 00:00:00 111 71 FALSE
8 008 Black 29 Female 00:00:00 100 64 TRUE
9 009 Asian 25 Male 00:30:00 106 70 FALSE
10 010 White 27 Male 00:30:00 121 68 FALSE
.. ... ... ... ... ... ... ... ...
Nếu bạn có một hoặc nhiều factor
(các) biến trong dữ liệu mà bạn muốn sao chép lại dput(head(mydata))
, hãy xem xét thêm droplevels
vào đó, để các mức yếu tố không có trong tập dữ liệu thu nhỏ không được đưa vào dput
đầu ra của bạn , để làm cho ví dụ tối thiểu :
dput(droplevels(head(mydata)))
Tôi tự hỏi nếu một http://old.r-fiddle.org/liên kết có thể là một cách rất gọn gàng để chia sẻ một vấn đề. Nó nhận được một ID duy nhất giống như và người ta thậm chí có thể nghĩ về việc nhúng nó vào SO.
Vui lòng không dán các đầu ra bảng điều khiển của bạn như thế này:
If I have a matrix x as follows:
> x <- matrix(1:8, nrow=4, ncol=2,
dimnames=list(c("A","B","C","D"), c("x","y")))
> x
x y
A 1 5
B 2 6
C 3 7
D 4 8
>
How can I turn it into a dataframe with 8 rows, and three
columns named `row`, `col`, and `value`, which have the
dimension names as the values of `row` and `col`, like this:
> x.df
row col value
1 A x 1
...
(To which the answer might be:
> x.df <- reshape(data.frame(row=rownames(x), x), direction="long",
+ varying=list(colnames(x)), times=colnames(x),
+ v.names="value", timevar="col", idvar="row")
)
Chúng tôi không thể sao chép-dán trực tiếp.
Để làm cho các câu hỏi và câu trả lời có thể tái tạo đúng cách, hãy cố gắng xóa +
& >
trước khi đăng nó và đưa #
ra các đầu ra và nhận xét như sau:
#If I have a matrix x as follows:
x <- matrix(1:8, nrow=4, ncol=2,
dimnames=list(c("A","B","C","D"), c("x","y")))
x
# x y
#A 1 5
#B 2 6
#C 3 7
#D 4 8
# How can I turn it into a dataframe with 8 rows, and three
# columns named `row`, `col`, and `value`, which have the
# dimension names as the values of `row` and `col`, like this:
#x.df
# row col value
#1 A x 1
#...
#To which the answer might be:
x.df <- reshape(data.frame(row=rownames(x), x), direction="long",
varying=list(colnames(x)), times=colnames(x),
v.names="value", timevar="col", idvar="row")
Một điều nữa, nếu bạn đã sử dụng bất kỳ chức năng nào từ gói nào đó, hãy đề cập đến thư viện đó.
Bạn có thể làm điều này bằng cách sử dụng reprex .
Như mt1022 đã lưu ý , "... gói tốt để tạo ra ví dụ tối thiểu, có thể tái tạo được là " reprex " từ domainsverse ".
Theo Tidyverse :
Mục tiêu của "reprex" là đóng gói mã có vấn đề của bạn theo cách mà người khác có thể chạy nó và cảm nhận được nỗi đau của bạn.
Một ví dụ được đưa ra trên trang web gọn gàng .
library(reprex)
y <- 1:4
mean(y)
reprex()
Tôi nghĩ đây là cách đơn giản nhất để tạo một ví dụ có thể tái tạo.
Ngoài tất cả các câu trả lời ở trên mà tôi thấy rất thú vị, đôi khi nó có thể rất dễ dàng vì nó được thảo luận ở đây: - LÀM THẾ NÀO ĐỂ LÀM MỘT VÍ DỤ NÂNG CẤP TỐI THIỂU ĐỂ ĐƯỢC GIÚP ĐỠ VỚI R
Có nhiều cách để tạo một vector ngẫu nhiên Tạo một vector 100 số có giá trị ngẫu nhiên trong R được làm tròn thành 2 số thập phân hoặc ma trận ngẫu nhiên trong R
mydf1<- matrix(rnorm(20),nrow=20,ncol=5)
Lưu ý rằng đôi khi rất khó chia sẻ một dữ liệu nhất định vì nhiều lý do khác nhau chẳng hạn như thứ nguyên, v.v. Tuy nhiên, tất cả các câu trả lời trên đều rất hay và rất quan trọng để suy nghĩ và sử dụng khi muốn tạo một ví dụ về dữ liệu có thể tái tạo. Nhưng lưu ý rằng để tạo dữ liệu đại diện như dữ liệu gốc (trong trường hợp OP không thể chia sẻ dữ liệu gốc), tốt hơn là bạn nên thêm một số thông tin với ví dụ dữ liệu như (nếu chúng ta gọi dữ liệu là mydf1)
class(mydf1)
# this shows the type of the data you have
dim(mydf1)
# this shows the dimension of your data
Hơn nữa, người ta phải biết kiểu, độ dài và các thuộc tính của dữ liệu có thể là Cấu trúc dữ liệu
#found based on the following
typeof(mydf1), what it is.
length(mydf1), how many elements it contains.
attributes(mydf1), additional arbitrary metadata.
#If you cannot share your original data, you can str it and give an idea about the structure of your data
head(str(mydf1))
Dưới đây là một số gợi ý của tôi:
- Cố gắng sử dụng bộ dữ liệu R mặc định
- Nếu bạn có tập dữ liệu của riêng mình, hãy bao gồm chúng
dput
, để những người khác có thể giúp bạn dễ dàng hơn - Không sử dụng
install.package()
trừ khi thực sự cần thiết, chỉ cần dùngrequire
hoặclibrary
Cố gắng ngắn gọn,
- Có một số tập dữ liệu
- Cố gắng mô tả đầu ra bạn cần càng đơn giản càng tốt
- Hãy tự mình làm trước khi đặt câu hỏi
- Dễ dàng tải lên một hình ảnh, vì vậy hãy tải lên các lô nếu bạn có
- Cũng bao gồm bất kỳ lỗi nào bạn có thể mắc phải
Tất cả những điều này là một phần của một ví dụ có thể tái tạo.
Bạn nên sử dụng các hàm từ testthat
gói để hiển thị những gì bạn mong đợi sẽ xảy ra. Do đó, những người khác có thể thay đổi mã của bạn cho đến khi nó chạy mà không bị lỗi. Điều này giúp giảm bớt gánh nặng cho những người muốn giúp bạn, vì nó có nghĩa là họ không phải giải mã mô tả bằng văn bản của bạn. Ví dụ
library(testthat)
# code defining x and y
if (y >= 10) {
expect_equal(x, 1.23)
} else {
expect_equal(x, 3.21)
}
rõ ràng hơn là "Tôi nghĩ x sẽ là 1,23 đối với y bằng hoặc vượt quá 10, và ngược lại là 3,21, nhưng tôi không nhận được kết quả". Ngay cả trong ví dụ ngớ ngẩn này, tôi nghĩ mã rõ ràng hơn các từ. Việc sử dụng testthat
cho phép người trợ giúp của bạn tập trung vào mã, điều này giúp tiết kiệm thời gian và nó cung cấp một cách để họ biết rằng họ đã giải quyết được vấn đề của bạn, trước khi họ đăng nó