Làm thế nào để tạo một ví dụ tái tạo R tuyệt vời


2473

Khi thảo luận về hiệu suất với các đồ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à ở đây trên Stack Overflow, một ví dụ có thể lặp lại thường được hỏi và luôn hữu ích.

Lời khuyên của bạn để tạo ra một ví dụ tuyệt vời là gì? Làm thế nào để bạn dán cấu trúc dữ liệu từtrong một định dạng văn bản? Những thông tin khác bạn nên bao gồm?

Có những thủ thuật khác ngoài việc sử dụng dput(), dump()hay 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 để làm cho một tuyệt vời ví dụ tái sản xuất?


34
Tôi bối rối về phạm vi của câu hỏi. Mọi người dường như đã nhảy vào việc giải thích ví dụ có thể lặp lại khi đặt câu hỏi về SO hoặc R-help (cách "tái tạo lỗi"). Điều gì về các ví dụ R tái tạo trong các trang trợ giúp? Trong gói demo? Trong hướng dẫn / thuyết trình?
baptiste

15
@baptiste: Cùng trừ lỗi. Tất cả các kỹ thuật tôi đã giải thích được sử dụng trong các trang trợ giúp gói, và trong các hướng dẫn và thuyết trình tôi đưa ra về R
Joris Meys

33
Dữ liệu đôi khi là yếu tố giới hạn, vì cấu trúc có thể quá phức tạp để mô phỏng. Để tạo dữ liệu công khai từ dữ liệu riêng tư: stackoverflow.com/a/10458688/742447 trong stackoverflow.com/questions/10454973/ Kẻ
Etienne Low-Décarie

Câu trả lời:


1727

Một ví dụ tái sản xuất tối thiểu bao gồm các mục sau đây:

  • một bộ dữ liệu tối thiểu, cần thiết để chứng minh vấn đề
  • có thể chạy 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, phiên bản R và hệ thống được sử dụng.
  • trong trường hợp các quá trình ngẫu nhiên, một hạt giống (được đặt bởi set.seed()) cho độ tái lập 1

Để biết 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 chức năng bạn đang sử dụng. Nói chung, tất cả các mã được cung cấp ở đó đáp ứng các yêu cầu của một ví dụ có thể lặp lại 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. Cũng xem xét các câu hỏi trên Stack Overflow với rất nhiều upvote.

Sản xuất một bộ 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ể được thực hiện dễ dàng bằng cách chỉ cung cấp một vectơ / khung 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.
Một danh sách đầy đủ các bộ dữ liệu tích hợp có thể được nhìn thấy với 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ụ với ?mtcars'mtcars' là một trong những bộ dữ liệu trong danh sách. Các gói khác có thể chứa các bộ dữ liệu bổ sung.

Làm một vector là dễ dàng. Đôi khi nó là cần thiết để thêm một số ngẫu nhiên cho nó, và có một số lượng lớn các chức năng để thực hiện điều đó. sample()có thể ngẫu nhiên một vectơ, hoặc đưa ra một vectơ ngẫu nhiên chỉ với một vài giá trị. letterslà một vector hữu ích chứa bảng chữ cái. Điều này có thể được sử dụng để làm cho 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 bình thường, x <- runif(10)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)

Làm khung dữ liệu có thể được thực hiện bằng cách sử dụng data.frame(). Người ta phải 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, định dạng cụ thể có thể cần thiết. Đối với những, người ta có thể sử dụng bất kỳ cung cấp as.someTypecá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 đặt 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ố có nhiều cấp độ, 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ể ngay cả khi chúng không có trong tập hợp con của dữ liệu của bạn. Để giải quyết vấn đề này, bạn có thể sử dụng droplevels()chức năng. Lưu ý bên dưới cách loài là một yếu 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 cảnh báo khác dputlà nó sẽ không hoạt động cho data.tablecác đối tượng được khóa hoặc cho nhóm tbl_df(lớp grouped_df) 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 texttham số của 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 sẽ là phần dễ dàng nhưng thường thì không. Những gì 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 đã ở định dạng chính xác (trừ khi đó là vấn đề của khóa học)
  • sao chép-dán toàn bộ chức năng / đoạn mã gây ra lỗi. Trước tiên, hãy cố gắng xác định chính xác các dòng dẫn đến lỗi. Thường xuyên hơn không bạn sẽ tìm ra vấn đề là gì chính mình.

Những gì bạn nên làm là:

  • thêm gói nào sẽ đượ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 hoặc xóa tệp (sử dụng unlink())
  • nếu bạn thay đổi tùy chọn, hãy đảm bảo mã chứa câu lệnh để hoàn nguyên chúng trở lại câu gốc. (ví dụ op <- par(mfrow=c(1,2)) ...some code... par(op))
  • kiểm tra chạy mã của bạn trong một phiên R mới, trống để đảm bảo mã có thể chạy được. Mọi người sẽ 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ư bạn có.

Cung cấp thêm thông tin

Trong hầu hết các trường hợp, chỉ có phiên bản R và hệ điều hành sẽ đủ. Khi xung đột xảy ra với các gói, việc đưa ra đầu ra sessionInfo()thực sự có thể giúp ích. Khi nói về các kết nối với các ứng dụng khác (có thể thông qua ODBC hoặc bất cứ 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ể cũng là thông tin cần thiết về thiết lập.

Nếu bạn đang chạy R trong R Studio 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 vấn đề 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 đầu ra packageVersion("name of the package").


1 Lưu ý: Đầu ra của các 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 nào 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 nhau khi làm theo các câu hỏi cũ. Để có được kết quả tương tự trong các trường hợp như vậy, bạn có thể sử dụng hàm-hàm RNGversion()trước set.seed()(ví dụ RNGversion("3.5.2"):).


6
Làm thế nào để bạn sử dụng dputnếu khung dữ liệu rất lớn và vấn đề được tạo ra ở giữa khung dữ liệu? Có cách nào để sử dụng dputđể tái tạo phần giữa của dữ liệu, giả sử các hàng 60 đến 70 không?
BgnR

27
@BgnR Bạn có thể trích xuất một phần của khung dữ liệu bằng các chỉ mục, ví dụ: tmp <- mydf[50:70,]theo sau dput(mydf). Nếu khung dữ liệu thực sự lớn, hãy thử cách ly vấn đề và chỉ gửi một vài dòng gây ra sự cố.
Joris Meys

4
@JorisMeys: Có cách nào để nói headhoặc dputgiới hạn dữ liệu ở cấp độ N đệ quy không? Tôi đang cố gắng đưa ra ví dụ có thể lặp lại và dữ liệu của tôi là một danh sách các khung dữ liệu. Vì vậy, dput(head(myDataObj))dường như là không đủ, vì nó tạo ra một tệp đầu ra có kích thước 14MB.
Alexanderr Blekh

5
@JorisMeys: Chỉ cần FYI - đã đăng câu hỏi trong phần bình luận ở trên dưới dạng một câu hỏi riêng biệt: stackoverflow.com/questions/25127026/ .
Alexanderr Blekh

4
@Konrad Điều tốt nhất bạn có thể làm là liên kết đến tệp và đưa ra lệnh tối thiểu để đọc trong tệp đó. Điều đó sẽ ít rắc rối hơn so với việc sao chép đầu ra của dput () :)
Joris Meys

590

(Đâ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 . Tôi đã cố gắng làm cho nó ngắn nhưng ngọt ngào)

Làm thế nào để viết một ví dụ tái sản xuất.

Bạn rất có thể nhận được sự giúp đỡ tốt với vấn đề R của bạn nếu bạn cung cấp một ví dụ có thể lặp lại. Một ví dụ có thể tái tạo cho phép người khác tạo lại vấn đề của bạn bằng cách chỉ sao chép và dán mã R.

Có bốn điều bạn cần đưa vào để làm cho ví dụ của bạn có thể lặp lại: các gói, dữ liệu, mã và mô tả về môi trường R của bạn.

  • Các gói nên được tải ở đầu tập lệnh, vì vậy thật dễ dàng để xem gói nào cần ví dụ.

  • Cách dễ nhất để bao gồm dữ liệu trong email hoặc câu hỏi Stack Overflow là sử dụng dput()để tạo mã R để tạo lại nó. Ví dụ: để tạo lại mtcarstập dữ liệu trong R, tôi sẽ thực hiện các bước sau:

    1. Chạy dput(mtcars)trong R
    2. Sao chép đầu ra
    3. Trong tập lệnh tái tạo của tôi, gõ mtcars <-sau đó dán.
  • Dành một chút thời gian để đảm bảo rằng của bạn dễ đọc cho người khác:

    • đảm bảo bạn đã sử dụng khoảng trắng và tên biến của bạn ngắn gọn, nhưng nhiều thông tin

    • sử dụng ý kiến ​​để chỉ ra vấn đề của bạn nằm ở đâu

    • làm hết sức mình để loại bỏ 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 bạn 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 đặt tất cả mã của bạn vào email, hãy xem xét đưa nó vào Gith github . Nó sẽ làm nổi bật mã cú pháp của bạn và bạn không phải lo lắng về bất cứ điều gì bị hệ thống email xử lý sai.


24
reprextrong tidyverselà một gói tốt để sản xuất ví dụ tối thiểu, có thể tái tạo: github.com/tidyverse/reprex
mt1022

19
Tôi thường xuyên nhận được email với mã trong đó. Tôi thậm chí còn nhận được email với các tài liệu từ đính kèm có chứa mã. Đôi khi tôi thậm chí nhận được email với các tài liệu từ đính kèm có chứa mã SCREENSHOTS.
hadley

304

Cá nhân, tôi thích "một" lót. 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 nhà văn 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 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 một tập dữ liệu có sẵn, đại loại 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 đối tượng 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 rastergói, bạn có thể tạo một số dữ liệu ngẫu nhiên. Rất nhiều ví dụ có thể được tìm thấy trong họa tiết gói, nhưng đây là một mẩu 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 cần một số đối tượng không gian như được triển khai sp, bạn có thể nhận một số bộ dữ liệu qua các tệp bên ngoài (như shapefile ESRI) trong các gói "không gian" (xem chế độ xem Không gian trong Chế độ xem nhiệm vụ).

library(rgdal)
ogrDrivers()
dsn <- system.file("vectors", package = "rgdal")[1]
ogrListLayers(dsn)
ogrInfo(dsn=dsn, layer="cities")
cities <- readOGR(dsn=dsn, layer="cities")

1
IMHO, khi sử dụng samplehoặc runifnó thận trọng set.seed. Ít nhất, đây là gợi ý tôi đã nhận được khi đưa ra các ví dụ chuyển tiếp về lấy mẫu hoặc tạo số ngẫu nhiên.
Konrad

1
@Konrad Tôi đồng ý, nhưng điều này có thể phụ thuộc. Nếu bạn chỉ cố gắng tạo ra một số số thì có thể không cần một hạt giống nhưng nếu bạn đang cố gắng hiểu một cái gì đó cụ thể ở những nơi cần số cố định, thì một hạt giống sẽ là bắt buộc.
Roman Luštrik

1
Luôn luôn tốt hơn với imo hạt giống, giúp dễ dàng so sánh giải pháp của riêng mình với đầu ra dự kiến, so sánh các giải pháp giữa chúng và theo cách này người dùng không biết (và không cần biết) các chức năng như runifhoặc samplekhông bị nhầm lẫn rằng họ không thể có được cùng một dữ liệu.
Moody_Mudskipper

2
@mikey bạn đã xem gói usmap chưa?
Roman Luštrik

2
@mikey gói tigris tải shapefiles từ Cục điều tra dân số theo nhiều định dạng
camille

277

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 myDatalà tên của đối tượng của bạn để sao chép, 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:

Chức năng này là một trình bao bọc thông minh để dputvà thực hiện các thao tá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. Có thể điều chỉnh kích thước mẫu)
  • tạo ra một dput đầu ra
  • cho phép bạn chỉ định các cột để xuất khẩu
  • gắn 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 máy mac, đầu ra được sao chép tự động vào bảng 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 ở đây:


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 có kích thước 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 nằm trong một dòng đơn dài, đẹp, không phải là một đoạn cao của các dòng được xắt nhỏ. Điều này làm cho nó dễ đọc hơn trên các câu hỏi SO và cũng dễ sao chép + dán hơn.


Cập nhật tháng 10 năm 2013:

Bây giờ bạn có thể chỉ định có bao nhiêu dòng đầu ra văn bản sẽ chiếm (nghĩa 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==

196

Đâ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 ta 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:

Cũng đảm bảo rằng bạn đã xác định được vấn đề là ở đâu. Ví dụ không nên 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 browser()) và Google, bạn sẽ có thể thực sự xác định được vấn đề ở đâu và tái tạo một ví dụ tầm thường trong đó điều tương tự xảy ra.


165

Danh sách gửi thư trợ giúp R ​​có một hướng dẫn đăng bao gồm cả hỏi và trả lời câu hỏi, bao gồm một ví dụ về việc tạo dữ liệu:

Ví dụ: Đôi khi nó giúp cung cấp một ví dụ nhỏ mà ai đó thực sự có thể chạy. 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 tôi có thể biến nó thành một khung dữ liệu với 8 hàng và ba cột có tên 'hàng', 'col' và 'value', có tên thứ nguyên là giá trị của 'hàng' 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 đến một ví dụ có thể lặp lại tối thiểu , điều đó có nghĩa là dữ liệu và mã phải đơn giản nhất có thể để giải thích vấn đề.

EDIT: Mã đẹp dễ đọc hơn mã xấu. Sử dụng một hướng dẫn phong cách .


164

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
") 

3
@ sebastian-c làm thế nào là tốt để làm ví dụ tái sản xuất ?? :)
TMS

@TMS Hãy suy nghĩ nghiêm túc, nếu người hỏi đã cung cấp dữ liệu và vấn đề nhỏ (nhưng có thể có một vài giải pháp), thì có thể nhanh hơn và bạn vẫn có thể làm theo tất cả các bước.
sebastian-c

146

Đôi khi, sự cố thực sự không thể lặp lại với một phần dữ liệu nhỏ hơn, cho dù bạn có cố gắng đến mức nào và không xảy ra với dữ liệu tổng hợp (mặc dù nó hữu ích để hiển thị cách bạn tạo ra các bộ dữ liệu tổng hợp không tái tạo vấn đề, bởi vì nó loại trừ một số giả thuyết).

  • Đăng dữ liệu lên web ở đâu đó và cung cấp URL có thể 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ể cung cấp e-mail cho các bên quan tâm (mặc dù điều này sẽ cắt 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ể tiết lộ dữ liệu của họ rất 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ý rằng trong một số trường hợp, người ta vẫn có thể đăng dữ liệu nếu nó được ẩn danh / bị 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 những điều này thì có lẽ bạn cần phải thuê một chuyên gia tư vấn để giải quyết vấn đề của bạn ...

biên tập : Hai câu hỏi SO hữu ích để ẩn danh / xáo trộn:


1
Để tạo các tập dữ liệu tổng hợp, các câu trả lời cho câu hỏi này đưa ra các ví dụ hữu ích, bao gồm các ứng dụng fitdistrfitdistrplus.
Lặp lại

137

Các câu trả lời cho đến nay rõ ràng là tuyệt vời cho phần tái sản xuất. Điều này chỉ đơn thuần là để làm rõ rằng một ví dụ có thể lặp lại không thể và không phải là thành phần duy nhất của câu hỏi. Đừng quên giải thích những gì 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, không chỉ là cách bạn đã cố gắng để đạt được điều đó cho đến nay. Mã không đủ; bạn cũng cần lời nói

Đây là một ví dụ có thể lặp lại về những điều cần tránh (rút ra từ một ví dụ thực tế, tên đã thay đổi để bảo vệ người vô tội):


Sau đây là dữ liệu mẫu và một phần chức năng tôi gặp rắc rối.

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 ?



124

Tôi có một cách rất dễ dàng và hiệu quả để tạo một ví dụ R chưa được đề cập ở trên. Bạn có thể xác định cấu trúc của bạn trước tiên. Ví dụ,

mydata <- data.frame(a=character(0), b=numeric(0),  c=numeric(0), d=numeric(0))

>fix(mydata)

Khi bạn thực thi lệnh 'fix', bạn sẽ nhận được hộp bật lên này

Sau đó, bạn có thể nhập dữ liệu của bạn bằng tay. Điều này là hiệu quả cho các ví dụ nhỏ hơn là lớn.


18
... sau đódput(mydata)
GSee

Frontend của bạn là gì? Nó sẽ là tốt đẹp để có một câu trả lời đầy đủ. Vv tạo một dữ liệu mà bạn có thể trực tiếp lặp như thế nào for (d in data) {...}.
Léo Léopold Hertz 30/10/2016

119

Để nhanh chóng tạo ra một dput dữ liệu của bạn, bạn chỉ cần sao chép (một phần) dữ liệu vào khay nhớ tạm của mình 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 sepsau này nếu cần thiết. Điều này sẽ chỉ hoạt động nếu dữ liệu của bạn là trong clipboard tất nhiên.


116

Hướng dẫn:


Mục tiêu chính của bạn trong việc tạo ra các câu hỏi của bạn là làm cho người đọc dễ hiểu và tái tạo vấn đề của bạn trên hệ thống của họ. Làm như vậy:

  1. Cung cấp dữ liệu đầu vào
  2. Cung cấp đầu ra dự kiến
  3. Giải thích vấn đề của bạn 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 càng nhiều càng tốt trong khi bảo vệ vấn đề / lỗi

Điều này có một số công việc nhưng có vẻ như một sự đánh đổi công bằng vì bạn đang yêu cầu người khác làm việc cho bạn.

Cung cấp dữ liệu:


Bộ dữ liệu tích hợp

Tùy chọn tốt nhất cho đến nay là dựa vào bộ dữ liệu tích hợp. Điều này làm cho nó rất dễ dàng cho những người khác làm việc về vấn đề của bạn. Nhập data()tại dấu nhắc R để xem dữ liệu nào có sẵn cho bạn. Một số ví dụ kinh điển:

  • iris
  • mtcars
  • ggplot2::diamonds (gói bên ngoài, nhưng hầu như mọi người đều có nó)

Xem SO QA này để biết cách tìm bộ dữ liệu phù hợp với vấn đề của bạn.

Nếu bạn có thể viết lại vấn đề của mình để sử dụng bộ dữ liệu tích hợp, bạn có nhiều khả năng nhận được câu trả lời tốt (và upvote).

Dữ liệu tự tạo

Nếu vấn đề của bạn rất cụ thể đối với một loại dữ liệu không được biểu thị 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à vấn đề của bạn thể hiệ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 xử lý vấn đề ngay lập tức.

dput

Như một phương sách cuối cùng , bạn có thể sử dụng dputđể chuyển đổi một đối tượng dữ liệu thành mã R (ví dụ dput(myData)). Tôi nói như một "phương sách cuối cùng" bởi vì đầu ra củadput thường khá khó sử dụng, gây khó chịu cho việc 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 dự kiến:


Ai đó đã từng nói:

Một bức tranh về sản lượng dự kiến ​​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 dự kiến ​​sẽ nhận được kết quả này":

   cyl   mean.hp
1:   6 122.28571
2:   4  82.63636
3:   8 209.21429

với câu hỏi của bạn, mọi người có nhiều khả năng nhanh chóng hiểu 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ó lẽ bạn chưa nghĩ đủ về cách đơn giản hóa vấn đề của mình (xem tiếp).

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. Đóng khung lại vấn đề để làm việc với các bộ dữ liệu tích hợp sẽ giúp ích rất nhiều cho vấn đề này. Bạn cũng sẽ thường thấy rằng chỉ bằng cách trải qua quá trình đơn giản hóa, bạn sẽ trả lời 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:

Trong cả hai trường hợp, các vấn đề của người dùng gần như chắc chắn không phải 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 đề của họ và áp dụng nó vào một tập dữ liệu đơn giản để đặt câu hỏi của họ.

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à cách thực hành tốt nhất: sử dụng các bộ dữ liệu tích hợp và cung cấp những gì bạn mong đợi do kết quả ở 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ẽ tăng lên bất kỳ sự nổi bật nào; đây chỉ là ở đây để tôi có thể liên kết với nó trong các bình luận cho câu hỏi của người mới.


113

Mã sinh sản 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 thậm chí một đoạn dữ liệu của họ. Chẳng hạn, họ có thể làm việc với dữ liệu nhạy cảm hoặc trên dữ liệu gốc được thu thập để sử dụng trong bài nghiên cứu. Vì bất kỳ lý do gì, tôi nghĩ rằng sẽ rất tố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 công khai. Các anonymizechức năng từ gói SciencesPorất ngớ ngẩn, nhưng đối với tôi nó hoạt động độc đáo với dputchứ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à lệnh 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

102

Thường thì bạn cần một số dữ liệu cho một 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 đó làm vấn đề

names(mtcars)
your problem demostrated on the mtcars data set

13
Nhiều bộ dữ liệu tích hợp (như bộ dữ liệu phổ biến mtcarsirisbộ dữ liệu) thực sự không cần datasử dụng cuộc gọi.
Gregor Thomas

92

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 .


90

Tôi đang phát triển gói Wakefield để giải quyết nhu cầu này để nhanh chóng chia sẻ dữ liệu có thể lặp lại, đôi khi dputhoạt động tốt đối với các tập dữ liệu nhỏ hơn nhưng nhiều vấn đề chúng tôi xử lý lớn hơn nhiều, việc chia sẻ một tập dữ liệu lớn như vậy dputlà không thực tế.

Trong khoảng:

Wakefield 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ố lượng hàng) và chỉ định bất kỳ số hàm biến số đặt trước nào (hiện có 70) bắt chước thực nếu dữ liệu (những thứ như giới tính, tuổi tác, 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, 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
.. ...   ... ...    ...      ... ...    ...   ...

72

Nếu bạn có một hoặc nhiều factorbiến trong dữ liệu mà bạn muốn tạo lại dput(head(mydata)), hãy xem xét thêm droplevelsvào nó, để mức độ các yếu tố không có trong tập dữ liệu được 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)))

65

Tôi tự hỏi nếu một liên kết http://old.r-fiddle.org/ 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 như thế và người ta thậm chí có thể nghĩ về việc nhúng nó vào SO.


47

Vui lòng không dán các đầu ra giao diện đ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 nó trực tiếp.

Để đặt câu hỏi và câu trả lời có thể sao chép chính xác, hãy thử xóa +& >trước khi đăng nó và đưa #ra kết quả và nhận xét 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")

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 nhất định, hãy đề cập đến thư viện đó.


2
Bạn có gỡ bỏ >và thêm #thủ công không hoặc có cách nào tự động để làm điều đó không?
BCArg

3
@BCArg Tôi xóa >thủ công. Nhưng, ngoài ra #, tôi sử dụng Ctrl+Shift+Cphím tắt trong RStudiotrình chỉnh sửa.
dùng2100721

33

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 là " reprex " từ tidyverse ".

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 nỗi đau của bạn.

Một ví dụ được đưa ra trên trang web tidyverse .

library(reprex)
y <- 1:4
mean(y)
reprex() 

Tôi nghĩ rằng đây là cách đơn giản nhất để tạo ra một ví dụ có thể tái tạo.


33

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 có thể rất dễ dàng khi được thảo luận ở đây: - CÁCH KIẾM MỘT VÍ DỤ TRẢ LỜI TỐI THIỂU ĐỂ GIÚP VỚI R

Có nhiều cách để tạo một vectơ ngẫu nhiên Tạo một vectơ 100 số với cá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 như kích thước, 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ụ dữ liệu có thể lặp lại. Nhưng lưu ý rằng để tạo dữ liệu làm đại diện như ban đầu (trong trường hợp OP không thể chia sẻ dữ liệu gốc), bạn nên thêm một số thông tin với ví dụ dữ liệu là (nếu chúng tôi 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 nên biết loại, độ dài và 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))

28

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 với 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 nó thực sự cần thiết, mọi người sẽ hiểu nếu bạn chỉ sử dụng requirehoặclibrary
  • Cố gắng súc tích,

    • Có một số dữ liệu
    • Cố gắng mô tả đầu ra bạn cần một cách đơn giản nhất có thể
    • Tự làm trước khi bạn đặt câu hỏi
  • Thật 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ể có

Tất cả những điều này là một phần của một ví dụ tái sản xuất.


1
Bạn đã không thực sự thêm bất cứ điều gì của chất ở đây. dput()đã được đề cập trước đây và phần lớn trong số này chỉ là nhắc lại các hướng dẫn SO tiêu chuẩn.
Rich Scriven

1
Tôi đã gặp vấn đề với install.packagechức năng được bao gồm trong ví dụ không thực sự cần thiết (theo ý kiến ​​của tôi). Hơn nữa, sử dụng bộ dữ liệu R mặc định sẽ giúp việc tái tạo dễ dàng hơn. Các hướng dẫn SO đã không nói bất cứ điều gì về các chủ đề này cụ thể. Hơn nữa, nó có nghĩa là để đưa ra ý kiến ​​của tôi và đây là những gì tôi đã gặp nhiều nhất.
TheRimalaya

18

Đó là một ý tưởng tốt để sử dụng các chức năng từ testthatgói để hiển thị những gì bạn mong đợi 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 gặp lỗi. Điều này giúp giảm bớt gánh nặng của những người muốn giúp bạn, bởi vì điều đó có nghĩa là họ không phải giải mã mô tả 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 "Tôi nghĩ rằng x sẽ là 1,23 cho y bằng hoặc vượt quá 10 và 3,21 nếu không, nhưng tôi không nhận được kết quả nào". Ngay cả trong ví dụ ngớ ngẩn này, tôi nghĩ rằng mã rõ ràng hơn các từ. Việc sử dụng testthatcho phép người trợ giúp của bạn tập trung vào mã, 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 vấn đề của bạn, trước khi họ đăng nó

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.