Jaka jest najbardziej przydatna sztuczka R? [Zamknięte]

88

Aby podzielić się kolejnymi wskazówkami i trikami dotyczącymi języka R , jaka jest Twoja najbardziej przydatna funkcja lub sztuczka? Sprytna wektoryzacja? Wejście / wyjście danych? Wizualizacja i grafika? Analiza statystyczna? Funkcje specjalne? Samo środowisko interaktywne?

Jedna pozycja na post i zobaczymy, czy wygramy w drodze głosów.

[Edit 25-Aug 2008]: Tak więc po tygodniu wydaje się, że str()ankietę wygrał prosty . Ponieważ lubię to polecać, jest to łatwa do zaakceptowania odpowiedź.

Dirk Eddelbuettel
źródło
8
@Dirk: „wiki społeczności” oznacza „należące do społeczności”, nie jest to synonim „pytania ankiety”. Nie słuchaj policji wiki społeczności.
Julia
4
Biorąc pod uwagę meta.stackexchange.com/questions/11740/ ... powinno być CW.
dmckee --- kociak byłego moderatora
8
Znów zastraszanie przez CW. Zobaczę twoją meta-SO i wychowam
ars
13
@ars: to pytanie, na które nie ma jednoznacznej odpowiedzi . Ergo sprawi, że będzie CW.
dmckee --- kociak byłego moderatora
2
@JD Długi zabawny komentarz. niestety został schowany za fałdą. Mam na myśli, że odpowiadanie na trudne pytania R nie opłaca się zbytnio. Więc jest dla mnie w porządku, jeśli faceci, którzy zadają fajne pytania, które umieszczają R na mapie, w końcu dostaną trochę uznania. Poza tym jest to z pewnością bardziej przydatne dla użytkowników języka R niż pytanie o to, jakie jest twoje ulubione pytanie dotyczące sztuczki C dla programistów C ...
Matt Bannert

Odpowiedzi:

64

str() informuje o strukturze dowolnego obiektu.

hadley
źródło
Python używa dir()- ma więcej sensu.
Hamish Grubijan
17
Ach, w wielu językach strjest też skrótem string.
Hamish Grubijan
Dlaczego nie class()? Wydaje się, że ujawnia podobny rodzaj informacji. Dlaczego istnieją dwa takie podobne polecenia?
hhh,
1
class()to tylko niewielka część informacji, które str()wyświetla
Hadley
64

Bardzo przydatną funkcją, której często używam, jest dput (), która pozwala na zrzucenie obiektu w postaci kodu R.

# Use the iris data set
R> data(iris)
# dput of a numeric vector
R> dput(iris$Petal.Length)
c(1.4, 1.4, 1.3, 1.5, 1.4, 1.7, 1.4, 1.5, 1.4, 1.5, 1.5, 1.6, 
1.4, 1.1, 1.2, 1.5, 1.3, 1.4, 1.7, 1.5, 1.7, 1.5, 1, 1.7, 1.9, 
1.6, 1.6, 1.5, 1.4, 1.6, 1.6, 1.5, 1.5, 1.4, 1.5, 1.2, 1.3, 1.4, 
1.3, 1.5, 1.3, 1.3, 1.3, 1.6, 1.9, 1.4, 1.6, 1.4, 1.5, 1.4, 4.7, 
4.5, 4.9, 4, 4.6, 4.5, 4.7, 3.3, 4.6, 3.9, 3.5, 4.2, 4, 4.7, 
3.6, 4.4, 4.5, 4.1, 4.5, 3.9, 4.8, 4, 4.9, 4.7, 4.3, 4.4, 4.8, 
5, 4.5, 3.5, 3.8, 3.7, 3.9, 5.1, 4.5, 4.5, 4.7, 4.4, 4.1, 4, 
4.4, 4.6, 4, 3.3, 4.2, 4.2, 4.2, 4.3, 3, 4.1, 6, 5.1, 5.9, 5.6, 
5.8, 6.6, 4.5, 6.3, 5.8, 6.1, 5.1, 5.3, 5.5, 5, 5.1, 5.3, 5.5, 
6.7, 6.9, 5, 5.7, 4.9, 6.7, 4.9, 5.7, 6, 4.8, 4.9, 5.6, 5.8, 
6.1, 6.4, 5.6, 5.1, 5.6, 6.1, 5.6, 5.5, 4.8, 5.4, 5.6, 5.1, 5.1, 
5.9, 5.7, 5.2, 5, 5.2, 5.4, 5.1)
# dput of a factor levels
R> dput(levels(iris$Species))
c("setosa", "versicolor", "virginica")

Bardzo przydatne może być publikowanie łatwo odtwarzalnych fragmentów danych, gdy prosisz o pomoc lub edytujesz lub zmieniasz kolejność poziomów współczynnika.

juba
źródło
42

head () i tail (), aby uzyskać pierwszą i ostatnią część ramki danych, wektora, macierzy, funkcji itp. Szczególnie w przypadku dużych ramek danych jest to szybki sposób sprawdzenia, czy został załadowany prawidłowo.

Rob Hyndman
źródło
38

Jedna fajna funkcja: odczyt danych wykorzystuje połączenia, które mogą być plikami lokalnymi, plikami zdalnymi dostępnymi przez http, potokami z innych programów lub więcej.

Jako prosty przykład rozważmy ten dostęp dla N = 10 losowych liczb całkowitych od min = 100 do max = 200 z random.org (który dostarcza prawdziwe liczby losowe oparte na szumie atmosferycznym, a nie na generatorze liczb pseudolosowych):

R> site <- "http://random.org/integers/"         # base URL
R> query <- "num=10&min=100&max=200&col=2&base=10&format=plain&rnd=new"
R> txt <- paste(site, query, sep="?")            # concat url and query string
R> nums <- read.table(file=txt)                  # and read the data
R> nums                                          # and show it
   V1  V2
1 165 143
2 107 118
3 103 132
4 191 100
5 138 185
R>

Tak na marginesie, losowo pakiet zawiera kilka funkcji wygody dostępu do random.org .

Dirk Eddelbuettel
źródło
BTW-- bym sugerują, że należy dokonać selfanswers CW, jeżeli (1) Państwo umieszczać je niezwłocznie i (2) nie dokonać pytanie CW. W przeciwnym razie wygląda to trochę tak, jakbyś próbował oszukać system rep. YMMV i tak dalej.
dmckee --- kociak byłego moderatora
1
To nie gra w system, tylko rozpoczęcie rzeczy. Nadal może zaakceptować każdą inną odpowiedź.
ars
2
@ars: Może to zaakceptować. Nie będę też próbował zmusić go do korzystania z wiki, jeśli wygra; nie posłuchaj mojej rady. Ale nie opublikuję przygotowanej autoodpowiedzi bez zaznaczenia jej jako wiki, ani nie zagłosuję na taką bez niej. Weź to za to, ile jest warte.
dmckee --- ex-moderator kitten
4
@Dirk: odpowiedź na własne pytanie jest w pełni akceptowalna, nawet zachęcana przez Jeffa i Joela. Nie ma ŻADNEGO wymagania, nawet nieformalnego, aby twoja odpowiedź była CW. Najwyraźniej nie grasz w system. Jeszcze raz zignoruj ​​policję społecznościową wiki.
Julia
8
Muszę się zgodzić, że część stron ma na celu dostarczenie najlepszych odpowiedzi na typowe problemy i ogólne zasoby. Zadanie pytań i udzielenie dobrej odpowiedzi może pomóc wzmocnić temat. Jest to szczególnie przydatne w przypadku nowych / małych tagów, takich jak R.
kpierce8
35

Uważam, że używam with()i within()coraz więcej. Nigdy więcej $zaśmiecania mojego kodu i nie trzeba zaczynać dołączania obiektów do ścieżki wyszukiwania. Mówiąc poważnie, uważam, że with()itp. Sprawiają, że intencja moich skryptów analizy danych jest znacznie jaśniejsza.

> df <- data.frame(A = runif(10), B = rnorm(10))
> A <- 1:10 ## something else hanging around...
> with(df, A + B) ## I know this will use A in df!
 [1]  0.04334784 -0.40444686  1.99368816  0.13871605 -1.17734837
 [6]  0.42473812  2.33014226  1.61690799  1.41901860  0.8699079

with()konfiguruje środowisko, w którym jest oceniane wyrażenie R. within()robi to samo, ale umożliwia modyfikację obiektu danych używanego do tworzenia środowiska.

> df <- within(df, C <- rpois(10, lambda = 2))
> head(df)
           A          B C
1 0.62635571 -0.5830079 1
2 0.04810539 -0.4525522 1
3 0.39706979  1.5966184 3
4 0.95802501 -0.8193090 2
5 0.76772541 -1.9450738 2
6 0.21335006  0.2113881 4

Coś, czego nie zdawałem sobie sprawy, kiedy pierwszy raz użyłem, within()to fakt, że musisz wykonać przypisanie jako część ocenianego wyrażenia i przypisać zwrócony obiekt (jak powyżej), aby uzyskać pożądany efekt.

Gavin Simpson
źródło
34

Sztuczka wprowadzania danych = pakiet RGoogleDocs

http://www.omegahat.org/RGoogleDocs/

Odkryłem, że arkusze kalkulacyjne Google to fantastyczny sposób, aby wszyscy współpracownicy znajdowali się na tej samej stronie. Co więcej, Google Forms pozwala na przechwytywanie danych od respondentów i bezproblemowe zapisywanie ich w arkuszu kalkulacyjnym Google. Ponieważ dane zmieniają się często i prawie nigdy nie są ostateczne, lepiej jest, gdy R czyta bezpośrednio arkusz kalkulacyjny Google, niż pobiera pliki csv i odczytuje je.

# Get data from google spreadsheet
library(RGoogleDocs)
ps <-readline(prompt="get the password in ")
auth = getGoogleAuth("[email protected]", ps, service="wise")
sheets.con <- getGoogleDocsConnection(auth)
ts2=getWorksheets("Data Collection Repos",sheets.con)
names(ts2)
init.consent <-sheetAsMatrix(ts2$Sheet1,header=TRUE, as.data.frame=TRUE, trim=TRUE)

Nie mogę sobie przypomnieć, które z poniższych poleceń, oprócz jednego lub dwóch, zajmuje kilka sekund.

  1. getGoogleAuth

  2. getGoogleDocsConnection

  3. getWorksheets

Farrel
źródło
27

Użyj odwrotnych znaków, aby odwołać się do niestandardowych nazw.

> df <- data.frame(x=rnorm(5),y=runif(5))
> names(df) <- 1:2
> df
           1         2
1 -1.2035003 0.6989573
2 -1.2146266 0.8272276
3  0.3563335 0.0947696
4 -0.4372646 0.9765767
5 -0.9952423 0.6477714
> df$1
Error: unexpected numeric constant in "df$1"
> df$`1`
[1] -1.2035003 -1.2146266  0.3563335 -0.4372646 -0.9952423

W tym przypadku działałoby również df [, "1"]. Ale kleszcze działają w formułach!

> lm(`2`~`1`,data=df)

Call:
lm(formula = `2` ~ `1`, data = df)

Coefficients:
(Intercept)          `1`  
     0.4087      -0.3440  

[Edytuj] Dirk pyta, dlaczego ktoś miałby podawać nieprawidłowe nazwiska? Nie wiem! Ale z pewnością dość często spotykam się z tym problemem w praktyce. Na przykład używając pakietu zmiany kształtu Hadleya:

> library(reshape)
> df$z <- c(1,1,2,2,2)
> recast(df,z~.,id.var="z")
Aggregation requires fun.aggregate: length used as default
  z (all)
1 1     4
2 2     6
> recast(df,z~.,id.var="z")$(all)
Error: unexpected '(' in "recast(df,z~.,id.var="z")$("
> recast(df,z~.,id.var="z")$`(all)`
Aggregation requires fun.aggregate: length used as default
[1] 4 6
Eduardo Leoni
źródło
Ok, ale dlaczego miałbyś zastępować poprawne składniowo nazwy (takie jak x lub y) niepoprawnymi (np. 1 lub 2) wymagające znaków odwrotnych?
Dirk Eddelbuettel
3
Jest to również przydatne, read.tablegdy check.namesjest fałszywe - tj. Gdy chcesz pracować z oryginalnymi nazwami kolumn.
Hadley
25

Nie wiem, jak dobrze to jest / nie jest znane, ale zdecydowanie skorzystałem z możliwości przekazywania przez referencje środowisk.

zz <- new.env()
zz$foo <- c(1,2,3,4,5)
changer <- function(blah) {
   blah$foo <- 5
}
changer(zz)
zz$foo

W tym przykładzie nie ma sensu, dlaczego miałoby to być przydatne, ale jeśli mijasz wokół dużych obiektów, może to pomóc.

geoffjentry
źródło
23

Moją nową ulubioną rzeczą jest biblioteka foreach. Pozwala na wykonanie wszystkich przyjemnych czynności związanych z zastosowaniem, ale z nieco łatwiejszą składnią:

list_powers <- foreach(i = 1:100) %do% {
  lp <- x[i]^i
  return (lp)
}

Najlepsze jest to, że jeśli robisz coś, co faktycznie wymaga znacznej ilości czasu, możesz przełączyć się z %do%do %dopar%(z odpowiednią biblioteką zaplecza), aby natychmiast zrównoleglać, nawet w obrębie klastra. Bardzo zgrabny.

JAShapiro
źródło
19

Robię wiele podstawowych operacji na danych, więc oto dwie wbudowane funkcje ( transformacja , podzbiór ) i jedna biblioteka ( sqldf ), których używam codziennie.

stworzyć przykładowe dane sprzedażowe

sales <- expand.grid(country = c('USA', 'UK', 'FR'),
                     product = c(1, 2, 3))
sales$revenue <- rnorm(dim(sales)[1], mean=100, sd=10)

> sales
  country product   revenue
1     USA       1 108.45965
2      UK       1  97.07981
3      FR       1  99.66225
4     USA       2 100.34754
5      UK       2  87.12262
6      FR       2 112.86084
7     USA       3  95.87880
8      UK       3  96.43581
9      FR       3  94.59259

użyj transform (), aby dodać kolumnę

## transform currency to euros
usd2eur <- 1.434
transform(sales, euro = revenue * usd2eur)

>
  country product   revenue     euro
1     USA       1 108.45965 155.5311
2      UK       1  97.07981 139.2125
3      FR       1  99.66225 142.9157
...

użyj subset (), aby wyciąć dane

subset(sales, 
       country == 'USA' & product %in% c(1, 2), 
       select = c('product', 'revenue'))

>
  product  revenue
1       1 108.4597
4       2 100.3475

użyj sqldf () do wycinania i agregowania za pomocą SQL

Pakiet sqldf zapewnia interfejs SQL dla ramek danych R

##  recast the previous subset() expression in SQL
sqldf('SELECT product, revenue FROM sales \
       WHERE country = "USA" \
       AND product IN (1,2)')

>
  product  revenue
1       1 108.4597
2       2 100.3475

Wykonaj agregację lub GROUP BY

sqldf('select country, sum(revenue) revenue \ 
       FROM sales \
       GROUP BY country')

>
  country  revenue
1      FR 307.1157
2      UK 280.6382
3     USA 304.6860

Aby uzyskać bardziej wyrafinowaną funkcję zmniejszania map w ramkach danych, sprawdź pakiet plyr . A jeśli znajdziesz się chcąc wyciągnąć włosy, polecam sprawdzanie manipulacji danymi z badań .

medriscoll
źródło
18
?ave

Podzbiory „x []” są uśredniane, przy czym każdy podzbiór składa się z tych obserwacji o tych samych poziomach czynników. Użycie: ave (x, ..., FUN = mean)

Używam go cały czas. (np. w tej odpowiedzi tutaj w so )

Eduardo Leoni
źródło
czym się to różni od tapply (x, factor, fun)?
TMS,
1
@Tomas ave zachowuje kolejność i długość. więc możesz na przykład dodać wektor środków grupowych do zbioru danych w jednym kroku.
Eduardo Leoni
18

Sposób na przyspieszenie kodu i wyeliminowanie pętli.

zamiast pętli for, które przechodzą przez ramkę danych w poszukiwaniu wartości. po prostu weź podzbiór df z tymi wartościami, znacznie szybciej.

więc zamiast:

for(i in 1:nrow(df)){
  if (df$column[i] == x) {
    df$column2[i] <- y
    or any other similiar code
  }
}

zrób coś takiego:

df$column2[df$column1 == x] <- y

ta podstawowa koncepcja ma zastosowanie niezwykle często i jest świetnym sposobem na pozbycie się pętli for

Dan
źródło
11
Jest tu mała pułapka, która cały czas mnie łapała. Jeśli df $ kolumna1 zawiera wartości NA, podzbiór przy użyciu == wyciągnie wszystkie wartości, które są równe x i wszystkie NA. Aby tego uniknąć, użyj „% w%” zamiast „==”.
Matt Parker
Matt, masz absolutną rację i tego nienawidzę, chociaż podoba mi się twoja metoda. Zwykle sprawdzam kolumnę pod kątem NA, a następnie usuwam je za pomocą szybkiej funkcji, którą wykonałem, która pobiera kolumnę ramki danych i zwraca ramkę danych bez wierszy z NA tylko w tej kolumnie.
Dan
Zasadniczo łączę ramkę danych w dół do kolumn, w których muszę mieć wartości, a następnie używam na.omit, aby uzyskać prawidłowe wiersze, a następnie podzbiór oryginalnego zestawu danych tylko z tymi wierszami. Samo użycie na.omit spowodowałoby usunięcie dowolnego wiersza z dowolnym NA, chociaż mogę się mylić.
Dan
16

Czasami potrzebujesz rbindwielu ramek danych. do.call()pozwoli ci to zrobić (ktoś musiał mi to wyjaśnić, gdy bind zadałem to pytanie, ponieważ nie wydaje się to oczywiste zastosowanie).

foo <- list()

foo[[1]] <- data.frame(a=1:5, b=11:15)
foo[[2]] <- data.frame(a=101:105, b=111:115)
foo[[3]] <- data.frame(a=200:210, b=300:310)

do.call(rbind, foo)
andrewj
źródło
Dobre połączenie: uważam, że jest to często prostsze niż używanie unsplit.
Richie Cotton
16

W programowaniu R (nie interaktywnych sesji), używam if (bad.condition) stop("message")jest dużo . Każda funkcja zaczyna się od kilku z nich, a kiedy pracuję nad obliczeniami, również je wprowadzam. Chyba przyzwyczaiłem się do używania assert()w C. Korzyści są dwojakie. Po pierwsze, uzyskanie działającego kodu jest dużo szybsze po wprowadzeniu tych sprawdzeń. Po drugie, i prawdopodobnie ważniejsze, o wiele łatwiej jest pracować z istniejącym kodem, gdy widzisz te kontrole na każdym ekranie w swoim edytorze. Nie będziesz musiał się zastanawiać x>0, czy zaufać komentarzowi stwierdzającemu, że jest ... będziesz wiedział , na pierwszy rzut oka, że ​​tak jest.

PS. mój pierwszy post tutaj. Bądź delikatny!

wilgotny
źródło
12
Niezły nawyk, a R oferuje jeszcze inny sposób: stopfifnot(!bad.condition)który jest bardziej zwięzły.
Dirk Eddelbuettel
13

traceback()Funkcja jest koniecznością kiedy masz gdzieś błąd i nie rozumiem go łatwo. Wyświetli ślad stosu, co jest bardzo pomocne, ponieważ R domyślnie nie jest zbyt rozwlekły.

Wtedy ustawienie options(error=recover)pozwoli Ci „wejść” do funkcji podnoszącej błąd i spróbować zrozumieć, co się dokładnie dzieje, tak jakbyś miał nad nią pełną kontrolę i mógł browser()w niej umieścić .

Te trzy funkcje mogą naprawdę pomóc w debugowaniu kodu.

Calimo
źródło
1
options(error=recover)to moja ulubiona metoda debugowania.
Joshua Ulrich
12

Jestem naprawdę zaskoczony, że nikt nie opublikował na temat aplikacji, tapply, lapply i sapply. Ogólna zasada, której używam podczas robienia rzeczy w R, jest taka, że ​​jeśli mam pętlę for, która przetwarza dane lub przeprowadza symulacje, próbuję ją rozłożyć na czynniki i zastąpić *. Niektórzy ludzie unikają funkcji * Apply, ponieważ uważają, że można przekazywać tylko funkcje pojedynczego parametru. Nic nie może być dalsze od prawdy! Podobnie jak w przypadku przekazywania funkcji z parametrami jako obiektów pierwszej klasy w JavaScript, robisz to w języku R z funkcjami anonimowymi. Na przykład:

 > sapply(rnorm(100, 0, 1), round)
  [1]  1  1  0  1  1 -1 -2  0  2  2 -2 -1  0  1 -1  0  1 -1  0 -1  0  0  0  0  0
 [26]  2  0 -1 -2  0  0  1 -1  1  5  1 -1  0  1  1  1  2  0 -1  1 -1  1  0 -1  1
 [51]  2  1  1 -2 -1  0 -1  2 -1  1 -1  1 -1  0 -1 -2  1  1  0 -1 -1  1  1  2  0
 [76]  0  0  0 -2 -1  1  1 -2  1 -1  1  1  1  0  0  0 -1 -3  0 -1  0  0  0  1  1


> sapply(rnorm(100, 0, 1), round(x, 2)) # How can we pass a parameter?
Error in match.fun(FUN) : object 'x' not found


# Wrap your function call in an anonymous function to use parameters
> sapply(rnorm(100, 0, 1), function(x) {round(x, 2)})
  [1] -0.05 -1.74 -0.09 -1.23  0.69 -1.43  0.76  0.55  0.96 -0.47 -0.81 -0.47
 [13]  0.27  0.32  0.47 -1.28 -1.44 -1.93  0.51 -0.82 -0.06 -1.41  1.23 -0.26
 [25]  0.22 -0.04 -2.17  0.60 -0.10 -0.92  0.13  2.62  1.03 -1.33 -1.73 -0.08
 [37]  0.45 -0.93  0.40  0.05  1.09 -1.23 -0.35  0.62  0.01 -1.08  1.70 -1.27
 [49]  0.55  0.60 -1.46  1.08 -1.88 -0.15  0.21  0.06  0.53 -1.16 -2.13 -0.03
 [61]  0.33 -1.07  0.98  0.62 -0.01 -0.53 -1.17 -0.28 -0.95  0.71 -0.58 -0.03
 [73] -1.47 -0.75 -0.54  0.42 -1.63  0.05 -1.90  0.40 -0.01  0.14 -1.58  1.37
 [85] -1.00 -0.90  1.69 -0.11 -2.19 -0.74  1.34 -0.75 -0.51 -0.99 -0.36 -1.63
 [97] -0.98  0.61  1.01  0.55

# Note that anonymous functions aren't being called, but being passed.
> function() {print('hello #rstats')}()
function() {print('hello #rstats')}()
> a = function() {print('hello #rstats')}
> a
function() {print('hello #rstats')}
> a()
[1] "hello #rstats"

(Dla tych, którzy śledzą #rstats, również to tam opublikowałem).

Pamiętaj, użyj aplikacji Apply, Sapply, Lapply, Tapply i do. ​​Call! Skorzystaj z wektoryzacji R. Nigdy nie powinieneś podchodzić do zestawu kodu R i zobaczyć:

N = 10000
l = numeric()
for (i in seq(1:N)) {
    sim <- rnorm(1, 0, 1)
    l <- rbind(l, sim)
}

Nie tylko nie jest to wektoryzowane, ale struktura tablicy w R nie jest powiększana tak, jak w Pythonie (podwajanie rozmiaru, gdy skończy się miejsce, IIRC). Więc każdy krok rbind musi najpierw urosnąć na tyle, aby zaakceptować wyniki rbind (), a następnie skopiować całą zawartość poprzedniego l. Dla zabawy wypróbuj powyższe w R. Zwróć uwagę, jak długo to trwa (nie potrzebujesz nawet Rprof ani żadnej funkcji czasowej). Więc spróbuj

N=10000
l <- rnorm(N, 0, 1)

Poniższe są również lepsze niż pierwsza wersja:

N = 10000
l = numeric(N)
for (i in seq(1:N)) {
    sim <- rnorm(1, 0, 1)
    l[i] <- sim
}
Vince
źródło
zastosować, sapply, lapply i tapply są przydatne. Jeśli chcesz przekazać parametry do nazwanej funkcji, takiej jak round, możesz po prostu przekazać ją razem z Apply zamiast pisać funkcję anonimową. Spróbuj „sapply (rnorm (10, 0, 1), round, digits = 2)”, co daje wynik „[1] -0,29 0,29 1,31 -0,06 -1,90 -0,84 0,21 0,02 0,23 -1,10”.
Daniel
11

Za radą Dirka zamieszczam pojedyncze przykłady. Mam nadzieję, że nie są zbyt „urocze” [sprytne, ale mnie to nie obchodzi] ani trywialne dla tej publiczności.

Modele liniowe są chlebem powszednim R. Gdy liczba zmiennych niezależnych jest wysoka, można wybrać dwie możliwości. Pierwszym jest użycie lm.fit (), które otrzymuje macierz projektową x i odpowiedź y jako argumenty, podobnie jak w Matlabie. Wadą tego podejścia jest to, że zwracaną wartością jest lista obiektów (dopasowanych współczynników, reszt itp.), A nie obiekt klasy „lm”, który można ładnie podsumować, wykorzystać do predykcji, selekcji krokowej itp. podejście polega na stworzeniu formuły:

> A
           X1         X2          X3         X4         y
1  0.96852363 0.33827107 0.261332257 0.62817021 1.6425326
2  0.08012755 0.69159828 0.087994158 0.93780481 0.9801304
3  0.10167545 0.38119304 0.865209832 0.16501662 0.4830873
4  0.06699458 0.41756415 0.258071616 0.34027775 0.7508766
   ...

> (f=paste("y ~",paste(names(A)[1:4],collapse=" + ")))
[1] "y ~ X1 + X2 + X3 + X4"

> lm(formula(f),data=A)

Call:
lm(formula = formula(f), data = A)

Coefficients:
(Intercept)           X1           X2           X3           X4  
    0.78236      0.95406     -0.06738     -0.43686     -0.06644  
przerwy
źródło
Co powiesz na to, że wybierzesz jeden na post i zilustrujesz przykładem? Możemy wtedy działać przez wiele dni i publikować nowe przykłady z nowymi poleceniami ... [BTW: Jak pamiętam, potrzebujesz as.formula (wklej (...)) do użycia formuły. ]
Dirk Eddelbuettel,
Nie musisz jawnie tworzyć formuły, aby pokryć wszystkie kolumny, ponieważ obejmuje to format „y ~. - 1”. „.” oznacza „wszystkie kolumny z wyjątkiem zmiennej zależnej, a„ - 1 ”wyklucza stałą, jak w przykładzie.
Dirk Eddelbuettel
To prawda w tym konkretnym przykładzie, ale dla X z ncols >> nrows, często usuwam niektóre zmienne niezależne, szczególnie w końcowych etapach analizy. W takim przypadku tworzenie formuły na podstawie nazw ramek danych jest nadal przydatne.
gappy
10

Możesz przypisać wartość zwracaną z bloku if-else.

Zamiast np

condition <- runif(1) > 0.5
if(condition) x <- 1 else x <- 2

możesz to zrobić

x <- if(condition) 1 else 2

Dokładnie jak to działa, to głęboka magia.

Richie Cotton
źródło
6
Możesz również zrobić to tak, jak x <- ifelse (condition, 1, 2), w którym to przypadku każdy komponent jest wektoryzowany.
Shane
Shane, mógłbyś, ale chyba że naprawdę głęboko zastanawiasz się, co robi ifelse (), prawdopodobnie nie powinieneś! Łatwo jest źle zrozumieć ...
Harlan
Co w tym magicznego? Tak właśnie if-then-elsedziałają wyrażenia w każdym języku funkcjonalnym (nie mylić ze if-then-else stwierdzeniami ). Bardzo podobny do trójskładnikowego ?:operatora języków podobnych do C.
Frank
10

Jako totalny noob dla R i nowicjusz w statystykach, uwielbiam unclass() drukować wszystkie elementy ramki danych jako zwykłą listę.

Jest to bardzo przydatne, gdy spojrzysz na pełny zestaw danych za jednym razem, aby szybko przyjrzeć się potencjalnym problemom.

Jan
źródło
9

CrossTable()z gmodelspakietu zapewnia łatwy dostęp do tabel przestawnych typu SAS i SPSS, wraz ze zwykłymi testami (Chisq, McNemar itp.). Zasadniczo ma xtabs()fantazyjną wydajność i kilka dodatkowych testów - ale ułatwia dzielenie się wynikami z poganami.

Matt Parker
źródło
Ładny!! Używam dość często modeli gmodels, ale przegapiłem ten
Abhijit
Dobra odpowiedź, wszystko, co może powstrzymać mnie od nadmiernego objaśniania tabel z poganami, to dobre wykorzystanie czasu.
Stedy
7

Ostatecznie system(). Możliwość dostępu do wszystkich narzędzi unixowych (przynajmniej pod Linuksem / MacOSX) z wnętrza środowiska R szybko stała się nieoceniona w moim codziennym przepływie pracy.

Paolo
źródło
1
To wiąże się z moim wcześniejszym komentarzem na temat połączeń: możesz również użyć pipe () do przekazywania danych z lub do poleceń systemu Unix. Zobacz help(connections)szczegóły i przykłady.
Dirk Eddelbuettel
6

Oto irytujące obejście polegające na zamianie współczynnika na liczbowy. (Podobnie jak w przypadku innych typów danych)

old.var <- as.numeric(levels(old.var))[as.numeric(old.var)]
Ryana Rosario
źródło
2
Może miałeś na myśli wektor „w postać”. W takim przypadku „as.character (old.var)” jest prostsze.
Dirk Eddelbuettel
1
Zawsze uważałem, że ta rada (którą można czytać na współczynniku) była błędna. Musisz być pewien, że old.var jest czynnikiem, a to będzie się różnić w zależności od opcji ustawionych dla sesji R. Używanie as.numeric (as.character (old.var)) jest zarówno bezpieczniejsze, jak i czystsze.
Eduardo Leoni
Naprawdę nie warto negatywnie oceniać, ale nieważne. To działa dla mnie.
Ryan R. Rosario
Ryan - Czy mógłbyś naprawić swój kod? Jeśli stary.var <- współczynnik (1: 2); Twój kod da [1] "1" "2" (nie numeryczne). Być może miałeś na myśli as.numeric (poziomy (stary.var) [stary.var])?
Eduardo Leoni
3
Lub trochę wydajniej:as.numeric(levels(old.var))[old.var]
hadley,
6

Chociaż to pytanie pojawia się od jakiegoś czasu, niedawno odkryłem na blogu SAS i R świetną sztuczkę dotyczącą używania polecenia cut. Polecenie służy do dzielenia danych na kategorie i użyję zbioru danych tęczówki jako przykładu i podzielę go na 10 kategorii:

> irisSL <- iris$Sepal.Length
> str(irisSL)
 num [1:150] 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
> cut(irisSL, 10)
  [1] (5.02,5.38] (4.66,5.02] (4.66,5.02] (4.3,4.66]  (4.66,5.02] (5.38,5.74] (4.3,4.66]  (4.66,5.02] (4.3,4.66]  (4.66,5.02]
 [11] (5.38,5.74] (4.66,5.02] (4.66,5.02] (4.3,4.66]  (5.74,6.1]  (5.38,5.74] (5.38,5.74] (5.02,5.38] (5.38,5.74] (5.02,5.38]
 [21] (5.38,5.74] (5.02,5.38] (4.3,4.66]  (5.02,5.38] (4.66,5.02] (4.66,5.02] (4.66,5.02] (5.02,5.38] (5.02,5.38] (4.66,5.02]
 [31] (4.66,5.02] (5.38,5.74] (5.02,5.38] (5.38,5.74] (4.66,5.02] (4.66,5.02] (5.38,5.74] (4.66,5.02] (4.3,4.66]  (5.02,5.38]
 [41] (4.66,5.02] (4.3,4.66]  (4.3,4.66]  (4.66,5.02] (5.02,5.38] (4.66,5.02] (5.02,5.38] (4.3,4.66]  (5.02,5.38] (4.66,5.02]
 [51] (6.82,7.18] (6.1,6.46]  (6.82,7.18] (5.38,5.74] (6.46,6.82] (5.38,5.74] (6.1,6.46]  (4.66,5.02] (6.46,6.82] (5.02,5.38]
 [61] (4.66,5.02] (5.74,6.1]  (5.74,6.1]  (5.74,6.1]  (5.38,5.74] (6.46,6.82] (5.38,5.74] (5.74,6.1]  (6.1,6.46]  (5.38,5.74]
 [71] (5.74,6.1]  (5.74,6.1]  (6.1,6.46]  (5.74,6.1]  (6.1,6.46]  (6.46,6.82] (6.46,6.82] (6.46,6.82] (5.74,6.1]  (5.38,5.74]
 [81] (5.38,5.74] (5.38,5.74] (5.74,6.1]  (5.74,6.1]  (5.38,5.74] (5.74,6.1]  (6.46,6.82] (6.1,6.46]  (5.38,5.74] (5.38,5.74]
 [91] (5.38,5.74] (5.74,6.1]  (5.74,6.1]  (4.66,5.02] (5.38,5.74] (5.38,5.74] (5.38,5.74] (6.1,6.46]  (5.02,5.38] (5.38,5.74]
[101] (6.1,6.46]  (5.74,6.1]  (6.82,7.18] (6.1,6.46]  (6.46,6.82] (7.54,7.9]  (4.66,5.02] (7.18,7.54] (6.46,6.82] (7.18,7.54]
[111] (6.46,6.82] (6.1,6.46]  (6.46,6.82] (5.38,5.74] (5.74,6.1]  (6.1,6.46]  (6.46,6.82] (7.54,7.9]  (7.54,7.9]  (5.74,6.1] 
[121] (6.82,7.18] (5.38,5.74] (7.54,7.9]  (6.1,6.46]  (6.46,6.82] (7.18,7.54] (6.1,6.46]  (5.74,6.1]  (6.1,6.46]  (7.18,7.54]
[131] (7.18,7.54] (7.54,7.9]  (6.1,6.46]  (6.1,6.46]  (5.74,6.1]  (7.54,7.9]  (6.1,6.46]  (6.1,6.46]  (5.74,6.1]  (6.82,7.18]
[141] (6.46,6.82] (6.82,7.18] (5.74,6.1]  (6.46,6.82] (6.46,6.82] (6.46,6.82] (6.1,6.46]  (6.46,6.82] (6.1,6.46]  (5.74,6.1] 
10 Levels: (4.3,4.66] (4.66,5.02] (5.02,5.38] (5.38,5.74] (5.74,6.1] (6.1,6.46] (6.46,6.82] (6.82,7.18] ... (7.54,7.9]
Stedy
źródło
5

Kolejna sztuczka. Niektóre pakiety, jak glmnet, tylko przyjąć jako wejścia macierz projektowania i zmienną odpowiedzi. Jeśli ktoś chce dopasować model do wszystkich interakcji między cechami, nie może użyć formuły „y ~. ^ 2”. Używanie expand.grid()pozwala nam skorzystać z potężnego indeksowania tablic i operacji na wektorach R.

interArray=function(X){
    n=ncol(X)
    ind=expand.grid(1:n,1:n)
    return(X[,ind[,1]]*X[,ind[,2]])
}

> X
          X1         X2
1 0.96852363 0.33827107
2 0.08012755 0.69159828
3 0.10167545 0.38119304
4 0.06699458 0.41756415
5 0.08187816 0.09805104

> interArray(X)
           X1          X2        X1.1        X2.1
1 0.938038022 0.327623524 0.327623524 0.114427316
2 0.006420424 0.055416073 0.055416073 0.478308177
3 0.010337897 0.038757974 0.038757974 0.145308137
4 0.004488274 0.027974536 0.027974536 0.174359821
5 0.006704033 0.008028239 0.008028239 0.009614007
gappy
źródło
3
Jeśli funkcja modelująca nie akceptuje formuły (co jest bardzo rzadkie!), Czy nie byłoby lepiej skonstruować macierz projektu za pomocą model.matrix?
Hadley
Niezłe. Nie wiedziałem o istnieniu tej funkcji. Powyższa funkcja jest równoważna model.matrix (~. ^ 2-1, X) Ale jeśli chodzi o przekazywanie macierzy, poza glmnet, często przekazuję wskaźniki tablic do niestandardowych funkcji C. Rzeczywiście, nie wiedziałbym, jak przekazać formułę do funkcji. Czy masz przykład zabawki?
gappy
5

Jedną z moich ulubionych, jeśli nie nieco niekonwencjonalnych sztuczek, jest użycie eval()i parse(). Ten przykład może być ilustracją tego, jak może to być pomocne

NY.Capital <- 'Albany'
state <- 'NY'
parameter <- 'Capital'
eval(parse(text=paste(state, parameter, sep='.')))

[1] "Albany"

Tego typu sytuacje zdarzają się częściej niż nie, a wykorzystanie eval()i parse()może pomóc w rozwiązaniu tego problemu. Oczywiście cieszę się z wszelkich opinii na temat alternatywnych sposobów kodowania tego.

andrewj
źródło
1
Można to również zrobić za pomocą nazwanych elementów wektorowych.
Dirk Eddelbuettel
3
biblioteka (fortunes); fortune (106) Jeśli odpowiedź brzmi parse (), zwykle należy ponownie przemyśleć pytanie. - Thomas Lumley R-help (luty 2005)
Eduardo Leoni
Oto przykład, w którym eval () i parse () mogą być przydatne. Obejmuje to pakiet Bioconductor, np. Hgu133a.db i gdzie próbujesz uzyskać różne informacje o identyfikatorze sondy. Na przykład: parametr biblioteki (hgu133a.db) <- 'SYMBOL' mget ('202431_s_at', env = eval (parse (text = paste ('hgu133a', parameter, sep = '')))) parametr <- 'ENTREZID 'mget (' 202431_s_at ', env = eval (parse (text = paste (' hgu133a ', parameter, sep =' '))))
andrewj
Jak mówi Dirk, lepiej jest to zrobić z nazwanymi elementami wektorowymi lub `get (paste (state, parameter, sep = '.'))`
hadley
@Hadley, nie wiedziałem, że możesz użyć get () w ten sposób. Dzięki.
andrewj
5

set.seed() ustawia stan generatora liczb losowych.

Na przykład:

> set.seed(123)
> rnorm(1)
[1] -0.5604756
> rnorm(1)
[1] -0.2301775
> set.seed(123)
> rnorm(1)
[1] -0.5604756
Christopher DuBois
źródło
bardzo przydatne w przypadku przykładów, które używają funkcji losowych ... pomaga wszystkim znaleźć się na tej samej stronie
JD Long,
5

Dla tych, którzy piszą C, aby zadzwonić z R: .Internal(inspect(...))jest przydatne. Na przykład:

> .Internal(inspect(quote(a+2)))
  @867dc28 06 LANGSXP g0c0 [] 
  @8436998 01 SYMSXP g1c0 [MARK,gp=0x4000] "+"
  @85768b0 01 SYMSXP g1c0 [MARK,NAM(2)] "a"
  @8d7bf48 14 REALSXP g0c1 [] (len=1, tl=0) 2
Joshua Ulrich
źródło
4

d = '~ / R Kod / Biblioteka /'

files = list.files (d, '. r $')

for (f in files) {if (! (f == 'mysource.r')) {print (paste ('Sourcing', f)) source (paste (d, f, sep = ''))}}

Używam powyższego kodu do pozyskiwania wszystkich plików w katalogu podczas uruchamiania z różnymi programami narzędziowymi, których używam podczas mojej sesji interaktywnej z R. Jestem pewien, że są lepsze sposoby, ale uważam, że jest to przydatne w mojej pracy. Linia, która to robi, jest następująca.

źródło („~ / R Code / Library / mysource.r”)

mcheema
źródło
6
Nie rób tego. Napisz paczkę.
Dirk Eddelbuettel
Dzięki. Patrzyłem na wątek lub dwa na roxygen i wydaje mi się, że prawdopodobnie jestem na poziomie, na którym powinienem spróbować napisać prosty pakiet do samodzielnego użytku.
mcheema
3

Wykonywanie operacji na wielu zmiennych w ramce danych. To zostało skradzione z subset.data.frame.

get.vars<-function(vars,data){
    nl <- as.list(1L:ncol(data))
    names(nl) <- names(data)
    vars <- eval(substitute(vars), nl, parent.frame())
    data[,vars]
    #do stuff here
}

get.vars(c(cyl:hwy,class),mpg)
Ian Fellows
źródło
1
Na początku wydaje się to fajne, ale tego rodzaju kod na dłuższą metę nie sprawi ci końca. Zawsze lepiej jest mówić wprost.
Hadley
bum, ostatnio dość często używam tej sztuczki. Czy mógłbyś bardziej szczegółowo opisać jego nieograniczone problemy?
Ian Fellows
Może hadley sugeruje użycie zamiast tego pakietu plyr?
Christopher DuBois
3
Nie, to nie jest zawoalowana sugestia, aby zamiast tego użyć Plyr. Zasadniczy problem z twoim kodem polega na tym, że jest on semantycznie leniwy - zamiast zmuszać użytkownika do jawnego przeliterowania tego, czego chce, robisz trochę "magii", aby odgadnąć. Problem polega na tym, że bardzo utrudnia to programowanie funkcji - tj. Trudno jest napisać funkcję, która wywołuje get.varsbez przeskakiwania przez całą masę kółek.
Hadley
3

Opublikowałem to już raz, ale używam go tak często, że pomyślałem, że opublikuję go ponownie. To tylko niewielka funkcja zwracająca nazwy i numery pozycji ramki data.frame. Nie jest to nic specjalnego, ale prawie nigdy nie przechodzę przez sesję bez wielokrotnego korzystania z niej.

##creates an object from a data.frame listing the column names and location

namesind = function (df) {

temp1=names(df)
temp2=seq(1,length(temp1))
temp3=data.frame(temp1,temp2)
names(temp3)=c("VAR","COL")
return(temp3)
rm(temp1,temp2,temp3)

}

ni <- namesind

kpierce8
źródło
4
To jest naprawdę jedna linijka:data.frame(VAR = names(df), COL = seq_along(df))
hadley
bardzo eleganckie, może przełączę to na ni <- function (df) {data.frame (VAR = names (df), COL = seq_along (df))}
kpierce8
1
Używam: data.frame (colnames (the.df))
Tal Galili