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


2473

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

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

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

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


34
Я запутался в объеме вопроса. Люди, похоже, поспешили интерпретировать воспроизводимый пример, задавая вопросы о SO или R-help (как «воспроизвести ошибку»). Как насчет воспроизводимых примеров R на страницах справки? В демоверсиях пакетов? В учебниках / презентациях?
крестить

15
@baptiste: тоже самое минус ошибка. Все методы, которые я объяснил, используются на страницах справки пакета, а также в уроках и презентациях, которые я даю о R
Joris Meys

33
Данные иногда являются ограничивающим фактором, так как структура может быть слишком сложной для моделирования. Чтобы создать общедоступные данные из личных данных: stackoverflow.com/a/10458688/742447 в stackoverflow.com/questions/10454973/…
Этьен Лоу-Декари

Ответы:


1727

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

  • минимальный набор данных, необходимый для демонстрации проблемы
  • минимальный исполняемый код, необходимый для воспроизведения ошибки, который может быть запущен для данного набора данных
  • необходимая информация об используемых пакетах, версии R и системе, в которой она запущена.
  • в случае случайных процессов, семя (задано set.seed()) для воспроизводимости 1

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

Создание минимального набора данных

В большинстве случаев это легко сделать, просто предоставив вектор / фрейм данных с некоторыми значениями. Или вы можете использовать один из встроенных наборов данных, которые поставляются с большинством пакетов.
Полный список встроенных наборов данных можно увидеть с 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(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")

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

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

При использовании dputвы также можете включить только соответствующие столбцы:

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

Еще одно предостережение в dputтом, что он не будет работать для data.tableобъектов с ключами или сгруппированных tbl_df(класс 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").


1 Примечание: вывод set.seed()отличается между R> 3.6.0 и предыдущими версиями. Укажите, какую версию R вы использовали для случайного процесса, и не удивляйтесь, если вы получите несколько отличающиеся результаты при ответе на старые вопросы. Чтобы получить такой же результат в таких случаях, вы можете использовать RNGversion()-функцию раньше set.seed()(например :) RNGversion("3.5.2").


6
Как вы используете, dputесли датафрейм очень большой и проблема генерируется серединой фрейма? Есть ли способ использовать dputдля воспроизведения средней части данных, скажем, строки с 60 по 70?
BgnR

27
@BgnR Вы можете извлечь часть фрейма данных, используя индексы, например: с tmp <- mydf[50:70,]последующим dput(mydf). Если кадр данных действительно большой, попробуйте выделить проблему и просто отправьте несколько строк, которые вызывают проблему.
Йорис Мейс

4
@JorisMeys: есть ли способ сказать headили dputограничить данные до уровня N рекурсивно? Я пытаюсь придумать воспроизводимый пример, и мои данные представляют собой список фреймов данных. Так что, dput(head(myDataObj))похоже, этого недостаточно, поскольку он генерирует выходной файл размером 14 МБ.
Александр Блех

5
@JorisMeys: Просто к вашему сведению - опубликовал вопрос в комментарии выше как отдельный вопрос: stackoverflow.com/questions/25127026/… .
Александр Блех

4
@Konrad Лучшее, что вы можете сделать, это создать ссылку на файл и дать минимальную команду для чтения в этом файле. Это будет меньше хлопот, чем вставка копий в вывод dput () :)
Joris Meys

590

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

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

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

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

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

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

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

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

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

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

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

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

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


24
reprexв tidyverseэто хороший пакет для получения минимальной, воспроизводимый пример: github.com/tidyverse/reprex
mt1022

19
Я регулярно получаю электронные письма с кодом в них. Я даже получаю электронные письма с прикрепленными документами Word, которые содержат код. Иногда я даже получаю электронные письма с прикрепленными текстовыми документами, которые содержат СКРИНШОТЫ кода.
хэдли

304

Лично я предпочитаю «один» лайнер. Нечто подобное:

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

1
ИМХО, при использовании sampleили runifразумно set.seed. По крайней мере, это предположение я получил при создании примеров, касающихся выборки или генерации случайных чисел.
Конрад

1
@ Конрад Я согласен, но это может зависеть. Если вы просто пытаетесь сгенерировать какие-то числа, семя может не понадобиться, но если вы пытаетесь понять что-то конкретное, где нужны фиксированные числа, начальное число будет обязательным.
Роман Луштрик

1
Всегда лучше с начальным imo, это облегчает сравнение собственного решения с ожидаемым результатом, сравнение решений между собой, и таким образом пользователи, которые не знают (и не должны знать) функции, такие как runifили sampleне смущенные что они не могут получить те же данные.
Moody_Mudskipper

2
@mikey вы смотрели на пакет usmap ?
Роман Луштрик

2
@mikey пакет Тигр скачивает шейпфайлы из Бюро переписи населения в различных форматах
Камиль

277

Вдохновленный этим постом, я теперь использую удобную функцию,
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 х 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==

196

Вот хороший гид .

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

РЕДАКТИРОВАТЬ:

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


165

Список рассылки R-help содержит руководство по отправке, которое охватывает как вопросы, так и ответы, включая пример создания данных:

Примеры: Иногда это помогает привести небольшой пример, который кто-то может запустить. Например:

Если у меня есть матрица х следующим образом:

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

)

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

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


164

Начиная с R.2.14 (я полагаю), вы можете передать свое текстовое представление данных напрямую 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
@ Себастьян-с, как это хорошо для того, чтобы сделать воспроизводимый пример? :)
TMS

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

146

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

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

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

редактировать : два полезных вопроса для анонимизации / шифрования:


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

137

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

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


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

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

Как мне этого добиться?



124

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

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

>fix(mydata)

Когда вы выполните команду «исправить», вы получите это всплывающее окно

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


18
... тогдаdput(mydata)
GSee

Какой у вас интерфейс? Было бы неплохо получить полный ответ. Etc создает данные, которые вы можете напрямую зациклить for (d in data) {...}.
Лео Леопольд Герц 준영

119

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

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

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

для данных в текстовом файле:

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

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


116

Методические рекомендации:


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

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

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

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


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

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

  • iris
  • mtcars
  • ggplot2::diamonds (внешний пакет, но есть почти у всех)

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

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

Собственные данные

Если ваша проблема очень специфична для типа данных, который не представлен в существующих наборах данных, предоставьте код 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

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

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


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

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

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

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


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


113

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

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

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

например,

data(mtcars)

а затем сделать проблему

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

13
Многие встроенные наборы данных (например, популярные mtcarsи irisнаборы данных) фактически не нуждаются в dataвызове.
Грегор Томас

92

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

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

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


90

Я разрабатываю пакет 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
.. ...   ... ...    ...      ... ...    ...   ...

72

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

dput(droplevels(head(mydata)))

65

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


47

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

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

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


2
Вы удаляете >и добавляете #вручную или есть автоматический способ сделать это?
BCArg

3
@BCArg я удаляю >вручную. Но, для добавления #, я использую Ctrl+Shift+Cярлык в RStudioредакторе.
user2100721

33

Вы можете сделать это с использованием представительства .

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

По словам Тидиверса :

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

Пример приведен на сайте Tidyverse .

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

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


33

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

Есть много способов сделать случайный вектор Создать вектор из 100 чисел со случайными значениями в R, округленными до 2 десятичных знаков или случайной матрицей в R

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

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

28

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

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

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

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


1
Вы действительно не добавили ничего существенного здесь. dput()было упомянуто ранее, и большая часть этого просто повторяет стандартные рекомендации SO.
Rich Scriven

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

18

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.