6 votes

Trouver le span où la condition est Vraie en utilisant NumPy

Imaginez que j'ai un tableau numpy et que je dois trouver les plages où cette condition est vraie. Par exemple, j'ai le tableau suivant dans lequel j'essaie de trouver les plages où les éléments sont supérieurs à 1 :

[0, 0, 0, 2, 2, 0, 2, 2, 2, 0]

Je devrais trouver les indices (début, fin) :

(3, 5) 
(6, 9)

La chose la plus rapide que j'ai réussi à implémenter est de créer un tableau booléen de :

truth = data > threshold

puis de parcourir le tableau en utilisant numpy.argmin et numpy.argmax pour trouver les positions de début et de fin.

    pos = 0
    truth = container[RATIO,:] > threshold

    while pos < len(truth):
        start = numpy.argmax(truth[pos:]) + pos + offset
        end = numpy.argmin(truth[start:]) + start  + offset
        if not truth[start]:#rien de plus
            break
        if start == end:#va jusqu'à la fin
            end = len(truth)
        pos = end

Mais ceci a été trop lent pour les milliards de positions dans mes tableaux et le fait que les plages que je trouve sont en général juste quelques positions d'affilée. Est-ce que quelqu'un connaît un moyen plus rapide de trouver ces plages ?

7voto

Andy Hayden Points 38010

Comment est une façon. Premièrement, prenez le tableau booléen que vous avez :

In [11]: a
Out[11]: array([0, 0, 0, 2, 2, 0, 2, 2, 2, 0])

In [12]: a1 = a > 1

Décalez-le d'une position vers la gauche (pour obtenir l'état suivant à chaque indice) en utilisant roll:

In [13]: a1_rshifted = np.roll(a1, 1)

In [14]: starts = a1 & ~a1_rshifted  # c'est True mais le précédent ne l'était pas

In [15]: ends = ~a1 & a1_rshifted

Là où il y a des valeurs non nulles est le début de chaque lot True (ou, respectivement, lot de fin) :

In [16]: np.nonzero(starts)[0], np.nonzero(ends)[0]
Out[16]: (array([3, 6]), array([5, 9]))

Et en les regroupant ensemble :

In [17]: zip(np.nonzero(starts)[0], np.nonzero(ends)[0])
Out[17]: [(3, 5), (6, 9)]

3voto

djspoulter Points 31

Si vous avez accès à la bibliothèque scipy :

Vous pouvez utiliser scipy.ndimage.measurements.label pour identifier les régions de valeur non nulle. Il renvoie un tableau où la valeur de chaque élément est l'identifiant d'une plage dans le tableau d'origine.

Vous pouvez ensuite utiliser scipy.ndimage.measurements.find_objects pour retourner les tranches dont vous auriez besoin pour extraire ces plages. Vous pouvez accéder directement aux valeurs de début / fin de ces tranches.

Dans votre exemple :

import numpy
from scipy.ndimage.measurements import label, find_objects

data = numpy.array([0, 0, 0, 2, 2, 0, 2, 2, 2, 0])

labels, number_of_regions = label(data)
ranges = find_objects(labels)

for identified_range in ranges:
    print(identified_range[0].start, identified_range[0].stop)

Vous devriez voir :

3 5
6 9

J'espère que cela vous aidera !

0voto

Tatarize Points 490

Au lieu d'utiliser np.roll qui a un sérieux problème de roulement. Il est préférable de simplement faire deux copies. Une avec un remplissage à droite et l'autre avec un remplissage à gauche. J'avais besoin de cela pour une image.

        a = np.pad(im, ((0, 0), (0, 1)), constant_values=0)
        b = np.pad(im, ((0, 0), (1, 0)), constant_values=0)
        starts = a & ~b
        ends = ~a & b
        sx, sy = np.nonzero(starts)
        ex, ey = np.nonzero(ends)

La réponse approuvée a un problème, en ce sens que, si vous terminez par True, cela se retrouve au début et perturbe les valeurs. Vous voulez vraiment remplir ces valeurs avec des 0.

Ensuite, vous recherchez les transitions de 1 -> 0 et les transitions de 0 -> 1 et les mettez dans le format nécessaire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X