Rでデータ連結

次のようなデータがあって、同じ遺伝子(1列目)のデータを連結したい(横に並べたい)とします。

1つ目のデータ

logCPM	logFC	PValue	FDR	
AT5G07990.1	6.502	-0.955	2.55E-13	9.32E-09
AT5G48880.1	7.027	-0.567	1.18E-11	2.15E-07
AT5G48880.3	6.990	-0.545	7.42E-11	9.03E-07
AT5G48880.2	7.020	-0.537	1.26E-10	1.15E-06
(以下数万行)

2つ目のデータ

logCPM	logFC	PValue	FDR	
AT5G58240.1	4.668	-1.552	3.34E-30	1.22E-25
AT5G58240.2	4.415	-1.406	2.56E-20	4.68E-16
AT5G22650.1	7.769	-0.926	1.66E-15	1.83E-11
AT5G22650.2	7.705	-0.925	2.00E-15	1.83E-11
(以下数万行)

1列目の並び順が異なるだけの場合

データの列名をrownamesで取り出して、それの並び順をorderでつくります。d1[order(rownames(d1)),]とすることで、1列目の順に並べます。2つ目のデータも同じように並べ替えて、cbindで連結します。

> d1 <- read.table("data1.txt")
> d2 <- read.table("data2.txt")
> cbind(d1[order(rownames(d1)),], d2[order(rownames(d2)),])
	logCPM	logFC	PValue	FDR	logCPM	logFC	PValue	FDR
AT1G01010.1	4.849	-0.290	0.077	0.499	4.849	-0.109	0.509	1.000
AT1G01020.1	4.446	-0.098	0.553	0.751	4.446	0.118	0.459	1.000
AT1G01020.2	4.154	-0.109	0.559	0.755	4.154	0.094	0.619	1.000
AT1G01030.1	4.926	-0.264	0.104	0.514	4.926	-0.040	0.809	1.000
(以下数万行)

行数が異なる場合

上の例のように行数が同じ場合でもmerge関数を使えば、コードもより簡単になります。どの列の値で連結するかをbyに与えます。今回は列名なので0です。どちらかに欠損値がある場合は、allをつけておくと行数が減りません。

> merge(d1, d2, by = 0)
	Row.names	logCPM.x	logFC.x	PValue.x	FDR.x	logCPM.y	logFC.y	PValue.y	FDR.y
1	AT1G01010.1	4.849	-0.290	0.077	0.499	4.849	-0.109	0.509	1
2	AT1G01020.1	4.446	-0.098	0.553	0.751	4.446	0.118	0.459	1
3	AT1G01020.2	4.154	-0.109	0.559	0.755	4.154	0.094	0.619	1
4	AT1G01030.1	4.926	-0.264	0.104	0.514	4.926	-0.040	0.809	1
(以下数万行)

2つのファイルなら上記で済みますが、3つ4つと増えていくとコードを書いていくのが大変です。そこで、繰り返しなどを使って楽をしようと思うのですが、最初の処理だけ別になって面倒になります。

一つ目だけ別の処理をしているあまり面白くないコード

files <- list.files(path = ".", pattern = "*.txt", full.names = TRUE)
for(file in files){
	buf <- read.table(file)
	if(file == files[1]){
		d <- buf
	}else{
		d <- merge(d, buf, by = 0)
		# mergeすると行についた名前が1列目になってしまうので、これを行名に戻す
		rownames(d) <- d[,1]
		d <- d[, -1]
	}
}

if(file == files[1]) として、1つ目のデータだけ別の処理をしています。いまいち格好よくありません。

イケてると思うコード

data <- lapply(files, read.table)
d <- Reduce(function (d1, d2){
	d <- merge(d1, d2, by = 0)
	rownames(d) <- d[,1]
	d[, -1]
}, data)

Reduce関数を使うと同じ型のデータを集約していくことが簡単にできます。