Conversion de type sur place d'un tableau NumPy

127

Étant donné un tableau NumPy de int32, comment puis-je le convertir float32 en place ? Donc en gros, j'aimerais faire

a = a.astype(numpy.float32)

sans copier le tableau. C'est gros.

La raison de cela est que j'ai deux algorithmes pour le calcul de a. L'un d'eux renvoie un tableau de int32, l'autre renvoie un tableau de float32(et ceci est inhérent aux deux algorithmes différents). Tous les calculs ultérieurs supposent qu'il as'agit d'un tableau de float32.

Actuellement, je fais la conversion dans une fonction C appelée via ctypes. Existe-t-il un moyen de faire cela en Python?

Sven Marnach
la source
Utiliser ctypesc'est autant "en Python" que utiliser numpy. :)
Karl Knechtel
3
@Karl: Non, car je dois coder et compiler moi-même la fonction C.
Sven Marnach
Oh je vois. Je pense que vous êtes probablement SOL sur celui-ci.
Karl Knechtel
3
@Andrew: Il existe de nombreuses façons de savoir s'il renvoie une copie. L'un d'eux est de lire la documentation .
Sven Marnach le
1
En place signifie simplement "utiliser la même mémoire que la matrice d'origine". Jetez un œil à la réponse acceptée - la dernière partie montre que les nouvelles valeurs ont effectivement écrasé la même mémoire.
Sven Marnach

Réponses:

110

Vous pouvez créer une vue avec un dtype différent, puis la copier sur place dans la vue:

import numpy as np
x = np.arange(10, dtype='int32')
y = x.view('float32')
y[:] = x

print(y)

rendements

array([ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.], dtype=float32)

Pour montrer que la conversion était en place, notez que la copie de x vers a été ymodifiée x:

print(x)

impressions

array([         0, 1065353216, 1073741824, 1077936128, 1082130432,
       1084227584, 1086324736, 1088421888, 1090519040, 1091567616])
unutbu
la source
26
Remarque pour ceux (comme moi) qui veulent une conversion entre dtype de taille d'octet différente (par exemple 32 à 16 bits): Cette méthode échoue car y.size <> x.size. Logique une fois que vous y pensez :-(
Juh_
Cette solution fonctionnait-elle pour une ancienne version de Numpy? Quand je le fais np.arange(10, dtype=np.int32).view(np.float32)sur Numpy 1.8.2, j'obtiens array([ 0.00000000e+00, 1.40129846e-45, ... [snip] ... 1.26116862e-44], dtype=float32).
Bas Swinckels
3
@BasSwinckels: C'est attendu. La conversion se produit lorsque vous attribuez y[:] = x.
unutbu
pour clarifier le point soulevé à propos de la taille des éléments (nombre de bits) auquel se réfèrent la réponse originale et @Juh_ par exemple: a = np.arange(10, dtype='float32'); b = a[::-1]; c = np.vstack((a,b)); d = c.view('float64')Ce code prend 10 + 10 float32 et donne 10, plutôt que 20 float64
dcanelhas
1
Ce changement sur place peut économiser sur l'utilisation de la mémoire, mais il est plus lent qu'une simple x.astype(float)conversion. Je ne le recommanderais pas à moins que votre script ne soit à la limite de MemoryError.
hpaulj
158

Mise à jour: cette fonction évite uniquement la copie si elle le peut, ce n'est donc pas la bonne réponse à cette question. La réponse d'unutbu est la bonne.


a = a.astype(numpy.float32, copy=False)

numpy astype a un indicateur de copie. Pourquoi ne devrions-nous pas l'utiliser?

Vikas
la source
14
Une fois ce paramètre pris en charge dans une version de NumPy, nous pourrions bien sûr l'utiliser, mais il n'est actuellement disponible que dans la branche de développement. Et au moment où j'ai posé cette question, elle n'existait pas du tout.
Sven Marnach
2
@SvenMarnach Il est maintenant pris en charge, du moins dans ma version (1.7.1).
PhilMacKay
Il semble fonctionner parfaitement en python3.3 avec la dernière version numpy.
CHM
1
Je trouve que cela est environ 700 fois plus lent que a = a.view ((float, len (a.dtype.names)))
JJ
14
L'indicateur de copie indique seulement que si la modification peut être effectuée sans copie, elle sera effectuée sans copie. Cependant, si le type est différent, il sera toujours copié.
coderforlife
14

Vous pouvez changer le type de tableau sans effectuer de conversion comme ceci:

a.dtype = numpy.float32

mais vous devez d'abord changer tous les entiers en quelque chose qui sera interprété comme le flottant correspondant. Une manière très lente de le faire serait d'utiliser le structmodule de python comme ceci:

def toi(i):
    return struct.unpack('i',struct.pack('f',float(i)))[0]

... appliqué à chaque membre de votre tableau.

Mais peut-être un moyen plus rapide serait d'utiliser les outils ctypeslib de numpy (avec lesquels je ne suis pas familier)

- Éditer -

Étant donné que ctypeslib ne semble pas fonctionner, je procéderais à la conversion avec la numpy.astypeméthode typique , mais je procéderais avec des tailles de bloc qui sont dans les limites de votre mémoire:

a[0:10000] = a[0:10000].astype('float32').view('int32')

... puis changez le dtype une fois terminé.

Voici une fonction qui accomplit la tâche pour tous les dtypes compatibles (ne fonctionne que pour les dtypes avec des éléments de même taille) et gère les tableaux de forme arbitraire avec un contrôle utilisateur sur la taille des blocs:

import numpy

def astype_inplace(a, dtype, blocksize=10000):
    oldtype = a.dtype
    newtype = numpy.dtype(dtype)
    assert oldtype.itemsize is newtype.itemsize
    for idx in xrange(0, a.size, blocksize):
        a.flat[idx:idx + blocksize] = \
            a.flat[idx:idx + blocksize].astype(newtype).view(oldtype)
    a.dtype = newtype

a = numpy.random.randint(100,size=100).reshape((10,10))
print a
astype_inplace(a, 'float32')
print a
Paul
la source
1
Merci pour votre réponse. Honnêtement, je ne pense pas que ce soit très utile pour les grands tableaux - c'est beaucoup trop lent. Réinterpréter les données du tableau sous un type différent est facile - par exemple en appelant a.view(numpy.float32). Le plus dur est de convertir les données. numpy.ctypeslibaide uniquement à réinterpréter les données, pas à les convertir réellement.
Sven Marnach
D'accord. Je n'étais pas sûr des limites de votre mémoire / processeur. Voir ma modification.
Paul
Merci pour la mise à jour. Le faire par blocs est une bonne idée - probablement le meilleur que vous puissiez obtenir avec l'interface NumPy actuelle. Mais dans ce cas, je m'en tiendrai probablement à ma solution ctypes actuelle.
Sven Marnach
-1
import numpy as np
arr_float = np.arange(10, dtype=np.float32)
arr_int = arr_float.view(np.float32)

utilisez view () et le paramètre 'dtype' pour changer le tableau en place.

蒋志强
la source
Le but de la question était de convertir réellement les données en place. Après avoir corrigé le type de la dernière ligne en int, cette réponse ne réinterpréterait que les données existantes sous un type différent, ce qui n'est pas ce que je demandais.
Sven Marnach
Que voulez-vous dire? dtype n'est que l'apparence des données en mémoire, cela fonctionne vraiment. Cependant, dans np.astype, le paramètre 'casting' peut contrôler la méthode de conversion par défaut 'unsafe'.
蒋志强
Ouais, je suis d'accord avec la première réponse acceptée. Cependant arr_.astype (new_dtype, copy = False) renvoie toujours un tableau nouvellement alloué. Comment satisfaire les dtype, orderet les subokexigences de retourner une copie du tableau? Je ne le résous pas.
蒋志强
-5

Utilisez ceci:

In [105]: a
Out[105]: 
array([[15, 30, 88, 31, 33],
       [53, 38, 54, 47, 56],
       [67,  2, 74, 10, 16],
       [86, 33, 15, 51, 32],
       [32, 47, 76, 15, 81]], dtype=int32)

In [106]: float32(a)
Out[106]: 
array([[ 15.,  30.,  88.,  31.,  33.],
       [ 53.,  38.,  54.,  47.,  56.],
       [ 67.,   2.,  74.,  10.,  16.],
       [ 86.,  33.,  15.,  51.,  32.],
       [ 32.,  47.,  76.,  15.,  81.]], dtype=float32)
Ankit Barik
la source
5
Êtes-vous sûr que ce n'est pas une copie? Pouvez-vous vérifier et expliquer un peu plus?
Michele d'Amico
-5

a = np.subtract(a, 0., dtype=np.float32)

MIO
la source
1
Bien que cet extrait de code puisse être la solution, inclure une explication contribue vraiment à améliorer la qualité de votre message. N'oubliez pas que vous répondez à la question aux lecteurs à l'avenir et que ces personnes pourraient ne pas connaître les raisons de votre suggestion de code.
Sebastialonso
Pourquoi cela devrait-il être une conversion en place ? numpy.subtractrenvoie une copie, n'est-ce pas? Seul le nom aréutilisé pour un autre bloc de données ... Veuillez expliquer, si je me trompe à ce sujet.
koffein
Merci de le signaler, il semble que vous ayez raison - une copie est produite.
MIO