Как сделать отличный R воспроизводимый пример

2481

При обсуждении работы с коллегами, обучении, отправке отчета об ошибке или поиске инструкций в списках рассылки, а также в разделе "Переполнение стека" часто повторяется воспроизводимый пример и всегда полезно.

Каковы ваши советы по созданию отличного примера? Как вы вставляете структуры данных из в текстовом формате? Какую еще информацию вы должны включить?

Существуют ли другие трюки в дополнение к использованию dput(), dump() или structure()? Когда следует включать в себя library() или require()? Какие зарезервированные слова следует избегать, помимо c, df, data и т.д.?

Как сделать отличный воспроизводимый пример?

  • 34
    Я запутался в объеме вопроса. Люди, похоже, поспешили интерпретировать воспроизводимый пример, задавая вопросы о SO или R-help (как «воспроизвести ошибку»). Как насчет воспроизводимых примеров R на страницах справки? В демоверсиях пакетов? В учебниках / презентациях?
  • 15
    @baptiste: то же самое минус ошибка. Все методы, которые я объяснил, используются на страницах справки пакета, а также в учебниках и презентациях, которые я даю о R
Показать ещё 1 комментарий
Теги:
r-faq

23 ответа

1543
Лучший ответ

Минимальный воспроизводимый пример состоит из следующих элементов:

  • минимальный набор данных, необходимый для воспроизведения ошибки
  • минимальный исполняемый код, необходимый для воспроизведения ошибки, который может быть запущен на данном наборе данных.
  • необходимую информацию о используемых пакетах, версию 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.factor, as.Date, as.xts ,... Они в сочетании с трюками векторных и/или данных.

Скопируйте данные

Если у вас есть некоторые данные, которые было бы слишком сложно построить с помощью этих советов, вы всегда можете сделать подмножество исходных данных, используя, например, head(), 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")

Если ваш фрейм данных имеет коэффициент со многими уровнями, выход dput может быть громоздким, поскольку он все равно будет перечислять все возможные уровни факторов, даже если они не присутствуют в подмножестве ваших данных. Чтобы решить эту проблему, вы можете использовать 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 объекты или для группировки tbl_df (class grouped_df) из dplyr. В этих случаях вы можете конвертировать обратно в обычный кадр данных до совместного использования, dput(as.data.frame(my_data)).

В худшем случае вы можете дать текстовое представление, которое можно прочитать при использовании text параметра 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)

Создание минимального кода

Это должно быть легкой частью, но часто это не так. То, что вы не должны делать, это:

  • добавьте все виды преобразований данных. Убедитесь, что предоставленные данные уже находятся в правильном формате (если это не проблема, конечно)
  • копировать-вставить целую функцию/кусок кода, который дает ошибку. Сначала попробуйте найти, какие строки точно приводят к ошибке. Чаще всего вы узнаете, в чем проблема.

Что вы должны делать, это:

  • добавьте, какие пакеты следует использовать, если вы используете их (используя library())
  • если вы открываете соединения или создаете файлы, добавляете какой-то код, чтобы закрыть их или удалить файлы (используя unlink())
  • если вы измените параметры, убедитесь, что код содержит инструкцию, чтобы вернуть их обратно к исходным. (например, op <- par(mfrow=c(1,2))...some code... par(op))
  • проверите свой код в новом, пустом сеансе R, чтобы убедиться, что код запущен. Люди должны иметь возможность просто скопировать ваши данные и ваш код в консоли и получить то же самое, что и у вас.

Предоставьте дополнительную информацию

В большинстве случаев достаточно только версии R и операционной системы. Когда конфликты возникают с пакетами, предоставление вывода sessionInfo() может действительно помочь. Говоря о подключении к другим приложениям (будь то через ODBC или что-то еще), нужно также указать номера версий для них и, если возможно, также необходимую информацию об установке.

Если вы используете R в R Studio, используя rstudioapi::versionInfo() может быть полезно сообщить о вашей версии RStudio.

Если у вас возникла проблема с конкретным пакетом, вы можете предоставить версию пакета, предоставив результат packageVersion("name of the package").

  • 5
    Как вы используете dput если dput очень большой и проблема генерируется серединой фрейма? Есть ли способ использовать dput для воспроизведения средней части данных, скажем, строк с 60 по 70?
  • 25
    @BgnR Вы можете извлечь часть фрейма данных, используя индексы, например: tmp <- mydf[50:70,] а затем dput(mydf) . Если кадр данных действительно большой, попробуйте выделить проблему и просто отправьте несколько строк, которые вызывают проблему.
Показать ещё 11 комментариев
540

(Здесь мой совет от Как написать воспроизводимый пример. Я пытался сделать его коротким, но сладким)

Как написать воспроизводимый пример.

Вы, скорее всего, получите хорошую помощь с вашей проблемой R, если вы предоставите воспроизводимый пример. Воспроизводимый пример позволяет кому-то еще воссоздать вашу проблему, просто скопировав и вставив R-код.

Есть четыре вещи, которые необходимо включить, чтобы сделать ваш пример воспроизводимым: требуемые пакеты, данные, код и описание вашей среды R.

  • Пакеты должны быть загружены в верхней части script, поэтому легко посмотрите, какие из них нужны для примера.

  • Самый простой способ включить данные в вопрос по электронной почте или переполнение стека - использовать dput() для генерации R-код для его воссоздания. Например, чтобы воссоздать набор данных mtcars в R, Я бы выполнил следующие шаги:

    • Запустите dput(mtcars) в R
    • Скопировать вывод
    • В моем воспроизводимом script введите mtcars <-, затем вставьте.
  • Проведите немного времени, гарантируя, что ваш код легко для других следующим образом:

    • убедитесь, что вы использовали пробелы, а имена переменных краткие, но информативный

    • используйте комментарии, чтобы указать, где находится ваша проблема.

    • сделайте все возможное, чтобы удалить все, что не связано с проблемой.
      Чем короче ваш код, тем легче это понимать.

  • Включите вывод sessionInfo() в комментарии в коде. Это суммирует ваш R окружающей среды и позволяет легко проверить, используете ли вы устаревшие пакет.

Вы можете проверить, действительно ли вы сделали воспроизводимый пример, запустив новый сеанс R и вставив script в.

Перед тем, как поместить весь свой код в электронное письмо, подумайте о том, чтобы положить его на Gist github. Это придаст вашему коду приятный синтаксический подсветка, и вам не придется беспокоиться о чем-либо, искаженном системой электронной почты.

  • 13
    reprex в tidyverse - хороший пакет для создания минимального воспроизводимого примера: github.com/tidyverse/reprex
  • 0
    Зачем кому-то помещать код в электронное письмо?
Показать ещё 1 комментарий
289

Лично я предпочитаю "один" лайнер. Что-то вроде строк:

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) в "пространственных" пакетах (см. Пространственный вид в представлениях задач).

library(rgdal)
ogrDrivers()
dsn <- system.file("vectors", package = "rgdal")[1]
ogrListLayers(dsn)
ogrInfo(dsn=dsn, layer="cities")
cities <- readOGR(dsn=dsn, layer="cities")
  • 0
    ИМХО, при использовании sample или runif целесообразно установить set.seed . По крайней мере, это предположение я получил при создании примеров, касающихся выборки или генерации случайных чисел.
  • 0
    @ Конрад Я согласен, но это может зависеть. Если вы просто пытаетесь сгенерировать какие-то числа, семя может не понадобиться, но если вы пытаетесь понять что-то конкретное, где нужны фиксированные числа, начальное число будет обязательным.
Показать ещё 1 комментарий
269

Вдохновленный этим самым сообщением, теперь я использую удобную функцию
 reproduce(<mydata>), когда мне нужно отправить сообщение в StackOverflow.


БЫСТРЫЕ ИНСТРУКЦИИ

Если myData - это имя вашего объекта для воспроизведения, запустите в R:

install.packages("devtools")
library(devtools)
source_url("https://raw.github.com/rsaporta/pubR/gitbranch/reproduce.R")

reproduce(myData)

Детали:

Эта функция является интеллектуальной оболочкой для dput и выполняет следующие действия:

  • автоматически отображает большой набор данных (в зависимости от размера и класса. Размер выборки можно настроить)
  • создает вывод dput
  • позволяет указать, какие столбцы экспортировать
  • присоединяется к нему objName <- ..., чтобы его можно было легко скопировать + вставить, но...
  • Если вы работаете на Mac, вывод автоматически копируется в буфер обмена, так что вы можете просто запустить его, а затем вставить в свой вопрос.

Источник доступен здесь:


Пример:

# 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 составляет около 100 x 102. Я хочу пробовать 10 строк и несколько конкретных столбцов

reproduce(DF, cols=c("id", "X1", "X73", "Class"))  # I could also specify the column number. 

Дает следующий вывод:

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

Обратите внимание также, что весь вывод находится в хорошей одиночной, длинной строке, а не в высоком абзаце изрезанных строк. Это облегчает чтение сообщений на SO-сообщениях, а также проще скопировать + вставить.


Обновление октябрь 2013:

Теперь вы можете указать, сколько строк текстового вывода будет занимать (то есть, что вы будете вставлять в StackOverflow). Для этого используйте аргумент lines.out=n. Пример:

reproduce(DF, cols=c(1:3, 17, 23), lines.out=7) дает:

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

Вот хорошее руководство:

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

Но самое главное: просто убедитесь, что вы создаете небольшой фрагмент кода, который мы можем запустить, чтобы посмотреть, в чем проблема. Полезной функцией для этого является dput(), но если у вас очень большие данные, вы можете захотеть создать небольшой набор данных образца или использовать только первые 10 строк.

EDIT:

Также убедитесь, что вы определили, где проблема сама. В примере не должно быть целого R script с "В строке 200 есть ошибка". Если вы используете инструменты отладки в R (я люблю browser()) и google, вы должны быть в состоянии действительно определить, где проблема, и воспроизвести тривиальный пример, в котором то же самое происходит неправильно.

162

В списке рассылки 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")

)

Особенно важно слово small. Вы должны стремиться к минимальному воспроизводимому примеру, что означает, что данные и код должны быть как можно более простыми, чтобы объяснить проблему.

РЕДАКТИРОВАТЬ: Довольно простой код легче читать, чем уродливый код. Используйте руководство по стилю.

149

Так как R.2.14 (я думаю), вы можете подавать свое текстовое представление данных непосредственно на read.table:

df <- read.table(header=T, 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
") 
  • 26
    Вы также можете использовать read.table("clipboard", header=TRUE) .
  • 2
    @ Себастьян-с, как это хорошо для того, чтобы сделать воспроизводимый пример ?? :)
Показать ещё 1 комментарий
136

Иногда проблема действительно не воспроизводится с меньшим объемом данных, как бы вы ни старались, и не происходит с синтетическими данными (хотя полезно показать, как вы создали синтетические наборы данных, которые сделали не воспроизвести проблему, поскольку она исключает некоторые гипотезы).

  • Проводка данных в Интернет где-то и предоставление URL-адреса может быть необходимо.
  • Если данные не могут быть выпущены широкой публике, но могут быть доступны вообще, вы можете предложить отправить по электронной почте заинтересованным сторонам (хотя это сократит количество людей, которые будут потрудитесь работать над ним).
  • Я на самом деле не видел этого, потому что люди, которые не могут опубликовать свои данные, чувствительны к выпуску любой формы, но было бы правдоподобно, что в некоторых случаях все еще можно было бы отправлять данные, если бы они были достаточно анонимными/скремблированными/поврежден в некотором роде.

Если вы не можете сделать ни одно из них, вам, вероятно, потребуется нанять консультанта для решения вашей проблемы...

edit: два полезных вопроса SO для анонимности/скремблирования:

  • 1
    Для получения синтетических наборов данных ответы на этот вопрос дают полезные примеры, в том числе приложения fitdistr и fitdistrplus .
120

Ответы, по-видимому, отлично подходят для части воспроизводимости. Это просто пояснить, что воспроизводимый пример не может и не должен быть единственным компонентом вопроса. Не забудьте объяснить, что вы хотите, чтобы это выглядело, и контуры вашей проблемы, а не только то, как вы пытались добраться до сих пор. Кода недостаточно; вам также нужны слова.

Здесь воспроизводимый пример того, чего следует избегать (нарисованный из реального примера, имена изменены для защиты невинных):


Ниже приведены примеры данных и часть функции, с которой я столкнулся.

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

Как я могу это достичь?


115

Чтобы быстро создать dput ваших данных, вы можете просто скопировать (часть) данных в буфер обмена и запустить в R:

для данных в Excel:

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

для данных в файле txt:

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

При необходимости вы можете изменить sep в последнем. Это будет работать, только если ваши данные находятся в буфере обмена, конечно.

112

У меня очень простой и эффективный способ сделать пример R, который не упоминался выше. Сначала вы можете определить свою структуру. Например,

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

>fix(mydata)

Изображение 4261

Затем вы можете ввести свои данные вручную. Это эффективно для небольших примеров, а не для больших.

  • 18
    ... тогда dput(mydata)
  • 0
    Какой твой интерфейс? Было бы неплохо получить полный ответ. Etc создает данные, которые вы можете напрямую зациклить, как for (d in data) {...} .
Показать ещё 1 комментарий
110

Рекомендация:


Ваша основная задача в решении ваших вопросов должна заключаться в том, чтобы как можно проще понять и воспроизвести вашу проблему в своих системах. Для этого:

  • Предоставить входные данные
  • Предоставить ожидаемый результат
  • Объясните свою проблему лаконично
    • Если у вас более 20 строк текста + кода, вы можете вернуться назад и упростить
    • упростите свой код как можно больше, сохранив проблему/ошибку

Это требует некоторой работы, но кажется хорошим компромиссом, поскольку вы просите других работать для вас.

Предоставление данных:


Встроенные наборы данных

Лучший вариант на сегодняшний день - полагаться на встроенные наборы данных. Это облегчает для других работу над вашей проблемой. Введите data() в командной строке R, чтобы узнать, какие данные доступны для вас. Некоторые классические примеры:

  • 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

к вашему вопросу, люди гораздо чаще будут быстро понимать, что вы пытаетесь сделать. Если ваш ожидаемый результат большой и громоздкий, то вы, вероятно, недостаточно думали о том, как упростить свою проблему (см. Следующий).

Кратко объясните свою проблему


Главное, чтобы максимально упростить вашу проблему, прежде чем задавать свой вопрос. Повторное создание проблемы для работы со встроенными наборами данных очень поможет в этом отношении. Вы также часто обнаружите, что просто пройдя процесс упрощения, вы ответите на собственную проблему.

Вот несколько примеров хороших вопросов:

В обоих случаях проблемы пользователей почти наверняка не с простыми примерами, которые они предоставляют. Скорее они отвлекли характер своей проблемы и применили ее к простому набору данных, чтобы задать свой вопрос.

Почему еще один ответ на этот вопрос?


В этом ответе основное внимание уделяется тому, что я считаю лучшей практикой: используйте встроенные наборы данных и обеспечивайте то, что вы ожидаете в результате в минимальной форме. Наиболее важные ответы сосредоточены на других аспектах. Я не ожидаю, что этот ответ станет заметным; это здесь исключительно для того, чтобы я мог ссылаться на него в комментариях к вопросам новичков.

109

Воспроизводимый код является ключевым для получения справки. Однако есть много пользователей, которые могут скептически относиться к вставке даже части их данных. Например, они могут работать с конфиденциальными данными или с исходными данными, собранными для использования в исследовательском документе. По какой-то причине я подумал, что было бы неплохо иметь удобную функцию для "деформирования" моих данных, прежде чем публиковать ее. Функция anonymize из пакета SciencesPo очень глупа, но для меня она отлично работает с функцией 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

Также может потребоваться выборка нескольких переменных вместо всех данных, прежде чем применять команду anonymization и 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
98

Часто вам нужны некоторые данные для примера, однако вы не хотите публиковать свои точные данные. Чтобы использовать некоторые существующие data.frame в установленной библиотеке, используйте команду data для ее импорта.

например.

data(mtcars)

а затем выполните задачу

names(mtcars)
your problem demostrated on the mtcars data set
  • 13
    Многие встроенные наборы данных (например, популярные mtcars данных mtcars и iris ) фактически не нуждаются в вызове data .
82

Если у вас есть большой набор данных, который нельзя легко поместить в script с помощью dput(), отправьте свои данные в pastebin и загрузите их с помощью read.table:

d <- read.table("http://pastebin.com/raw.php?i=m1ZJuKLH")

Вдохновленный @Henrik.

78

Я разрабатываю пакет wakefield, чтобы решить эту проблему, чтобы быстро обмениваться воспроизводимыми данными, иногда dput отлично работает для небольших наборов данных но многие проблемы, с которыми мы имеем дело, намного больше, использование такого большого набора данных через dput нецелесообразно.

О:

wakefield позволяет пользователю делиться минимальным кодом для воспроизведения данных. Пользователь устанавливает n (количество строк) и задает любое количество предустановленных переменных (в настоящее время 70), которые имитируют реальные данные (такие как пол, возраст, доход и т.д.).

Установка:

В настоящее время (2015-06-11), wakefield является пакетом GitHub, но после этого будет отправлен в CRAN после модульных тестов. Для быстрой установки используйте:

if (!require("pacman")) install.packages("pacman")
pacman::p_load_gh("trinker/wakefield")

Пример:

Вот пример:

r_data_frame(
    n = 500,
    id,
    race,
    age,
    sex,
    hour,
    iq,
    height,
    died
)

Это дает:

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

Если у вас есть одна или несколько переменных factor в ваших данных, которые вы хотите воспроизвести с помощью dput(head(mydata)), подумайте о добавлении к ней droplevels, чтобы уровни факторов, которых нет в минимизированной набор данных не включены в ваш вывод dput, чтобы сделать пример минимальным:

dput(droplevels(head(mydata)))
58

Интересно, может ли ссылка http://old.r-fiddle.org/ быть очень аккуратным способом поделиться проблемой. Он получает уникальный идентификатор типа, и можно даже подумать о встраивании его в SO.

  • 0
    но больше не работает ...
  • 0
    Слишком плохо - по общему признанию я полностью забыл о надежности некоторого веб-сервиса, когда я отправил ответ тогда. Я как-то ожидал, что полезный сервис сохранится.
Показать ещё 1 комментарий
42

Не вставляйте консольные выходы следующим образом:

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

Мы не можем скопировать-вставить его напрямую.

Чтобы правильно отвечать на вопросы и ответы, попробуйте удалить + & > перед публикацией и поставить # для выходов и комментариев следующим образом:

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

Еще одна вещь, если вы использовали какую-либо функцию из определенного пакета, укажите эту библиотеку.

  • 1
    Вы удаляете > и добавляете # вручную или есть автоматический способ сделать это?
  • 2
    @BCArg я удаляю > вручную. Но для добавления # я использую Ctrl+Shift+C в редакторе RStudio .
26

Помимо всех вышеперечисленных ответов, которые я нашел очень интересными, иногда это может быть очень просто, поскольку здесь обсуждается: - КАК СДЕЛАТЬ МИНИМАЛЬНЫЙ ВОСПРОИЗВЕДЕННЫЙ ПРИМЕР ПОЛУЧИТЬ ПОМОЩЬ С 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))
23

Вот некоторые из моих предложений:

  • Попробуйте использовать наборы данных по умолчанию R
  • Если у вас есть собственный набор данных, включите их в dput, чтобы другие могли помочь вам легче.
  • Не используйте install.package(), если это действительно необходимо, люди поймут, если вы просто используете require или library
  • Постарайтесь быть краткими,

    • Имейте некоторый набор данных
    • Попробуйте описать требуемый результат как можно проще
    • Сделайте это самостоятельно, прежде чем задавать вопрос
  • Легко загрузить изображение, поэтому загрузите сюжеты, если у вас есть
  • Также укажите любые ошибки, которые могут возникнуть

Все это часть воспроизводимого примера.

  • 1
    Вы действительно не добавили ничего существенного здесь. dput() упоминался ранее, и большая часть этого просто повторяет стандартные рекомендации SO.
  • 1
    У меня была проблема с функцией install.package включенной в пример, которая на самом деле не нужна (по моему мнению). Кроме того, использование набора данных R по умолчанию облегчит воспроизводимость. В руководящих принципах СО ничего конкретно об этих темах не говорится. Кроме того, это было сделано, чтобы высказать свое мнение, и именно с этим я столкнулся больше всего.
22

Вы можете сделать это, используя reprex.

Как отметил mt1022, "... хороший пакет для создания минимального воспроизводимого примера - " reprex " из tidyverse".

Согласно Tidyverse:

Цель "reprex" - упаковать ваш проблемный код таким образом, чтобы другие люди могли запускать его и чувствовать вашу боль.

Пример приведен на веб-сайте tidyverse.

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

Я думаю, что это самый простой способ, чтобы создать воспроизводимый пример.

13

Рекомендуется использовать функции из пакета testthat, чтобы показать, что вы ожидаете. Таким образом, другие люди могут изменять ваш код до тех пор, пока он не будет работать без ошибок. Это облегчает бремя тех, кто хотел бы помочь вам, потому что это означает, что им не нужно расшифровывать ваше текстовое описание. Например

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

понятнее, чем "Я думаю, х получится равным 1,23 для y, равным или превышающим 10, и 3.21 в противном случае, но я не получил ни результата". Даже в этом глупом примере, я думаю, код более ясный, чем слова. Использование testthat позволяет вашему помощнику сосредоточиться на коде, что экономит время, и это дает им возможность узнать, что они решили вашу проблему, прежде чем отправлять ее

Ещё вопросы

Сообщество Overcoder
Наверх
Меню