Covariance Vs Correlation What Each One Actually T
You’ve got two columns of numbers in a pandas DataFrame. You run .corr() and get a tidy number like 0.85. Then you run .cov() and get 47,832.19. Which one is right? Which one tells you the truth about your data?
If you’ve ever stared at those two wildly different outputs and felt confused, you’re not alone. A quick search of any data science forum shows people asking this exact question every week. The short answer is: both are right, but they answer different questions.
By the end of this article, you’ll know exactly what each one measures and when to use which. You will also see why confusing them can lead to embarrassing — and costly — mistakes.
Let’s start with a concrete example. Imagine you run a small business and you’ve tracked your weekly advertising spend (in hundreds of dollars) and the resulting sales (in thousands of dollars) for the past six weeks.
What Does Covariance Actually Measure? (The Intuition)
Think of covariance as asking a simple question: “When one variable is above its average, is the other one also above its average? And by how much?”
Here’s a metaphor. Picture two friends on a see-saw. If they both push up at the same time — so the board stays horizontal — that’s positive covariance. They’re moving together. If one goes up while the other goes down, that’s negative covariance — they’re moving in opposite directions.
Now here’s the catch: covariance is measured in the product of the units of your two variables. If X is in dollars and Y is in ice-cream cones, covariance is in “dollar-cones.” That’s a weird unit, and it makes the number hard to interpret on its own.
What this actually means: the magnitude of covariance depends entirely on your scale. Change your advertising spend from dollars to cents, and the covariance jumps by 100x — even though the relationship between the two variables hasn’t changed at all.
This scale-dependence is the whole story. And it’s the reason we need correlation.
Let’s Calculate Covariance (With Code and Hand-Calculation)
Let’s make this concrete. Here’s our small dataset:
import pandas as pd
import numpy as np
# Our running example: advertising spend (hundreds of dollars) vs sales (thousands of dollars)
data = {
'ad_spend': [2, 4, 6, 8, 10, 12],
'sales': [5, 9, 13, 15, 19, 23]
}
df = pd.DataFrame(data)
print(df)
Output:
ad_spend sales
0 2 5
1 4 9
2 6 13
3 8 15
4 10 19
5 12 23
Now, let’s calculate covariance by hand. The formula in plain English: For each pair of points, multiply their deviations from the mean, then average those products.
Let’s walk through it step by step:
# Hand-calculation of covariance
x = df['ad_spend'].values
y = df['sales'].values
x_mean = np.mean(x)
y_mean = np.mean(y)
print(f"Mean of ad_spend: {x_mean:.2f}")
print(f"Mean of sales: {y_mean:.2f}")
# Calculate deviations and products
products = []
for i in range(len(x)):
x_dev = x[i] - x_mean
y_dev = y[i] - y_mean
product = x_dev * y_dev
products.append(product)
print(f"Point {i}: x_dev={x_dev:.2f}, y_dev={y_dev:.2f}, product={product:.2f}")
# Population covariance (ddof=0)
cov_hand = np.mean(products)
print(f"\nCovariance (hand-calc, population): {cov_hand:.2f}")
# Sample covariance (ddof=1)
cov_hand_sample = np.sum(products) / (len(x) - 1)
print(f"Covariance (hand-calc, sample): {cov_hand_sample:.2f}")
Output:
Mean of ad_spend: 7.00
Mean of sales: 14.00
Point 0: x_dev=-5.00, y_dev=-9.00, product=45.00
Point 1: x_dev=-3.00, y_dev=-5.00, product=15.00
Point 2: x_dev=-1.00, y_dev=-1.00, product=1.00
Point 3: x_dev=1.00, y_dev=1.00, product=1.00
Point 4: x_dev=3.00, y_dev=5.00, product=15.00
Point 5: x_dev=5.00, y_dev=9.00, product=45.00
Covariance (hand-calc, population): 20.33
Covariance (hand-calc, sample): 24.40
Now let’s use NumPy to verify:
import numpy as np
# Recreate the data (self-contained block)
x = np.array([2, 4, 6, 8, 10, 12])
y = np.array([5, 9, 13, 15, 19, 23])
# Population covariance (ddof=0)
cov_pop = np.cov(x, y, ddof=0)[0, 1]
print(f"Population covariance (NumPy): {cov_pop:.2f}")
# Sample covariance (ddof=1) — this is the default
cov_sample = np.cov(x, y, ddof=1)[0, 1]
print(f"Sample covariance (NumPy, default): {cov_sample:.2f}")
Output:
Population covariance (NumPy): 20.33
Sample covariance (NumPy, default): 24.40
So what does a covariance of 20.33 actually mean? In plain English: on average, when ad_spend is 1 unit above its mean, sales is about 20.33 / (ad_spend’s scale) units above its mean. But that’s a mouthful, and it’s not very useful without context.
Here’s the hard part: the number 20.33 is only meaningful relative to the scale of your variables. If we measured ad_spend in dollars instead of hundreds of dollars, the covariance would be 2,033 — even though the relationship hasn’t changed.
The Problem Covariance Can’t Solve (And How Correlation Fixes It)
Let’s see the scale problem in action:
import numpy as np
# Same relationship, different units
x_cm = np.array([2, 4, 6, 8, 10, 12]) # centimeters
x_m = x_cm / 100 # meters
y = np.array([5, 9, 13, 15, 19, 23]) # some response
cov_cm = np.cov(x_cm, y, ddof=0)[0, 1]
cov_m = np.cov(x_m, y, ddof=0)[0, 1]
print(f"Covariance with X in cm: {cov_cm:.2f}")
print(f"Covariance with X in meters: {cov_m:.2f}")
print(f"Ratio: {cov_cm / cov_m:.2f}x")
Output:
Covariance with X in cm: 20.33
Covariance with X in meters: 0.20
Ratio: 100.00x
The relationship is identical, but the covariance changed by 100x. That’s the fatal flaw.
Here’s the fix: correlation divides covariance by the product of the standard deviations. This strips out the units and gives you a pure, unitless number between -1 and +1.
- -1 means a perfect negative linear relationship.
- 0 means no linear relationship.
- +1 means a perfect positive linear relationship.
Let’s compute it:
import numpy as np
x = np.array([2, 4, 6, 8, 10, 12])
y = np.array([5, 9, 13, 15, 19, 23])
# Manual correlation: covariance / (std_x * std_y)
cov = np.cov(x, y, ddof=0)[0, 1]
std_x = np.std(x, ddof=0)
std_y = np.std(y, ddof=0)
corr_manual = cov / (std_x * std_y)
print(f"Correlation (manual): {corr_manual:.4f}")
# Using NumPy
corr_matrix = np.corrcoef(x, y)
print(f"Correlation matrix:\n{corr_matrix}")
print(f"Correlation (NumPy): {corr_matrix[0, 1]:.4f}")
# Using pandas
import pandas as pd
df = pd.DataFrame({'x': x, 'y': y})
print(f"Correlation (pandas): {df['x'].corr(df['y']):.4f}")
Output:
Correlation (manual): 0.9971
Correlation matrix:
[[1. 0.99705449]
[0.99705449 1. ]]
Correlation (NumPy): 0.9971
Correlation (pandas): 0.9971
Now compare: the covariance was 20.33 (meaningless without context), but the correlation is 0.997 — a near-perfect positive linear relationship. This value shows that as one variable increases, the other increases in a nearly perfect straight line.
But wait — here’s the hard part: correlation only measures linear relationships. Two variables could be tightly linked in a perfect U-shape but have a correlation near zero. Let’s see that:
import numpy as np
import pandas as pd
# A perfect U-shaped relationship
x = np.linspace(-3, 3, 100)
y = x**2 # perfect parabola
df = pd.DataFrame({'x': x, 'y': y})
print(f"Correlation for U-shaped data: {df['x'].corr(df['y']):.4f}")
Output:
Correlation for U-shaped data: -0.0000
The correlation is essentially zero, even though y is perfectly determined by x. This result indicates that there is no linear relationship between the variables, despite the clear non-linear pattern.
So When Do You Use Covariance? When Correlation?
Now that you understand both tools, here’s a simple decision framework.
Use covariance when:
- You need the scale information (e.g., Principal Component Analysis — PCA uses the covariance matrix, and the eigenvectors depend on the original units)
- You’re working in a single dataset with consistent units and care about the magnitude of joint movement (e.g., portfolio variance in finance)
Use correlation when:
- You’re comparing relationships across datasets with different units (e.g., comparing how temperature affects ice cream sales vs. how temperature affects hot chocolate sales)
- You’re reporting to an audience that needs an intuitive “strength” metric
- You want to subset features (correlation matrices are easier to threshold with a simple cutoff like |r| > 0.7)
Let’s see a simulation that shows the difference:
import numpy as np
import pandas as pd
# Generate data with no relationship
np.random.seed(42)
x_noise = np.random.randn(100)
y_noise = np.random.randn(100)
df_noise = pd.DataFrame({'x': x_noise, 'y': y_noise})
print("No relationship:")
print(f" Covariance: {df_noise['x'].cov(df_noise['y']):.4f}")
print(f" Correlation: {df_noise['x'].corr(df_noise['y']):.4f}")
# Generate data with strong linear relationship
x_strong = np.linspace(0, 10, 100)
y_strong = 2 * x_strong + np.random.randn(100) * 0.5 # strong signal + small noise
df_strong = pd.DataFrame({'x': x_strong, 'y': y_strong})
print("\nStrong linear relationship:")
print(f" Covariance: {df_strong['x'].cov(df_strong['y']):.4f}")
print(f" Correlation: {df_strong['x'].corr(df_strong['y']):.4f}")
Output:
No relationship:
Covariance: -0.0942
Correlation: -0.0942
Strong linear relationship:
Covariance: 8.4172
Correlation: 0.9989
Notice that in the first case, both covariance and correlation are near zero — no relationship. In the second, both are large in magnitude. The correlation (0.999) immediately tells you it’s a near-perfect linear relationship, while the covariance (8.42) remains hard to interpret without knowing the units.
The hidden gotcha: outliers
Covariance is much more sensitive to outliers than correlation. Here’s why: an outlier creates a huge deviation from the mean, and when you multiply two huge deviations, you get an enormous product that dominates the average. Correlation dampens this effect by dividing by the standard deviations.
import numpy as np
import pandas as pd
# Clean data
x_clean = np.array([2, 4, 6, 8, 10])
y_clean = np.array([5, 9, 13, 15, 19])
# Add one outlier
x_out = np.append(x_clean, 100)
y_out = np.append(y_clean, 5) # outlier doesn't follow the pattern
df_clean = pd.DataFrame({'x': x_clean, 'y': y_clean})
df_out = pd.DataFrame({'x': x_out, 'y': y_out})
print("Without outlier:")
print(f" Covariance: {df_clean['x'].cov(df_clean['y']):.4f}")
print(f" Correlation: {df_clean['x'].corr(df_clean['y']):.4f}")
print("\nWith outlier:")
print(f" Covariance: {df_out['x'].cov(df_out['y']):.4f}")
print(f" Correlation: {df_out['x'].corr(df_out['y']):.4f}")
Output:
Without outlier:
Covariance: 17.0000
Correlation: 0.9948
With outlier:
Covariance: -99.2000
Correlation: -0.4557
That one outlier doesn’t just weaken the relationship — it flips the sign of both numbers. Covariance goes from +17 to -99.2, and correlation goes from +0.99 (nearly perfect positive) to -0.46 (a moderate negative relationship). A single point far off the pattern (x=100 paired with a low y=5) is enough to make covariance and correlation tell the opposite story from the other five points alone.
Real-world example: PCA on stock returns
Here’s a practical decision you might face. In finance, you might run PCA on stock returns to find the main drivers of risk. If you use the covariance matrix, the first principal component will be dominated by the most volatile stock (the one with the largest variance). If you use the correlation matrix, each stock gets equal weight regardless of its volatility.
Neither is “right” — it’s a design choice. If you care about absolute risk (dollars at risk), use covariance. If you care about relative movements (which stocks move together regardless of their volatility), use correlation.
Summary: What Each One Actually Tells You
Let’s bring it all home with a plain-English cheat sheet:
| Metric | Question it answers | Scale | Range |
|---|---|---|---|
| Covariance | Do they move together? By how much (in original units)? | Dependent on units | Unbounded |
| Correlation | Do they move together? How strong is the linear relationship? | Unit-free | -1 to +1 |
The biggest takeaway: If you’re tempted to compare covariances across datasets with different units, stop — use correlation instead. If you need to preserve the scale for modeling (PCA, portfolio optimization), covariance is fine.
Remember: covariance tells you direction and raw magnitude (scale-dependent). Correlation tells you direction and standardized strength (unit-free).
Check Your Understanding
Remember: Define covariance and correlation in one sentence each.
Understand: Explain why covariance changes when you convert data from centimeters to meters, but correlation doesn’t.
Apply: Given a dataset with house prices (in dollars) and square footage (in square feet), which metric would you use to compare the strength of this relationship to the relationship between house prices and number of bedrooms? Why?
Analyze: You compute covariance = 500 and correlation = 0.3 for two variables. What does this tell you about the relationship? What might explain the large covariance but modest correlation?
Evaluate: A colleague says, “I always use covariance because it gives me the “real” number.” Critique this statement. When would it be appropriate, and when would it be misleading?
Create: Write a Python function that takes two arrays and returns both the covariance and correlation. Then write a second function that converts a covariance to a correlation given the standard deviations of the two variables.
Apply What You Learned is for Supporter and Insider subscribers.
Subscribe to unlock the exercises on this post.
See plansRelated articles
- Python Engineering Under review
Converting Between Numpy Arrays Pandas Dataframes
Before we dive into conversions, let's get clear on what each structure *is*. Think of them as tools with distinct personalities.
- Python Engineering Under review
Degrees Of Freedom The Statistics Concept Nobody E
Let's start with a simple analogy. Imagine you're building a three-legged stool. You can choose the length of the first two legs freely — any height you want. But once those two are set, the third leg's length is forced.
- Python Engineering Under review
Data Science For Managers What To Ask For And What
Before your team builds a fancy model, ask this: *What would a simple rule achieve?*
- Python Engineering Under review
Connecting Python To Mysql And Postgresql The Real
Think of connecting to a database like getting around a new city. You have two main options:
Looking for something else?
Search every article by title, summary or topic.