Определение положения медианы массива, содержащего в основном нули

У меня очень большой 1d-массив, большинство элементов которого равны нулю, в то время как ненулевые элементы сгруппированы вокруг нескольких островов, разделенных множеством нулей: (вот уменьшенная версия этого для целей MWE)

In [1]: import numpy as np

In [2]: A=np.array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,6,20,14,10,5,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,5,5,18,18,16,14,10,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,3,6,16,4,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])

Я хочу найти медиану и ее положение (даже приблизительно) с точки зрения индекса, соответствующего среднему значению каждого острова. Неудивительно, что я получаю ноль, чего я не хочу:

In [3]: np.median(A)
Out[3]: 0.0

In [4]: np.argsort(A)[len(A)//2]
Out[4]: 12

В случае одного острова ненулевых элементов, чтобы обойти это предостережение и удовлетворить мое требование, чтобы только ненулевые элементы были физически значимыми, я сначала удаляю все нули, а затем беру медианное значение оставшихся элементов:

In [5]: masks = np.where(A>0)
In [6]: A[masks]
Out[6]: array([ 1,  3,  6, 20, 14, 10,  5,  1])

На этот раз я правильно получил медиану нового массива, однако позиция (индекс) не будет правильной, поскольку она очевидна и также указывается в комментариях как некорректно определенная математически.

In [7]: np.median(A[masks])
Out[7]: 5.5

In [8]: np.argsort(A[masks])[len(A[masks])//2]
Out[8]: 2

В соответствии с этим приближением я знаю, что реальная медиана находится в третьем индексе модифицированного массива, но я хотел бы перевести ее обратно в формат исходного массива, где позиция (индекс) медианы должна быть где-то посередине. первого острова ненулевых элементов, соответствующих большему индексу (где все индексы нулей подсчитываются правильно). В комментариях также даны ответы на два предложения по определению положения медианы с учетом одного острова ненулевых элементов посреди моря нулей. Но что, если таких островов несколько? Как можно было вычислить индекс, соответствующий медиане каждого острова в контексте исходного массива гистограмм, где все нули считаются?

Мне интересно, есть ли какой-нибудь простой способ вычислить положение медианы в таких массивах из многих нулей. Если нет, что еще я должен добавить к своим строкам кода, чтобы сделать это возможным после того, как я узнал позицию в измененном массиве? Ваша помощь очень ценится.


person Ash    schedule 09.04.2018    source источник
comment
Как насчет np.median(masks)? Какой ожидаемый п / п? Как насчет случая, когда имеется более одного островка ненулевых значений?   -  person Divakar    schedule 09.04.2018
comment
Дорогой Уоррен, ты прав. Но меня интересуют только ненулевые элементы. Но единственная информация, которая мне может понадобиться, - это найти эту медиану в исходном массиве с нулями. Думаю, комментарий Дивакара можно было бы рассматривать как ответ.   -  person Ash    schedule 09.04.2018
comment
Дорогой Мирадуло, это даст мне что-то вдвое больше, чем хотелось бы.   -  person Ash    schedule 09.04.2018
comment
Это была отличная идея, и мне все еще нужно реализовать Дивакар. В нескольких случаях возможно иметь более одного острова. В таких случаях мне понадобится позиция каждого острова. Что я знаю точно, так это то, что любое количество этих островов будет локализовано в космосе.   -  person Ash    schedule 09.04.2018
comment
Так что обобщенный ответ, включающий в себя предостережение о наличии более чем одного острова, разделенного множеством нулей, был бы идеальным для моей проблемы.   -  person Ash    schedule 09.04.2018
comment
Положение медианы не является точно определенной величиной. Медиана [3, 8, 9, 7, 2, 1, 9, 7] равна 7, но как бы вы определили его положение?   -  person Warren Weckesser    schedule 09.04.2018
comment
Рассмотрите возможность добавления MWE для покрытия таких случаев нескольких островов с ожидаемым o / p.   -  person Divakar    schedule 09.04.2018
comment
Является ли A гистограммой (т. Е. Массивом значений), и хотите ли вы получить медианное значение подсчитываемых значений? Тогда, возможно, сработает что-то вроде np.searchsorted(A.cumsum(), A.sum()/2).   -  person Warren Weckesser    schedule 09.04.2018
comment
Это правда, Уоррен, мои фактические ненулевые элементы находятся в диапазоне от 10 ^ {{- 3}} $ до 10 ^ {{7}} $. Эти данные хороши тем, что я априори знаю, что моя статистика горная, то есть узкие островки с шипами. Итак, что я сделаю в этих случаях, так это то, что я должен выбрать то, которое ближе всего к среднему значению, поскольку я знаю, что мои распределения являются гауссовскими.   -  person Ash    schedule 09.04.2018
comment
И да, я бы рассмотрел это как второй ответ, поскольку A на самом деле представляет собой дискретную гистограмму со многими ячейками, и добавление одного элемента в такой большой массив не имеет большого значения.   -  person Ash    schedule 09.04.2018


Ответы (1)


Основываясь на комментарии «A на самом деле представляет собой дискретную гистограмму с множеством интервалов», я думаю, что вам нужно среднее значение подсчитываемых значений. Если A является целочисленным массивом значений, то точная (но, вероятно, очень неэффективная, если у вас есть значения до 1e7) формула для медианы:

np.median(np.repeat(np.arange(len(A)), A))  # Do not use if A contains very large values!

В качестве альтернативы вы можете использовать

np.searchsorted(A.cumsum(), 0.5*A.sum())

которая будет целой частью медианы.

Например:

In [157]: A
Out[157]: 
array([ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  3,
        6, 20, 14, 10,  5,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0])

In [158]: np.median(np.repeat(np.arange(len(A)), A))
Out[158]: 35.5

In [159]: np.searchsorted(A.cumsum(), 0.5*A.sum())
Out[159]: 35

Другой пример:

In [167]: B
Out[167]: 
array([  0,   0,   0,   1, 100,  21,   8,   3,   2,   1,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0])

In [168]: np.median(np.repeat(np.arange(len(B)), B))
Out[168]: 4.0

In [169]: np.searchsorted(B.cumsum(), 0.5*B.sum())
Out[169]: 4
person Warren Weckesser    schedule 09.04.2018
comment
Спасибо, дорогой Уоррен, за исчерпывающий ответ. Я только что отредактировал вопрос, чтобы он включал более одного острова ненулевых элементов. Не могли бы вы изменить свой ответ, чтобы отразить это? - person Ash; 09.04.2018
comment
Это изменение еще больше усложняет проблему. Теперь вы хотите идентифицировать кластеры и вычислить медианное значение для каждого кластера. В моем ответе нет простой модификации, которая делает это. Этот ответ можно применить к каждому кластеру после того, как вы их определили. Но определение кластеров должно стать новым вопросом SO (конечно, после того, как вы попытались заставить его работать самостоятельно). - person Warren Weckesser; 09.04.2018