Як зробити чудовий приклад для відтворення R


2473

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

Які ваші поради щодо створення прекрасного прикладу? Як вставити структури даниху текстовому форматі? Яку іншу інформацію слід включити?

Чи є інші прийоми на додаток до використання 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 / ...
Etienne Low-Decarie

Відповіді:


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: Просто FYI - розміщено питання у коментарі вище як окреме запитання: stackoverflow.com/questions/25127026/… .
Олександр Блех

4
@Konrad Найкраще, що ви могли зробити, - це посилання на файл і дати мінімальну команду для читання у цьому файлі. Це буде менше клопоту, ніж копіювання вставлення виводу dput () :)
Joris Meys

589

(Ось моя порада, як написати відтворювальний приклад . Я намагався зробити його коротким, але солодким)

Як написати відтворюваний приклад.

Ви, швидше за все, отримаєте гарну допомогу щодо своєї проблеми з 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
Я регулярно отримую електронні листи з кодом у них. Я навіть отримую електронні листи із доданими текстовими документами, які містять код. Іноді я навіть отримую електронні листи із доданими текстовими документами, які містять ЕКРАННІ КОТИ.
Хадлі

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

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

1
IMHO під час використання sampleабо runifдоцільного використання set.seed. Принаймні, це пропозиція, яку я отримав, створюючи приклади, що стосуються вибірки чи генерації випадкових чисел.
Конрад

1
@Konrad Я згоден, але це може залежати. Якщо ви просто намагаєтеся генерувати деякі цифри, насіння може не знадобитися, але якщо ви намагаєтесь зрозуміти щось конкретне, де потрібні фіксовані числа, насіння буде обов'язковим.
Роман Луштрик

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

Коефіцієнт покажчика становить приблизно 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-довідки є посібник з публікацією, який охоплює і запитання, і відповіді на запитання, включаючи приклад генерації даних:

Приклади: іноді це допомагає навести невеликий приклад того, що хтось може насправді бігати. Наприклад:

Якщо у мене є матриця 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 рядками та трьома стовпцями з назвою "рядок", "кол" та "значення", які мають назви розмірів як значення "рядок" і "кол", як це:

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

)

Слово малий особливо важливе. Ви повинні прагнути до мінімально відтворюваного прикладу, а це означає, що дані та код повинні бути максимально простими для пояснення проблеми.

EDIT: Гарний код легше читати, ніж некрасивий код. Скористайтеся посібником зі стилів .


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
@ sebastian-c як це добре для того, щоб зробити відтворюваний приклад ?? :)
TMS

@TMS Даючи серйозну думку, якщо запитувач надав дані, і проблема невелика (але може мати кілька рішень), то це може бути швидше, і ви все одно можете виконати всі кроки.
sebastian-c

146

Іноді проблема справді не відтворюється за допомогою меншої частини даних, як би ви не старалися, і не буває із синтетичними даними (хоча корисно показати, як ви створювали синтетичні набори даних, які не відтворювали проблему, оскільки це виключає деякі гіпотези).

  • Можливо, буде необхідним розміщення даних у Інтернеті та надання URL-адреси.
  • Якщо дані не можуть бути оприлюднені широкою громадськістю, але взагалі можуть бути надані, ви можете запропонувати надіслати їх електронною поштою зацікавленим сторонам (хоча це зменшить кількість людей, які будуть заважати працювати на цьому).
  • Я насправді цього не бачив, тому що люди, які не можуть оприлюднити свої дані, чутливі до оприлюднення його будь-якої форми, але, здавалося б, правдоподібно, що в деяких випадках все-таки можна було розміщувати дані, якщо вони були достатньо анонімізовані / скрембліровані / зіпсовані якимось чином.

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

редагувати : Два корисних питання SO для анонімізації / скремблювання:


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

для даних у файлі txt:

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(), розмістіть свої дані на пастібі та завантажте їх, використовуючи 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редакторі.
користувач2100721

33

Це можна зробити за допомогою репрексу .

Як зазначав mt1022 , "... хороший пакет для створення мінімального, відтворюваного прикладу - " репрекс " від tidyverse ".

За даними 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()вже згадувалося раніше, і значна частина цього лише повторює стандартні вказівки щодо ПЗ.
Багатий скрипт

1
У мене виникли проблеми з install.packageфункцією, включеною в приклад, який насправді не потрібен (на мою думку). Крім того, використання даних R за замовчуванням полегшить відтворення. Настанови SO не говорили нічого про ці теми конкретно. Далі, я мав на меті висловити свою думку, і саме з цим я стикався найбільше.
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.