Qual é o truque de R mais útil? [fechadas]


88

Para compartilhar mais dicas e truques para R , qual é o seu recurso ou truque mais útil? Vetorização inteligente? Entrada / saída de dados? Visualização e gráficos? Análise estatística? Funções especiais? O próprio ambiente interativo?

Um item por postagem, e veremos se conseguimos um vencedor por meio de votos.

[Editar 25 de agosto de 2008]: Então, após uma semana, parece que o simples str()ganhou a votação. Como eu mesma gosto de recomendar essa, é uma resposta fácil de aceitar.


8
@Dirk: "wiki da comunidade" significa "propriedade da comunidade", não é sinônimo de "questão da enquete". Não dê ouvidos à polícia da comunidade wiki.
Julieta de


8
Bullying CW novamente. Vou ver seu meta-SO e falar com
ars

13
@ars: é uma pergunta que não tem uma resposta definitiva . Portanto, torne-o CW.
dmckee --- ex-gatinho moderador

2
@JD Longo comentário hilário. infelizmente, estava escondido atrás da dobra. Quer dizer, responder a perguntas R difíceis realmente não compensa a repetição de pilha. Então está tudo bem para mim se os caras que fazem perguntas legais que colocam R no mapa finalmente recebem algum crédito. Além disso, isso é certamente mais útil para usuários de R do que qual seria a sua pergunta capciosa favorita de C para programadores de C ...
Matt Bannert

Respostas:


64

str() informa a estrutura de qualquer objeto.


Python usa dir()- faz mais sentido.
Hamish Grubijan

17
Ah, strtambém é abreviação de stringem muitos idiomas.
Hamish Grubijan

Porque não class()? Parece revelar um tipo de informação semelhante. Por que existem dois comandos semelhantes?
hhh

1
class()é apenas uma pequena parte das informações que são str()exibidas
hadley

64

Uma função muito útil que uso com frequência é dput (), que permite fazer dump de um objeto na forma de código 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")

Pode ser muito útil postar blocos de dados facilmente reproduzíveis quando você pedir ajuda ou editar ou reordenar os níveis de um fator.


42

head () e tail () para obter a primeira e a última parte de um dataframe, vetor, matriz, função, etc. Especialmente com grandes frames de dados, esta é uma maneira rápida de verificar se ele foi carregado corretamente.


38

Um recurso interessante: a leitura de dados usa conexões que podem ser arquivos locais, arquivos remotos acessados ​​via http, canais de outros programas ou mais.

Como um exemplo simples, considere este acesso para N = 10 inteiros aleatórios entre min = 100 e max = 200 de random.org (que fornece números aleatórios verdadeiros com base no ruído atmosférico em vez de um gerador de números pseudo-aleatórios):

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>

Como um aparte, o pacote aleatório fornece várias funções de conveniência para acessar random.org .


BTW-- eu sugiro que você deve fazer auto-respostas CW se (1) você publicá-las prontamente e (2) você não fizer a pergunta CW. Caso contrário, parece que você está tentando burlar o sistema de repetições. YMMV e tudo isso.
dmckee --- ex-moderador gatinho de

1
Não se trata de enganar o sistema, apenas dar início às coisas. Ele ainda está livre para aceitar qualquer outra resposta.
ars de

2
@ars: Ele está livre para aceitar este. Nem vou tentar forçá-lo a fazer um wiki se ele ganhar; siga meu conselho. Mas não vou postar uma auto-resposta preparada sem marcá-la no wiki e também não vou votar em uma sem ela. Pegue isso pelo que vale a pena.
dmckee --- ex-moderador gatinho de

4
@Dirk: é totalmente aceitável, até mesmo incentivado por Jeff e Joel, responder sua própria pergunta. NÃO há exigência, nem mesmo informal, para fazer sua resposta CW. Você claramente não está enganando o sistema. Mais uma vez, simplesmente ignore a polícia da comunidade wiki.
Julieta de

8
Tenho que concordar que parte do propósito dos sites é fornecer as melhores respostas para problemas comuns e um recurso geral. Fazer perguntas e dar boas respostas pode ajudar a fortalecer um tópico. Isso é especialmente útil com tags novas / pequenas, como R.
kpierce8

35

Acho que estou usando with()e within()cada vez mais. Chega de $bagunçar meu código e não é necessário começar a anexar objetos ao caminho de pesquisa. Mais a sério, acho que with()etc. tornam a intenção dos meus scripts de análise de dados muito mais clara.

> 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()configura um ambiente no qual a expressão R é avaliada. within()faz a mesma coisa, mas permite que você modifique o objeto de dados usado para criar o 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

Algo que não percebi quando usei pela primeira vez within()é que você deve fazer uma atribuição como parte da expressão avaliada e atribuir o objeto retornado (como acima) para obter o efeito desejado.


34

Truque de entrada de dados = pacote RGoogleDocs

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

Eu descobri que as planilhas do Google são uma maneira fantástica de todos os colaboradores estarem na mesma página. Além disso, o Formulários Google permite capturar dados dos entrevistados e gravá-los sem esforço em uma planilha do Google. Como os dados mudam com frequência e quase nunca são finais, é muito preferível que R leia uma planilha do Google diretamente do que fazer download de arquivos csv e lê-los.

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

Não consigo lembrar qual, mas um ou dois dos seguintes comandos levam vários segundos.

  1. getGoogleAuth

  2. getGoogleDocsConnection

  3. getWorksheets


27

Use crases para fazer referência a nomes não padronizados.

> 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

Nesse caso, df [, "1"] também funcionaria. Mas as marcações para trás funcionam dentro das fórmulas!

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

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

Coefficients:
(Intercept)          `1`  
     0.4087      -0.3440  

[Editar] Dirk pergunta por que alguém daria nomes inválidos? Eu não sei! Mas certamente encontro esse problema na prática com bastante frequência. Por exemplo, usando o pacote reshape do 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, mas por que você precisaria substituir nomes sintaticamente válidos (como x ou y) por nomes inválidos (como 1 ou 2) exigindo crases?
Dirk Eddelbuettel

3
Também é útil read.tablequando check.namesé falso - ou seja, quando você deseja trabalhar com os nomes das colunas originais.
hadley

25

Não sei se isso é / não é bem conhecido, mas algo de que definitivamente tirei vantagem são os recursos de passagem por referência dos ambientes.

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

Para este exemplo, não faz sentido por que seria útil, mas se você estiver passando objetos grandes ao redor, pode ajudar.


23

Minha nova coisa favorita é a biblioteca foreach. Ele permite que você faça todas as coisas legais de aplicação, mas com uma sintaxe um pouco mais fácil:

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

A melhor parte é que, se você estiver fazendo algo que realmente requer uma quantidade significativa de tempo, poderá alternar de %do%para %dopar%(com a biblioteca de back-end apropriada) para paralelizar instantaneamente, mesmo em um cluster. Muito habilidoso.


19

Eu faço muitas manipulações básicas de dados, então aqui estão duas funções integradas ( transformar , subconjunto ) e uma biblioteca ( sqldf ) que eu uso diariamente.

criar dados de vendas de amostra

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

use transform () para adicionar uma coluna

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

use subset () para dividir os dados

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

>
  product  revenue
1       1 108.4597
4       2 100.3475

use sqldf () para dividir e agregar com SQL

O pacote sqldf fornece uma interface SQL para quadros de dados 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

Realize uma agregação ou 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

Para uma funcionalidade mais sofisticada do tipo mapa-redução em frames de dados, verifique o pacote plyr . E se encontrar-se querer puxar seu cabelo para fora, eu recomendo verificar Manipulação de dados com R .


18
?ave

Subconjuntos de 'x []' são calculados, em que cada subconjunto consiste nas observações com os mesmos níveis de fator. Uso: ave (x, ..., FUN = média)

Eu uso isso o tempo todo. (por exemplo , nesta resposta aqui também )


como isso difere de tapply (x, fator, diversão) ??
TMS

1
@Tomas ave preserva a ordenação e o comprimento. então você pode, por exemplo, adicionar um vetor de médias de grupo a um conjunto de dados, em uma etapa.
Eduardo Leoni

18

Uma forma de acelerar o código e eliminar os loops for.

em vez de loops for que percorrem um dataframe em busca de valores. apenas pegue um subconjunto do df com esses valores, muito mais rápido.

então, em vez de:

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

faça algo assim:

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

esse conceito básico é aplicável com muita frequência e é uma ótima maneira de se livrar de loops for


11
Há uma pequena armadilha aqui que costumava me apanhar o tempo todo. Se df $ column1 contiver valores NA, o subconjunto usando == extrairá quaisquer valores iguais a x e quaisquer NAs. Para evitar isso, use "% em%" em vez de "==".
Matt Parker

Matt, você está absolutamente certo e é algo que odeio, mas gosto do seu método. Eu geralmente verifico a coluna para NAs e, em seguida, removo-os com uma função rápida que fiz que pega uma coluna do dataframe e retorna o dataframe menos as linhas com NAs apenas nessa coluna.
Dan

essencialmente, reduzo um dataframe às colunas que preciso ter valores e, em seguida, uso na.omit para obter as linhas corretas e, em seguida, subconjunto do conjunto de dados original com apenas essas linhas. Apenas usar na.omit removeria qualquer linha de qualquer NA, mas posso estar enganado.
Dan

16

Às vezes, você precisa de rbindvários quadros de dados. do.call()vai deixar você fazer isso (alguém teve que me explicar isso quando fiz essa pergunta ao bind, pois não parece ser um uso óbvio).

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)

Boa ideia: acho que isso geralmente é mais simples do que usar unsplit.
Richie Cotton

16

Na programação R (não sessões interativas), eu uso if (bad.condition) stop("message")um monte . Cada função começa com alguns deles e, à medida que trabalho nos cálculos, também os aponto. Acho que adquiri o hábito de usar assert()em C. Os benefícios são duplos. Em primeiro lugar, é muito mais rápido colocar o código em funcionamento com essas verificações. Em segundo lugar, e provavelmente o mais importante, é muito mais fácil trabalhar com o código existente quando você vê essas verificações em todas as telas do seu editor. Você não terá que se perguntar se x>0, ou confiar em um comentário afirmando que é ... você saberá , à primeira vista, que é.

PS. meu primeiro post aqui. Seja gentil!


12
Não é um mau hábito, e R oferece ainda outra maneira: stopfifnot(!bad.condition)que é mais concisa.
Dirk Eddelbuettel

13

A traceback()função é obrigatória quando você tem um erro em algum lugar e não o entende prontamente. Ele imprimirá um traço da pilha, muito útil, pois R não é muito prolixo por padrão.

Então, a configuração options(error=recover)permitirá que você "entre" na função que gera o erro e tente entender o que acontece exatamente, como se você tivesse total controle sobre ela e pudesse inserir um browser()nela.

Essas três funções podem realmente ajudar a depurar seu código.


1
options(error=recover)é meu método de depuração favorito.
Joshua Ulrich

12

Estou realmente surpreso que ninguém postou sobre aplicar, bater, lapidar e sensacionalmente. Uma regra geral que uso ao fazer coisas em R é que se eu tiver um loop for que está fazendo processamento de dados ou simulações, tento fatorá-lo e substituí-lo por um * apply. Algumas pessoas evitam as funções * apply porque acham que apenas funções de parâmetro único podem ser transmitidas. Nada poderia estar mais longe da verdade! Assim como passar funções com parâmetros como objetos de primeira classe em Javascript, você faz isso em R com funções anônimas. Por exemplo:

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

(Para aqueles que seguem #rstats, também postei isso lá).

Lembre-se de usar apply, sapply, lapply, tapply e do.call! Aproveite a vetorização de R. Você nunca deve ir até um monte de código R e ver:

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

Não apenas não é vetorizado, mas a estrutura do array em R não cresce como no Python (dobrando o tamanho quando o espaço acaba, IIRC). Portanto, cada etapa de rbind deve primeiro crescer l o suficiente para aceitar os resultados de rbind () e, em seguida, copiar todo o conteúdo de l anterior. Para se divertir, experimente o acima em R. Observe quanto tempo leva (você nem precisará do Rprof ou de qualquer função de tempo). Então tente

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

O seguinte também é melhor do que a primeira versão:

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

apply, sapply, lapply e tapply são úteis. Se você quiser passar parâmetros para uma função nomeada como round, você pode simplesmente passá-lo junto com apply em vez de escrever uma função anônima. Tente "sapply (rnorm (10, 0, 1), round, digits = 2)" que resulta em "[1] -0,29 0,29 1,31 -0,06 -1,90 -0,84 0,21 0,02 0,23 -1,10".
Daniel

11

Seguindo o conselho de Dirk, estou postando exemplos isolados. Espero que não sejam muito "fofos" [inteligentes, mas não me importo] ou triviais para esse público.

Modelos lineares são o pão com manteiga de R. Quando o número de variáveis ​​independentes é alto, temos duas escolhas. A primeira é usar lm.fit (), que recebe a matriz de design xea resposta y como argumentos, de maneira semelhante ao Matlab. A desvantagem dessa abordagem é que o valor de retorno é uma lista de objetos (coeficientes ajustados, resíduos, etc.), não um objeto da classe "lm", que pode ser bem resumido, usado para predição, seleção passo a passo, etc. O segundo abordagem é criar uma fórmula:

> 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  

Que tal se você escolher um por postagem e ilustrar com um exemplo? Podemos continuar por dias a fio e postar novos exemplos com novos comandos ... [BTW: Pelo que me lembro, você precisa de as.formula (paste (...)) para usar a fórmula. ]
Dirk Eddelbuettel

Você não precisa da criação de fórmula explicitamente para cobrir todas as colunas, pois o formulário "y ~. - 1" cobre isso. O "." significa 'todas as colunas, exceto a variável dependente, e o' - 1 'exclui a constante como em seu exemplo.
Dirk Eddelbuettel

Isso é correto para este exemplo específico, mas para X com ncols >> nrows, geralmente removo algumas variáveis ​​independentes, especialmente nos estágios finais da análise. Nesse caso, criar uma fórmula a partir dos nomes dos quadros de dados ainda é útil.
gappy

10

Você pode atribuir um valor que retorna de um bloco if-else.

Em vez de, por exemplo

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

você pode fazer

x <- if(condition) 1 else 2

Exatamente como isso funciona é magia profunda.


6
Você também pode fazer isso como x <- ifelse (condição, 1, 2), caso em que cada componente é vetorizado.
Shane

Shane, você poderia, mas a menos que você realmente grogue profundamente o que ifelse () faz, você provavelmente não deveria! É fácil de entender mal ...
Harlan

O que há de mágico nisso? É assim que as if-then-elseexpressões funcionam em qualquer linguagem funcional (não deve ser confundido com if-then-else instruções ). Muito semelhante ao ?:operador ternário de linguagens semelhantes a C.
Frank de

10

Como um novato total em R e um novato em estatísticas, adoro unclass() imprimir todos os elementos de um quadro de dados como uma lista comum.

É muito útil dar uma olhada em um conjunto de dados completo de uma só vez para avaliar rapidamente quaisquer problemas potenciais.


9

CrossTable()do gmodelspacote fornece acesso fácil a crosstabs estilo SAS e SPSS, junto com os testes usuais (Chisq, McNemar, etc.). Basicamente, é uma xtabs()saída sofisticada e alguns testes adicionais - mas torna o compartilhamento da saída com os pagãos mais fácil.


Agradável!! Eu uso gmodels bastante, mas perdi aquele
Abhijit,

Boa resposta, qualquer coisa que possa me afastar de explicações excessivas de tabelas com os pagãos é um bom uso do tempo.
Stedy

7

Definitivamente system(). Ter acesso a todas as ferramentas unix (pelo menos no Linux / MacOSX) de dentro do ambiente R tornou-se rapidamente inestimável em meu fluxo de trabalho diário.


1
Isso está vinculado ao meu comentário anterior sobre conexões: você também pode usar pipe () para passar dados de ou para comandos Unix. Veja help(connections)para detalhes e exemplos.
Dirk Eddelbuettel

6

Aqui está uma solução alternativa irritante para converter um fator em um numérico. (Semelhante para outros tipos de dados também)

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

2
Talvez você quisesse dizer "em um vetor característico". Nesse caso, "as.character (old.var)" é mais simples.
Dirk Eddelbuettel

1
Sempre achei esse conselho (que pode ser lido pelo fator?) Equivocado. Você deve ter certeza de que old.var é um fator, e isso irá variar de acordo com as opções definidas para a sessão R. Usar as.numeric (as.character (old.var)) é mais seguro e mais limpo.
Eduardo Leoni de

Realmente não vale um downvote, mas tanto faz. Isso funciona para mim.
Ryan R. Rosario

Ryan - Você poderia consertar seu código? Se old.var <- fator (1: 2); seu código fornecerá [1] "1" "2" (não numérico.) talvez você quisesse dizer as.numeric (levels (old.var) [old.var])?
Eduardo Leoni de

3
Ou um pouco mais eficiente:as.numeric(levels(old.var))[old.var]
hadley de

6

Embora esta questão já esteja levantada há um tempo, descobri recentemente um grande truque no blog SAS e R para usar o comando cut. O comando é usado para dividir os dados em categorias e vou usar o conjunto de dados iris como um exemplo e dividi-lo em 10 categorias:

> 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

Outro truque. Alguns pacotes, como o glmnet, usam apenas como entradas a matriz de design e a variável de resposta. Se alguém deseja ajustar um modelo com todas as interações entre recursos, não pode usar a fórmula "y ~. ^ 2". O uso expand.grid()nos permite tirar proveito da poderosa indexação de matriz e operações de vetor de 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 uma função de modelagem não aceita uma fórmula (o que é muito raro!), Não seria melhor construir a matriz de design com model.matrix?
hadley

Agradável. Não sabia da existência desta função. A função acima é equivalente a model.matrix (~. ^ 2 -1, X) Mas em relação à passagem de matrizes, além de glmnet, é frequente para mim passar ponteiros de array para funções C personalizadas. Na verdade, eu não saberia como passar uma fórmula para uma função. Você tem um exemplo de brinquedo?
gappy

5

Um dos meus truques favoritos, se não um tanto heterodoxos, é o uso de eval()e parse(). Este exemplo talvez ilustra como pode ser útil

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

[1] "Albany"

Esse tipo de situação ocorre com mais frequência do que não e o uso de eval()e parse()pode ajudar a resolvê-lo. É claro que agradeço qualquer feedback sobre formas alternativas de codificar isso.


1
Isso também pode ser feito com elementos vetoriais nomeados.
Dirk Eddelbuettel

3
biblioteca (fortunas); fortuna (106) Se a resposta for parse (), você geralmente deve repensar a questão. - Thomas Lumley R-help (fevereiro de 2005)
Eduardo Leoni

Aqui está um exemplo em que eval () e parse () podem ser úteis. Isso envolve um pacote Bioconductor, por exemplo, hgu133a.db e onde você está tentando obter várias informações sobre um id de conjunto de probes. Por exemplo: 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

Como Dirk diz, isso é melhor feito com elementos vetoriais nomeados, ou `get (paste (paste (state, parameter, sep = '.'))`
hadley

@Hadley, não sabia que você poderia usar get () dessa forma. Obrigado.
andrewj

5

set.seed() define o estado do gerador de número aleatório.

Por exemplo:

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

super útil com exemplos que usam funções aleatórias ... ajuda a colocar todos na mesma página
JD Long

5

Para quem está escrevendo C, ser chamado de R: .Internal(inspect(...))é útil. Por exemplo:

> .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 = '~ / Código R / Biblioteca /'

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

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

Eu uso o código acima para fornecer todos os arquivos em um diretório na inicialização com vários programas utilitários que uso em minha sessão interativa com R. Tenho certeza de que existem maneiras melhores, mas acho que é útil para o meu trabalho. A linha que faz isso é a seguinte.

fonte ("~ / Código R / Biblioteca / mysource.r")


6
Não faça isso. Escreva um pacote.
Dirk Eddelbuettel

Obrigado. Estive analisando um ou dois tópicos sobre roxygen e parece que provavelmente estou no nível em que devo tentar escrever um pacote simples para uso próprio.
mcheema

3

Para realizar uma operação em várias variáveis ​​em um quadro de dados. Isso é roubado de 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
Isso parece legal no início, mas esse tipo de código causará muitos problemas a longo prazo. É sempre melhor ser explícito.
hadley

hum, tenho usado esse truque bastante ultimamente. Você poderia ser mais específico sobre seu problema ilimitado?
Ian Fellows de

Talvez hadley esteja sugerindo usar o pacote plyr em vez disso?
Christopher DuBois

3
Não, esta não é uma sugestão velada para usar o plyr. O problema básico com o seu código é que ele é semanticamente preguiçoso - em vez de fazer o usuário soletrar explicitamente o que deseja, você faz uma "mágica" para adivinhar. O problema com isso é que torna a função muito difícil de programar - ou seja, é difícil escrever uma função que chame get.varssem pular através de um monte de arcos.
hadley

3

Já postei isso uma vez, mas uso tanto que pensei em postar novamente. É apenas uma pequena função para retornar os nomes e números de posição de um data.frame. Não é nada especial com certeza, mas quase nunca consigo terminar uma sessão sem usá-lo várias vezes.

##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
Este é realmente um one-liner:data.frame(VAR = names(df), COL = seq_along(df))
hadley

muito elegante, talvez eu mude para ni <- function (df) {data.frame (VAR = names (df), COL = seq_along (df))}
kpierce8

1
Eu uso: data.frame (colnames (the.df))
Tal Galili
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.