Многомерные массивы Python - наиболее эффективный способ подсчета количества ненулевых записей - PullRequest
6 голосов
/ 28 ноября 2010

Привет субботней веселой ночи,

Я сижу на питоне и мне это очень нравится.

Предположим, у меня есть массив python:

x = [1, 0, 0, 1, 3]

Какой самый быстрый способ подсчитать все ненулевые элементы в списке (ответ: 3)? Также я хотел бы сделать это без для циклов, если это возможно - наиболее лаконично и кратко, скажем что-то концептуально, например

[counter += 1 for y in x if y > 0]

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

for p in range(BINS):
    for q in range(BINS):
        for r in range(BINS):
            if (mat3D[p][q][r] > 0): some_feature_set_count += 1

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

Идеи, кто-нибудь?

Ответы [ 7 ]

12 голосов
/ 28 ноября 2010

Для одномерного корпуса:

sum(1 for i in x if i)

Для многомерного случая вы можете вложить:

sum(sum(1 for i in row if i) for row in rows)

или сделать все это в рамках одной конструкции:

sum(1 for row in rows
      for i in row if i)
3 голосов
/ 28 ноября 2010

Если вы используете numpy, как предполагает тот факт, что вы используете многомерные массивы в Python, следующее похоже на ответ @ Marcelo, но немного чище:

>>> a = numpy.array([[1,2,3,0],[0,4,2,0]])
>>> sum(1 for i in a.flat if i)
5
>>>
2 голосов
/ 28 ноября 2010

Если вы используете numpy, а ваш 3D-массив представляет собой numpy-массив, этот однострочник сделает свое дело:

numpy.where(your_array_name != 0, 1, 0).sum()

пример:

In [23]: import numpy

In [24]: a = numpy.array([ [[0, 1, 2], [0, 0, 7], [9, 2, 0]], [[0, 0, 0], [1, 4, 6], [9, 0, 3]], [[1, 3, 2], [3, 4, 0], [1, 7, 9]] ])

In [25]: numpy.where(a != 0, 1, 0).sum()
Out[25]: 18
0 голосов
/ 28 ноября 2010

Использование цепочки для уменьшения поиска в массиве:

from itertools import chain
BINS = [[[2,2,2],[0,0,0],[1,2,0]],
        [[1,0,0],[0,0,2],[1,2,0]],
        [[0,0,0],[1,1,1],[1,3,0]]]
sum(1 for c in chain.from_iterable(chain.from_iterable(BINS)) if c > 0)
14

Я не делал никаких проверок производительности по этому вопросу. Но он не использует значительную память. Обратите внимание, что он использует выражение генератора, а не понимание списка. Добавление синтаксиса [список понимания] создаст массив для суммирования вместо подачи одного числа за раз.

0 голосов
/ 28 ноября 2010

Я бы немного изменил ответ Марсело на следующий:

len([x for x in my_list if x != 0])

Сумма (), приведенная выше, обманула меня на секунду, так как я думал, что он получает общее значение вместо счетчика, пока я не увидел 1, зависший в начале. Я предпочел бы быть явным с len ().

0 голосов
/ 28 ноября 2010
def zeros(n):
    return len(filter(lambda x:type(x)==int and x!=0,n))+sum(map(zeros,filter(lambda x:type(x)==list,n)))

Не могу сказать, действительно ли это самый быстрый способ, но он рекурсивный и работает с N-мерными списками.

zeros([1,2,3,4,0,[1,2,3,0,[1,2,3,0,0,0]]]) => 10
0 голосов
/ 28 ноября 2010

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

def sum(li):
  s = 0
  for l in li:
    if isinstance(l, list):
      s += sum(l)
    elif l:
      s += 1
  return s
...