[Numpy-discussion] A faster median (Wirth's method)

Dag Sverre Seljebotn dagss at student.matnat.uio.no
Tue Sep 1 15:51:58 EDT 2009


Sturla Molden wrote:
> We recently has a discussion regarding an optimization of NumPy's median 
> to average O(n) complexity. After some searching, I found out there is a 
> selection algorithm competitive in speed with Hoare's quick select. It 
> has the advantage of being a lot simpler to implement. In plain Python:
> 
> import numpy as np
> 
> def wirthselect(array, k):
>    
>     """ Niklaus Wirth's selection algortithm """
> 
>     a = np.ascontiguousarray(array)
>     if (a is array): a = a.copy()
> 
>     l = 0
>     m = a.shape[0] - 1
>     while l < m:
>         x = a[k]
>         i = l
>         j = m
>         while 1:
>             while a[i] < x: i += 1
>             while x < a[j]: j -= 1
>             if i <= j:
>                 tmp = a[i]
>                 a[i] = a[j]
>                 a[j] = tmp
>                 i += 1
>                 j -= 1
>             if i > j: break
>         if j < k: l = i
>         if k < i: m = j
> 
>     return a
> 
> 
> Now, the median can be obtained in average O(n) time as:
> 
> 
> def median(x):
> 
>     """ median in average O(n) time """
> 
>     n = x.shape[0]
>     k = n >> 1
>     s = wirthselect(x, k)
>     if n & 1:
>         return s[k]
>     else:
>         return 0.5*(s[k]+s[:k].max())
> 
> 
> The beauty of this is that Wirth select is extremely easy to migrate to 
> Cython:
> 
> 
> import numpy
> ctypedef numpy.double_t T # or whatever
> 
> def wirthselect(numpy.ndarray[T, ndim=1] array, int k):
>    
>     cdef int i, j, l, m
>     cdef T x, tmp
>     cdef T *a
> 
>     _array = np.ascontiguousarray(array)
>     if (_array is array): _array = _array.copy()
>     a = <T *> _array.data
> 
>     l = 0
>     m = <int> a.shape[0] - 1

Nitpick: This will fail on large arrays. I guess numpy.npy_intp is the 
right type to use in this case?

-- 
Dag Sverre



More information about the NumPy-Discussion mailing list