Как сделать отличный воспроизводимый пример R
При обсуждении производительности с коллегами, обучении, отправке отчета об ошибке или поиске руководства в списках рассылки и здесь, в Stack Overflow, часто спрашивают воспроизводимый пример, который всегда оказывается полезным.
Каковы ваши советы по созданию отличного примера? Как вставить структуры данных из r в текстовый формат? Какую еще информацию вы должны включить?
Есть ли другие уловки помимо использования dput()
, dump()
или structure()
? Когда следует включать library()
или require()
утверждения? Какие зарезервированные слова следует один избежать, в дополнение к c
, df
, data
и т.д.?
Как один сделать большой г воспроизводимый пример?
Ответы
Минимальный воспроизводимый пример состоит из следующих элементов:
- минимальный набор данных, необходимый для демонстрации проблемы
- минимальный исполняемый код, необходимый для воспроизведения ошибки, который может быть запущен для данного набора данных
- необходимую информацию об используемых пакетах, версии 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)
) - test запустите свой код в новом пустом сеансе 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")
.
(Вот мой совет из статьи Как написать воспроизводимый пример . Я попытался сделать его кратким, но милым)
Как написать воспроизводимый пример.
Скорее всего, вы получите хорошую помощь в решении проблемы с R, если предоставите воспроизводимый пример. Воспроизводимый пример позволяет кому-то другому воссоздать вашу проблему, просто скопировав и вставив код R.
Чтобы сделать ваш пример воспроизводимым, вам нужно включить четыре вещи: необходимые пакеты, данные, код и описание вашей среды R.
Пакеты должны загружаться в верхней части скрипта, чтобы было легко увидеть, какие из них нужны в примере.
Самый простой способ включить данные в электронное письмо или вопрос о переполнении стека - это использовать
dput()
для создания кода R для его воссоздания. Например, чтобы воссоздатьmtcars
набор данных в R, я бы выполнил следующие шаги:- Беги
dput(mtcars)
в R - Скопируйте вывод
- В моем воспроизводимом скрипте введите и
mtcars <-
вставьте.
- Беги
Потратьте немного времени на то, чтобы ваш код было легко читать другим:
убедитесь, что вы использовали пробелы и имена переменных были краткими, но информативными
используйте комментарии, чтобы указать, в чем заключается ваша проблема
постарайтесь удалить все, что не имеет отношения к проблеме.
Чем короче ваш код, тем легче его понять.
Включите вывод
sessionInfo()
в комментарий в свой код. Это обобщает вашу среду R и позволяет легко проверить, используете ли вы устаревший пакет.
Вы можете проверить, действительно ли вы создали воспроизводимый пример, запустив новый сеанс R и вставив свой скрипт.
Прежде чем помещать весь свой код в электронное письмо, подумайте о том, чтобы разместить его на Gist github . Это придаст вашему коду приятную подсветку синтаксиса, и вам не придется беспокоиться о том, что почтовая система может что-то испортить.
Лично я предпочитаю «одинарные» вкладыши. Что-то вроде:
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")
Вдохновленный этим постом, теперь я использую удобную функцию,
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, вывод автоматически копируется в буфер обмена, так что вы можете просто запустить его, а затем вставить в свой вопрос.
Источник доступен здесь:
- Github - pubR / duplicate.R
Пример:
# 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==
Вот хорошее руководство .
Самый важный момент: просто убедитесь, что вы создали небольшой фрагмент кода, который мы можем запустить, чтобы увидеть, в чем проблема . Для этого полезна функция dput()
, но если у вас очень большие данные, вы можете создать небольшой образец набора данных или использовать только первые 10 строк или около того.
РЕДАКТИРОВАТЬ:
Также убедитесь, что вы сами определили, в чем проблема. Примером не должно быть всего сценария R с надписью «В строке 200 есть ошибка». Если вы используете инструменты отладки в R (я люблю browser()
) и Google, вы действительно сможете определить, в чем проблема, и воспроизвести тривиальный пример, в котором то же самое идет не так.
В списке рассылки 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.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
")
Иногда проблема действительно не воспроизводится с меньшим фрагментом данных, как бы вы ни старались, и не возникает с синтетическими данными (хотя полезно показать, как вы создали наборы синтетических данных, которые не воспроизводили проблему, потому что это исключает некоторые гипотезы).
- Может потребоваться размещение данных в Интернете и предоставление URL-адреса.
- Если данные не могут быть опубликованы для широкой публики, но могут быть переданы вообще, вы можете предложить отправить их по электронной почте заинтересованным сторонам (хотя это сократит количество людей, которые будут беспокоиться о работе. в теме).
- Я на самом деле не видел этого, потому что люди, которые не могут выпустить свои данные, чувствительны к выпуску их в любой форме, но может показаться правдоподобным, что в некоторых случаях можно было бы публиковать данные, если бы они были достаточно анонимными / зашифрованными / слегка поврежденными каким-то образом.
Если вы не можете сделать ни одно из этих действий, вам, вероятно, потребуется нанять консультанта для решения вашей проблемы ...
edit : Два полезных вопроса SO для анонимизации / скремблирования:
- Как создать примерный набор данных из частных данных (заменив имена и уровни переменных неинформативными заполнителями)?
- Учитывая набор случайных чисел, взятых из непрерывного одномерного распределения, найдите распределение
Пока что ответы явно хороши в отношении воспроизводимости. Это просто для того, чтобы прояснить, что воспроизводимый пример не может и не должен быть единственным компонентом вопроса. Не забудьте объяснить, как вы хотите, чтобы это выглядело, и контуры вашей проблемы, а не только то, как вы до сих пор пытались ее решить. Кода недостаточно; вам также нужны слова.
Вот воспроизводимый пример того, чего следует избегать (взят из реального примера, имена изменены для защиты невиновных):
Ниже приведены примеры данных и часть функции, с которой у меня возникли проблемы.
code
code
code
code
code (40 or so lines of it)
Как я могу этого добиться?
У меня есть очень простой и эффективный способ сделать пример R, который не упоминался выше. Вы можете сначала определить свою структуру. Например,
mydata <- data.frame(a=character(0), b=numeric(0), c=numeric(0), d=numeric(0))
>fix(mydata)
Затем вы можете ввести свои данные вручную. Это более эффективно для небольших примеров, чем для больших.
Чтобы быстро создать dput
часть своих данных, вы можете просто скопировать (часть) данных в буфер обмена и запустить в R следующее:
для данных в Excel:
dput(read.table("clipboard",sep="\t",header=TRUE))
для данных в txt файле:
dput(read.table("clipboard",sep="",header=TRUE))
При необходимости вы можете изменить sep
последнее. Это будет работать, только если ваши данные, конечно, находятся в буфере обмена.
Руководящие указания:
Ваша основная цель при составлении вопросов должна заключаться в том, чтобы читателям было как можно проще понять и воспроизвести вашу проблему в своих системах. Для этого:
- Предоставьте входные данные
- Обеспечьте ожидаемый результат
- Кратко объясните свою проблему
- если у вас более 20 строк текста + код, вы, вероятно, можете вернуться и упростить
- максимально упростите свой код, сохранив проблему / ошибку
Это требует некоторой работы, но кажется справедливым компромиссом, поскольку вы просите других делать работу за вас.
Предоставление данных:
Встроенные наборы данных
На сегодняшний день лучший вариант - полагаться на встроенные наборы данных. Это упрощает другим работу над вашей проблемой. Введите data()
в приглашении R, чтобы узнать, какие данные вам доступны. Некоторые классические примеры:
iris
mtcars
ggplot2::diamonds
(внешний пакет, но он есть почти у всех)
Изучите встроенные наборы данных, чтобы найти подходящий для вашей проблемы.
Если вы сможете перефразировать свою проблему и использовать встроенные наборы данных, у вас гораздо больше шансов получить хорошие ответы (и положительные голоса).
Самостоятельно созданные данные
Если ваша проблема очень специфична для типа данных, который не представлен в существующих наборах данных, предоставьте код 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
Отвечая на ваш вопрос, люди с гораздо большей вероятностью поймут, что вы пытаетесь сделать. Если ваш ожидаемый результат большой и громоздкий, то вы, вероятно, недостаточно думали о том, как упростить вашу задачу (см. Далее).
Кратко объясните свою проблему
Главное, что нужно сделать, это максимально упростить вашу проблему, прежде чем вы зададите свой вопрос. В этом отношении очень поможет переформулирование проблемы для работы со встроенными наборами данных. Вы также часто обнаруживаете, что, просто пройдя через процесс упрощения, вы решите свою собственную проблему.
Вот несколько примеров хороших вопросов:
- со встроенным набором данных
- с пользовательскими данными
В обоих случаях проблемы пользователя почти наверняка связаны не с теми простыми примерами, которые они предоставляют. Скорее они абстрагировались от природы своей проблемы и применили ее к простому набору данных, чтобы задать свой вопрос.
Почему еще один ответ на этот вопрос?
В этом ответе основное внимание уделяется тому, что я считаю лучшей практикой: использовать встроенные наборы данных и предоставлять то, что вы ожидаете, в минимальной форме. Наиболее известные ответы сосредоточены на других аспектах. Я не ожидаю, что этот ответ станет известен; это здесь исключительно для того, чтобы я мог ссылаться на него в комментариях к вопросам новичков.
Воспроизводимый код - ключ к получению помощи. Однако многие пользователи могут скептически относиться к вставке даже части своих данных. Например, они могут работать с конфиденциальными данными или с исходными данными, собранными для использования в исследовательской работе. По какой-то причине я подумал, что было бы неплохо иметь удобную функцию для «деформации» моих данных перед их публичной вставкой. 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
Часто вам нужны данные для примера, однако вы не хотите публиковать свои точные данные. Чтобы использовать существующий data.frame в установленной библиотеке, используйте команду data для его импорта.
например,
data(mtcars)
а затем займитесь проблемой
names(mtcars)
your problem demostrated on the mtcars data set
Если у вас большой набор данных, который не может быть легко помещен в скрипт с помощью dput()
, опубликуйте свои данные в pastebin и загрузите их, используя read.table
:
d <- read.table("http://pastebin.com/raw.php?i=m1ZJuKLH")
На основе @Henrik .
Я разрабатываю пакет wakefield , чтобы удовлетворить эту потребность в быстром обмене воспроизводимыми данными, иногда dput
отлично работает для небольших наборов данных, но многие проблемы, с которыми мы сталкиваемся, намного больше, совместное использование такого большого набора данных через посредство dput
непрактично.
О:
wakefield позволяет пользователю делиться минимальным кодом для воспроизведения данных. Пользователь устанавливаетn
(количество строк) и определяет любое количество предварительно установленных функций переменных (в настоящее время их 70), которые имитируют реальные данные if (такие как пол, возраст, доход и т. Д.)
Установка:
В настоящее время (11.06.2015) 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
.. ... ... ... ... ... ... ... ...
Если у вас есть одна или несколько factor
переменных в ваших данных, которые вы хотите сделать воспроизводимыми dput(head(mydata))
, подумайте о добавлении droplevels
к ним, чтобы уровни факторов, которые не присутствуют в минимизированном наборе данных, не включались в ваш dput
вывод, чтобы сделайте пример минимальным :
dput(droplevels(head(mydata)))
Интересно, если http://old.r-fiddle.org/ссылка может быть очень изящным способом поделиться проблемой. Он получает уникальный идентификатор вроде и можно даже подумать о встраивании его в SO.
Пожалуйста, не вставляйте выходные данные консоли следующим образом:
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")
Еще одна вещь: если вы использовали какую-либо функцию из определенного пакета, укажите эту библиотеку.
Вы можете сделать это с помощью REPEX .
Как заметил mt1022 , «... хороший пакет для создания минимального воспроизводимого примера - это « реплекс » из tidyverse ».
Согласно Tidyverse :
Цель "REPEX" - упаковать ваш проблемный код таким образом, чтобы другие люди могли запустить его и почувствовать вашу боль.
Пример приведен на сайте tidyverse .
library(reprex)
y <- 1:4
mean(y)
reprex()
Я думаю, что это самый простой способ создать воспроизводимый пример.
Помимо всех приведенных выше ответов, которые мне показались очень интересными, иногда это может быть очень просто, поскольку это обсуждается здесь: - КАК СДЕЛАТЬ МИНИМАЛЬНЫЙ ВОСПРОИЗВОДИМЫЙ ПРИМЕР ДЛЯ ПОЛУЧЕНИЯ ПОМОЩИ С 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 по умолчанию
- Если у вас есть собственный набор данных, включите его
dput
, чтобы другие могли вам помочь. - Не используйте,
install.package()
если это действительно не нужно, люди поймут, если вы просто используетеrequire
илиlibrary
Постарайтесь быть краткими,
- Есть какой-то набор данных
- Постарайтесь как можно проще описать желаемый результат
- Сделайте это сами, прежде чем задать вопрос
- Загрузить изображение легко, поэтому загружайте графики, если у вас есть
- Также укажите все возможные ошибки.
Все это часть воспроизводимого примера.
Рекомендуется использовать функции из 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
позволяет вашему помощнику сосредоточиться на коде, что экономит время, и дает им возможность узнать, что они решили вашу проблему, прежде чем опубликовать ее.