I am aware of limpa, but at the moment I have an entire pipeline developed for proteomics based on limma, so it was easier for me to do it this way. But I will definitely give limpa a go later.
I have tried all the approaches you outline in this post. The one with participant as a block variable (see my original post) removes a lot of significance (in the original post I stated mistakenly that there were no significant proteins, which was due to a bug in my code). Here is what I get. Below I quote the numbers of significant (FDR < 0.05) proteins.
- For the model
~ treatment + visit: 257 (drug1), 247 (drug2) and 8 across all visits. - For the model
~ treatment + visitwith participant as a block variable: 3 (drug1), 0 (drug2) and 33 across all visits.
From duplicateCorrelation() I get the consensus correlation of 0.38.
Essentially, including participant as a block variable destroys nearly all drug effect. Does it mean the results from the non-block model are mostly false positives? I think I understand now what you mean by saying "[non-block] model ignores the repeated measures on the patients, so it is over-stating (by a factor of 5) the number of independent observations in your dataset."
Does it have to do anything with the fact that participant and treatment are confounded (each participant receives only one treatment)?
When you refer to "significant proteins", significant for what comparison? You don't show any code that tests any hypotheses.
The interaction model is a totally different model from the additive model, and the coefficients have different interpretations. There are no coefficients in the interaction model that correspond to the treatment term in the additive model, so I suspect that you have not just fitted a more complex model, but tested a different hypothesis as well. As you may already know, the limma authors strongly recommend that you fit interaction models using a oneway layout in order to simplify and clarify the process of forming constrasts. Most analysts are not skilled at forming meaningful contrasts from factorial models. In our opinion, the interaction model concept of a "main effect" has little scientific relevance to omics analyses.
If you're using limma for mass spectrometry data, it would make sense to use the limpa package (https://doi.org/doi:10.18129/B9.bioc.limpa), which gives an enhanced limma pipeline specifically for this type of data.
Thank you for your answer. To find "significantly changing" proteins I tabulate the p-values and fold changes from
eBayes()for all coefficients except the intercept. See the code snippet below.Would you recommend to use specific contrasts driven by the biological questions instead? I tried this, with no significant results. For example one of the questions is to compare proteins between drug and placebo at each visit. I used the following approach. This is an example for just one contrast, but I tested multiple contrasts - at each visit and comparing drug1 and drug2 vs placebo:
In any approach I would proceed with the following to tabulate the results for all coefficients, except for the intercept:
Your code doesn't correspond to either of the design matrices defined in your question. I will assume that the interaction formula shown in your question was just a red herring, and you actually formed visit-specific contrasts as shown above.
Apologies for not being clear. What I wanted to say was that I tried different approaches: first with additive, then with interaction formula (the original question) and then I also tried a fews models with selected contrasts (the followup).
The second part of the followup post (the one with
topTable()) is my attempt to answer how I found "significant" proteins. You said "You don't show any code that tests any hypotheses". Correct me if I'm wrong:eBayes()performs statistical testing and returns t-statistics and the corresponding p-values for all coefficients under the null hypothesis that the mean(coefficient level) - mean(coefficient baseline) = 0. This is how I understand it. I simply extract the p-values using topTable(). I don't perform any additional tests.