np.searchsorted only for 1D arrays.
I have a lexicographically sorted 2D array, meaning that 0-th row is sorted, then for same values of 0-th row corresponding elements of 1-th row are sorted too, for same values of 1-th row values of 2-th row are sorted too. In other words tuples consisting of columns are sorted.
I have some other 2D array with tuples-columns that need to be inserted into first 2D array into correct positions of columns. For 1D case np.searchsorted was usually used in order to find correct positions.
But for 2D array is there an alternative to np.searchsorted? Something analagous to how np.lexsort is a 2D alternative for 1D np.argsort.
If no such function then can be this functionality implemented in an efficient way using existing numpy functions?
I am interested in efficient solutions for arrays of any dtype including np.object_.
One naive way to handle any dtype case would be to convert each column of both arrays to 1D array (or tuple) and then store these columns as another 1D array of dtype = np.object_. Maybe it is not that naive and could be even fast especially if columns are quite high.
                        
I've created several more advanced strategies.
Also simple strategy using
tupleslike in another my answer is implemented.Timings of all solutions are measured.
Most of strategies are using
np.searchsortedas underlying engine. To implement these advanced strategies a special wrapping class_CmpIxwas used in order to provide custom comparison function (__lt__) fornp.searchsortedcall.py.tuplesstrategy just converts all columns to tuples and stores them as numpy 1D array ofnp.object_dtype and then doing regular searchsorting.py.zipuses python's zip for lazily doing same task.np.lexsortstrategy just usesnp.lexsortin order to compare two columns lexicographically.np.nonzerousesnp.flatnonzero(a != b)expression.cmp_numbauses ahead of time compiled numba code inside_CmpIxwrapper for fast lexicographically lazy comparing of two provided elements.np.searchsorteduses standard numpy's function but is measured for 1D case only.numbastrategy whole search algorithm is implemented from scratch using Numba engine, algorithm is based on binary search. There is_pyand_nmvariants of this algorithm,_nmis much faster as it uses Numba compiler, while_pyis same algorithm but un-compiled. Also there is_sortedflavor which does extra optimization of array to be inserted is already sorted.view1d- methods suggested by @MadPhysicist in this answer. Commented out them in code, because they were returning incorrect answers for most of tests for all key lengths >1, probably due to some problems of raw viewing into array.Try it online!
outputs:
As it appears from timings
numba_nmimplementation is the fastest, it outperforms next fastest (py.ziporpy.tuples_cached) by15-100xtimes. And it has comparable speed (1.85xslower) to standardnp.searchsortedfor 1D case. Also it appeared to be that_sortedflavor doesn't improve situation (i.e. using information about inserted array being sorted).cmp_numbamethod that is machine-code compiled appears to be around1.5xtimes faster on average thanpy.zipthat does same algorithm but in pure python. Due to average maximum equal-key depth being around15-18elements numba doesn't gain much speedup here. If depth was hundreds then numba code would probably have a huge speedup.py.tuples_cachedstrategy is faster thanpy.zipfor the case of key length<= 100.Also it appears to be that
np.lexsortis in fact very slow, either it is not optimized for the case of just two columns, or it spends time doing preprocessing like splitting rows into list, or it does non-lazy lexicographical comparison, the last case is probably the real reason as lexsort slows down with key length grow.Strategy
np.nonzerois also non-lazy hence works slow too, and slows down with key length growth (but slows down not that fast asnp.lexsortdoes).Timings above may be not precise, because my CPU slows down cores frequency 2-2.3 times at random times whenever it is overheated, and it overheats often because it is a powerful CPU inside laptop.