medians for degree measurements
Steve Howell
showell30 at yahoo.com
Sat Jan 23 02:03:05 EST 2010
On Jan 22, 10:29 pm, Nobody <nob... at nowhere.com> wrote:
> On Fri, 22 Jan 2010 16:09:03 -0800, Steve Howell wrote:
> > I just saw the thread for medians, and it reminded me of a problem
> > that I need to solve. We are writing some Python software for
> > sailing, and we need to detect when we've departed from the median
> > heading on the leg. Calculating arithmetic medians is
> > straightforward, but compass bearings add a twist.
>
> > The numerical median of 1, 2, 3, 4, 5, 6, 359 is 4. But for
> > navigational purposes you would actually order the numbers 359, 1, 2,
> > 3, 4, 5, 6, so the desired median heading of the boat is actually 3.
>
> > Of course, you could express 359 better as -1 degrees to north, then
> > the sequence would be -1, 1, 2, 3, 4, 5, and 6. And you'd be good.
>
> > But that trick does not generalize if you go south instead, as you
> > have similar issues with -179, 174, 175, 176, 177, 178, and 179.
>
> > Has anybody solved this in Python, either for compass bearings or a
> > different domain? I can think of kind of a brute force solution where
> > you keep rotating the sequence until the endpoints are closest
> > together mod 360, but I wonder if there is something more elegant.
>
> First, there isn't always a solution; what would you consider to be the
> median of [0, 90, 180, 270]?
>
You probably posted before seeing my recent post. I agree that the
problem is ill-defined for certain cases.
> In the case where the bearings are clustered, one approach is to
> convert each bearing from polar to cartesian coordinates, compute the
> centroid, then convert back to polar coordinates, i.e.:
>
> from math import degrees, radians, sin, cos, atan2
>
> def mean(bearings):
> x = sum(sin(radians(a)) for a in bearings)
> y = sum(cos(radians(a)) for a in bearings)
> return degrees(atan2(x, y))
>
> Then, subtract the mean from each bearing, coerce all angles into the
> range -180..+180, calculate the median, add the mean, coerce back to
> 0..360.
>
> def median(bearings):
> m = mean(bearings)
> bearings = [(a - m + 180) % 360 - 180 for a in bearings]
> bearings.sort()
> median = bearings[len(bearings) / 2]
> median += m
> median %= 360
> return median
Yep, that's exactly the solution I'm leaning toward now.
More information about the Python-list
mailing list