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]? 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 -- http://mail.python.org/mailman/listinfo/python-list