Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Redimmensionner une très grosse matrice numpy

+3 votes

J'ai une matrice numpy carrée qui fait plusieurs millions de points. J'aimerais la redimmensionner en 5000 x 5000 afin de la plotter avec imshow() de pylab en faisant une moyenne des points. Les solutions que j'ai trouvé utilise reshape() et mean() de numpy mais nécessitent que le facteur entre les deux matrices soit un entier (ce qui n'est pas le cas chez moi), de cette manière :

def rebin(a, shape):
   sh = shape[0],a.shape[0]//shape[0],shape[1],a.shape[1]//shape[1]
   return a.reshape(sh).mean(-1).mean(1)

J'ai tenté des interpolations avec scipy (notamment zoom) mais je me retrouve avec des Memory Error. Je me demande si il n'y a pas moyen d'avoir une solution peu gourmande en mémoire qui me permettrait d'obtenir cette matrice.

demandé 28-Oct-2015 par Jev (388 points)

je ne suis pas sûr de bien capter. Tu veux faire une réduction de matrice basée sur la moyenne de tes points? la moyenne serait faite sur combien de points à la fois?

4 Réponses

0 votes

Pour redimensionner une matrice avec numpy, il suffit d'utiliser la fonction :
numpy.matrix.resize

Change shape and size of array in-place.

un exemple tiré de la doc :

>>> a = np.array([[0, 1], [2, 3]], order='C')
>>> a.resize((2, 1))
>>> a
array([[0],
       [1]])

voici un aperçu de ce que ça fait réelement :

This reallocates space for the data area if necessary.

Only contiguous arrays (data elements consecutive in memory) can be
resized.

The purpose of the reference count check is to make sure you do not
use this array as a buffer for another Python object and then
reallocate the memory. However, reference counts can increase in other
ways so if you are sure that you have not shared the memory for this
array with another Python object, then you may safely set refcheck to
False.

Lectures : SO1, SO2, students.mimuw

répondu 29-Oct-2015 par boblinux (3,092 points)

Il me semble que le PO cherche à "zoomer" sa matrice, en d'autre terme faire une interpolation, pas de brutalement virer une partie des données.

+1 vote

Pas sûr que ça résolve le Memory Error, mais il y a cette discussion qui est intéressante : en plus du zoom, il y a imresize et sinon ils parlent d'aller voir du côté scikit-image.

A mon avis, le problème du Memory Error vient de numpy et du fait qu'il crée des tableaux intermédiaires à chaque opération. Un moyen d'éviter ça serait décrire soi-même l'algorithme complet avec toutes les boucles et de le compiler avec cython ou numba.

répondu 30-Oct-2015 par bubulle (2,248 points)
+1 vote

traite ton array avec a.resize in place au lieu de dans une fonction, tu éviteras une copie pour le return.
utilise mean sans argument, tu économises un vecteur.

Mais, tu es en train de te poser une mauvaise question : ce que tu veux c'est faire une image pixélisée, non ?

pourquoi ne pas simplement taper dans l'array avec un slice double a[0:50,0:50] et tu ajustes les indexes astucieusement pour faire une moyenne glissante.

et pouf.

répondu 6-Nov-2015 par Atrament (258 points)

Ca ne fait pas avancer la question mais j'ai deux remarques :

  • arr.reshape() ne fait pas de copie quand ce n'est pas nécessaire mais renvoie une vue à la place.
  • mean() sans argument renvoie la moyenne de tous les éléments du tableau "mis à plat". Je doute que ce soit utile dans son exemple.

Je pensais plutôt à un truc comme ça :

def thumbplot(array,height,width):
    """plots a w*h thumnbnail of array"""
    thumb = np.zeros((height,width))
    h_a, w_a = array.shape
    pixh, pixw = h_a // height, w_a // width
    for l, c in product(range(height), range(width)):
        thumb[l,c] = array[pixh*l:pixh*(l+1), 
                           pixw*c:pixh*(c+1)].mean()
    plt.imshow(thumb,interpolation='none')

c'est l'avant-dernière ligne logique qui me semble pas mal : pas de copie de données, puisque on utilise des slices, donc valide même avec des références d'array en array de partout.

Oui ça me parait bien ça.
Il faut faire plus de boucle mais c'est le jeu habituel cpu vs mémoire.

ça a pas l'air très cher en CPU sur un array de random de 1000*1000 sur ma bécane.

du coup je fais une vraie réponse avec ça.

+1 vote

Essaie avec qq chose dans ce goût là :

def thumbplot(array,height,width):
    """plots a w*h thumnbnail of array"""
    thumb = np.zeros((height,width))
    h_a, w_a = array.shape
    pixh, pixw = h_a // height, w_a // width
    for l, c in product(range(height), range(width)):
        thumb[l,c] = array[pixh*l:pixh*(l+1), 
                           pixw*c:pixh*(c+1)].mean()
    plt.imshow(thumb,interpolation='none')

Il n'y a qu'un parcours du gros array bloc par bloc, c'est forcément un peu coûteux en CPU (comme le pointe très justement @bubulle) mais ça a l'air de fonctionner.

voir application sommaire à https://gist.github.com/AlbericC/c66dc9e05441846e392a

répondu 7-Nov-2015 par Atrament (258 points)
...