Python >> Tutoriel Python >  >> Python Tag >> NumPy

numpy crée un tableau du nombre maximum de paires consécutives dans un autre tableau

Une solution au problème par paires consiste à utiliser la fonction np.maximum et le découpage de tableau :

B = np.maximum(A[:-1], A[1:])

Une solution sans boucle consiste à utiliser max sur les fenêtres créées par skimage.util.view_as_windows :

list(map(max, view_as_windows(A, (2,))))
[8, 33, 33, 4, 6]

Exemple copier/coller :

import numpy as np
from skimage.util import view_as_windows

A = np.array([8, 2, 33, 4, 3, 6])

list(map(max, view_as_windows(A, (2,))))

Dans ce Q&A, nous demandons essentiellement des valeurs maximales glissantes. Cela a déjà été exploré - Max dans une fenêtre glissante dans le tableau NumPy. Puisque, nous cherchons à être efficaces, nous pouvons voir plus loin. L'un d'entre eux serait numba et voici deux variantes finales avec lesquelles j'ai fini avec cet effet de levier parallel directive qui booste les performances par rapport à une version sans :

import numpy as np
from numba import njit, prange

@njit(parallel=True)
def numba1(a, W):
    L = len(a)-W+1
    out = np.empty(L, dtype=a.dtype)
    v = np.iinfo(a.dtype).min
    for i in prange(L):
        max1 = v
        for j in range(W):
            cur = a[i + j]
            if cur>max1:
                max1 = cur                
        out[i] = max1
    return out 

@njit(parallel=True)
def numba2(a, W):
    L = len(a)-W+1
    out = np.empty(L, dtype=a.dtype)
    for i in prange(L):
        for j in range(W):
            cur = a[i + j]
            if cur>out[i]:
                out[i] = cur                
    return out 

D'après les questions et réponses liées précédemment, la version équivalente de SciPy serait -

from scipy.ndimage.filters import maximum_filter1d

def scipy_max_filter1d(a, W):
    L = len(a)-W+1
    hW = W//2 # Half window size
    return maximum_filter1d(a,size=W)[hW:hW+L]

Analyse comparative

Autres approches de travail publiées pour la fenêtre générique arg :

from skimage.util import view_as_windows

def rolling(a, window):
    shape = (a.size - window + 1, window)
    strides = (a.itemsize, a.itemsize)
    return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)

# @mathfux's soln
def npmax_strided(a,n):
    return np.max(rolling(a, n), axis=1)

# @Nicolas Gervais's soln
def mapmax_strided(a, W):
    return list(map(max, view_as_windows(a,W)))

cummax = np.maximum.accumulate
def pp(a,w):
    N = a.size//w
    if a.size-w+1 > N*w:
        out = np.empty(a.size-w+1,a.dtype)
        out[:-1] = cummax(a[w*N-1::-1].reshape(N,w),axis=1).ravel()[:w-a.size-1:-1]
        out[-1] = a[w*N:].max()
    else:
        out = cummax(a[w*N-1::-1].reshape(N,w),axis=1).ravel()[:w-a.size-2:-1]
    out[1:N*w-w+1] = np.maximum(out[1:N*w-w+1],
                            cummax(a[w:w*N].reshape(N-1,w),axis=1).ravel())
    out[N*w-w+1:] = np.maximum(out[N*w-w+1:],cummax(a[N*w:]))
    return out

Utilisation de benchit package (peu d'outils d'analyse comparative regroupés ; avertissement :j'en suis l'auteur) pour évaluer les solutions proposées.

import benchit
funcs = [mapmax_strided, npmax_strided, numba1, numba2, scipy_max_filter1d, pp]
in_ = {(n,W):(np.random.randint(0,100,n),W) for n in 10**np.arange(2,6) for W in [2, 10, 20, 50, 100]}
t = benchit.timings(funcs, in_, multivar=True, input_name=['Array-length', 'Window-length'])
t.plot(logx=True, sp_ncols=1, save='timings.png')

Ainsi, les numba sont parfaits pour les tailles de fenêtre inférieures à 10 , auquel il n'y a pas de gagnant clair et sur des tailles de fenêtre plus grandes pp gagne avec SciPy un à la deuxième place.