2013-05-02 4 views
27

У меня есть 2D-массив, содержащий целые числа (как положительные, так и отрицательные). Каждая строка представляет значения со временем для определенного пространственного сайта, тогда как каждый столбец представляет значения для различных пространственных сайтов в течение заданного времени.Самый эффективный способ поиска в массиве numpy

Так, если массив подобен:

1 3 4 2 2 7 
5 2 2 1 4 1 
3 3 2 2 1 1 

Результат должен быть

1 3 2 2 2 1 

Обратите внимание, что при наличии нескольких значений для режима, любой один (выбирается случайным образом) может быть установлен в качестве режима ,

Я могу перебирать режим поиска столбцов по одному, но я надеялся, что numpy может иметь встроенную функцию для этого. Или если есть хитрость, чтобы найти это эффективно без цикла.

+0

Существует http://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.mstats.mode.html и ответ здесь: http://stackoverflow.com/questions/6252280/find -the-most-often-number-in-a-numpy-vector – tom10

+1

@ tom10: Вы имеете в виду [scipy.stats.mode()] (http: //docs.scipy.org/doc/scipy/reference/generated/scipy.stats.mode.html # scipy.stats.mode), правильно? Другой, похоже, выводит маску в маске. – fgb

+0

@fgb: справа, спасибо за исправление (и +1 за ваш ответ). – tom10

ответ

52

Проверить scipy.stats.mode() (вдохновленный @ tom10 замечания):

import numpy as np 
from scipy import stats 

a = np.array([[1, 3, 4, 2, 2, 7], 
       [5, 2, 2, 1, 4, 1], 
       [3, 3, 2, 2, 1, 1]]) 

m = stats.mode(a) 
print(m) 

Выход:

ModeResult(mode=array([[1, 3, 2, 2, 1, 1]]), count=array([[1, 2, 2, 2, 1, 2]])) 

Как вы можете видеть, он возвращается как режим, а также имеет значение. Вы можете выбрать режимы непосредственно через m[0]:

print(m[0]) 

Выход:

[[1 3 2 2 1 1]] 
+3

Так что numpy сам по себе не поддерживает такую ​​функциональность? – Nik

+1

По-видимому, нет, но [реализация scipy зависит только от numpy] (http://stackoverflow.com/questions/12399107/alternative-to-scipy-mode-function-in-numpy), поэтому вы можете просто скопировать этот код в свой собственной функции. – fgb

+5

Просто обратите внимание, что для людей, которые смотрят на это в будущем: вам нужно явно импортировать файл scipy.stats, он не включается, когда вы просто выполняете 'import scipy'. – ffledgling

10

Это сложная проблема, так как там не так много там режим расчета по оси. Решение является прямым для 1-D массивов, где numpy.bincount удобно, а также numpy.unique с return_counts arg как True. Наиболее распространенная n-мерная функция, которую я вижу, - scipy.stats.mode, хотя она и не слишком медленная, особенно для больших массивов со множеством уникальных значений. В качестве решения, я разработал эту функцию, и использовать его в большой степени:

import numpy 

def mode(ndarray, axis=0): 
    # Check inputs 
    ndarray = numpy.asarray(ndarray) 
    ndim = ndarray.ndim 
    if ndarray.size == 1: 
     return (ndarray[0], 1) 
    elif ndarray.size == 0: 
     raise Exception('Cannot compute mode on empty array') 
    try: 
     axis = range(ndarray.ndim)[axis] 
    except: 
     raise Exception('Axis "{}" incompatible with the {}-dimension array'.format(axis, ndim)) 

    # If array is 1-D and numpy version is > 1.9 numpy.unique will suffice 
    if all([ndim == 1, 
      int(numpy.__version__.split('.')[0]) >= 1, 
      int(numpy.__version__.split('.')[1]) >= 9]): 
     modals, counts = numpy.unique(ndarray, return_counts=True) 
     index = numpy.argmax(counts) 
     return modals[index], counts[index] 

    # Sort array 
    sort = numpy.sort(ndarray, axis=axis) 
    # Create array to transpose along the axis and get padding shape 
    transpose = numpy.roll(numpy.arange(ndim)[::-1], axis) 
    shape = list(sort.shape) 
    shape[axis] = 1 
    # Create a boolean array along strides of unique values 
    strides = numpy.concatenate([numpy.zeros(shape=shape, dtype='bool'), 
           numpy.diff(sort, axis=axis) == 0, 
           numpy.zeros(shape=shape, dtype='bool')], 
           axis=axis).transpose(transpose).ravel() 
    # Count the stride lengths 
    counts = numpy.cumsum(strides) 
    counts[~strides] = numpy.concatenate([[0], numpy.diff(counts[~strides])]) 
    counts[strides] = 0 
    # Get shape of padded counts and slice to return to the original shape 
    shape = numpy.array(sort.shape) 
    shape[axis] += 1 
    shape = shape[transpose] 
    slices = [slice(None)] * ndim 
    slices[axis] = slice(1, None) 
    # Reshape and compute final counts 
    counts = counts.reshape(shape).transpose(transpose)[slices] + 1 

    # Find maximum counts and return modals/counts 
    slices = [slice(None, i) for i in sort.shape] 
    del slices[axis] 
    index = numpy.ogrid[slices] 
    index.insert(axis, numpy.argmax(counts, axis=axis)) 
    return sort[index], counts[index] 

Результат:

In [2]: a = numpy.array([[1, 3, 4, 2, 2, 7], 
         [5, 2, 2, 1, 4, 1], 
         [3, 3, 2, 2, 1, 1]]) 

In [3]: mode(a) 
Out[3]: (array([1, 3, 2, 2, 1, 1]), array([1, 2, 2, 2, 1, 2])) 

Некоторые тесты:

In [4]: import scipy.stats 

In [5]: a = numpy.random.randint(1,10,(1000,1000)) 

In [6]: %timeit scipy.stats.mode(a) 
10 loops, best of 3: 41.6 ms per loop 

In [7]: %timeit mode(a) 
10 loops, best of 3: 46.7 ms per loop 

In [8]: a = numpy.random.randint(1,500,(1000,1000)) 

In [9]: %timeit scipy.stats.mode(a) 
1 loops, best of 3: 1.01 s per loop 

In [10]: %timeit mode(a) 
10 loops, best of 3: 80 ms per loop 

In [11]: a = numpy.random.random((200,200)) 

In [12]: %timeit scipy.stats.mode(a) 
1 loops, best of 3: 3.26 s per loop 

In [13]: %timeit mode(a) 
1000 loops, best of 3: 1.75 ms per loop 

EDIT: Прилагается больше фона и модифицированный подход к большей экономии памяти

3

Расширение на this method, appl чтобы найти режим данных, где вам может понадобиться индекс фактического массива, чтобы увидеть, как далеко находится значение от центра распределения.

(_, idx, counts) = np.unique(a, return_index=True, return_counts=True) 
index = idx[np.argmax(counts)] 
mode = a[index] 

Помните, чтобы отменить режим, когда LEN (np.argmax (отсчеты))> 1, а также для проверки, если это на самом деле представитель центрального распределения ваших данных вы можете проверить, попадает ли он внутри стандартного отклонения интервал.

Смежные вопросы