← Research

// article

Averaging the Wind

The wind blew north. The average said east-southeast.

November 7, 2025 Article
Polar wind rose of 524 north-blowing grid cells: cyan wedges pile against due north on both sides of the 0/360 seam, a cyan circular-mean arrow shoots straight up into the cluster at 9 degrees while a red naive-mean arrow points off into empty space at 105 degrees, labelled as a 96-degree error

A compass direction is an angle, and the arithmetic mean does not know the number line wraps. That single fact is the whole article. Here is what it costs you.

I pulled 524 grid cells where the wind was blowing roughly north, every one within 30 degrees of due north on either side of the compass seam at 0/360. Then I took the arithmetic mean of the direction column. It came back 104.67 degrees. East-southeast. No wind blows that way anywhere in this slice. The circular mean, the honest answer, is 8.61 degrees: due north, where the arrows actually point. That is a 96-degree gap between what the calculator returned and what the data does. The cyan arrow is the truth. The red arrow is df['dir'].mean().

The data

A sampled global wind grid from vega-datasets: 4,800 cells, each with a longitude, a latitude, a direction in whole degrees (dir, 0 to 360), a binned direction category (dirCat), and a speed. One caveat up front. This is a gridded snapshot of a wind field, not a stack of station observations. The cells sit on a regular lat/lon mesh, so neighboring points are spatially correlated and are not independent draws from anything. I am using it to demonstrate the arithmetic, not to make a climatological claim. Speeds run from a near-dead 0.01 up to 12.18, mean 4.54, and the dataset does not label units, so I leave them unitless. The math, not the meteorology, is the point.

Why the average lies

A compass direction is an angle. 359 degrees and 1 degree are two degrees apart, not 358. The arithmetic mean has no idea the number line wraps, so it averages 359 and 1 to 180, which points the opposite way from both. Picture two winds, one coming from 350 and one from 10, both leaning north. The naive formula stands them back to back and reports their midpoint as 180, due south, the one bearing neither wind is doing.

The fix is to stop treating directions as numbers and treat them as vectors. Turn each bearing into a unit vector, (cos θ, sin θ), average the x-components and the y-components separately, then read the resulting angle back with atan2. The wraparound takes care of itself, because 359 and 1 degrees land right next to each other on the circle. Three lines: mean the sines, mean the cosines, atan2(mean_sin, mean_cos).

On those 524 north-blowing cells the difference is the entire point. The histogram below shows them piled against both edges, a clump near 0 and another near 360, with nothing between. The naive mean in red falls into the empty middle at 105 degrees. The circular mean in cyan lands at 9 degrees, dead in the cluster.

Histogram of the 524 north-blowing cells on a 0-to-360 degree axis: two tall clumps against the left and right edges with an empty span between them, a red rule marking the naive mean at 105 degrees sitting in the gap and a cyan rule marking the circular mean at 9 degrees inside the left clump

Laid on a straight axis the trap is plain: the two clumps sit at the far edges and the naive mean splits the difference, dropping into the empty span between them. The polar version sits at the top of this piece, where the cyan arrow shoots into the wedge the data occupies and the red arrow points off toward 105 where nothing blows. The mean of a wrapped quantity is not a smaller version of the right answer. It is a different number entirely.

Resultant length, or how to know if there even is an average

The vector trick hands you a second number for free. When you average those unit vectors, the length of the result, call it R, tells you how concentrated the directions are. Think of each bearing as a person pulling on a rope from the center of a circle. All pulling the same way, the knot flies to the rim and R goes to 1. Pulling evenly in all directions, the knot stays put and R goes to 0, and the mean direction is meaningless noise.

For those 524 north cells R is 0.9631. Tightly aligned. They really are all blowing north, and the mean direction means something. Run the same calculation over all 4,800 cells in the grid and R collapses to 0.133. That is a field with no prevailing direction. The circular mean of the whole grid is 129.68 degrees, but with R that low I would not trust that arrow to point anywhere in particular. It is the average of winds going every which way across the globe, and they mostly cancel. The naive mean of the full grid, for the record, is 154.87, off from the circular value by 25 degrees: still wrong, just less spectacularly than the north slice.

So R does double duty. It is the dial that tells you whether the mean direction you just computed is worth reporting. R near 1, report it. R near 0, the average direction is a fiction.

Polar wind rose of all 4,800 grid cells: grey wedges of roughly even height spread around the entire compass with no dominant lobe, and a faint cyan circular-mean arrow at 130 degrees labelled as untrustworthy because R is only 0.13

The wind rose for the full field shows why R is so low: the bars spread across most of the compass with no single dominant lobe. The circular-mean arrow is drawn on there too, but at R = 0.13 it is decoration, not a conclusion.

Does speed care about direction?

Last question, and the one I expected to be a shrug. I grouped mean speed by the dirCat sector and the spread is wider than I would have guessed. The fastest sector is 120 degrees, averaging 8.21. The slowest is 60 degrees at 2.30. That is a 5.9 gap, the fast sector blowing nearly four times harder than the slow one, against an overall mean of 4.54.

Lollipop chart of mean wind speed by direction sector, each sector a stem rising or falling from a dashed overall-mean reference line; the 120-degree sector stands tallest in cyan at 8.21 and the 60-degree sector dips lowest in cyan at 2.30

I want to be careful here. This is not a physical law of the atmosphere. It is a property of this gridded snapshot, where the fast cells happen to live in regions oriented around 120 degrees and the calm cells around 60. With spatially correlated grid points and no time dimension, I cannot tell you whether it holds tomorrow. But within the snapshot, direction and speed are plainly not independent. The calm sectors and the fast sectors point different ways.

The takeaway is narrower than “wind is interesting.” The moment your numbers wrap around, whether compass bearings, clock times, phases, or angles of any kind, df['col'].mean() is a bug, not an estimate. The grid here turned a 96-degree error into something you can see. Mean the sines and cosines instead, and check R before you believe the answer.