This one is, or rather, should be, fairly simple. I have a list of tuples of XY positions, and am trying to pull out angles relative to the origin from it. However, unlike atan2 / friends, I want it to be continuous, in other words not wrapping. I know (and am assuming) that the differences between successive XY coordinates are small, and that there is a minimum radius. (In other words, it should pick the multiple of \$2\pi\$ that makes the difference between the previous angle and the current angle the smallest.)

Currently, this is the relevant snippet:

thetas = [math.atan2(coords[0][1], coords[0][0])]
prev = thetas[0]
for index in range(1, len(coords)):
    coord = coords[index]
    theta = math.atan2(coord[1], coord[0]) + (prev // (2*math.pi)) * (2 * math.pi)
    while theta - prev > math.pi:
        theta -= 2*math.pi
    while prev - theta > math.pi:
        theta += 2*math.pi
    assert abs(theta - prev) < math.pi/2
    prev = theta

However, it's slow, and I doubt it's Pythonic. It's also doing a lot of "magic", for lack of a better word. Any suggestions for improvement?

Line profiler results here.

  • 1
    \$\begingroup\$ a simple question is why does it need to be continuous? \$\endgroup\$ – ratchet freak Feb 16 '15 at 16:28
  • \$\begingroup\$ How can you assert that abs(theta - prev) < math.pi/2? Is there some guarantee that the XY coordinates are clustered in some quadrant? \$\endgroup\$ – 200_success Feb 16 '15 at 18:21
  • 1
    \$\begingroup\$ Quote: "I know (and am assuming) that the differences between successive XY coordinates are small." I should have specified that I know that there is a minimum radius as well. \$\endgroup\$ – TLW Feb 16 '15 at 19:11
  • \$\begingroup\$ @ratchetfreak, For plotting purposes, mainly. It's far easier to read a graph of something versus theta when theta doesn't wrap. \$\endgroup\$ – TLW Feb 16 '15 at 19:13
  • for index in range() is almost always wrong. Consider

    prev = coords[0]
    for coord in coords[1:]:
        theta = ...
        prev = coord
  • Instead of computing theta directly, compute delta - this way you may actually take an advantage of small differences:

    prev = (1, 0)    # Initial vector is along the X axis
    theta = 0.0
    for coord in coords[]:
        delta = angle_between(coord, prev)
        theta += delta
        prev = coord

    Computing delta is simply an application of a cross-product formula:

    def angle_between(curr, prev):
        return asin(cross_product(curr, prev) / (norm(curr) / norm(prev))
| improve this answer | |
  • \$\begingroup\$ Three problems with this. First off, that angle-between function doesn't make sense as written. (I'm working in 2D, for one thing, and for another arcsine isn't defined for vectors.) Secondly, that will end up with compounding rounding errors. Third, that method of indexing (coords[1:]) will make a temporary copy of the entire array. That's not exactly optimal for performance. Is there no good way to avoid that copy? \$\endgroup\$ – TLW Feb 17 '15 at 3:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.