# How to create a list of differentially expressed (DE) genes after normalization with RUVSeq?

I am using edgeR to perform differential expression (DE) analysis on a set of RNA-seq data samples (2 controls; 8 treatments). To correct for batch effects, I am using RUVSeq.

I am able to get a list of DE genes without normalization:

x <- as.factor(rep(c("Ctl","Inf"),c(2,8)))
set <- newSeqExpressionSet(as.matrix(counttable),phenoData=data.frame(x,row.names=colnames(counttable)))
design <- model.matrix(~x, data=pData(set))
y <- DGEList(counts=counts(set), group=x)
y <- calcNormFactors(y, method="upperquartile")
y <- estimateGLMCommonDisp(y, design)
y <- estimateGLMTagwiseDisp(y, design)
fit <- glmFit(y, design)
lrt <- glmLRT(fit, coef=2)
top <- topTags(lrt, n=nrow(set))$table write.table(top, paste(OUT, "DE_genelist.txt", sep=""))  Then immediately after creating the "top" object, I use RUVg to normalize: # [...] top <- topTags(lrt, n=nrow(set))$table
empirical <- rownames(set)[which(!(rownames(set) %in% rownames(top)[1:5000]))]
ruvg <- RUVg(set, empirical, k=1)
write.table(ruvg, paste(OUT, "DE_RUVg_genelist.txt", sep=""))


And I get the error:

Error in as.data.frame.default(x[[i]], optional = TRUE) :
cannot coerce class ‘structure("SeqExpressionSet", package = "EDASeq")’ to a data.frame


I am not sure how to print the list of normalized results like I can with the unnormalized data. Ideally, I would get a file with the same format as the edgeR output (as a .csv or .txt file):

"logFC" "logCPM" "LR" "PValue" "FDR"
"COBLL1" -2.150 4.427061248733 75.0739519350016 4.53408921348828e-18 9.51203608115384e-15
"UBE2D1" -2.178 3.577168782408 74.9346752854903 4.86549160161322e-18 9.51203608115384e-15
"NEK7" -2.404 4.020072739285 72.6539117671717 1.54500340443843e-17 2.71843349010941e-14
"SMC6" -2.300 5.674738981329 61.8130019860261 3.7767230643666e-15 3.4974443325016e-12


How can I get a list of genes as an output after normalization with RUVSeq?

You do the normalization before running your edgeR. The purpose of RUVg is to remove "Remove Unwanted Variation Using Control Genes". In your code, you ran edgeR and then normalize the data using RUVg, which is only going to return you the normalized counts.

Using the example dataset in vignette:

library(RUVSeq)
library(zebrafishRNASeq)
data(zfGenes)
filter <- apply(zfGenes, 1, function(x) length(x[x>5])>=2)
filtered <- zfGenes[filter,]
genes <- rownames(filtered)[grep("^ENS", rownames(filtered))]
spikes <- rownames(filtered)[grep("^ERCC", rownames(filtered))]

x <- as.factor(rep(c("Ctl", "Trt"), each=3))
set <- newSeqExpressionSet(as.matrix(filtered),
phenoData = data.frame(x, row.names=colnames(filtered)))
set <- betweenLaneNormalization(set, which="upper")

set1 <- RUVg(set, spikes, k=1)


You can look at it, it's an expression set with counts etc, not results:

set1
SeqExpressionSet (storageMode: lockedEnvironment)
assayData: 20865 features, 6 samples
element names: counts, normalizedCounts, offset
protocolData: none
phenoData
sampleNames: Ctl1 Ctl3 ... Trt13 (6 total)
varLabels: x W_1
featureData: none
experimentData: use 'experimentData(object)'
Annotation:


You run edgeR now on the results of RUVg:

design <- model.matrix(~x + W_1, data=pData(set1))
y <- DGEList(counts=counts(set1), group=x)
y <- calcNormFactors(y, method="upperquartile")
y <- estimateGLMCommonDisp(y, design)
y <- estimateGLMTagwiseDisp(y, design)
fit <- glmFit(y, design)
lrt <- glmLRT(fit, coef=2)
topTags(lrt)

• This is great! Definitely answers the question. [Although the very last line topTags(lrt) will only provide a list of 10 genes, so I'll use topTags(lrt, n=nrow(set))\$table for a full list]. Jul 1, 2020 at 23:35
• Follow-up question for you then: What is the utility of performing the "upper-quartile" (UQ) normalization before using RUVg and then running edgeR? My thought was, "if I am using RUVg to correct for batch effects, why should I also use UQ?" Perhaps it is important? Jul 1, 2020 at 23:36
• This is what I know about UQ normalization: scaling with or without UQ before RUVg will usually lead to similar results. It is only important to include the edgeR offset -- calcNormFactors(y, method="upperquartile") -- if you decide to scale prior to RUV use. If no UQ scaling is applied, the edgeR offset should be set to 0 (zero). Jul 2, 2020 at 14:15
• The betweenLaneNormalization() in limma adjust for signal intensity difference (in arrays) or in edgeR, you try to normalize between samples such that most genes are unchanged. RUVg adjust for unseen variation on top of this, for example batch effects. So you don't want to introduce this as a kind of variation for RUVg model to pick up. Hence you do the normalization before. And as you noted, if you use edgeR, RUV also ask you to provide the offset , which is the normalization factors. Jul 2, 2020 at 21:57
• Wonderful explanation! Thanks for your help @StupidWolf!! Jul 3, 2020 at 13:34

I have not used this package but from your code it seems that ruvg is not a table. Instead, it is an R object, which means that you cannot use write.table. I think the results you want is stored in the object. All R objects contain "slots" of data, which can be accessed by @. If I were you, I would type ruvg@ and should be able to see which data slots are contained in the object.

• This certainly gives me more direction than I had before, so I appreciate that. Just looking at all the data stored by the ruvg object (Large SeqExpressionSet (1.1 Mb)) tells me that it may take some time to find what I am looking for in there. I'll report back if I find the results I want. Thanks for your suggestion! Jul 1, 2020 at 21:57