¿Cuál es el truco R más útil? [cerrado]

88

Para compartir algunos consejos y trucos más para R , ¿cuál es su característica o truco más útil? ¿Vectorización inteligente? ¿Entrada / salida de datos? ¿Visualización y gráficos? ¿Análisis estadístico? ¿Funciones especiales? ¿El propio entorno interactivo?

Un artículo por publicación, y veremos si conseguimos un ganador mediante votos.

[Edición del 25 de agosto de 2008]: Después de una semana, parece que el sencillo str()ganó la encuesta. Como me gusta recomendarlo yo mismo, es una respuesta fácil de aceptar.

Dirk Eddelbuettel
fuente
8
@Dirk: "wiki de comunidad" significa "propiedad de la comunidad", no es sinónimo de "pregunta de encuesta". No escuches a la policía de la comunidad wiki.
Juliet
4
Teniendo en cuenta meta.stackexchange.com/questions/11740/… debería ser CW.
dmckee --- ex-moderador gatito
8
CW bullying de nuevo. Veré
ars
13
@ars: es una pregunta que no tiene una respuesta definitiva . Ergo hazlo CW.
dmckee --- ex-moderador gatito
2
@JD Long comentario hilarante. lamentablemente estaba escondido detrás del redil. Me refiero a que responder preguntas difíciles de R no paga realmente en cuanto a repeticiones de pila. Así que está bien para mí si los chicos que hacen buenas preguntas que ponen a R en el mapa finalmente obtienen algo de crédito. Además, esto es ciertamente más útil para los usuarios de R que
cuál

Respuestas:

64

str() te dice la estructura de cualquier objeto.

Hadley
fuente
Python usa dir()- tiene más sentido.
Hamish Grubijan
17
Ah, strtambién es la abreviatura de stringen muchos idiomas.
Hamish Grubijan
¿Por qué no class()? Parece revelar un tipo de información similar. ¿Por qué hay dos comandos similares?
hhh
1
class()es solo una pequeña parte de la información que se str()muestra
hadley
64

Una función muy útil que uso a menudo es dput (), que te permite volcar un objeto en 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")

Puede resultar muy útil publicar fragmentos de datos fácilmente reproducibles cuando solicita ayuda, o para editar o reordenar los niveles de un factor.

juba
fuente
42

head () y tail () para obtener la primera y la última parte de un marco de datos, vector, matriz, función, etc. Especialmente con marcos de datos grandes, esta es una forma rápida de comprobar que se ha cargado correctamente.

Rob Hyndman
fuente
38

Una característica interesante: la lectura de datos utiliza conexiones que pueden ser archivos locales, archivos remotos a los que se accede a través de http, tuberías de otros programas o más.

Como ejemplo simple, considere este acceso para N = 10 enteros aleatorios entre min = 100 y max = 200 de random.org (que proporciona verdaderos números aleatorios basados ​​en el ruido atmosférico en lugar de un generador de números pseudoaleatorios):

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>

Además, el paquete aleatorio proporciona varias funciones de conveniencia para acceder a random.org .

Dirk Eddelbuettel
fuente
BTW-- me gustaría sugerir que usted debe hacer selfanswers CW si (1) que publique con prontitud y (2) usted no hace la pregunta CW. De lo contrario, parece que estás tratando de jugar con el sistema de repeticiones. YMMV y todo eso.
dmckee --- ex-moderador gatito
1
No se trata de jugar con el sistema, solo de comenzar. Todavía es libre de aceptar cualquier otra respuesta.
ars
2
@ars: Es libre de aceptar este. Tampoco voy a intentar obligarlo a escribirlo en la wiki si no lo hace; no seguiré mi consejo. Pero no publicaré una respuesta personal preparada sin marcarla como wiki, y tampoco votaré por una sin ella. Toma eso por lo que vale.
dmckee --- ex-moderador gatito
4
@Dirk: es totalmente aceptable, incluso alentado por Jeff y Joel, responder a su propia pregunta. NO hay ningún requisito, ni siquiera uno informal, para que su respuesta sea CW. Claramente no estás jugando con el sistema. Una vez más, simplemente ignore la policía de la comunidad wiki.
Juliet
8
Tengo que aceptar que parte del propósito de los sitios es proporcionar las mejores respuestas para problemas comunes y un recurso general. Hacer preguntas y dar una buena respuesta puede ayudar a reforzar un tema. Esto es especialmente útil con etiquetas nuevas / pequeñas como R.
kpierce8
35

Encuentro que estoy usando with()y within()más y más. No más $ensuciar mi código y uno no necesita comenzar a adjuntar objetos a la ruta de búsqueda. Más en serio, encuentro que with()etc. aclaran mucho la intención de mis scripts de análisis de datos.

> 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()establece un entorno en el que se evalúa la expresión R. within()hace lo mismo pero le permite modificar el objeto de datos utilizado para crear el entorno.

> 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 de lo que no me di cuenta cuando utilicé por primera vez within()es que tienes que hacer una asignación como parte de la expresión evaluada y asignar el objeto devuelto (como se muestra arriba) para obtener el efecto deseado.

Gavin Simpson
fuente
34

Truco de entrada de datos = paquete RGoogleDocs

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

He descubierto que las hojas de cálculo de Google son una forma fantástica para que todos los colaboradores estén en la misma página. Además, Google Forms permite capturar datos de los encuestados y escribirlos sin esfuerzo en una hoja de cálculo de Google. Dado que los datos cambian con frecuencia y casi nunca son definitivos, es mucho mejor que R lea una hoja de cálculo de Google directamente a que se dedique a descargar archivos csv y leerlos.

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

No recuerdo cuál sino uno o dos de los siguientes comandos toma varios segundos.

  1. getGoogleAuth

  2. getGoogleDocsConnection

  3. getWorksheets

Farrel
fuente
27

Utilice comillas invertidas para hacer referencia a nombres no estándar.

> 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

En este caso, df [, "1"] también funcionaría. ¡Pero las garrapatas traseras funcionan dentro de las fórmulas!

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

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

Coefficients:
(Intercept)          `1`  
     0.4087      -0.3440  

[Editar] Dirk pregunta por qué uno daría nombres inválidos. ¡No lo sé! Pero ciertamente encuentro este problema en la práctica con bastante frecuencia. Por ejemplo, usando el paquete de remodelación de 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
Eduardo Leoni
fuente
Ok, pero ¿por qué necesitarías reemplazar nombres sintácticamente válidos (como xoy) con nombres inválidos (como 1 o 2) que requieren las comillas invertidas?
Dirk Eddelbuettel
3
También es útil read.tablecuando check.nameses falso, es decir, cuando desea trabajar con los nombres de columna originales.
hadley
25

No sé qué tan conocido es o no es esto, pero algo de lo que definitivamente he aprovechado son las capacidades de paso por referencia de los entornos.

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

Para este ejemplo, no tiene sentido por qué sería útil, pero si está pasando objetos grandes a su alrededor, puede ayudar.

geoffjentry
fuente
23

Mi nueva cosa favorita es la biblioteca foreach. Te permite hacer todas las cosas agradables de aplicación, pero con una sintaxis algo más sencilla:

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

La mejor parte es que si está haciendo algo que realmente requiere una cantidad significativa de tiempo, puede cambiar de %do%a %dopar%(con la biblioteca de backend adecuada) para paralelizar instantáneamente, incluso en un clúster. Muy resbaladizo.

JAShapiro
fuente
19

Hago mucha manipulación básica de datos, por lo que aquí hay dos funciones integradas ( transformación , subconjunto ) y una biblioteca ( sqldf ) que uso a diario.

crear datos de ventas de muestra

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 agregar una columna

## 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 los datos

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 cortar y agregar con SQL

El paquete sqldf proporciona una interfaz SQL para marcos de datos 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

Realizar una agregación 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

Para una funcionalidad más sofisticada similar a la de reducción de mapas en marcos de datos, consulte el paquete plyr . Y si encuentras querer a tirar de los pelos, te recomiendo que compruebes manipulación de datos con R .

medriscoll
fuente
18
?ave

Se promedian subconjuntos de 'x []', donde cada subconjunto consta de aquellas observaciones con los mismos niveles de factor. Uso: ave (x, ..., FUN = mean)

Lo uso todo el tiempo. (por ejemplo, en esta respuesta aquí en así )

Eduardo Leoni
fuente
¿En qué se diferencia esto de tapply (x, factor, fun)?
TMS
1
@Tomas ave conserva orden y longitud. para que pueda, por ejemplo, agregar un vector de medias de grupo a un conjunto de datos, en un solo paso.
Eduardo Leoni
18

Una forma de acelerar el código y eliminar los bucles for.

en lugar de bucles for que recorren un marco de datos en busca de valores. simplemente tome un subconjunto del df con esos valores, mucho más rápido.

entonces en lugar de:

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

haz algo como esto:

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

ese concepto básico se aplica con mucha frecuencia y es una excelente manera de deshacerse de los bucles for

Dan
fuente
11
Aquí hay una pequeña trampa que solía atraparme todo el tiempo. Si df $ column1 contiene valores NA, el subconjunto con == extraerá cualquier valor que sea igual a x y cualquier NA. Para evitar esto, use "% in%" en lugar de "==".
Matt Parker
Matt, tienes toda la razón y es algo que odio, aunque me gusta tu método. Por lo general, reviso la columna en busca de NA y luego las elimino con una función rápida que hice que toma una columna de marco de datos y devuelve el marco de datos menos filas con NA solo en esa columna.
Dan
esencialmente, reduzco un marco de datos a las columnas que necesito para tener valores, luego uso na.omit para obtener las filas correctas y luego subconjunto el conjunto de datos original con solo esas filas. El solo uso de na.omit eliminaría cualquier fila con cualquier NA, aunque podría estar equivocado.
Dan
16

A veces necesita rbindvarios marcos de datos. do.call()te dejará hacer eso (alguien tuvo que explicarme esto cuando bind I hice esta pregunta, ya que no parece ser un uso obvio).

foo <- list()

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

do.call(rbind, foo)
andrewj
fuente
Buena decisión: encuentro que esto suele ser más simple que usarlo unsplit.
Richie Cotton
16

En la programación R (no sesiones interactivas), utilizo if (bad.condition) stop("message")un montón . Cada función comienza con algunos de estos, y mientras trabajo con los cálculos, también los agrego. Supongo que adquirí el hábito de usar assert()en C. Los beneficios son dobles. En primer lugar, es mucho más rápido conseguir que el código funcione con estas comprobaciones. En segundo lugar, y probablemente más importante, es mucho más fácil trabajar con el código existente cuando ves estas comprobaciones en cada pantalla de tu editor. No tendrá que preguntarse si x>0, o confiar en un comentario que indique que lo es ... sabrá , de un vistazo, que lo es.

PD. mi primera publicación aquí. ¡Sé gentil!

húmedo
fuente
12
No es un mal hábito, y R ofrece otra forma más: stopfifnot(!bad.condition)que es más concisa.
Dirk Eddelbuettel
13

los traceback() función es imprescindible cuando tiene un error en alguna parte y no lo comprende fácilmente. Imprimirá un rastro de la pila, muy útil ya que R no es muy detallado por defecto.

Luego estableciendo options(error=recover) le permitirá "ingresar" en la función que genera el error e intentar comprender qué sucede exactamente, como si tuviera el control total sobre ella y pudiera poner unbrowser() .

Estas tres funciones realmente pueden ayudar a depurar su código.

Calimo
fuente
1
options(error=recover)es mi método de depuración favorito.
Joshua Ulrich
12

Estoy realmente sorprendido de que nadie haya publicado sobre aplicar, tapply, lapply y sapply. Una regla general que uso cuando hago cosas en R es que si tengo un bucle for que está haciendo procesamiento de datos o simulaciones, trato de factorizarlo y reemplazarlo con un * aplicar. Algunas personas evitan las funciones * apply porque piensan que solo se pueden pasar funciones de un solo parámetro. ¡Nada podría estar más lejos de la verdad! Al igual que pasar funciones con parámetros como objetos de primera clase en Javascript, lo hace en R con funciones anónimas. Por ejemplo:

 > 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 aquellos que siguen #rstats, también publiqué esto allí).

Recuerde, use aplicar, aplicar, lapear, tapply y do.call! Aproveche la vectorización de R. Nunca debe acercarse a un montón de código R y ver:

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

Esto no solo no está vectorizado, sino que la estructura de la matriz en R no crece como en Python (duplicando el tamaño cuando se agota el espacio, IIRC). Por lo tanto, cada paso de rbind primero debe crecer lo suficiente para aceptar los resultados de rbind (), luego copiar todo el contenido de los anteriores. Para divertirse, pruebe lo anterior en R. Observe cuánto tiempo lleva (ni siquiera necesitará Rprof ni ninguna función de temporización). Entonces intenta

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

Lo siguiente también es mejor que la primera versión:

N = 10000
l = numeric(N)
for (i in seq(1:N)) {
    sim <- rnorm(1, 0, 1)
    l[i] <- sim
}
Vince
fuente
aplicar, aplicar, lapear y tapply son útiles. Si desea pasar parámetros a una función con nombre como round, puede pasarlos junto con apply en lugar de escribir una función anónima. Pruebe "sapply (rnorm (10, 0, 1), round, digits = 2)" que da como resultado "[1] -0,29 0,29 1,31 -0,06 -1,90 -0,84 0,21 0,02 0,23 -1,10".
Daniel
11

Siguiendo el consejo de Dirk, estoy publicando ejemplos únicos. Espero que no sean demasiado "lindos" [inteligentes, pero no me importa] o triviales para esta audiencia.

Los modelos lineales son el pan y la mantequilla de R. Cuando el número de variables independientes es alto, uno tiene dos opciones. La primera es usar lm.fit (), que recibe la matriz de diseño xy la respuesta y como argumentos, de manera similar a Matlab. El inconveniente de este enfoque es que el valor de retorno es una lista de objetos (coeficientes ajustados, residuos, etc.), no un objeto de la clase "lm", que puede resumirse muy bien, usarse para predicción, selección por pasos, etc. El segundo enfoque es crear una 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  
gappy
fuente
¿Qué tal si eliges uno por publicación y lo ilustras con un ejemplo? Luego podemos continuar durante días y publicar nuevos ejemplos con nuevos comandos ... [Por cierto: según recuerdo, necesita as.formula (pegar (...)) para el uso de fórmulas. ]
Dirk Eddelbuettel
No es necesario que la creación de fórmulas explícitamente cubra todas las columnas, ya que la forma "y ~. - 1" las cubre. Los "." significa 'todas las columnas excepto la variable dependiente, y el' - 1 'excluye la constante como en su ejemplo.
Dirk Eddelbuettel
Eso es correcto para este ejemplo específico, pero para X con ncols >> nrows, a menudo elimino algunas variables independientes, especialmente en las etapas finales del análisis. En este caso, la creación de una fórmula a partir de los nombres de los marcos de datos sigue siendo útil.
gappy
10

Puede asignar un valor que regrese de un bloque if-else.

En lugar de, por ejemplo

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

tu puedes hacer

x <- if(condition) 1 else 2

Exactamente cómo funciona esto es magia profunda.

Richie algodón
fuente
6
También puede hacer esto como x <- ifelse (condition, 1, 2), en cuyo caso cada componente está vectorizado.
Shane
Shane, podrías, pero a menos que realmente asimiles lo que hace ifelse (), ¡probablemente no deberías! Es fácil de entender mal ...
Harlan
¿Qué tiene de mágico eso? Así es como funcionan las if-then-elseexpresiones en cualquier lenguaje funcional (no debe confundirse con if-then-else declaraciones ). Muy similar al ?:operador ternario de los lenguajes tipo C.
Frank
10

Como novato total en R y novato en estadísticas, me encanta unclass() imprimir todos los elementos de un marco de datos como una lista normal.

Es bastante útil echar un vistazo a un conjunto de datos completo de una sola vez para observar rápidamente cualquier problema potencial.

Juan
fuente
9

CrossTable()del gmodelspaquete proporciona un fácil acceso a las tablas de referencias cruzadas de estilo SAS y SPSS, junto con las pruebas habituales (Chisq, McNemar, etc.). Básicamente, tiene una xtabs()salida elegante y algunas pruebas adicionales, pero facilita compartir la salida con los paganos.

Matt Parker
fuente
¡¡Agradable!! Uso gmodels bastante, pero me perdí ese
Abhijit
Buena respuesta, cualquier cosa que pueda mantenerme alejado de la explicación excesiva de las mesas con los paganos es un buen uso del tiempo.
Stedy
7

Definitivamente system(). Poder tener acceso a todas las herramientas de Unix (al menos bajo Linux / MacOSX) desde dentro del entorno R se ha convertido rápidamente en invaluable en mi flujo de trabajo diario.

Paolo
fuente
1
Eso se relaciona con mi comentario anterior sobre las conexiones: también puede usar pipe () para pasar datos desde o hacia comandos Unix. Consulte help(connections)para obtener detalles y ejemplos.
Dirk Eddelbuettel
6

Aquí hay una molesta solución para convertir un factor en numérico. (Similar para otros tipos de datos también)

old.var <- as.numeric(levels(old.var))[as.numeric(old.var)]
Ryan Rosario
fuente
2
Quizás quisiste decir el vector "en un personaje". En cuyo caso, "as.character (old.var)" es más simple.
Dirk Eddelbuettel
1
Siempre he pensado que este consejo (que se puede leer en? Factor) estaba equivocado. Debe estar seguro de que old.var es un factor, y esto variará según las opciones que establezca para la sesión R. Usar as.numeric (as.character (old.var)) es más seguro y más limpio.
Eduardo Leoni
Realmente no vale la pena un voto negativo, pero lo que sea. Esto funciona para mi.
Ryan R. Rosario
Ryan - ¿Podrías arreglar tu código? Si old.var <- factor (1: 2); su código dará [1] "1" "2" (no numérico.) ¿Quizás quiso decir como.numeric (niveles (old.var) [old.var])?
Eduardo Leoni
3
O un poco más eficientemente:as.numeric(levels(old.var))[old.var]
hadley
6

Aunque esta pregunta ha estado pendiente por un tiempo, recientemente descubrí un gran truco en el blog de SAS y R para usar el comando cut. El comando se usa para dividir datos en categorías y usaré el conjunto de datos de iris como ejemplo y lo dividiré en 10 categorías:

> 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]
Tacaño
fuente
5

Otro truco. Algunos paquetes, como glmnet, solo toman como entradas la matriz de diseño y la variable de respuesta. Si uno quiere ajustar un modelo con todas las interacciones entre características, no puede usar la fórmula "y ~. ^ 2". El uso expand.grid()nos permite aprovechar la potente indexación de matrices y las operaciones vectoriales 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
boquiabierto
fuente
3
Si una función de modelado no acepta una fórmula (¡lo cual es muy raro!), ¿No sería mejor construir la matriz de diseño con ella model.matrix?
hadley
Buena esa. No sabía de la existencia de esta función. La función anterior es equivalente a model.matrix (~. ^ 2 -1, X) Pero con respecto al paso de matrices, aparte de glmnet, es frecuente que pase punteros de matriz a funciones C personalizadas. De hecho, no sabría cómo pasar una fórmula a una función. ¿Tienes un juguete de ejemplo?
gappy
5

Uno de mis trucos favoritos, si no poco ortodoxo, es el uso de eval()y parse(). Este ejemplo quizás ilustra cómo puede ser útil

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

[1] "Albany"

Este tipo de situación ocurre con mayor frecuencia y el uso eval()y parse()puede ayudar a abordarla. Por supuesto, agradezco cualquier comentario sobre formas alternativas de codificar esto.

andrewj
fuente
1
Esto también se puede hacer con elementos vectoriales con nombre.
Dirk Eddelbuettel
3
biblioteca (fortunas); fortuna (106) Si la respuesta es parse (), normalmente debería repensar la pregunta. - Thomas Lumley R-help (febrero de 2005)
Eduardo Leoni
Aquí hay un ejemplo donde eval () y parse () pueden ser útiles. Se trata de un paquete de bioconductores, por ejemplo, hgu133a.db, y en el que intenta obtener varios datos sobre la identificación de un conjunto de sondas. Por ejemplo: biblioteca (hgu133a.db) parámetro <- 'SYMBOL' mget ('202431_s_at', env = eval (parse (text = paste ('hgu133a', parameter, sep = '')))) parámetro <- 'ENTREZID 'mget (' 202431_s_at ', env = eval (parse (text = paste (' hgu133a ', parameter, sep =' '))))
andrewj
Como dice Dirk, esto se hace mejor con elementos vectoriales con nombre, o `get (paste (state, parameter, sep = '.'))`
hadley
@Hadley, no sabía que podrías usar get () de esa manera. Gracias.
andrewj
5

set.seed() establece el estado del generador de números aleatorios.

Por ejemplo:

> set.seed(123)
> rnorm(1)
[1] -0.5604756
> rnorm(1)
[1] -0.2301775
> set.seed(123)
> rnorm(1)
[1] -0.5604756
Christopher DuBois
fuente
súper útil con ejemplos que usan funciones aleatorias ... ayuda a que todos estén en la misma página
JD Long
5

Para aquellos que escriben C, ser llamados desde R: .Internal(inspect(...))es útil. Por ejemplo:

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

d = '~ / Código R / Biblioteca /'

archivos = lista de archivos (d, '. r $')

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

Utilizo el código anterior para obtener todos los archivos en un directorio al inicio con varios programas de utilidad que uso en mi sesión interactiva con R. Estoy seguro de que hay mejores formas, pero lo encuentro útil para mi trabajo. La línea que hace esto es la siguiente.

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

mcheema
fuente
6
No lo hagas. Escribe un paquete.
Dirk Eddelbuettel
Gracias. He estado mirando uno o dos hilos en roxygen y parece que probablemente estoy en el nivel en el que debería intentar escribir un paquete simple de uso propio.
mcheema
3

Para realizar una operación en una serie de variables en un marco de datos. Esto se roba 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)
Becarios Ian
fuente
1
Esto parece genial al principio, pero este tipo de código le causará un sinfín de problemas a largo plazo. Siempre es mejor ser explícito.
hadley
hum, he estado usando este truco bastante últimamente. ¿Podría ser más específico sobre su problema ilimitado?
Ian Fellows
¿Quizás hadley sugiere usar el paquete plyr en su lugar?
Christopher DuBois
3
No, esta no es una sugerencia velada para usar plyr en su lugar. Básicamente, el problema con su código es que es semánticamente perezoso: en lugar de hacer que el usuario deletree explícitamente lo que quiere, hace algo de "magia" para adivinar. El problema con esto es que hace que la función sea muy difícil de programar, es decir, es difícil escribir una función que llame get.varssin pasar por un montón de obstáculos.
hadley
3

Ya publiqué esto una vez, pero lo uso tanto que pensé en volver a publicarlo. Es solo una pequeña función para devolver los nombres y números de posición de un data.frame. No es nada especial, sin duda, pero casi nunca logro completar una sesión sin usarlo varias veces.

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

namesind = function (df) {

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

}

ni <- namesind

kpierce8
fuente
4
Esto es realmente una data.frame(VAR = names(df), COL = seq_along(df))
frase
muy elegante, tal vez lo cambie a ni <- function (df) {data.frame (VAR = names (df), COL = seq_along (df))}
kpierce8
1
Yo uso: data.frame (colnames (the.df))
Tal Galili