什么是最有用的R技巧?

为了分享一些更多的技巧和窍门,什么是你最单一的有用的function或技巧? 聪明的vector化? 数据input/输出? 可视化和graphics? 统计分析? 特殊function? 互动环境本身?

每个职位一个项目,我们将看看我们是否通过投票获得了胜利者。

[编辑2008年8月25日]:所以一个星期后,似乎简单的str()赢得了民意调查。 我喜欢自己推荐一个,这是一个容易接受的答案。

str()告诉你任何对象的结构。

我经常使用的一个非常有用的函数是dput(),它允许您以R代码的forms转储对象。

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

当您寻求帮助或编辑或重新排列因子的级别时,发布容易重现的数据块可能非常有用。

head()和tail()来获取dataframe,向量,matrix,函数等的第一部分和最后一部分。特别是对于大dataframe,这是检查它是否已加载好的快速方法。

一个很好的function:读取数据使用的连接可以是本地文件,通过http访问的远程文件,来自其他程序的pipe道或更多。

作为一个简单的例子,考虑从random.org (它提供基于大气噪声的真正的随机数而不是伪随机数发生器)的min = 100和max = 200之间的N = 10个随机整数的访问:

 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> 

另外, 随机包提供了访问random.org的一些便利function。

我发现我越来越多地使用with()within() 。 没有更多的垃圾我的代码,并不需要开始附加对象的searchpath。 更严重的是,我发现with()等使我的数据分析脚本的意图更加清晰。

 > 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()build立了一个Rexpression式被评估的环境。 within()做同样的事情,但允许你修改用来创build环境的数据对象。

 > df <- within(df, C <- rpois(10, lambda = 2)) > head(df) ABC 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 

当我第一次within()使用的时候,我没有意识到的一点是,你必须做一个赋值作为expression式求值的一部分, 赋值返回的对象(如上)来获得所需的效果。

数据inputtrick = RGoogleDocs包

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

我发现Google电子表格是让所有合作伙伴处于同一页面的绝佳方式。 此外,Google表单允许从受访者那里获取数据,并轻松将其写入Google电子表格。 由于数据经常变化,并且几乎不会是最终的,所以R最好直接阅读谷歌电子表格,而不是下载csv文件并读取它们。

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

我不能记住只有一个或两个下面的命令需要几秒钟。

  1. getGoogleAuth

  2. getGoogleDocsConnection

  3. getWorksheets

使用反引号来引用非标准名称。

 > 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 

在这种情况下,df [,“1”]也可以工作。 但是回到公式里面的工作!

 > lm(`2`~`1`,data=df) Call: lm(formula = `2` ~ `1`, data = df) Coefficients: (Intercept) `1` 0.4087 -0.3440 

[编辑]德克问为什么会给无效的名字? 我不知道! 但是我经常在实践中遇到这个问题。 例如,使用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 

不知道这个是多么知名,但是我肯定利用的是环境的传递参考能力。

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

对于这个例子,为什么它是有用的没有意义,但如果你周围传递大对象可以帮助。

我最喜欢的东西是foreach库。 它可以让你做所有的应用程序,但有一个更简单的语法:

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

最好的部分是,如果您正在做的事情实际上需要大量的时间,那么您可以从%do%%dopar% (使用适当的后端库)进行切换,即使在整个集群中也可以立即进行并行处理。 很光滑。

我做了很多基本的数据操作,所以这里有两个我每天使用的内置函数( transformsubset )和一个库( sqldf )。

创build示例销售数据

 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 

使用transform()添加一列

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

使用subset()来切片数据

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

使用sqldf()来分割和聚合SQL

sqldf包为R数据框提供了一个SQL接口

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

执行聚合或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 

对于数据框架上的更复杂的map-reduce-likefunction,请查看plyr软件包。 如果发现自己想把头发拉出来,我build议使用R检查数据操作 。

 ?ave 

对'x []'的子集进行平均,其中每个子集由具有相同因子水平的观察值组成。 用法:ave(x,…,FUN = mean)

我用它所有的时间。 (例如, 在这个答案在这里 )

一种加速代码并消除循环的方法。

而不是循环遍历数据框寻找值的循环。 只要用这些值取一个df的子集,就可以快得多。

所以不是:

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

做这样的事情:

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

该基本概念非常经常使用,是摆脱循环的好方法

有时候你需要rbind多个dataframe。 do.call()会让你这样做(有人必须解释这个时绑定我问这个问题,因为它似乎不是一个明显的用途)。

 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) 

在R编程(不是交互式会话)中,我使用if (bad.condition) stop("message") 很多 。 每个function都从其中的一些开始,当我通过计算工作时,我也将这些functionjoin到这些function中。 我想我习惯于使用C中的assert() 。好处是双重的。 首先,使用这些检查来获得工作代码要快得多。 其次,也许更重要的是,当您在编辑器的每个屏幕上看到这些检查时,使用现有代码更容易。 你不必怀疑x>0 ,或者相信一个评论,说明它是……你一眼就知道它是。

PS。 我的第一篇文章 要温柔!

当你在某个地方出现错误而且不易理解时, traceback()函数是必须的。 它将打印一个堆栈的轨迹,非常有用,因为R默认情况下不是很详细。

然后设置options(error=recover)将允许你“进入”提出错误的函数,并试图了解到底发生了什么,就好像你完全控制它,并可以把一个browser()

这三个函数确实可以帮助您debugging代码。

我真的很惊讶,没有人发表过有关申请,提交,推荐和补贴的文章。 我在R中使用的一般规则是,如果我有一个for循环正在进行数据处理或模拟,我试着把它分解出来,并用* applyreplace它。 有些人回避* apply函数,因为他们认为只有单个参数函数可以通过。没有什么可以离开事实! 就像在JavaScript中用参数作为第一类对象传递函数一样,你可以在R中使用匿名函数。 例如:

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

(对于那些后面的#rstats,我也在那里发布)。

请记住,使用适用,sapply,lapply,tapply,do.call! 利用R的vector化。 你不应该走到一堆R代码,看看:

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

这不仅不是vector化的,而且R中的数组结构并不像Python中那样增长(当空间用完时,将数据量加倍,IIRC)。 因此,每个绑定步骤必须先增长到足以接受来自rbind()的结果,然后复制到前面的l的内容。 为了好玩,请在R中尝试上面的内容。注意需要多长时间(甚至不需要Rprof或任何计时function)。 然后尝试

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

以下比第一个版本更好:

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

根据德克的build议,我张贴单个例子。 我希望他们对于这个观众不是太“可爱”[聪明,但我不在乎]或微不足道。

线性模型是R的面包和黄油。当自variables的数量很高时,有两种select。 首先是使用lm.fit(),它接收devisematrixx和响应y作为参数,类似于Matlab。 这种方法的缺点是返回值是对象列表(拟合系数,残差等),而不是类“lm”的对象,可以很好地概括,用于预测,逐步select等。第二方法是创build一个公式:

 > 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 

您可以分配从if-else块返回的值。

而不是,例如

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

你可以做

 x <- if(condition) 1 else 2 

究竟这是如何运作的深魔术。

作为一个总的noob R和统计新手我喜欢unclass()打印一个数据框的所有元素作为一个普通的列表。

看一个完整的数据集一眼就能迅速地看到任何潜在的问题,这是非常方便的。

来自gmodels软件包的CrossTable()可以轻松访问gmodels和SPSS式交叉表,以及通常的testing(Chisq,McNemar等)。 基本上,这是xtabs()花哨的输出和一些额外的testing – 但它确实使与异教徒共享输出更容易。

明确的system() 。 为了能够在R环境中访问所有unix工具(至less在Linux / MacOSX下),在我的日常工作stream程中迅速变得非常重要。

这是一个恼人的解决方法,将一个因素转换为数字。 (类似于其他数据types)

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

虽然这个问题已经有一段时间了,但是我最近在SAS和R博客上发现了一个使用命令cut 。 该命令用于将数据分成不同的类别,我将以虹膜数据集为例,将其分为10类:

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

另一个窍门 一些软件包,比如glmnet, 把devisematrix和响应variables作为input。 如果想要拟合具有特征之间所有相互作用的模型,她不能使用公式“y〜。^ 2”。 使用expand.grid()允许我们利用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 

我最喜欢的,即使不是非传统的技巧,是使用eval()parse() 。 这个例子可能说明它是如何有用的

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

这种情况经常发生,使用eval()parse()可以解决这个问题。 当然,我欢迎任何关于这种编码方式的反馈意见。

set.seed()设置随机数发生器的状态。

例如:

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

对于那些正在编写C的人来说,R: .Internal(inspect(...))是方便的。 例如:

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

d ='〜/ R代码/库/'

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

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

I use the above code to source all the files in a directory at start up with various utility programs I use in my interactive session with R. I am sure there are better ways but I find it useful for my work. The line that does this is as follows.

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

To perform an operation on a number of variables in a data frame. This is stolen from 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) 

I've posted this once before but I use it so much I thought I'd post it again. Its just a little function to return the names and position numbers of a data.frame. Its nothing special to be sure, but I almost never make it through a session without using it multiple times.

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