Glossary · RNA-seq
Log fold change (log2FC)
The number that decides which genes count as "changed", and the one number in your results table that people misread most often.
By Ming "Tommy" Tang, Director of Bioinformatics in Big Pharma · Reviewed September 2026 · 3 min read
Also: log2FC, LFC
Definition
Log2 fold change (log2FC, also written LFC) is the base-2 logarithm of the ratio of expression between two conditions: log2(condition1 / condition2), equivalently log2(condition1) − log2(condition2). A log2FC of 1 means expression doubled, −1 means it was cut in half, and 0 means no change. Because it's a log ratio rather than a raw ratio, it is symmetric around zero: a doubling (log2FC = 1) and a halving (log2FC = −1) have equal magnitude and opposite sign, which a raw fold change of 2 and 0.5 does not give you.
You meet log2FC the first time you open a DESeq2::results() table or a Seurat FindAllMarkers() output, it's the log2FoldChange or avg_log2FC column sitting right next to your p-value. It's also the x-axis of every volcano plot you'll ever make, and the ratio metric behind copy-number gain/loss calls. In every one of those contexts, log2FC is doing the same job: telling you the size of a change, separate from whether that change is statistically distinguishable from noise.
That separation is exactly why the term matters enough to get a chapter. A p-value tells you whether a difference is real given your sample size and variance; log2FC tells you whether the difference is big enough to care about. With enough cells or enough replicates, a change of a few percent can hit p = 1e-20. Log2FC is the filter that keeps you from chasing statistically real but biologically trivial noise.
Why it matters
Get the cutoff wrong and you either drown in false leads or throw away the genes that actually run the biology. The standard cutoff, |log2FC| ≥ 1 (2-fold) with adjusted p < 0.05, is a convention, not a law of biology, a 50% change (log2FC ≈ 0.58) in a dosage-sensitive gene can be the whole story. Beta-thalassemia is caused by roughly a 50% drop in HBB expression; X-chromosome inactivation escape shows up as roughly a 50% increase in expression from the "silenced" X. A strict log2FC ≥ 1 filter would keep the HBB case (log2(0.5) = −1) but throw out the XCI escape signal (log2(1.5) ≈ 0.58), same biological magnitude, different fate, purely because of where the cutoff line sits.
In single-cell marker gene calling this cuts the other way: clustering first and then testing for differential expression between clusters is double dipping, and it makes p-values artificially tiny across thousands of genes. When p-values stop being informative, log2FC becomes your real filter for which genes are worth reporting, which is why FindAllMarkers() exposes a logfc.threshold argument (default 0.25) as a pre-filter, separate from the significance test.
Where people get it wrong
The most common mistake is reading log2FC as if it were the fold change itself. A log2FC of 2 does not mean "2-fold", it means 2^2 = 4-fold, because the value is the exponent, not the ratio. People also default to the textbook cutoff (|log2FC| ≥ 1, FDR < 0.05) without asking whether it fits the biology of the gene set in question; for transcription factors and other dosage-sensitive regulators, a 0.5 cutoff paired with a very low adjusted p-value is often more defensible than the "standard" one.
A second, more technical trap: log2FC is not computed the same way by every tool. DESeq2 and edgeR fit it from a negative binomial GLM (accounting for library size and dispersion), not from a simple ratio of normalized counts. Seurat and Scanpy differ from each other too, Seurat averages in linear space then logs (mean(exp(log_values))), Scanpy logs the mean of the log values (exp(mean(log_values))), and both add a pseudocount before the log transform to avoid log(0). Swapping the pseudocount (1 vs. 1e-9) or switching tools can visibly shift log2FC for low-expression genes, so a log2FC value is only comparable to another log2FC value if it came from the same pipeline.
A concrete example
Gene X has a mean normalized count of 20 in control and 80 in treatment. The raw fold change is 80/20 = 4. The log2 fold change is log2(4) = 2, not "2-fold," but 4-fold, since 2^2 = 4. This is the number that would show up in a DESeq2 results table for that comparison.
# Manual check of what a DESeq2 log2FoldChange means
control_mean <- 20
treatment_mean <- 80
raw_fc <- treatment_mean / control_mean # 4
log2fc <- log2(treatment_mean / control_mean) # 2
# In practice, pull it straight from DESeq2:
# res <- results(dds, contrast = c('condition', 'treatment', 'control'))
# res$log2FoldChange is fit from the NB GLM, not this raw ratio,
# but it estimates the same quantity after accounting for
# library size and dispersion.
cat(sprintf('raw fold change: %.1f, log2FC: %.1f\n', raw_fc, log2fc))
# raw fold change: 4.0, log2FC: 2.0Related terms
Questions people ask
- What does a log2FC of 1 mean?
It means expression doubled in condition 1 relative to condition 2, a 2-fold increase. A log2FC of −1 means expression was cut in half, and log2FC of 0 means no change. To convert back to a raw fold change, compute 2^(log2FC).
- What log2FC cutoff should I use for differentially expressed genes?
There's no universal answer. |log2FC| ≥ 1 with adjusted p < 0.05 is the most common convention, but a 0.5 cutoff paired with very low adjusted p-values is often more appropriate for dosage-sensitive genes like transcription factors, where a 50% change can drive real biology. Let the gene's known biology, not a default, set the threshold.
- Why use log2 fold change instead of raw fold change?
Log2 fold change is symmetric around zero: a doubling and a halving get equal-magnitude opposite-sign values (1 and −1), while raw fold change gives 2 and 0.5, which look unequal and are harder to compare or plot. That symmetry is also why volcano plots and MA plots use log2FC on their axes instead of raw ratios.
- Fold change vs log fold change, what's the actual difference?
Fold change is the raw ratio between two conditions (treatment/control); log fold change is the log, usually base 2, of that ratio. A common error is reading a log2FC value as if it were the fold change itself, log2FC = 2 means 4-fold (2^2), not 2-fold.
- Why do Seurat and Scanpy give different log2FC values for the same single-cell data?
They average differently: Seurat computes the mean in linear space and then takes the log, while Scanpy takes the mean of the already-logged values. The two also handle the pseudocount added before log transformation differently, and these choices diverge most for lowly expressed genes, so avg_log2FC values from Seurat and Scanpy are not directly comparable.
Related pages
Related reading on the blog
Sources
- Do you really understand log2Fold change in single-cell RNAseq data? — Seurat vs Scanpy calculation discrepancy, pseudocount effects
- FindAllMarkers documentation — Seurat's avg_log2FC formula