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: "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.
@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
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.
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.
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
11651432107118310313241911005138185
R>
Además, el paquete aleatorio proporciona varias funciones de conveniencia para acceder a random.org .
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.404446861.993688160.13871605-1.17734837[6]0.424738122.330142261.616907991.419018600.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
10.62635571-0.5830079120.04810539-0.4525522130.397069791.5966184340.95802501-0.8193090250.76772541-1.9450738260.213350060.21138814
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.
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.
[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)114226> 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]46
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.
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.
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 1108.459652 UK 197.079813 FR 199.662254 USA 2100.347545 UK 287.122626 FR 2112.860847 USA 395.878808 UK 396.435819 FR 394.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 1108.45965155.53112 UK 197.07981139.21253 FR 199.66225142.9157...
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
11108.459722100.3475
Realizar una agregación o GROUP BY
sqldf('select country, sum(revenue) revenue \
FROM sales \
GROUP BY country')>
country revenue
1 FR 307.11572 UK 280.63823 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 .
Se promedian subconjuntos de 'x []', donde cada subconjunto consta de aquellas observaciones con los mismos niveles de factor. Uso: ave (x, ..., FUN = mean)
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).
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.
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.
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]11011-1-2022-2-101-101-10-100000[26]20-1-2001-1151-1011120-11-110-11[51]211-2-10-12-11-11-10-1-2110-1-11120[76]000-2-111-21-1111000-1-30-100011> 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.230.69-1.430.760.550.96-0.47-0.81-0.47[13]0.270.320.47-1.28-1.44-1.930.51-0.82-0.06-1.411.23-0.26[25]0.22-0.04-2.170.60-0.10-0.920.132.621.03-1.33-1.73-0.08[37]0.45-0.930.400.051.09-1.23-0.350.620.01-1.081.70-1.27[49]0.550.60-1.461.08-1.88-0.150.210.060.53-1.16-2.13-0.03[61]0.33-1.070.980.62-0.01-0.53-1.17-0.28-0.950.71-0.58-0.03[73]-1.47-0.75-0.540.42-1.630.05-1.900.40-0.010.14-1.581.37[85]-1.00-0.901.69-0.11-2.19-0.741.34-0.75-0.51-0.99-0.36-1.63[97]-0.980.611.010.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
}
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
10.968523630.338271070.2613322570.628170211.642532620.080127550.691598280.0879941580.937804810.980130430.101675450.381193040.8652098320.165016620.483087340.066994580.417564150.2580716160.340277750.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.782360.95406-0.06738-0.43686-0.06644
¿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.5if(condition) x <-1else x <-2
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-elsedeclaraciones ). 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.
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.
¡¡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.
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)
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.14.94.74.655.44.654.44.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]
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.
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.
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.
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.
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.
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
Respuestas:
str()
te dice la estructura de cualquier objeto.fuente
dir()
- tiene más sentido.str
también es la abreviatura destring
en muchos idiomas.class()
? Parece revelar un tipo de información similar. ¿Por qué hay dos comandos similares?class()
es solo una pequeña parte de la información que sestr()
muestraUna función muy útil que uso a menudo es dput (), que te permite volcar un objeto en forma de código R.
Puede resultar muy útil publicar fragmentos de datos fácilmente reproducibles cuando solicita ayuda, o para editar o reordenar los niveles de un factor.
fuente
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.
fuente
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):
Además, el paquete aleatorio proporciona varias funciones de conveniencia para acceder a random.org .
fuente
Encuentro que estoy usando
with()
ywithin()
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 quewith()
etc. aclaran mucho la intención de mis scripts de análisis de datos.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.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.fuente
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.
No recuerdo cuál sino uno o dos de los siguientes comandos toma varios segundos.
getGoogleAuth
getGoogleDocsConnection
getWorksheets
fuente
Utilice comillas invertidas para hacer referencia a nombres no estándar.
En este caso, df [, "1"] también funcionaría. ¡Pero las garrapatas traseras funcionan dentro de las fórmulas!
[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:
fuente
read.table
cuandocheck.names
es falso, es decir, cuando desea trabajar con los nombres de columna originales.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.
Para este ejemplo, no tiene sentido por qué sería útil, pero si está pasando objetos grandes a su alrededor, puede ayudar.
fuente
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:
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.fuente
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
use transform () para agregar una columna
use subset () para dividir los datos
use sqldf () para cortar y agregar con SQL
El paquete sqldf proporciona una interfaz SQL para marcos de datos R
Realizar una agregación o GROUP BY
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 .
fuente
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í )
fuente
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:
haz algo como esto:
ese concepto básico se aplica con mucha frecuencia y es una excelente manera de deshacerse de los bucles for
fuente
A veces necesita
rbind
varios 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).fuente
unsplit
.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 usarassert()
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 six>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!
fuente
stopfifnot(!bad.condition)
que es más concisa.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.
fuente
options(error=recover)
es mi método de depuración favorito.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:
(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:
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
Lo siguiente también es mejor que la primera versión:
fuente
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:
fuente
Puede asignar un valor que regrese de un bloque if-else.
En lugar de, por ejemplo
tu puedes hacer
Exactamente cómo funciona esto es magia profunda.
fuente
if-then-else
expresiones en cualquier lenguaje funcional (no debe confundirse conif-then-else
declaraciones ). Muy similar al?:
operador ternario de los lenguajes tipo C.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.
fuente
CrossTable()
delgmodels
paquete 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 unaxtabs()
salida elegante y algunas pruebas adicionales, pero facilita compartir la salida con los paganos.fuente
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.fuente
help(connections)
para obtener detalles y ejemplos.Aquí hay una molesta solución para convertir un factor en numérico. (Similar para otros tipos de datos también)
fuente
as.numeric(levels(old.var))[old.var]
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:fuente
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.fuente
model.matrix
?Uno de mis trucos favoritos, si no poco ortodoxo, es el uso de
eval()
yparse()
. Este ejemplo quizás ilustra cómo puede ser útilEste tipo de situación ocurre con mayor frecuencia y el uso
eval()
yparse()
puede ayudar a abordarla. Por supuesto, agradezco cualquier comentario sobre formas alternativas de codificar esto.fuente
set.seed()
establece el estado del generador de números aleatorios.Por ejemplo:
fuente
Para aquellos que escriben C, ser llamados desde R:
.Internal(inspect(...))
es útil. Por ejemplo:fuente
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")
fuente
Para realizar una operación en una serie de variables en un marco de datos. Esto se roba de subset.data.frame.
fuente
get.vars
sin pasar por un montón de obstáculos.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.
namesind = function (df) {
}
ni <- namesind
fuente
data.frame(VAR = names(df), COL = seq_along(df))