Qual è il trucco R più utile? [chiuso]


88

Per condividere altri suggerimenti e trucchi per R , qual è la tua caratteristica o trucco più utile? Vettorializzazione intelligente? Ingresso / uscita dati? Visualizzazione e grafica? Analisi statistica? Funzioni speciali? L'ambiente interattivo stesso?

Un articolo per post e vedremo se otteniamo un vincitore per mezzo dei voti.

[Modifica 25 agosto 2008]: Quindi, dopo una settimana, sembra che il semplice abbia str()vinto il sondaggio. Dato che mi piace consigliarlo io stesso, è una risposta facile da accettare.


8
@Dirk: "wiki della comunità" significa "di proprietà della comunità", non è un sinonimo di "domanda del sondaggio". Non ascoltare la polizia wiki della comunità.
Juliet


8
Di nuovo bullismo in CW. Vedrò il tuo meta-SO e ti solleverò: meta.stackexchange.com/questions/392/…
ars

13
@ars: è una domanda che non ha una risposta definitiva . Ergo rendilo CW.
dmckee --- gattino ex moderatore

2
@JD lungo commento esilarante. purtroppo era nascosto dietro la piega. Voglio dire, rispondere a domande difficili su R non paga davvero lo stack-rep. Quindi va bene per me se i ragazzi che hanno posto belle domande che hanno messo R sulla mappa finalmente ottengono un po 'di credito. Inoltre questo è sicuramente più utile per gli utenti R di quanto sarebbe la tua domanda trucco preferita in C per i programmatori C ...
Matt Bannert

Risposte:


64

str() ti dice la struttura di qualsiasi oggetto.


Python usa dir()- ha più senso.
Hamish Grubijan

17
Ah, strè anche l'abbreviazione stringin molte lingue.
Hamish Grubijan

Perché no class()? Sembra rivelare un tipo simile di informazioni. Perché ci sono due comandi simili?
hhh

1
class()è solo una piccola parte delle informazioni str()visualizzate
Hadley

64

Una funzione molto utile che uso spesso è dput (), che ti consente di scaricare un oggetto sotto forma di codice 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")

Può essere molto utile pubblicare blocchi di dati facilmente riproducibili quando chiedi aiuto, oppure modificare o riordinare i livelli di un fattore.


42

head () e tail () per ottenere la prima e l'ultima parte di un dataframe, vettore, matrice, funzione, ecc. Specialmente con grandi frame di dati, questo è un modo rapido per verificare che sia stato caricato correttamente.


38

Una caratteristica interessante: la lettura dei dati utilizza le connessioni che possono essere file locali, file remoti accessibili tramite http, pipe da altri programmi o altro.

Come semplice esempio, considera questo accesso per N = 10 numeri interi casuali tra min = 100 e max = 200 da random.org (che fornisce numeri casuali veri basati sul rumore atmosferico piuttosto che un generatore di numeri pseudo casuali):

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>

Per inciso , il pacchetto random fornisce diverse funzioni utili per accedere a random.org .


BTW-- Io suggerirei che dovrebbe fare selfanswers CW se (1) li pubblichi tempestivamente e (2) non si fanno la domanda CW. Altrimenti sembra che tu stia cercando di giocare con il sistema di ripetizioni. YMMV e tutto il resto.
dmckee --- gattino ex moderatore

1
Non si tratta di giocare con il sistema, ma solo di iniziare. È ancora libero di accettare qualsiasi altra risposta.
ars

2
@ars: è libero di accettare questo. Né cercherò di costringerlo a wiki it se non ha vinto; t seguire il mio consiglio. Ma non posterò una risposta personale preparata senza contrassegnarla come wiki e non voterò nemmeno per una senza di essa. Prendilo per quello che vale.
dmckee --- gattino ex moderatore

4
@Dirk: è del tutto accettabile, anche incoraggiato da Jeff e Joel, rispondere alla tua stessa domanda. Non c'è NESSUN requisito, nemmeno informale, per rendere la tua risposta CW. Chiaramente non stai giocando con il sistema. Ancora una volta, ignora la polizia wiki della comunità.
Juliet

8
Devo ammettere che parte dello scopo del sito è fornire le migliori risposte a problemi comuni e una risorsa generale. Porre una domanda e fornire una buona risposta può aiutare a rafforzare un argomento. Ciò è particolarmente utile con tag nuovi / piccoli come R.
kpierce8

35

Trovo che sto usando with()e within()sempre di più. Non è più necessario $sporcare il mio codice e non è necessario iniziare ad allegare oggetti al percorso di ricerca. Più seriamente, trovo che with()ecc . Rendano molto più chiara l' intenzione dei miei script di analisi dei dati.

> 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()imposta un ambiente all'interno del quale viene valutata l'espressione R. within()fa la stessa cosa ma consente di modificare l'oggetto dati utilizzato per creare l'ambiente.

> 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

Qualcosa di cui non mi ero reso conto quando l'ho usato per la prima volta within()è che devi fare un compito come parte dell'espressione valutata e assegnare l'oggetto restituito (come sopra) per ottenere l'effetto desiderato.


34

Trucco di input dati = pacchetto RGoogleDocs

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

Ho scoperto che i fogli di lavoro di Google sono un modo fantastico per tutti i collaboratori di essere sulla stessa pagina. Inoltre, Google Forms consente di acquisire i dati dagli intervistati e di scriverli senza sforzo su un foglio di calcolo di Google. Poiché i dati cambiano frequentemente e non sono quasi mai definitivi, è di gran lunga preferibile che R legga direttamente un foglio di calcolo Google piuttosto che futz scaricando file csv e leggendoli.

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

Non riesco a ricordare quale, ma uno o due dei seguenti comandi richiede diversi secondi.

  1. getGoogleAuth

  2. getGoogleDocsConnection

  3. getWorksheets


27

Usa i backtick per fare riferimento a nomi non standard.

> 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

In questo caso, funzionerebbe anche df [, "1"]. Ma le zecche posteriori funzionano all'interno delle formule!

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

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

Coefficients:
(Intercept)          `1`  
     0.4087      -0.3440  

[Modifica] Dirk chiede perché si dovrebbero dare nomi non validi? Non lo so! Ma certamente incontro questo problema nella pratica abbastanza spesso. Ad esempio, utilizzando il pacchetto di risagoma di hadley:

> 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, ma perché dovresti sostituire i nomi sintatticamente validi (come x o y) con quelli non validi (come 1 o 2) che richiedono i backtick?
Dirk Eddelbuettel

3
È anche utile read.tablequando check.namesè falso, cioè quando vuoi lavorare con i nomi delle colonne originali.
Hadley

25

Non so quanto sia noto o meno, ma qualcosa di cui ho sicuramente approfittato sono le capacità di pass-by-reference degli ambienti.

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

Per questo esempio non ha senso il motivo per cui sarebbe utile, ma se gli passi attorno oggetti di grandi dimensioni può essere d'aiuto.


23

La mia nuova cosa preferita è la libreria foreach. Ti consente di fare tutte le belle cose di applicazione, ma con una sintassi un po 'più semplice:

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

La parte migliore è che se stai facendo qualcosa che in realtà richiede una notevole quantità di tempo, puoi passare da %do%a %dopar%(con la libreria di backend appropriata) per parallelizzare istantaneamente, anche su un cluster. Molto lucido.


19

Faccio molta manipolazione di base dei dati, quindi ecco due funzioni integrate ( trasformazione , sottoinsieme ) e una libreria ( sqldf ) che uso quotidianamente.

creare dati di vendita di esempio

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

usa transform () per aggiungere una colonna

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

utilizzare subset () per suddividere i dati

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

>
  product  revenue
1       1 108.4597
4       2 100.3475

usa sqldf () per suddividere e aggregare con SQL

Il pacchetto sqldf fornisce un'interfaccia SQL ai frame di dati 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

Eseguire un'aggregazione o 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

Per funzionalità più sofisticate simili alla riduzione delle mappe sui frame di dati, controlla il pacchetto plyr . E se trovate a voler tirare i capelli, vi consiglio di check-out Data Manipulation con R .


18
?ave

Viene calcolata la media dei sottoinsiemi di "x []", dove ogni sottoinsieme consiste di quelle osservazioni con gli stessi livelli di fattore. Uso: ave (x, ..., FUN = mean)

Io lo uso per tutto il tempo. (ad esempio in questa risposta qui a così )


in che modo questo differisce da tapply (x, factor, fun) ??
TMS

1
@Tomas ave preserva l'ordinazione e la lunghezza. così puoi, ad esempio, aggiungere un vettore di mezzi di gruppo a un set di dati, in un unico passaggio.
Eduardo Leoni

18

Un modo per velocizzare il codice ed eliminare i cicli for.

invece di cicli for che eseguono un ciclo attraverso un dataframe alla ricerca di valori. basta prendere un sottoinsieme di df con quei valori, molto più velocemente.

quindi invece di:

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

fai qualcosa di simile:

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

questo concetto di base è applicabile molto spesso ed è un ottimo modo per sbarazzarsi dei cicli for


11
C'è una piccola trappola qui che mi prendeva tutto il tempo. Se df $ colonna1 contiene valori NA, il sottoinsieme utilizzando == estrarrà tutti i valori uguali a x e qualsiasi NA. Per evitare ciò, utilizza "% in%" invece di "==".
Matt Parker

Matt hai perfettamente ragione ed è qualcosa che odio, mi piace però il tuo metodo. Di solito controllo la colonna per NA e poi li rimuovo con una funzione rapida che ho creato che prende una colonna dataframe e restituisce il dataframe meno le righe con NA in quella colonna.
Dan

essenzialmente, abbasso un dataframe alle colonne di cui ho bisogno per avere valori, quindi uso na.omit per ottenere le righe corrette e quindi sottoinsieme il set di dati originale con solo quelle righe. Il semplice utilizzo di na.omit rimuoverà qualsiasi riga con qualsiasi NA, tuttavia potrei sbagliarmi.
Dan

16

A volte è necessario rbindpiù frame di dati. do.call()ti permetterò di farlo (qualcuno doveva spiegarmelo quando bind ho posto questa domanda, poiché non sembra essere un uso ovvio).

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)

Buona scelta: trovo che questo sia spesso più semplice dell'uso unsplit.
Richie Cotton

16

Nella programmazione R (non sessioni interattive), io uso if (bad.condition) stop("message")un sacco . Ogni funzione inizia con alcune di queste, e mentre lavoro sui calcoli, inserisco anche questi. Immagino di aver preso l'abitudine di usare assert()in C. I vantaggi sono duplici. Innanzitutto, è molto più veloce ottenere codice funzionante con questi controlli in atto. Secondo, e probabilmente più importante, è molto più facile lavorare con il codice esistente quando vedi questi controlli su ogni schermata del tuo editor. Non dovrai chiederti se x>0o fidarti di un commento che afferma che è ... lo saprai , a colpo d'occhio, che lo è.

PS. il mio primo post qui. Sii gentile!


12
Non è una cattiva abitudine, e R offre ancora un altro modo: stopfifnot(!bad.condition)che è più conciso.
Dirk Eddelbuettel

13

La traceback()funzione è un must quando si ha un errore da qualche parte e non lo si capisce subito. Stamperà una traccia dello stack, molto utile in quanto R non è molto dettagliato per impostazione predefinita.

Quindi l'impostazione options(error=recover)ti permetterà di "entrare" nella funzione che solleva l'errore e cercare di capire cosa succede esattamente, come se avessi il pieno controllo su di essa e potessi inserirci una browser().

Queste tre funzioni possono davvero aiutare a eseguire il debug del codice.


1
options(error=recover)è il mio metodo di debug preferito.
Joshua Ulrich

12

Sono davvero sorpreso che nessuno abbia pubblicato informazioni su applica, tapply, lapply e sapply. Una regola generale che uso quando faccio cose in R è che se ho un ciclo for che sta eseguendo l'elaborazione dei dati o le simulazioni, cerco di scomporlo e sostituirlo con un * apply. Alcune persone evitano le funzioni * apply perché pensano che solo le funzioni di un singolo parametro possano essere passate. Niente potrebbe essere più lontano dalla verità! Come il passaggio di funzioni con parametri come oggetti di prima classe in Javascript, lo fai in R con funzioni anonime. Per esempio:

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

(Per quelli che seguono #rstats, ho postato anche questo lì).

Ricorda, usa applica, sapply, lapply, tapply e do.call! Prendi il vantaggio della vettorializzazione di R. Non dovresti mai avvicinarti a un mucchio di codice R e vedere:

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

Non solo non è vettorializzato, ma la struttura dell'array in R non è cresciuta come in Python (raddoppiando le dimensioni quando lo spazio si esaurisce, IIRC). Quindi ogni passaggio di rbind deve prima crescere abbastanza da accettare i risultati di rbind (), quindi copiare tutto il contenuto precedente. Per divertimento, prova quanto sopra in R. Nota quanto tempo ci vuole (non avrai nemmeno bisogno di Rprof o di alcuna funzione di temporizzazione). Allora prova

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

Anche quanto segue è migliore della prima versione:

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

applicare, sapply, lapply e tapply sono utili. Se vuoi passare parametri a una funzione denominata come round, puoi semplicemente passarlo insieme a apply invece di scrivere una funzione anonima. Prova "sapply (rnorm (10, 0, 1), round, digits = 2)" che restituisce "[1] -0,29 0,29 1,31 -0,06 -1,90 -0,84 0,21 0,02 0,23 -1,10".
Daniel

11

Su consiglio di Dirk, sto postando singoli esempi. Spero che non siano troppo "carini" [intelligenti, ma non mi interessa] o banali per questo pubblico.

I modelli lineari sono il pane quotidiano di R. Quando il numero di variabili indipendenti è alto, si hanno due scelte. Il primo è usare lm.fit (), che riceve la matrice di progetto x e la risposta y come argomenti, in modo simile a Matlab. Lo svantaggio di questo approccio è che il valore restituito è un elenco di oggetti (coefficienti adattati, residui, ecc.), Non un oggetto della classe "lm", che può essere ben riassunto, utilizzato per la previsione, la selezione graduale, ecc. l'approccio è creare una formula:

> 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  

Che ne dici se ne scegli uno per post e lo illustri con un esempio? Possiamo quindi continuare per giorni e giorni e pubblicare nuovi esempi con nuovi comandi ... [BTW: come ricordo, hai bisogno di as.formula (incolla (...)) per l'uso della formula. ]
Dirk Eddelbuettel

Non è necessaria la creazione esplicita della formula per coprire tutte le colonne poiché la forma "y ~. - 1" la copre. Il "." significa "tutte le colonne tranne la variabile dipendente e" - 1 "esclude la costante come nel tuo esempio.
Dirk Eddelbuettel

È corretto per questo esempio specifico, ma per X con ncols >> nrows, spesso rimuovo alcune variabili indipendenti, specialmente nelle fasi finali dell'analisi. In questo caso, la creazione di una formula dai nomi dei frame di dati è ancora utile.
gappy

10

È possibile assegnare un valore restituito da un blocco if-else.

Invece di, ad es

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

tu puoi fare

x <- if(condition) 1 else 2

Il modo esatto in cui funziona è magia profonda.


6
Puoi anche farlo come x <- ifelse (condition, 1, 2), nel qual caso ogni componente è vettorizzato.
Shane

Shane, potresti, ma a meno che tu non soffra davvero a fondo su cosa fa ifelse (), probabilmente non dovresti! È facile fraintendere ...
Harlan

Cosa c'è di magico in questo? Questo è solo il modo in cui le if-then-elseespressioni funzionano in qualsiasi linguaggio funzionale (da non confondere con le if-then-else istruzioni ). Molto simile ?:all'operatore ternario dei linguaggi C-like.
Frank

10

Come un noob totale a R e un principiante alle statistiche che amo unclass() stampare tutti gli elementi di un data frame come un normale elenco.

È piuttosto utile dare un'occhiata a un set di dati completo tutto in una volta per individuare rapidamente eventuali problemi potenziali.


9

CrossTable()dal gmodelspacchetto fornisce un facile accesso ai campi incrociati in stile SAS e SPSS, insieme ai normali test (Chisq, McNemar, ecc.). Fondamentalmente, è xtabs()con un output di fantasia e alcuni test aggiuntivi, ma semplifica la condivisione dell'output con i pagani.


Bello!! Uso un po 'di gmodels, ma ho perso quello
Abhijit

Buona risposta, tutto ciò che può tenermi lontano da un'eccessiva spiegazione delle tabelle con i pagani è un buon uso del tempo.
Stedy

7

Definitivamente system(). Essere in grado di avere accesso a tutti gli strumenti unix (almeno sotto Linux / MacOSX) dall'interno dell'ambiente R è diventato rapidamente inestimabile nel mio flusso di lavoro quotidiano.


1
Ciò si ricollega al mio precedente commento sulle connessioni: puoi anche usare pipe () per passare dati da, o verso, comandi Unix. Vedi help(connections)per dettagli ed esempi.
Dirk Eddelbuettel

6

Ecco una fastidiosa soluzione alternativa per convertire un fattore in un valore numerico. (Simile anche per altri tipi di dati)

old.var <- as.numeric(levels(old.var))[as.numeric(old.var)]

2
Forse intendevi il vettore "in un carattere". In tal caso "as.character (old.var)" è più semplice.
Dirk Eddelbuettel

1
Ho sempre pensato che questo consiglio (che può essere letto in? Factor) fosse fuorviante. Devi essere sicuro che old.var sia un fattore, e questo varierà in base alle opzioni che imposti per la sessione R. Usare as.numeric (as.character (old.var)) è sia più sicuro che più pulito.
Eduardo Leoni

Non vale davvero un voto negativo, ma qualunque cosa. Questo funziona per me.
Ryan R. Rosario

Ryan - Potresti correggere il tuo codice? Se old.var <- factor (1: 2); il tuo codice darà [1] "1" "2" (non numerico.) forse intendevi come.numeric (livelli (old.var) [old.var])?
Eduardo Leoni

3
O leggermente più efficiente:as.numeric(levels(old.var))[old.var]
hadley

6

Sebbene questa domanda sia stata sollevata da un po 'di tempo, ho scoperto di recente un ottimo trucco sul blog SAS e R per l'utilizzo del comando cut. Il comando viene utilizzato per dividere i dati in categorie e userò il set di dati iris come esempio e lo dividerò in 10 categorie:

> 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

Un altro trucco. Alcuni pacchetti, come glmnet, accettano solo come input la matrice di progettazione e la variabile di risposta. Se uno vuole adattare un modello con tutte le interazioni tra le caratteristiche, non può usare la formula "y ~. ^ 2". L' expand.grid()utilizzo ci consente di sfruttare la potente indicizzazione di array e le operazioni vettoriali di 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
Se una funzione di modellazione non accetta una formula (cosa molto rara!) Non sarebbe meglio costruire la matrice di progettazione con model.matrix?
hadley

Ben fatto. Non sapevo dell'esistenza di questa funzione. La funzione sopra è equivalente a model.matrix (~. ^ 2 -1, X) Ma per quanto riguarda il passaggio di matrici, a parte glmnet, è frequente per me passare puntatori di array a funzioni C personalizzate. In effetti, non saprei come passare una formula a una funzione. Hai un esempio di giocattolo?
gappy

5

Uno dei miei trucchi preferiti, se non un po 'poco ortodossi, è l'uso di eval()e parse(). Questo esempio forse illustra come può essere utile

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

[1] "Albany"

Questo tipo di situazione si verifica il più delle volte e l'uso di eval()e parse()può aiutare ad affrontarlo. Naturalmente, accolgo con favore qualsiasi feedback su modi alternativi di codificare questo.


1
Questo può essere fatto anche con elementi vettoriali con nome.
Dirk Eddelbuettel

3
library (fortunes); fortune (106) Se la risposta è parse () di solito dovresti ripensare alla domanda. - Thomas Lumley R-help (febbraio 2005)
Eduardo Leoni

Ecco un esempio in cui eval () e parse () possono essere utili. Ciò implica un pacchetto Bioconductor, ad esempio hgu133a.db e dove stai cercando di ottenere varie informazioni su un id del probe. Ad esempio: library (hgu133a.db) parameter <- 'SYMBOL' mget ('202431_s_at', env = eval (parse (text = paste ('hgu133a', parameter, sep = '')))) parameter <- 'ENTREZID 'mget (' 202431_s_at ', env = eval (parse (text = paste (' hgu133a ', parameter, sep =' '))))
andrewj

Come dice Dirk, è meglio farlo con elementi vettoriali denominati, o `get (paste (state, parameter, sep = '.'))`
hadley

@ Hadley, non sapevo che avresti potuto usare get () in quel modo. Grazie.
andrewj

5

set.seed() imposta lo stato del generatore di numeri casuali.

Per esempio:

> set.seed(123)
> rnorm(1)
[1] -0.5604756
> rnorm(1)
[1] -0.2301775
> set.seed(123)
> rnorm(1)
[1] -0.5604756

super utile con esempi che utilizzano funzioni casuali ... aiuta a mettere tutti sulla stessa pagina
JD Long

5

Per chi scrive C essere chiamato da R: .Internal(inspect(...))è comodo. Per esempio:

> .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 Code / Library /'

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

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

Uso il codice sopra per generare tutti i file in una directory all'avvio con vari programmi di utilità che uso nella mia sessione interattiva con R. Sono sicuro che ci sono modi migliori ma lo trovo utile per il mio lavoro. La linea che lo fa è la seguente.

sorgente ("~ / R Code / Library / mysource.r")


6
Non farlo. Scrivi un pacchetto.
Dirk Eddelbuettel

Grazie. Ho guardato un thread o due su roxygen e sembra che probabilmente sono al livello in cui dovrei provare a scrivere un semplice pacchetto di uso personale.
mcheema

3

Per eseguire un'operazione su un numero di variabili in un data frame. Questo è stato rubato da 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
All'inizio sembra interessante, ma questo tipo di codice ti causerà problemi a lungo termine. È sempre meglio essere espliciti.
Hadley,

hum, ho usato questo trucco un po 'ultimamente. Potresti essere più specifico riguardo ai suoi problemi illimitati?
Ian Fellows

Forse Hadley sta suggerendo di usare invece il pacchetto plyr?
Christopher DuBois

3
No, questo non è un suggerimento velato per usare invece plyr. Il problema fondamentale con il tuo codice è che è semanticamente pigro - invece di costringere l'utente a precisare esplicitamente ciò che vuole, fai un po 'di "magia" per indovinare. Il problema con questo è che rende la funzione molto difficile da programmare - cioè è difficile scrivere una funzione che chiama get.varssenza saltare attraverso un sacco di cerchi.
Hadley,

3

L'ho già pubblicato una volta, ma lo uso così tanto che ho pensato di postarlo di nuovo. È solo una piccola funzione per restituire i nomi e i numeri di posizione di un data.frame. Non è niente di speciale, ma non riesco quasi mai a superare una sessione senza usarlo più volte.

##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
Questo è davvero un one-liner:data.frame(VAR = names(df), COL = seq_along(df))
hadley

molto elegante, forse lo passerò a ni <- function (df) {data.frame (VAR = names (df), COL = seq_along (df))}
kpierce8

1
Io uso: data.frame (colnames (the.df))
Tal Galili
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.