Repository navigation
Description
What is the problem this feature will solve?
Currentlly there are at least 3 different implementations of sorting columns and none of them seem quite right.
Table.argsort()makes a numpy structured array using.as_array()and calls argsort. So for aTimecolumn this will make an object column with each row as a newTimeobject. Bad. AndTable.argsortexposes akindkwarg which will cause an exception when sorting the table on aTimecolumn.astropy/astropy/table/table.py
Lines 3444 to 3445 in aa2c05d
data = self.as_array(names=keys) else: table/groups.pyusesrepresent_mixins_as_columnsto flatten the sorting table. This is one way to do it but forTimethis gives columns withjd1andjd2instead of using theinfo.get_sortable_arrays()machinery.astropy/astropy/table/groups.py
Line 79 in aa2c05d
if not table_index and isinstance(table_keys, Table): table/index.pydoes something different as well, making a list of columns to sort lexically, including a special-case forTimethat repeates theget_sortable_arrayscode.astropy/astropy/table/index.py
Line 103 in aa2c05d
new_columns = []
It seems like the ideal way to argsort a list of table columns is to call info.get_sortable_arrays on each one and assemble all the results into a list of arrays which are then passed to np.lexsort. But one problem is that this (once again) would render moot the kind argument of Table.argsort (and Table.sort).
Describe the desired outcome
More unified and consistent approach to sorting table columns.
Additional context
Performance and API changes are potential concerns.
Activity
taldcroft commentedon Jun 14, 2023
One question for @mhvk - the current implementation of
Time.info.get_sortable_arrays()is rigorously correct but relatively slow. So two questions:jd1andjd2values to conform to theday_frac()scheme? I think so, but I don't know if there could be corner cases otherwise.get_sortable_arrays()simply returnjd1andjd2?mhvk commentedon Jun 14, 2023
@taldcroft - I think the internal control of
jd1andjd2is pretty good these days, but I'm not sure I would completely count on it - especially with rounding to even. Regardless, probably good to keep that as a separate issue - what is there currently is the same as used fortime.argsort()so it won't be any slower (and of course is far faster than making an object array!).taldcroft commentedon Jun 14, 2023
@mhvk - Ahah, never mind on the Time argsort. It turns out to be essentially as fast as it can be. I was being misled by the implementation in
get_sortable_arrayswhich gets the remainder by making an entirely new Time object and doing a Time difference (and I'm assuming this is pretty slow):The implementation in
Time.argsortinstead just does pure numpy floating point operations:Should we use that floating point version in
get_sortable_arrays?mhvk commentedon Jun 14, 2023
Yes, we should! See https://github.com/astropy/astropy/pull/11907/files#r662320174 where we discussed why there was a call to
self.__class__also for.argsort- we just forgot to updateget_sortable_arrays(maybe we should useget_sortable_arraysin.argsort??)taldcroft commentedon Oct 15, 2023
Looking at the numpy.lexsort docs it states that
Structured arrays are sorted lexically by argsort. I cannot find any more specific statement in the argsort docs, but it seems likely that thekindargument does not fully apply if passing in a structured array (as is currently done forTable.argsort). More likely it is just using the defaultstablesort under the hood (and regardless of our current API docs it has always been doing this).So I'd be inclined to make our API reflect this reality and just issue a warning if
kindis specified as anything other thanNone,stable, ormergesort. All of these are basically what's going on under the hood.mhvk commentedon Oct 15, 2023
👍 on simply noting what our API does. I think the larger point of this issue still stands, though: as you suggest on top, one should use
.info.get_sortable_arraysand then do a lexsort on those.