Search⌘ K
AI Features

Avoiding Misleading Visualizations

Explore techniques to identify and avoid misleading visualizations in data storytelling. Learn to properly set axis baselines, handle dual axes, and disclose date ranges to ensure your charts truthfully represent the data. This lesson equips you to design charts that communicate findings accurately and maintain reader trust.

A marketing report shows conversion rising from 3.0% to 3.3%, yet its vertical axis begins at 2.9%. The end of the line sits about four times as far above the axis floor as the start, so a reader may conclude that conversion jumped dramatically. The data do not support that claim. The change is 0.3 percentage points, a 10% relative increase, and the chart has enlarged it through framing.

A truncated axis is an axis that starts above zero or ends before the full relevant range. Truncation can sometimes help a line chart reveal small changes when the chart clearly states its scale and the decision truly depends on those changes. It is rarely appropriate for bar charts that compare magnitudes, because a bar’s height visually represents distance from zero.

The correction goal is not to make every chart flat. The goal is to make the apparent difference match the actual difference, while preserving enough context for a reader to judge the result honestly.

Start bar comparisons at zero

Because bars encode magnitude with their length, the same conversion rates can produce sharply different impressions when only the y-axis range changes. The two charts below contain the same 3.0% and 3.3% values. Compare the bar heights, axis labels, and visual gap before deciding which chart supports a fair comparison.

Python
import pandas as pd
import matplotlib.pyplot as plt
def main() -> None:
df = pd.DataFrame(
{
"Variant": ["Control", "Test"],
"Conversion_Rate": [3.0, 3.3],
}
)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))
ax1.bar(
df["Variant"],
df["Conversion_Rate"],
color=["#3b82f6", "#1d4ed8"],
)
ax1.set_ylim(0, 4.0)
ax1.set_title("Zero Baseline (0 - 4%)")
ax1.set_ylabel("Conversion rate (%)")
ax2.bar(
df["Variant"],
df["Conversion_Rate"],
color=["#ef4444", "#b91c1c"],
)
ax2.set_ylim(2.9, 3.4)
ax2.set_title("Truncated Axis (2.9 - 3.4%)")
ax2.set_ylabel("Conversion rate (%)")
plt.tight_layout()
plt.savefig("output/avoiding_misleading_visualizations.png")
plt.show()
if __name__ == "__main__":
main()
    ...