studio統計 - 免費r語言




如何製作一個很好的R可重複的例子 (16)

指南:

制定問題的主要目的應該是讓讀者盡可能輕鬆地理解並在他們的系統上重現您的問題。 為此:

  1. 提供輸入數據
  2. 提供預期的產出
  3. 簡潔地解釋你的問題
    • 如果您有超過20行文本+代碼,您可以返回並簡化
    • 盡可能簡化代碼,同時保留問題/錯誤

這確實需要一些工作但似乎是公平的權衡,因為你要求別人為你工作。

提供數據:

內置數據集

到目前為止 ,最好的選擇是依賴內置數據集。 這使得其他人可以輕鬆解決您的問題。 在R提示符下鍵入data()以查看可用的數據。 一些經典的例子:

  • iris
  • mtcars
  • ggplot2::diamonds (外部包,但幾乎每個人都有)

有關如何查找適合您的問題的數據集,請參閱此SO QA

如果您能夠重新解釋您的問題以使用內置數據集,則您更有可能獲得良好的答案(和upvotes)。

自生成數據

如果您的問題非常特定於現有數據集中未表示的數據類型,則提供R代碼,該代碼生成您的問題在其中顯示的最小可能數據集。 例如

set.seed(1)  # important to make random data reproducible
myData <- data.frame(a=sample(letters[1:5], 20, rep=T), b=runif(20))

現在有人試圖回答我的問題,可以復制/粘貼這兩行,並立即開始處理問題。

dput

作為最後的手段 ,您可以使用dput將數據對象轉換為R代碼(例如dput(myData) )。 我說作為“最後的手段”,因為dput的輸出通常相當笨拙,很難復制粘貼,並且模糊了你的其餘問題。

提供預期輸出:

有人說過:

預期輸出的圖片價值1000字

- 一個非常聰明的人

如果您可以添加“我希望得到此結果”之類的內容:

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

對於你的問題,人們更有可能快速了解你想要做什麼。 如果您的預期結果很大且難以處理,那麼您可能還沒有充分考慮如何簡化問題(參見下文)。

簡潔地解釋你的問題

要做的主要是在提出問題之前盡可能簡化問題。 重新構建問題以使用內置數據集將在這方面提供很多幫助。 您還經常會發現,只需通過簡化過程,您就可以回答自己的問題。

以下是一些好問題的例子:

在這兩種情況下,用戶的問題幾乎肯定不是他們提供的簡單示例。 相反,他們抽像出問題的本質,並將其應用於一個簡單的數據集來提出問題。

為什麼還有這個問題的另一個答案?

這個答案側重於我認為最佳實踐:使用內置數據集並以最小的形式提供您期望的結果。 最突出的答案集中在其他方面。 我不希望這個答案上升到任何突出的地位; 這只是為了讓我可以在對新手問題的評論中鏈接到它。

在與同事討論性能,教學,發送錯誤報告或在郵件列表上搜索指導時,以及在Stack Overflow上,通常會詢問可重現的示例並始終提供幫助。

您有什麼建議創建優秀示例的提示? 如何以文本格式粘貼r中的數據結構? 您應該包括哪些其他信息?

除了使用dput()dump()還是structure()之外還有其他技巧嗎? 什麼時候應該包含library()require()語句? 除cdfdata等外,還應避免使用哪些保留字?

如何製作一個很好的可重複的例子?


R-help郵件列表有一個發布指南 ,其中包括詢問和回答問題,包括生成數據的示例:

示例:有時提供一個人可以實際運行的小例子會有所幫助。 例如:

如果我有一個矩陣x如下:

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

如何將其轉換為包含8行的數據框,以及名為“row”,“col”和“value”的三列,其維度名稱為“row”和“col”的值,如下所示:

  > x.df
     row col value
  1    A   x      1

...
(答案可能是:

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

小字這個詞特別重要。 您應該瞄準一個可重複性最小的示例,這意味著數據和代碼應該盡可能簡單地解釋問題。

編輯:漂亮的代碼比醜陋的代碼更容易閱讀。 使用樣式指南


以下是我的一些建議:

  • 嘗試使用默認的R數據集
  • 如果您有自己的數據集,請將其包含在內dput,以便其他人可以更輕鬆地幫助您
  • install.package()除非確實有必要,否則不要使用,如果您只是使用require或者,人們會理解library
  • 盡量簡潔,

    • 有一些數據集
    • 嘗試盡可能簡單地描述您需要的輸出
    • 在提出問題之前自己動手
  • 上傳圖片很容易,如果有的話上傳圖表
  • 還包括您可能遇到的任何錯誤

所有這些都是可重複的例子的一部分。


到目前為止,答案對於再現性部分來說顯然是很好的。 這僅僅是為了澄清一個可重複的例子不能也不應該是問題的唯一組成部分。 不要忘記解釋你想要它的樣子和問題的輪廓,而不僅僅是你到目前為止試圖到達那裡的方式。 代碼還不夠; 你還需要單詞。

這是一個可重複的例子,說明要避免做什麼(從一個真實的例子中提取,名稱改為保護無辜者):

以下是我遇到問題的示例數據和部分功能。

code
code
code
code
code (40 or so lines of it)

我怎樣才能做到這一點?


可重複性最小的示例包含以下項目:

  • 重現錯誤所必需的最小數據集
  • 重現錯誤所需的最小可運行代碼,可以在給定的數據集上運行。
  • 有關所使用的軟件包,R版本及其運行的系統的必要信息。
  • 在隨機過程的情況下,種子(由set.seed()設置)用於再現性

查看已使用函數的幫助文件中的示例通常很有幫助。 通常,那裡給出的所有代碼都滿足最小可重複示例的要求:提供數據,提供最少的代碼,並且一切都是可運行的。

生成最小數據集

對於大多數情況,只需提供帶有某些值的矢量/數據幀即可輕鬆完成。 或者您可以使用大多數軟件包提供的內置數據集之一。
可以使用library(help = "datasets")查看內置數據集的完整列表。 每個數據集都有一個簡短的描述,例如使用?mtcars可以獲得更多信息,其中'mtcars'是列表中的數據集之一。 其他包可能包含其他數據集。

製作矢量很容易。 有時需要為它添加一些隨機性,並且有許多功能可以實現。 sample()可以隨機化一個向量,或者給出一個只有幾個值的隨機向量。 letters是包含字母表的有用矢量。 這可以用於製作因素。

幾個例子:

  • 隨機值: x <- rnorm(10)表示正態分佈, x <- runif(10)表示均勻分佈,...
  • 一些值的置換: x <- sample(1:10)對於矢量1:10的隨機順序。
  • 隨機因素: x <- sample(letters[1:4], 20, replace = TRUE)

對於矩陣,可以使用matrix() ,例如:

matrix(1:10, ncol = 2)

可以使用data.frame()完成數據幀。 應注意在數據框中命名條目,並且不要使其過於復雜。

一個例子 :

set.seed(1)
Data <- data.frame(
    X = sample(1:10),
    Y = sample(c("yes", "no"), 10, replace = TRUE)
)

對於某些問題,可能需要特定格式。 對於這些,可以使用任何提供的as.someType函數: as.factoras.Dateas.xts ,...這些與向量和/或數據框技巧相結合。

複製您的數據

如果你有一些使用這些技巧難以構建的數據,那麼你總是可以使用例如head()subset()或索引來創建原始數據的subset() 。 然後使用例如。 dput()給我們一些可以立即放入R的東西:

> dput(head(iris,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 = 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")

如果您的數據框具有多個級別的因子,則輸出輸出可能不實用,因為它仍將列出所有可能的因子級別,即使它們不存在於數據子集中。 要解決此問題,可以使用droplevels()函數。 請注意以下物種是如何只有一個水平的因素:

> dput(droplevels(head(iris, 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")

另一個針對dput警告是,它不適用於鍵控data.table對像或來自dplyr分組tbl_df (class grouped_df )。 在這些情況下,您可以在共享之前轉換回常規數據框, dput(as.data.frame(my_data))

在最壞的情況下,您可以使用read.tabletext參數提供可以讀取的文本表示:

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)

生成最少的代碼

這應該是容易的部分,但往往不是。 你不應該做的是:

  • 添加所有類型的數據轉換。 確保提供的數據格式正確(除非這是問題)
  • 複製粘貼一個產生錯誤的整個函數/代碼塊。 首先,嘗試找出確切導致錯誤的行。 通常情況下,你會發現自己的問題是什麼。

你應該做的是:

  • 如果您使用any,請添加應使用的包(使用library()
  • 如果你打開連接或創建文件,添加一些代碼來關閉它們或刪除文件(使用unlink()
  • 如果更改選項,請確保代碼包含一個語句,以將其還原為原始語句。 (例如op <- par(mfrow=c(1,2)) ...some code... par(op)
  • 測試在新的空R會話中運行您的代碼,以確保代碼是可運行的。 人們應該能夠在控制台中復制粘貼您的數據和代碼,並獲得與您完全相同的信息。

提供額外信息

在大多數情況下,僅R版本和操作系統就足夠了。 當與包發生衝突時,給出sessionInfo()的輸出確實有幫助。 在談論與其他應用程序的連接時(無論是通過ODBC還是其他任何應用程序),還應該為這些應用程序提供版本號,如果可能,還應提供有關設置的必要信息。

如果您使用rstudioapi::versionInfo()R Studio中運行R,則可以幫助報告您的RStudio版本。

如果您對特定包有問題,可能需要通過提供packageVersion("name of the package")的輸出來提供包的版本。


可重複的代碼是獲得幫助的關鍵。 但是,有許多用戶可能會對即使是一大塊數據進行粘貼持懷疑態度。 例如,他們可能正在處理敏感數據或收集用於研究論文的原始數據。 出於任何原因,我認為在公開粘貼數據之前有一個方便的功能來“變形”我的數據會很好。 來自SciencesPo包的anonymize函數非常愚蠢,但對我來說它與dput函數很好地配合。

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

然後我將它匿名化:

> 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

在應用匿名化和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

如果數據中有一個或多個factor變量想要使用dput(head(mydata))重現,請考慮droplevels添加droplevels ,以便最小化數據集中不存在的因子級別為不包括在您的dput輸出中,以使示例最小化

dput(droplevels(head(mydata)))

就個人而言,我更喜歡“一個”襯裡。 一些事情:

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)

數據結構應該模仿作者的問題而不是確切的逐字結構。 當變量不覆蓋我自己的變量或上帝禁止的函數(如df )時,我真的很感激。

或者,可以剪切幾個角並指向預先存在的數據集,例如:

library(vegan)
data(varespec)
ord <- metaMDS(varespec)

不要忘記提及您可能使用的任何特殊包裝。

如果你想在更大的物體上展示一些東西,你可以試試

my.df2 <- data.frame(a = sample(10e6), b = sample(letters, 10e6, replace = TRUE))

如果您通過raster包處理空間數據,則可以生成一些隨機數據。 在包裝插圖中可以找到很多例子,但這裡有一個小塊。

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)

如果您需要在sp實現某些空間對象,則可以通過“空間”包中的外部文件(如ESRI shapefile)獲取一些數據集(請參閱任務視圖中的空間視圖)。

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

我想知道http://old.r-fiddle.org/鏈接是否是一種非常簡潔的方式來分享問題。 它接收一個唯一的ID,甚至可以考慮將其嵌入到SO中。


我有一個非常簡單有效的方法來製作一個上面沒有提到的R例子。 您可以先定義結構。 例如,

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

>fix(mydata)

然後您可以手動輸入數據​​。 這對於較小的示例而不是較大的示例是有效的。


有時,無論您嘗試多麼努力,問題都無法通過較小的數據再現,並且合成數據不會發生(儘管顯示您如何生成不能重現問題的合成數據集很有用,因為它排除了一些假設)。

  • 可能需要在某處將數據發佈到Web並提供URL。
  • 如果數據無法向公眾發布但可以共享,那麼您可以通過電子郵件將其發送給感興趣的各方(儘管這會減少需要工作的人數)在上面)。
  • 我實際上沒有看到這樣做,因為無法發布數據的人對於以任何形式發布數據都很敏感,但似乎有理由認為,如果數據被充分匿名化/加擾/損壞,仍然可以發布數據某種程度上來說。

如果您不能做其中任何一項,那麼您可能需要聘請一名顧問來解決您的問題......

編輯 :匿名/加擾的兩個有用的SO問題:


要快速創建數據dput ,您只需將數據(一段)複製到剪貼板並在R中運行以下命令:

對於Excel中的數據:

dput(read.table("clipboard",sep="\t",header=TRUE))

對於txt文件中的數據:

dput(read.table("clipboard",sep="",header=TRUE))

如有必要,您可以更改後者中的sep 。 這只適用於您的數據當然在剪貼板中的情況。


這是一個很好的指南:

http://www.r-bloggers.com/three-tips-for-posting-good-questions-to-r-help-and-stack-overflow/

但最重要的是:確保你製作了一小段我們可以運行的代碼來查看問題所在。 一個有用的函數是dput() ,但是如果你有非常大的數據,你可能想要製作一個小的樣本數據集,或者只使用前10行左右。

編輯:

還要確保您確定問題出在哪裡。 該示例不應該是整個R腳本,“On line 200存在錯誤”。 如果你使用R中的調試工具(我喜歡browser() )和谷歌你應該能夠真正確定問題所在,並重現一個簡單的例子,其中同樣的事情出錯。


通常,您需要一些示例數據,但是,您不希望發布您的確切數據。 要在已建立的庫中使用某些現有data.frame,請使用data命令將其導入。

例如,

data(mtcars)

然後解決問題

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

最好使用testthat包中的函數來顯示您期望發生的內容。因此,其他人可以更改您的代碼,直到它運行沒有錯誤。這減輕了那些想要幫助你的人的負擔,因為這意味著他們不必解碼你的文字描述。例如

library(testthat)
# code defining x and y
if (y >= 10) {
    expect_equal(x, 1.23)
} else {
    expect_equal(x, 3.21)
}

比“我認為對於等於或超過10的x會出現1.23,否則為3.21,但我沒有得到結果”更清楚。即使在這個愚蠢的例子中,我認為代碼比單詞更清晰。使用testthat讓你的助手專注於代碼,這可以節省時間,並為他們提供一種方式讓他們知道他們已經解決了你的問題,然後再發布它


除了我發現非常有趣的所有上述答案之外,它有時可能非常容易,因為它在這裡討論: - 如何做一個最小的可重複的例子來獲得R的幫助

製作隨機向量的方法有很多種方法創建一個100數字向量,其中R中的隨機值舍入為2位小數或R中的隨機矩陣

mydf1<- matrix(rnorm(20),nrow=20,ncol=5)

請注意,由於諸如尺寸等各種原因,有時共享給定數據非常困難。但是,當想要製作可重現的數據示例時,所有上述答案都很好並且非常重要。但請注意,為了使數據具有代表性(如果OP不能共享原始數據),最好用數據示例添加一些信息(如果我們調用數據mydf1)

class(mydf1)
# this shows the type of the data you have 
dim(mydf1)
# this shows the dimension of your data

此外,應該知道可以是數據結構的數據的類型,長度和屬性

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




r-faq