Jak zrobić wspaniały przykład odtwarzalnego R.


2473

Podczas omawiania wyników ze współpracownikami, nauczania, wysyłania raportu o błędach lub szukania wskazówek na temat list mailingowych i tutaj na temat przepełnienia stosu często powtarzalny przykład jest często zadawany i zawsze pomocny.

Jakie są twoje wskazówki, jak stworzyć doskonały przykład? Jak wklejasz struktury danychw formacie tekstowym? Jakie inne informacje powinieneś podać?

Czy istnieją inne sztuczki oprócz korzystania dput(), dump()albo structure()? Kiedy należy dołączyć library()lub require()oświadczenia? Który zastrzeżone słów należy unikać, w uzupełnieniu do c, df, data, itd.?

Jak można zrobić coś wspaniałego odtwarzalny przykład?


34
Jestem zdezorientowany co do zakresu pytania. Wydaje się, że ludzie skoczyli na interpretację odtwarzalnego przykładu, zadając pytania na temat pomocy SO lub R (jak „odtworzyć błąd”). Co z powtarzalnymi przykładami R na stronach pomocy? W pakietach demonstracyjnych? W tutorialach / prezentacjach?
baptiste

15
@baptiste: Taki sam minus błąd. Wszystkie techniki, które wyjaśniłem, są używane na stronach pomocy pakietów, a także w tutorialach i prezentacjach na temat R
Joris Meys,

33
Dane są czasem czynnikiem ograniczającym, ponieważ struktura może być zbyt złożona, aby ją symulować. Aby wygenerować dane publiczne z danych prywatnych: stackoverflow.com/a/10458688/742447 w stackoverflow.com/questions/10454973/...
Etienne Low-Décarie

Odpowiedzi:


1726

Minimalny powtarzalne przykład składa się z następujących elementów:

  • minimalny zestaw danych, niezbędny do zademonstrowania problemu
  • minimalny uruchamialny kod niezbędny do odtworzenia błędu, który można uruchomić na danym zbiorze
  • niezbędne informacje na temat używanych pakietów, wersji R i systemu, na którym jest uruchomiony.
  • w przypadku procesów losowych, ziarno (ustawione przez set.seed()) dla odtwarzalności 1

Przykłady dobrych minimalnych odtwarzalnych przykładów można znaleźć w plikach pomocy używanej funkcji. Zasadniczo cały podany tam kod spełnia wymagania minimalnego odtwarzalnego przykładu: dostarczane są dane, zapewniony jest minimalny kod i wszystko jest możliwe do uruchomienia. Zobacz także pytania dotyczące przepełnienia stosu z dużą ilością pozytywnych opinii.

Tworzenie minimalnego zestawu danych

W większości przypadków można to łatwo zrobić, po prostu zapewniając ramkę wektor / dane z pewnymi wartościami. Lub możesz użyć jednego z wbudowanych zestawów danych, które są dostarczane z większością pakietów.
Można zobaczyć pełną listę wbudowanych zestawów danych library(help = "datasets"). Każdy zestaw danych zawiera krótki opis i można uzyskać więcej informacji, na przykład o tym, ?mtcarsgdzie „mtcars” jest jednym z zestawów danych na liście. Inne pakiety mogą zawierać dodatkowe zestawy danych.

Wykonanie wektora jest łatwe. Czasami konieczne jest dodanie do niej losowości i jest do tego cała liczba funkcji. sample()może randomizować wektor lub dać losowy wektor z kilkoma wartościami. lettersto użyteczny wektor zawierający alfabet. Można to wykorzystać do tworzenia czynników.

Kilka przykładów:

  • wartości losowe: x <- rnorm(10)dla rozkładu normalnego, x <- runif(10)dla rozkładu równomiernego, ...
  • permutacja niektórych wartości: x <- sample(1:10)dla wektora 1:10 w losowej kolejności.
  • czynnik losowy: x <- sample(letters[1:4], 20, replace = TRUE)

W przypadku matryc można użyć matrix()np .:

matrix(1:10, ncol = 2)

Tworzenie ramek danych można wykonać za pomocą data.frame(). Należy zwrócić uwagę na nazwy wpisów w ramce danych i nie komplikować ich zbytnio.

Przykład :

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

W przypadku niektórych pytań mogą być potrzebne określone formaty. Dla nich, można użyć dowolnego z określonych as.someTypefunkcji: as.factor, as.Date, as.xts, ... To w połączeniu z wektorem i / lub ramka danych sztuczek.

Skopiuj swoje dane

Jeśli masz jakieś dane, które byłyby zbyt trudne do skonstruowania za pomocą tych wskazówek, to zawsze można zrobić podzbiór oryginalnego danych, używając head(), subset()lub indeksy. Następnie użyj, dput()aby dać nam coś, co można natychmiast umieścić w 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")

Jeśli ramka danych ma czynnik z wieloma poziomami, dane dputwyjściowe mogą być nieporęczne, ponieważ nadal będzie zawierać listę wszystkich możliwych poziomów czynników, nawet jeśli nie są one obecne w podzbiorze danych. Aby rozwiązać ten problem, możesz użyć droplevels()funkcji. Zauważ, że gatunek jest czynnikiem z tylko jednym poziomem:

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

Podczas korzystania dputmożesz także chcieć uwzględnić tylko odpowiednie kolumny:

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

Innym zastrzeżeniem dputjest to, że nie będzie działać dla data.tableobiektów z kluczami lub dla grup tbl_df(klas grouped_df) z dplyr. W takich przypadkach można przekształcić z powrotem do regularnego ramki danych przed udostępnianie dput(as.data.frame(my_data)).

W najgorszym przypadku można podać reprezentację tekstową, którą można odczytać za pomocą textparametru 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)

Produkcja minimalnego kodu

To powinna być łatwa część, ale często nie jest. Czego nie powinieneś robić, to:

  • dodaj wszelkiego rodzaju konwersje danych. Upewnij się, że podane dane są już w odpowiednim formacie (chyba że jest to oczywiście problem)
  • skopiuj-wklej całą funkcję / fragment kodu, który powoduje błąd. Najpierw spróbuj zlokalizować, które wiersze dokładnie powodują błąd. Najczęściej dowiesz się, na czym polega problem.

Co powinieneś zrobić, to:

  • dodaj, których pakietów należy użyć, jeśli używasz dowolnego (za pomocą library())
  • jeśli otwierasz połączenia lub tworzysz pliki, dodaj kod, aby je zamknąć lub usunąć pliki (używając unlink())
  • jeśli zmienisz opcje, upewnij się, że kod zawiera instrukcję, aby przywrócić je z powrotem do oryginalnych. (np. op <- par(mfrow=c(1,2)) ...some code... par(op))
  • przetestuj swój kod w nowej, pustej sesji R, aby upewnić się, że kod można uruchomić. Ludzie powinni móc po prostu skopiować i wkleić dane i kod w konsoli i uzyskać dokładnie to samo, co Ty.

Podaj dodatkowe informacje

W większości przypadków wystarczy wersja R i system operacyjny. Kiedy pojawiają się konflikty z pakietami, podanie wyniku sessionInfo()może naprawdę pomóc. Mówiąc o połączeniach z innymi aplikacjami (czy to przez ODBC, czy cokolwiek innego), należy również podać numery wersji tych aplikacji, a jeśli to możliwe, także niezbędne informacje na temat konfiguracji.

Jeśli używasz R w R Studio przy użyciu rstudioapi::versionInfo()mogą być pomocne zgłosić swoją wersję RStudio.

Jeśli masz problem z konkretnym pakietem, możesz podać jego wersję, podając wynik działania packageVersion("name of the package").


1 Uwaga: Wydajność set.seed()różni się między R> 3.6.0 i poprzednimi wersjami. Podaj, której wersji R użyłeś do losowego procesu i nie zdziw się, jeśli uzyskasz nieco inne wyniki podczas odpowiedzi na stare pytania. Aby uzyskać taki sam wynik w takich przypadkach, możesz wcześniej użyć funkcji RNGversion()-f set.seed()(np RNGversion("3.5.2"). :) .


6
Jak korzystać, dputjeśli ramka danych jest bardzo duża, a problem generowany jest przez środek ramki danych? Czy istnieje sposób na dputodtworzenie środkowej części danych, powiedzmy wiersze od 60 do 70?
BgnR

27
@BgnR Część ramki danych można wyodrębnić za pomocą indeksów, np .: tmp <- mydf[50:70,]po której następuje dput(mydf). Jeśli ramka danych jest naprawdę duża, spróbuj wyizolować problem i po prostu prześlij kilka wierszy, które powodują problem.
Joris Meys,

4
@JorisMeys: Czy istnieje sposób, aby powiedzieć headlub dputograniczyć dane do poziomu N rekurencyjnie? Próbuję wymyślić powtarzalny przykład, a moje dane to lista ramek danych. dput(head(myDataObj))Wydaje się więc nie wystarczać, ponieważ generuje plik wyjściowy o rozmiarze 14 MB.
Aleksandr Blekh

5
@JorisMeys: Właśnie FYI - pisał pytanie w komentarzu powyżej jako oddzielna pytanie: stackoverflow.com/questions/25127026/... .
Aleksandr Blekh

4
@Konrad Najlepszą rzeczą, jaką możesz zrobić, to link do pliku i podanie minimalnej liczby poleceń do odczytania w tym pliku. To będzie mniej kłopotliwe niż kopiowanie i wklejanie danych wyjściowych dput () :)
Joris Meys

589

(Oto moja rada z Jak napisać powtarzalny przykład . Starałem się streścić, ale słodko)

Jak napisać powtarzalny przykład.

Najprawdopodobniej uzyskasz dobrą pomoc z problemem R, jeśli podasz powtarzalny przykład. Powtarzalny przykład pozwala komuś innemu odtworzyć twój problem, po prostu kopiując i wklejając kod R.

Są cztery rzeczy, które musisz uwzględnić, aby Twój przykład był odtwarzalny: wymagane pakiety, dane, kod i opis środowiska R.

  • Pakiety powinny być ładowane u góry skryptu, aby łatwo było zobaczyć, które z nich są potrzebne w przykładzie.

  • Najłatwiejszym sposobem na włączenie danych do wiadomości e-mail lub pytania o przepełnienie stosu jest dput()wygenerowanie kodu R w celu jego odtworzenia. Na przykład, aby odtworzyć mtcarszestaw danych w R, wykonaj następujące czynności:

    1. Uruchom dput(mtcars)w R.
    2. Skopiuj dane wyjściowe
    3. W moim powtarzalnym skrypcie wpisz, mtcars <-a następnie wklej.
  • Poświęć trochę czasu na upewnienie się, że Twój kod jest łatwy do odczytania dla innych:

    • upewnij się, że użyłeś spacji, a nazwy zmiennych są zwięzłe, ale mają charakter informacyjny

    • użyj komentarzy, aby wskazać, gdzie leży twój problem

    • postaraj się usunąć wszystko, co nie jest związane z problemem.
      Im krótszy jest Twój kod, tym łatwiej go zrozumieć.

  • Uwzględnij wynik sessionInfo()w komentarzu w kodzie. To podsumowuje twoje środowisko R i ułatwia sprawdzenie, czy używasz nieaktualnego pakietu.

Możesz sprawdzić, czy rzeczywiście stworzyłeś powtarzalny przykład, rozpoczynając nową sesję R i wklejając skrypt.

Przed umieszczeniem całego kodu w wiadomości e-mail, rozważ umieszczenie go w Gist github . Dzięki temu twój kod będzie ładnie podświetlany pod względem składni i nie musisz się martwić o to, że system poczty e-mail zakłóci wszystko.


24
reprexin tidyversejest dobrym pakietem do tworzenia minimalnego, powtarzalnego przykładu: github.com/tidyverse/reprex
mt1022

19
Rutynowo otrzymuję wiadomości e-mail z kodem. Otrzymuję nawet e-maile z dołączonymi dokumentami tekstowymi zawierającymi kod. Czasami nawet dostaję e-maile z dołączonymi dokumentami tekstowymi, które zawierają ZRZUTY EKRANU kodu.
hadley,

304

Osobiście wolę wkładki „one”. Coś wzdłuż linii:

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)

Struktura danych powinna naśladować ideę problemu pisarza, a nie dokładną strukturę dosłowną. Naprawdę to doceniam, gdy zmienne nie nadpisują moich własnych zmiennych lub nie daj Boże, funkcje (jak df).

Alternatywnie można wyciąć kilka rogów i wskazać na wcześniej istniejący zestaw danych, na przykład:

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

Nie zapomnij wspomnieć o specjalnych pakietach, których możesz używać.

Jeśli próbujesz pokazać coś na większych obiektach, możesz spróbować

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

Jeśli pracujesz z danymi przestrzennymi za pośrednictwem rasterpakietu, możesz wygenerować losowe dane. Wiele przykładów można znaleźć w winiecie opakowania, ale oto mały samorodek.

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)

Jeśli potrzebujesz jakiegoś obiektu przestrzennego w postaci zaimplementowanej w sp, możesz pobrać niektóre zestawy danych za pomocą plików zewnętrznych (takich jak plik kształtu ESRI) w pakietach „przestrzennych” (zobacz Widok przestrzenny w widokach zadań).

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, podczas korzystania samplelub runifjest to rozsądne set.seed. Przynajmniej taką sugestię otrzymałem przy tworzeniu przykładów dotyczących próbkowania lub generowania liczb losowych.
Konrad

1
@Konrad Zgadzam się, ale to może zależeć. Jeśli próbujesz tylko wygenerować jakieś liczby, ziarno może nie być potrzebne, ale jeśli próbujesz zrozumieć coś konkretnego, gdzie potrzebne są stałe liczby, ziarno byłoby obowiązkowe.
Roman Luštrik

1
Zawsze jest lepiej z obrazem źródłowym, ułatwia porównywanie własnych rozwiązań z oczekiwanymi wynikami, porównywanie rozwiązań między sobą, w ten sposób użytkownicy, którzy nie znają (i nie muszą wiedzieć) funkcji takich jak runiflub samplenie są zdezorientowani że nie mogą uzyskać tych samych danych.
Moody_Mudskipper,

2
@ Mikey czy obejrzałeś pakiet usmap ?
Roman Luštrik,

2
@mikey pakiet tigris pobiera pliki kształtów z Biura Spisu Powszechnego w różnych formatach
Camille

277

Zainspirowany tym postem, używam teraz przydatnej funkcji,
reproduce(<mydata>)gdy muszę wysłać wiadomość do StackOverflow.


SZYBKIE INSTRUKCJE

Jeśli myDatajest to nazwa obiektu do odtworzenia, uruchom następujące polecenie w języku R:

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

reproduce(myData)

Detale:

Ta funkcja jest inteligentnym opakowaniem dputi wykonuje następujące czynności:

  • automatycznie próbkuje duży zestaw danych (na podstawie wielkości i klasy. Wielkość próbki można dostosować)
  • tworzy dputwynik
  • pozwala określić, które kolumny wyeksportować
  • dołącza się z przodu, objName <- ...aby można go było łatwo skopiować i wkleić, ale ...
  • W przypadku pracy na komputerze Mac dane wyjściowe są automatycznie kopiowane do schowka, dzięki czemu można po prostu uruchomić, a następnie wkleić do pytania.

Źródło jest dostępne tutaj:


Przykład:

# 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 wynosi około 100 x 102. Chcę spróbować 10 wierszy i kilku konkretnych kolumn

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

Daje następujące dane wyjściowe:

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

Zauważ też, że całość danych wyjściowych jest w ładnym pojedynczym, długim wierszu, a nie wysokim akapicie pociętych linii. Ułatwia to czytanie postów na pytania SO, a także łatwiejsze kopiowanie i wklejanie.


Aktualizacja października 2013:

Możesz teraz określić, ile linii tekstu wyjściowego zajmie (tj. Co wkleisz do StackOverflow). Użyj lines.out=ndo tego argumentu. Przykład:

reproduce(DF, cols=c(1:3, 17, 23), lines.out=7) daje:

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

Oto dobry przewodnik .

Najważniejsze jest to: Upewnij się, że wykonałeś mały fragment kodu, który możemy uruchomić, aby zobaczyć, na czym polega problem . Przydatną funkcją do tego jest dput(), ale jeśli masz bardzo duże dane, możesz utworzyć niewielki przykładowy zestaw danych lub użyć tylko pierwszych 10 wierszy.

EDYTOWAĆ:

Upewnij się także, że sam zidentyfikowałeś problem. Przykładem nie powinien być cały skrypt R z komunikatem „W wierszu 200 wystąpił błąd”. Jeśli używasz narzędzi do debugowania w R (uwielbiam browser()) i Google, powinieneś być w stanie naprawdę określić, gdzie jest problem i odtworzyć trywialny przykład, w którym to samo nie działa.


165

Lista mailingowa R-help zawiera przewodnik, który obejmuje zarówno zadawanie pytań, jak i udzielanie odpowiedzi, w tym przykład generowania danych:

Przykłady: Czasami pomaga podać mały przykład, który ktoś może uruchomić. Na przykład:

Jeśli mam macierz x w następujący sposób:

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

jak mogę przekształcić go w ramkę danych z 8 wierszami i trzema kolumnami o nazwach „row”, „col” i „value”, które mają nazwy wymiarów jako wartości „row” i „col”, jak poniżej:

  > x.df
     row col value
  1    A   x      1

...
(na które odpowiedź może brzmieć:

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

)

Słowo „ małe” jest szczególnie ważne. Powinieneś dążyć do minimalnego odtwarzalnego przykładu, co oznacza, że ​​dane i kod powinny być tak proste, jak to możliwe, aby wyjaśnić problem.

EDYCJA: Ładny kod jest łatwiejszy do odczytania niż brzydki kod. Skorzystaj z przewodnika po stylu .


164

Od R.2.14 (tak myślę) możesz przesyłać swoją reprezentację tekstu danych bezpośrednio do 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, jak to jest dobre, aby uzyskać powtarzalny przykład? :)
TMS

@ TMS Zastanawiając się poważnie, jeśli pytający dostarczył dane, a problem jest niewielki (ale może mieć kilka rozwiązań), może być szybszy i nadal można wykonać wszystkie kroki.
sebastian-c

146

Czasami problem tak naprawdę nie daje się odtworzyć przy mniejszym kawałku danych, bez względu na to, jak bardzo się starasz, i nie występuje w przypadku danych syntetycznych (chociaż przydatne jest pokazanie, w jaki sposób utworzono zestawy danych syntetycznych, które nie odtworzyły problemu, ponieważ wyklucza to pewne hipotezy).

  • Konieczne może być opublikowanie danych w Internecie i podanie adresu URL.
  • Jeśli danych nie można udostępnić ogółowi społeczeństwa, ale w ogóle można je udostępnić, możesz zaoferować przesłanie ich e-mailem do zainteresowanych stron (chociaż zmniejszy to liczbę osób, które będą kłopotać się pracą) na tym).
  • Tak naprawdę nie widziałem tego zrobionego, ponieważ ludzie, którzy nie mogą ujawnić swoich danych, są wrażliwi na ujawnienie ich w dowolnej formie, ale wydaje się prawdopodobne, że w niektórych przypadkach można by nadal opublikować dane, gdyby były wystarczająco anonimizowane / zakodowane / lekko zepsute w pewnym sensie.

Jeśli nie możesz tego zrobić, prawdopodobnie musisz zatrudnić konsultanta, aby rozwiązać problem ...

edycja : Dwa przydatne pytania SO do anonimizacji / mieszania:


1
W przypadku tworzenia syntetycznych zestawów danych odpowiedzi na to pytanie zawierają użyteczne przykłady, w tym zastosowania fitdistri fitdistrplus.
Iterator

137

Dotychczasowe odpowiedzi są oczywiście świetne dla części odtwarzalności. Ma to jedynie na celu wyjaśnienie, że odtwarzalny przykład nie może i nie powinien być jedynym elementem pytania. Nie zapomnij wyjaśnić, jak ma on wyglądać i konturów problemu, a nie tylko tego, jak próbowałeś się tam dostać. Kod to za mało; potrzebujesz także słów.

Oto powtarzalny przykład tego, czego należy unikać (zaczerpnięty z prawdziwego przykładu, nazwy zmieniono, aby chronić niewinnych):


Oto przykładowe dane i część funkcji, z którą mam problem.

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

Jak mogę to osiągnąć?



124

Mam bardzo łatwy i skuteczny sposób na stworzenie przykładu R, który nie został wspomniany powyżej. Możesz najpierw zdefiniować swoją strukturę. Na przykład,

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

>fix(mydata)

Po wykonaniu polecenia „napraw” pojawi się to okno podręczne

Następnie możesz wprowadzić swoje dane ręcznie. Jest to skuteczne w przypadku mniejszych przykładów niż dużych.


18
... wtedydput(mydata)
GSee

Jaki jest twój frontend? Byłoby miło mieć pełną odpowiedź. Itd zrobić dane, które można bezpośrednio zapętlić for (d in data) {...}.
Léo Léopold Hertz

119

Aby szybko utworzyć dput ze swoich danych, możesz po prostu skopiować (część) danych do schowka i uruchomić w R:

dla danych w Excelu:

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

dla danych w pliku txt:

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

W seprazie potrzeby możesz zmienić te ostatnie. Działa to tylko wtedy, gdy twoje dane są oczywiście w schowku.


116

Wytyczne:


Twoim głównym celem przy tworzeniu pytań powinno być ułatwienie czytelnikom zrozumienia i odtworzenia Twojego problemu w ich systemach. Aby to zrobić:

  1. Podaj dane wejściowe
  2. Podaj oczekiwany wynik
  3. Wyjaśnij zwięźle swój problem
    • jeśli masz ponad 20 wierszy tekstu + kodu, prawdopodobnie możesz wrócić i uprościć
    • maksymalnie uprościć kod, zachowując problem / błąd

To wymaga trochę pracy, ale wydaje się, że jest to sprawiedliwy kompromis, ponieważ prosisz innych o wykonanie pracy za Ciebie.

Podanie danych:


Wbudowane zestawy danych

Najlepszym rozwiązaniem zdecydowanie ma polegać na wbudowanej zbiorów danych. Ułatwia to innym pracę nad twoim problemem. Wpisz data()w wierszu R, aby zobaczyć, jakie dane są dostępne. Niektóre klasyczne przykłady:

  • iris
  • mtcars
  • ggplot2::diamonds (pakiet zewnętrzny, ale prawie wszyscy go mają)

Zobacz ten SO QA, aby znaleźć zestawy danych odpowiednie dla Twojego problemu.

Jeśli jesteś w stanie przeformułować swój problem, aby użyć wbudowanych zestawów danych, znacznie bardziej prawdopodobne jest uzyskanie dobrych odpowiedzi (i pozytywnych opinii).

Dane wygenerowane samodzielnie

Jeśli twój problem jest bardzo specyficzny dla typu danych, który nie jest reprezentowany w istniejących zestawach danych, podaj kod R, który generuje najmniejszy możliwy zestaw danych, na którym pojawia się twój problem. Na przykład

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

Teraz ktoś, kto próbuje odpowiedzieć na moje pytanie, może skopiować / wkleić te dwa wiersze i natychmiast rozpocząć pracę nad problemem.

dput

W ostateczności można użyć dputdo przekształcenia obiektu danych w kod R (np dput(myData).). Mówię jako „ostatnią deską ratunku”, ponieważ wyniki dputczęsto są dość nieporęczne, irytujące w kopiowaniu-wklejaniu i przesłaniają resztę pytania.

Zapewnij oczekiwany wynik:


Ktoś kiedyś powiedział:

Obraz oczekiwanej wydajności jest wart 1000 słów

- bardzo mądra osoba

Jeśli możesz dodać coś w stylu „Spodziewałem się tego wyniku”:

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

na twoje pytanie, ludzie znacznie częściej rozumieją, co próbujesz zrobić. Jeśli oczekiwany wynik jest duży i nieporęczny, prawdopodobnie nie zastanawiałeś się wystarczająco, jak uprościć swój problem (patrz dalej).

Wyjaśnij zwięźle swój problem


Najważniejsze jest, aby maksymalnie uprościć swój problem, zanim zadasz pytanie. Przeformułowanie problemu do pracy z wbudowanymi zestawami danych bardzo pomoże w tym względzie. Często też przekonasz się, że po prostu przez uproszczenie rozwiążesz swój problem.

Oto kilka przykładów dobrych pytań:

W obu przypadkach problemy użytkownika prawie na pewno nie wynikają z prostych przykładów, które podają. Zamiast tego wyodrębnili naturę swojego problemu i zastosowali go do prostego zestawu danych, aby zadać pytanie.

Dlaczego jeszcze jedna odpowiedź na to pytanie?


Ta odpowiedź koncentruje się na tym, co uważam za najlepszą praktykę: używaj wbudowanych zestawów danych i zapewniaj to, czego oczekujesz w wyniku w minimalnej formie. Najważniejsze odpowiedzi koncentrują się na innych aspektach. Nie oczekuję, że ta odpowiedź zyska na znaczeniu; jest to tutaj wyłącznie po to, bym mógł link do niego w komentarzach do pytań dla początkujących.


113

Powtarzalny kod jest kluczem do uzyskania pomocy. Jednak wielu użytkowników może sceptycznie wkleić nawet część swoich danych. Mogą na przykład pracować z danymi wrażliwymi lub nad danymi oryginalnymi zebranymi do wykorzystania w pracy badawczej. Z jakiegokolwiek powodu pomyślałem, że byłoby miło mieć przydatną funkcję do „deformowania” moich danych przed wklejeniem ich publicznie. anonymizeFunkcji z pakietu SciencesPojest bardzo głupie, ale dla mnie to działa ładnie z dputfunkcji.

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

Następnie anonimizuję to:

> 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

Można również spróbować pobrać kilka zmiennych zamiast całych danych przed zastosowaniem polecenia anonimizacji i polecenia 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

Często potrzebujesz pewnych danych, ale nie chcesz publikować dokładnych danych. Aby użyć istniejącej biblioteki data.frame w ustalonej bibliotece, użyj polecenia data, aby ją zaimportować.

na przykład,

data(mtcars)

a następnie zrób problem

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

13
Wiele wbudowanych zestawów danych (takich jak popularne mtcarsi iriszestawy danych) tak naprawdę nie wymaga użycia datawywołania.
Gregor Thomas

92

Jeśli masz duży zestaw danych, którego nie można łatwo umieścić w skrypcie dput(), opublikuj swoje dane w pastebin i załaduj je za pomocą read.table:

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

Inspirowany @Henrik .


90

Zajmuję się Wakefield pakiet do tego wymogu, aby szybko udostępniać dane powtarzalne, czasami dputdziała prawidłowo dla mniejszych zbiorów danych, ale wiele problemów mamy do czynienia są znacznie większe, dzieląc tak dużego zestawu danych poprzez dputto niepraktyczne.

O:

wakefield pozwala użytkownikowi współdzielić minimalny kod do reprodukcji danych. Użytkownik ustawian(liczbę wierszy) i określa dowolną liczbę predefiniowanych funkcji zmiennych (obecnie jest ich 70), które naśladują rzeczywiste dane (np. Płeć, wiek, dochód itp.)

Instalacja:

Obecnie (2015-06-11) wakefield jest pakietem GitHub, ale w końcu przejdzie do CRAN po napisaniu testów jednostkowych. Aby szybko zainstalować, użyj:

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

Przykład:

Oto przykład:

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

Daje to:

    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

Jeśli masz w danych jedną lub więcej factorzmiennych, które chcesz odtworzyć dput(head(mydata)), rozważ dodanie droplevelstej wartości, aby poziomy czynników nieobecnych w zminimalizowanym zbiorze danych nie zostały uwzględnione w danych dputwyjściowych, aby zminimalizuj przykład :

dput(droplevels(head(mydata)))


47

Nie wklejaj danych wyjściowych konsoli w ten sposób:

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

Nie możemy skopiować i wkleić go bezpośrednio.

Aby pytania i odpowiedzi były poprawnie odtwarzalne, spróbuj je usunąć +i >opublikować, a następnie wstaw #wyniki i komentarze w następujący sposób:

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

Jeszcze jedna rzecz, jeśli użyłeś dowolnej funkcji z określonego pakietu, wspomnij o tej bibliotece.


2
czy usuwasz >i dodajesz #ręcznie, czy jest to automatyczny sposób?
BCArg

3
@BCArg Usuwam >ręcznie. Ale na dodatek #używam Ctrl+Shift+Cskrótu w RStudioedytorze.
user2100721,

33

Możesz to zrobić za pomocą przedrostka .

Jak zauważył mt1022 , „... dobrym pakietem do tworzenia minimalnego, powtarzalnego przykładu jest „ reprezentacja ” z tidyverse ”.

Według Tidyverse :

Celem „reprezentowania” jest spakowanie twojego problematycznego kodu w taki sposób, aby inni ludzie mogli go uruchomić i poczuć twój ból.

Przykład podano na stronie internetowej Tidyverse .

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

Myślę, że jest to najprostszy sposób na stworzenie odtwarzalnego przykładu.


33

Oprócz wszystkich powyższych odpowiedzi, które uznałem za bardzo interesujące, czasami może być bardzo łatwe, ponieważ zostało to omówione tutaj: - JAK ZROBIĆ MINIMALNY PRZYKŁAD REPRODUKOWALNY, ABY POMÓC Z R

Istnieje wiele sposobów tworzenia losowego wektora Utwórz wektor o liczbie 100 z losowymi wartościami w R zaokrąglonymi do 2 miejsc po przecinku lub losową macierzą w R

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

Należy pamiętać, że czasami bardzo trudno jest udostępnić dane z różnych powodów, takich jak wymiar itp. Jednak wszystkie powyższe odpowiedzi są świetne i bardzo ważne, aby przemyśleć i wykorzystać je, gdy chcemy stworzyć przykładowy odtwarzalny przykład. Należy jednak pamiętać, że aby dane były tak reprezentatywne jak oryginalne (w przypadku gdy OP nie może udostępnić oryginalnych danych), dobrze jest dodać pewne informacje z przykładem danych jako (jeśli nazywamy dane mydf1)

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

Ponadto należy znać typ, długość i atrybuty danych, które mogą być strukturami danych

#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

Oto niektóre z moich sugestii:

  • Spróbuj użyć domyślnych zestawów danych R.
  • Jeśli masz własny zestaw danych, dołącz je do dput , aby inni mogli łatwiej ci pomóc
  • Nie używaj, install.package()chyba że jest to naprawdę konieczne, ludzie zrozumieją, czy po prostu używasz requirelublibrary
  • Staraj się być zwięzły,

    • Masz jakiś zestaw danych
    • Spróbuj opisać potrzebne dane wyjściowe tak prosto, jak to możliwe
    • Zrób to sam, zanim zadasz pytanie
  • Załadowanie obrazu jest łatwe, więc jeśli masz, prześlij działki
  • Uwzględnij również ewentualne błędy

Wszystko to jest częścią odtwarzalnego przykładu.


1
Tak naprawdę nie dodałeś tutaj niczego istotnego. dput()zostało wspomniane wcześniej, a wiele z nich przypomina tylko standardowe wytyczne SO.
Rich Scriven,

1
Miałem problem z install.packagefunkcją zawartą w przykładzie, która nie jest tak naprawdę konieczna (moim zdaniem). Ponadto użycie domyślnego zestawu danych R ułatwiłoby powtarzalność. Wytyczne SO nie mówiły konkretnie o tych tematach. Co więcej, miało to na celu wyrażenie mojej opinii i są to te, z którymi najczęściej się spotkałem.
TheRimalaya,

18

Dobrym pomysłem jest użycie funkcji z testthatpakietu, aby pokazać, czego się spodziewasz. Dlatego inne osoby mogą zmieniać Twój kod, dopóki nie zostanie uruchomiony bez błędów. Zmniejsza to ciężar tych, którzy chcieliby ci pomóc, ponieważ oznacza to, że nie muszą dekodować twojego opisu tekstowego. Na przykład

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

jest jaśniejsze niż „Myślę, że x wyniesie 1,23 dla y równego lub przekraczającego 10, a w przeciwnym razie 3,21, ale nie uzyskałem żadnego wyniku”. Nawet w tym głupim przykładzie kod wydaje się jaśniejszy niż słowa. Użycie testthatpozwala pomocnikowi skoncentrować się na kodzie, co pozwala zaoszczędzić czas, i pozwala mu wiedzieć, że rozwiązali problem, zanim go opublikują.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.