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


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


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.


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.


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.


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 .


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.


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("me@gmail.com", 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


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

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.


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.


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


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 )


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


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)

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!


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.


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
}

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  

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.


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.


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.


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


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

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]

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

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.


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

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

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


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)

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


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