22

R语言中的排序,集合运算,reshape,以及merge总结

一直以来我都是随便看了点R的编程教程,因为我学了一点点C,所以还算有基础,现在基本上简单看看教程就能懂一门语言了,区别只是熟练度而已。R用得比较多,所以还算擅长,但是很多快捷应用的地方,我总是寄希望于到时候再查资料,所以没能用心的记住,这次花了点时间好好整理了一下R里面关于数据操作的重点,我想,以后再碰到类似的数据处理要求,应该很快能解决了把。

首先看看排序:

在R中,和排序相关的函数主要有三个:sort(),rank(),order()。

sort(x)是对向量x进行排序,返回值排序后的数值向量。

rank()是求秩的函数,它的返回值是这个向量中对应元素的“排名”。

order()的返回值是对应“排名”的元素所在向量中的位置。

其中sort(x)等同于x[order(x)]

下面以一小段R代码来举例说明:

> x<-c(97,93,85,74,32,100,99,67)

> sort(x)

[1]  32  67  74  85  93  97  99 100

> order(x)

[1] 5 8 4 3 2 1 7 6

> rank(x)

[1] 6 5 4 3 1 8 7 2

> x[order(x)]

[1]  32  67  74  85  93  97  99 100

其中比较有用的order,它可以用来给数据框进行排序

dat[order(dat[,1]),] 以该数据框的第一列进行排序

dat[order(dat[,1],dat[,2]),] 以该数据框的第一列为主要次序,第二列为次要序列进行排序

 

然后我们看看集合运算:

在R里面除了简单的对两个向量求交集并集补集之外,比较重要的就是match和 %in% 了,需要重点讲讲。

#首先对集合A,B,C赋值

> A<-1:10

> B<-seq(5,15,2)

> C<-1:5

> #求A和B的并集

> union(A,B)

[1]  1  2  3  4  5  6  7  8  9 10 11 13 15

> #求A和B的交集

> intersect(A,B)

[1] 5 7 9

> #求A-B

> setdiff(A,B)

[1]  1  2  3  4  6  8 10

> #求B-A

> setdiff(B,A)

[1] 11 13 15

> #检验集合A,B是否相同

> setequal(A,B)

[1] FALSE

> #检验元素12是否属于集合C

> is.element(12,C)

[1] FALSE

> #检验集合A是否包含C

> all(C%in%A)

[1] TRUE

> all(C%in%B)

从上面可以看到%in%这个操作符只返回逻辑向量TRUE 或者FALSE,而且返回值应该与%in%这个操作符前面的向量程度相等。也就是说它相当于遍历了C里面的一个个元素,判断它们是否在B中出现过,然后返回是或者否即可。

而match(C,B)的结果就很不一样了,它的返回结果同样与前面的向量等长,但是它并非返回逻辑向量,而是遍历了C里面的一个个元素,判断它们是否在B中出现过,如果出现就返回在B中的索引号,如果没有出现,就返回NA

>B

[1]  5  7  9 11 13 15

>C

[1] 1 2 3 4 5

>match(C,B)

[1] NA NA NA NA  1

>C%in%B

[1] FALSE FALSE FALSE FALSE  TRUE

 接下来我们看看reshape:

这是一个需要安装的包,起得就是R里面最经典的把长型数据变宽,和把宽数据拉长的作用。

其中melt函数是把很宽的数据拉长,它就是需要指定几列数据是保证不被融合的, 其余每一列数据都必须被融合到一列了,融合后的这一列数据每个元素旁边就用列名来标记该数据来自于哪一列。

# example of melt function
library(reshape)
mdata <- melt(mydata, id=c("id","time"))

融合后的数据如下:

id time variable value
1 1 x1 5
1 2 x1 3
2 1 x1 6
2 2 x1 2
1 1 x2 6
1 2 x2 5
2 1 x2 1
2 2 x2 4

可以看到variable列里面有两个不同的元素,说明是把旧数据中的两列给融合了,融合后的一个很长的列就是value

而cast函数的功能就是把刚才融合好的数据给还原。

# cast the melted data
# cast(data, formula, function)
subjmeans <- cast(mdata, id~variable, mean)
timemeans <- cast(mdata, time~variable, mean)

可以看到它们返回的结果是:

subjmeans

id x1 x2
1 4 5.5
2 4 2.5

timemeans

time x1 x2
1 5.5 3.5
2 2.5 4.5

可以看到cast函数比较复杂一点,formula公式右边的变量是需要拆开的variable,这一列变量有多少不重复元素,就新增多少列,左边的变量是行标记了,其余所有数据都会被计算一下再放在合适的位置。

在reshape2这个包里面还进化出来dcast和acast函数,功能大同小异。

 

最后我们来看看merge函数:

这个函数的功能非常强大,类似于SQL语句里面的join系列函数

测试数据如下,它们这两个表的连接是作者名

Capture1

 

如果要实现类似sql里面的inner join 功能,则用代码

m1 <- merge(authors, books, by.x = "surname", by.y = "name")

如果要实现left join功能则用代码

m1 <- merge(authors, books, by.x = "surname", by.y = "name",all.x=TRUE)

right join功能代码

m1 <- merge(authors, books, by.x = "surname", by.y = "name",all.y=TRUE)

all join功能代码

m1 <- merge(authors, books, by.x = "surname", by.y = "name",all=TRUE)

关于单变量匹配的总结就是这些,但对于多变量匹配呢,例如下面两个表,需要对k1,k2两个变量都相等的情况下匹配

x <- data.frame(k1 = c(NA,NA,3,4,5), k2 = c(1,NA,NA,4,5), data = 1:5)

y <- data.frame(k1 = c(NA,2,NA,4,5), k2 = c(NA,NA,3,4,5), data = 1:5)

匹配代码如下merge(x, y, by = c("k1","k2"))  #inner join

另外一个多行匹配的例子如下:

Capture2

我们的测试数据如上,这两个表的连接在于作者名。下面这两个语句等价

merge(authors, books, by=1:2)

merge(authors, books, by.x=c("FirstName", "LastName"),

by.y=c("AuthorFirstName", "AuthorLastName"),

all.x=TRUE)

都可以把两张表关联起来。

当然,在我搜索资料的时候,发现了另外一个解决问题的方法:

A[with(A, paste(C1, C2, sep = "\r")) %in% with(B, paste(C1, C2, sep="\r")), ]

 

 

参考:http://blog.sina.com.cn/s/blog_6caea8bf0100spe9.html

http://blog.sina.com.cn/s/blog_6caea8bf010159dt.html

http://blog.csdn.net/u014801157/article/details/24372441

http://www.statmethods.net/management/reshape.html

https://docs.tibco.com/pub/enterprise-runtime-for-R/1.5.0_may_2013/TERR_1.5.0_LanguageRef/base/merge.html

http://r.789695.n4.nabble.com/Matching-multiple-columns-in-a-data-frame-td890832.html

http://bbs.pinggu.org/thread-3234639-1-1.html

http://www.360doc.com/content/14/0821/13/18951041_403561544.shtml

http://developer.51cto.com/art/201312/423612.htm

22

用R和mysql把基因对应的最大表达量探针挑出来

懂点芯片数据分析的都应该知道,芯片设计的时候,对一个基因设计了多个探针,这样设计是为了更好的捕获某些难以发现的基因,或者重点研究某些基因。
但是对我们的差异分析不方便,所以我们只分析哪些有对应了entrez ID的探针,而且对每个entrez ID,我们只需要挑选它表达量最高的那个探针。
所以就演化为一个编程问题:分组求最大值,多公共列合并!
如果是在R语言里面,那么首先这个table的表示形式如下
> esetDataTable[1:10,c(7,8)]
EGID rowMeans
1000_at 5595 1840.04259751826
1001_at 7075 799.075414422572
1002_f_at 1557 50.4884096416177
1003_s_at 643 142.372008051308
1004_at 643 211.65300963049
1005_at 1843 4281.29318032004
1006_at 4319 38.5784289213085
1007_s_at NA 1489.98158531843
1008_f_at 5610 4013.576753977
1009_at 3094 3070.50648167305
我们首先看看一个R语言函数处理方式吧,这个是比较容易想到的算法,但是用到了循环,非常的不经济,计算量很大。

Capture1
因为里面涉及到了双重循环。我进行了人工计时,这个程序耗费了一分钟十二秒,程序里面的计时器有点问题。

然后我再讲一个精简版的算法
dat=esetDataTable[,c(7,8)]
dat=as.data.frame(apply(dat,2,as.numeric))
dat$prob=rownames(esetDataTable)
首先可以得到需要提取的数据所在行的两个值
match_row=aggregate(dat,by=list(dat[,1]),max)
类似于这句SQL语句:SELECT max(a.rowMeans) as val, a.EGID FROM test.esetDataTable a group by a.EGID
现在要根据match_row表去原表esetDataTable里面提取我们的探针ID数据。

Capture2

这样就完美解决了问题,我们的合并后的数据的prob列,就是前面那个函数计算了一分多钟的返回的非冗余探针ID向量,relevantProbesets,但是这次只花了不到一秒钟。
tmp_prob=merge(dat,match_row,by=c("EGID","rowMeans"))
setdiff(as.character(relevantProbesets),as.character(tmp_prob$prob))
length(union(as.character(relevantProbesets),as.character(tmp_prob$prob)))
setdiff(as.character(tmp_prob$prob),as.character(relevantProbesets))
我顺便检查了一下上面那个复杂的R函数跟我这次精简版的结果,发现这次才是对的,上面那个错了。
你们能发现上面那个为什么错了吗?

如果是在mysql里面它的表现形式如下;
mysql> select row_names,EGID,rowMeans from esetDataTable order by EGID limit 10;
+------------+-------+------------------+
| row_names | EGID | rowMeans |
+------------+-------+------------------+
| 38912_at | 10 | 85.8820246154773 |
| 41654_at | 100 | 301.720067595558 |
| 907_at | 100 | 273.100008206028 |
| 2053_at | 1000 | 354.207060199715 |
| 2054_g_at | 1000 | 33.8472900312781 |
| 40781_at | 10000 | 425.007640082848 |
| 35430_at | 10001 | 152.885791914329 |
| 35431_g_at | 10001 | 181.915087187117 |
| 35432_at | 10001 | 184.869017764782 |
| 31366_at | 10002 | 44.9716205901791 |
+------------+-------+------------------+
10 rows in set (0.05 sec)

如果要用SQL来做同样的事情需要下面这个语句,这个就非常简单啦!
select b.*
from test.esetDataTable b
inner join (
SELECT max(a.rowMeans) as val, a.EGID FROM test.esetDataTable a group by a.EGID
) as c on b.EGID=c.EGID and b.rowMeans=c.val
结果是:8640 rows in set (4.56 sec) 看起来mysql还没有R语言快速,但是这个mysql语句很明显也不是很高效,但是我的mysql水平有限。
稍微解释一下这个mysql语句,其中SELECT max(a.rowMeans) as val, a.EGID FROM test.esetDataTable a group by a.EGID类似于R里面的match_row=aggregate(dat,by=list(dat[,1]),max),就是把每个entrez ID对应着的最大值提取出来成一个新的表
而inner join on b.EGID=c.EGID and b.rowMeans=c.val 就是我们的tmp_prob=merge(dat,match_row,by=c("EGID","rowMeans")) 根据两列来合并两个数据框