Skip to content

Improve frame comparison #3182

Closed
Closed

Description

@astrofrog

At the moment, one cannot compare whether frames are equivalent:

In [1]: from astropy.coordinates import FK5

In [2]: FK5(equinox='J2000') == FK5(equinox='J2010')
Out[2]: False

In [3]: FK5(equinox='J2000') == FK5(equinox='J2000')
Out[3]: False

It would be nice to have a way to check that all frame attributes are the same. Now == may be ambiguous because for frames with data this is not as well defined (are we comparing the frame attributes or the data? what if the data is a vector?).

Maybe the solution is to disable straight-off comparisons like the ones above but to instead have a method or function to check if two frames are the same (excluding data)?

cc @eteq @taldcroft

Activity

  1. embray commented on Dec 8, 2014

    @embray
    Member

    I think it should just compare the frame attributes, not the data. But that's just me.

  2. taldcroft commented on Dec 9, 2014

    @taldcroft
    Member

    At some level strict equality seems like relatively unambiguous even with data. For reference there is #2154 and #2650.

    What about doing the following:

    • Compare all defined attributes, including the frame class and representation. All must be exactly equal.
    • If neither frame has data, then return a boolean with the equality of attributes.
    • If one frame has data and other doesn't then return False.
    • If both frames have data and attributes are equal, then return a np boolean array (or scalar) with the per-element equality.

    As we have discussed, if you have data then in most cases what you really want is a coordinate allclose, but for the == operator perhaps this definition works as a catch-all in all cases. It is possible (likely) that if you start from a coordinate and do some round-trip transformation that the result may fail at equality, but that is just the way it goes with numerical equality of floats.

    We might relax a bit and change the representation of the right side to match that of the left before comparing data. I think that would allow for equality where the only change between left and right was a representation change. Again, if small floating point diffs make for inequality that's not our problem.

  3. adrn commented on Sep 19, 2017

    @adrn
    Member

    As @taldcroft mentioned, I think this is a duplicate of #2154, but I think the discussion here is more relevant now, so I'm going to close the other issue. I'm working on this now. It turns out we don't have a way to compare representations, so I need to start there...

  4. taldcroft commented on Sep 21, 2017

    @taldcroft
    Member

    Looking at this again, maybe we also want a coordinates.allclose function with sensible defaults? Then equality is strict equality but there is a way to allow allclose matching of the data (again require strict equality of frame attributes). Also allclose might make the representation of left and right be the same while == would not.

    👍 on implementing frame comparison since it is a pre-requisite for a conservative implementation of coordinate setitem. 😄 In other words, first thing you do for setting is make sure the left and right side frames are exactly the same, then setting the data from right will be fine (need to clear any caches of course, etc).

  5. taldcroft commented on Dec 21, 2017

    @taldcroft
    Member

    I ran into this problem (or a slight variant) again today:

    In [10]: sc = SkyCoord([1,2], [3,4], unit='deg')
    In [11]: sc2 = SkyCoord([1,2], [3,4], unit='deg')
    In [12]: sc == sc2
    Out[12]: False
    

    This really should return a bool array of True since the objects are the same down to the bit level.
    What about this:

    In [32]: def equal(sc1, sc2):
        ...:     if not sc1.is_equivalent_frame(sc2):
        ...:         return False
        ...:     if sc1.representation is not sc2.representation:  # Will be representation_type..
        ...:         return False
        ...:     if sc1.shape != sc2.shape:
        ...:         return False  # Maybe raise ValueError corresponding to future numpy behavior
        ...:     eq = np.ones(shape=sc1.shape, dtype=bool)
        ...:     for comp in sc.data.components:
        ...:         eq &= getattr(sc.data, comp) == getattr(sc2.data, comp)
        ...:     return eq
    

    [EDIT] - I wrote this thinking SkyCoord, but I think this can be in baseframe just like this.

  6. taldcroft commented on Feb 7, 2018

    @taldcroft
    Member

    In #6888 , I implemented the code from the previous comment as a new function skycoord_equal that is used for testing. Any interest in bringing something like this into coordinates proper?

    I have no idea if this works for coordinates with velocities. I do think that a strict equality comparison is a useful and well-defined thing for coordinates, basically the objects are the same at the bit level. This is different from an isclose kind of comparison.

    @eteq @mhvk @adrn

  7. mhvk commented on Feb 7, 2018

    @mhvk
    Contributor

    I'm in favour of testing for equality checking - as you say, it is well-defined. I do think we need to think a bit about what to return if frames or representations do not match. Here, python and numpy seem to be diverging, sadly (though also understandably), with python never allowing == to fail. In particular, if, e.g., the shapes can be broadcast but frames and/or representation types do not match, should one return an array of False, or just False, or raise an error?

  8. astrojuanlu commented on Sep 3, 2018

    @astrojuanlu
    Member

    Notice that is_equivalent_frame is not always correct, see #7793.

  9. changed the title [-]Feature request: frame comparison[/-] [+]Improve frame comparison[/+] on Dec 3, 2018
  10. astrojuanlu commented on Feb 10, 2019

    @astrojuanlu
    Member

    To update my previous comment, this still stands but #7793 was fixed.

  11. taldcroft commented on Oct 25, 2019

    @taldcroft
    Member

    I (AGAIN) ran into this problem (or a slight variant)

    In [10]: sc = SkyCoord([1,2], [3,4], unit='deg')
    In [11]: sc2 = SkyCoord([1,2], [3,4], unit='deg')
    In [12]: sc == sc2
    Out[12]: False
    

    This really should return a bool array of True since the objects are the same down to the bit level, not a scalar Python False.

    Would there be any interest (for 4.0) in issuing a FutureWarning that SkyCoord (or Frame) __eq__ will be fixed to respect the shape of the object? This idea has been tossed about for at least 5 years now, and the problem of testing for equality at the bit level is not insurmountable (I think??).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions