How to Analyze qPCR Data in GraphPad Prism With Error Bars
The most common mistake people make when they analyze qPCR data in GraphPad Prism is calculating fold changes for each biological replicate and then plotting mean ± SD of those fold changes. This produces asymmetric error that overstates upregulation and understates downregulation. The correct approach: do your statistics on ΔCt (or ΔΔCt) values, then transform the mean and error boundaries separately into fold-change space for plotting.
Here's the short version. Calculate ΔCt for each biological replicate. Get the mean and SD of those ΔCt values per group. Run your statistics (t-test, ANOVA) on ΔCt values. Then convert to fold change (2^−ΔΔCt^) only for the final plot. Your error bars become asymmetric — which is correct, because the log-to-linear transformation is nonlinear. Prism handles asymmetric error bars natively, so this is straightforward once you know the workflow.
Prepare your data outside Prism first
Before you open Prism, get your data into shape. For each biological replicate (not technical replicate), you should have a single Ct value for your gene of interest (GOI) and your reference gene. If you ran technical triplicates, average those first — but only if the replicate CV is < 0.5 Ct. Any technical replicate that deviates by more than 0.5 Ct from the others should be examined and likely excluded; it usually means a pipetting error.
Calculate ΔCt for each biological replicate:
ΔCt = Ct(GOI) − Ct(reference gene)
Then calculate ΔΔCt relative to your control group:
ΔΔCt = ΔCt(sample) − mean ΔCt(control)
At this point, your control group's mean ΔΔCt should be ~0, and treated groups will have positive values (downregulated) or negative values (upregulated). This is the stage where all statistics should happen.
A quick worked example. Say you're comparing VEGFA expression in normoxic vs. hypoxic HUVECs, normalized to HPRT1. Three biological replicates each:
| Replicate | Condition | Ct(VEGFA) | Ct(HPRT1) | ΔCt |
|---|---|---|---|---|
| 1 | Normoxia | 24.1 | 20.0 | 4.1 |
| 2 | Normoxia | 23.8 | 19.7 | 4.1 |
| 3 | Normoxia | 24.3 | 20.3 | 4.0 |
| 1 | Hypoxia | 21.5 | 19.9 | 1.6 |
| 2 | Hypoxia | 21.2 | 19.8 | 1.4 |
| 3 | Hypoxia | 21.8 | 20.1 | 1.7 |
Mean ΔCt normoxia = 4.067, SD = 0.058. Mean ΔCt hypoxia = 1.567, SD = 0.153. ΔΔCt for hypoxia = 1.567 − 4.067 = −2.500. Fold change = 2^2.5^ = 5.66.
Now for the error bars. You need to propagate the SD through the 2^−ΔΔCt^ transformation. The standard approach (Livak and Schmittgen, 2001) uses:
- Upper error bar: 2^−(ΔΔCt − SD)^
- Lower error bar: 2^−(ΔΔCt + SD)^
Where SD here is the SD of the ΔΔCt values. Since ΔΔCt is a difference of two means, propagate:
SD(ΔΔCt) = √(SD(ΔCt_control)² + SD(ΔCt_treated)²) = √(0.058² + 0.153²) = 0.164
So for hypoxia:
- Center: 2^2.500^ = 5.66
- Upper bound: 2^(2.500 + 0.164)^ = 2^2.664^ = 6.34
- Lower bound: 2^(2.500 − 0.164)^ = 2^2.336^ = 5.05
Upper error = 6.34 − 5.66 = 0.68. Lower error = 5.66 − 5.05 = 0.61. Notice the asymmetry — this is expected and correct.
For the normoxia control, fold change = 1.0 by definition, with its own small error bars calculated similarly if you wish, or you can set it to 1.0 with no error (both conventions exist; showing the control's error is more honest).
Setting up the Prism table and entering asymmetric error bars
Open Prism and create a Grouped data table. In the "New Table" dialog, select "Enter and plot error values already calculated elsewhere" and choose Asymmetric (different upper and lower) error bars. This is critical — the default symmetric error will distort your plot.
Your table columns will look like this:
| Normoxia | Hypoxia | |||||
|---|---|---|---|---|---|---|
| Mean | Lower | Upper | Mean | Lower | Upper | |
| Row 1 | 1.00 | 0.00 | 0.00 | 5.66 | 0.61 | 0.68 |
If you have multiple genes, each gene gets its own row. If you're comparing multiple treatment groups, each gets its own column set.
Alternatively, if you prefer Prism to calculate the error bars for you, you can enter individual ΔΔCt-derived fold-change values per replicate (i.e., calculate 2^−ΔΔCt^ for each biological replicate individually), then select "Mean with SD" under error bar options. This is the approach many people use, and it's approximately correct for small SDs, but it slightly distorts the error bars because the mean of the fold changes ≠ 2^(mean of ΔΔCt)^. With three replicates and small variance, the difference is usually negligible. With high variance or small sample sizes, the asymmetric approach above is more accurate.
Running statistics the right way
Here's where people reliably go wrong: they run a t-test or ANOVA on fold-change values. Don't do this. Fold-change data are log-normally distributed, which violates the normality assumption of parametric tests. You have two options:
Option 1 (preferred): Run statistics on ΔCt values directly. Create a separate Prism column table with ΔCt values for each group, and run an unpaired t-test (two groups) or one-way ANOVA (multiple groups). This is statistically appropriate because ΔCt values are continuous and approximately normally distributed.
Option 2: Run statistics on log2(fold change) values, which are mathematically identical to −ΔΔCt values. Same result, different framing.
In the worked example above: unpaired t-test on ΔCt values (normoxia: 4.1, 4.1, 4.0 vs. hypoxia: 1.6, 1.4, 1.7) gives t(4) = 25.6, p < 0.0001. Report the p-value on your fold-change bar graph.
For multiple comparisons (e.g., drug doses), run one-way ANOVA on ΔCt values with Dunnett's post-hoc test comparing each treatment to control. In Prism, this is under Analysis → Column Analyses → One-way ANOVA → Multiple Comparisons → Compare each column to control column → Dunnett's test. Then annotate your fold-change graph with the resulting p-values or significance brackets.
Formatting the figure
A few Prism-specific tips that will save you revision cycles:
- Y-axis: Label it "Relative expression (fold change)" or "Fold change vs. control." Start the axis at 0 unless you have a compelling reason not to.
- Baseline: Add a dashed line at y = 1.0 to mark the control level, especially if comparing multiple genes.
- Bar vs. dot: Bar graphs with individual data points overlaid (Prism's "Show individual points" option) are increasingly expected by reviewers. With n = 3, the bar chart alone hides your actual data.
- Error bar label: State in the figure legend whether error bars represent SD or SEM, and that they were calculated from ΔCt-propagated values. Example: "Error bars represent SD propagated through the 2^−ΔΔCt^ transformation (Livak and Schmittgen, 2001)."
- Color: If using SYBR Green data, resist the urge to make everything green. Use colorblind-friendly palettes — Prism has built-in options under "Change Colors" → "Color Schemes."
One formatting note that catches people: if you entered pre-calculated asymmetric errors, Prism won't let you overlay individual data points from that same table. You'd need a second data set layered on the same graph. Honestly, this is where the "enter individual fold changes and let Prism calculate" approach is more convenient, even if slightly less precise.
When this approach breaks down
The 2^−ΔΔCt^ method assumes roughly equal amplification efficiency between GOI and reference gene. If your GOI amplifies at 95% efficiency and your reference at 102%, the small difference is tolerable. If the gap is larger — say 85% vs. 105% — use the Pfaffl method (Pfaffl, 2001) instead, which incorporates gene-specific efficiencies:
Ratio = (E_GOI^ΔCt(GOI)^) / (E_ref^ΔCt(ref)^)
This makes error propagation more involved because you're now propagating uncertainty through two exponentials with different bases. The math is manageable in a spreadsheet but tedious to do by hand for every experiment.
The other scenario where this workflow gets painful is when you're normalizing to multiple reference genes (which you probably should be — single reference genes are a real source of noise, especially across tissues). geNorm (Vandesompele et al., 2002) or NormFinder give you a normalization factor from 2-3 reference genes, but correctly propagating error through a geometric mean of multiple references into a fold change with proper asymmetric error bars is genuinely fiddly in a spreadsheet.
This is exactly the kind of thing VoilaPCR handles automatically — you upload your Ct data, select your reference genes and groups, and it calculates properly propagated asymmetric error bars, runs statistics on ΔCt values, and exports publication-ready figures. Worth checking out if you're tired of maintaining a brittle spreadsheet that someone built three postdocs ago.
The one-paragraph version
Calculate ΔCt per biological replicate. Run statistics on ΔCt values. Convert mean ΔΔCt ± SD to fold change using 2^−(ΔΔCt ± SD)^ to get asymmetric error boundaries. Enter these into Prism as pre-calculated asymmetric error bars, or enter individual fold changes and accept the minor approximation. Label your axes, show your data points, and state your error bar type in the legend. That's it.