Conversion de dtypes numpy en types python natifs

238

Si j'ai un dtype numpy, comment le convertir automatiquement en son type de données python le plus proche? Par exemple,

numpy.float32 -> "python float"
numpy.float64 -> "python float"
numpy.uint32  -> "python int"
numpy.int16   -> "python int"

Je pourrais essayer de trouver un mappage de tous ces cas, mais numpy fournit-il un moyen automatique de convertir ses dtypes en types de python natifs les plus proches possibles? Ce mappage n'a pas besoin d'être exhaustif, mais il doit convertir les dtypes communs qui ont un analogue de python proche. Je pense que cela se produit déjà quelque part dans numpy.

conradlee
la source

Réponses:

325

Utilisez val.item()pour convertir la plupart des valeurs NumPy en un type Python natif:

import numpy as np

# for example, numpy.float32 -> python float
val = np.float32(0)
pyval = val.item()
print(type(pyval))         # <class 'float'>

# and similar...
type(np.float64(0).item()) # <class 'float'>
type(np.uint32(0).item())  # <class 'long'>
type(np.int16(0).item())   # <class 'int'>
type(np.cfloat(0).item())  # <class 'complex'>
type(np.datetime64(0, 'D').item())  # <class 'datetime.date'>
type(np.datetime64('2001-01-01 00:00:00').item())  # <class 'datetime.datetime'>
type(np.timedelta64(0, 'D').item()) # <class 'datetime.timedelta'>
...

(Une autre méthode est np.asscalar(val), cependant, elle est obsolète depuis NumPy 1.16).


Pour les curieux, pour construire une table de conversions de scalaires de tableau NumPy pour votre système:

for name in dir(np):
    obj = getattr(np, name)
    if hasattr(obj, 'dtype'):
        try:
            if 'time' in name:
                npn = obj(0, 'D')
            else:
                npn = obj(0)
            nat = npn.item()
            print('{0} ({1!r}) -> {2}'.format(name, npn.dtype.char, type(nat)))
        except:
            pass

Il y a quelques types numpy sans équivalent Python natif sur certains systèmes, y compris: clongdouble, clongfloat, complex192, complex256, float128, longcomplex, longdoubleet longfloat. Ceux-ci doivent être convertis en leur équivalent NumPy le plus proche avant utilisation .item().

Mike T
la source
J'utilise des pandas (0.23.0). Au moins pour cette version, np.str n'a pas la méthode .item () donc la seule façon que j'ai vue était d'envelopper .item () dans un bloc try.
Robert Lugg
3
@RobertLugg np.strn'est pas un type Numpy, c'est-à-dire np.str is strqu'il s'agit simplement d'un alias à un type Python standard. Même avec np.float, np.int, np.bool, np.complexet np.object. Les types Numpy ont une fin _, par exemple np.str_.
Mike T
Je comprends. Donc, le problème est "ce serait bien si" je pouvais faire: np.float64(0).item()et aussi np.float(0).item(). En d'autres termes, pour les cas où l'on sait quoi faire, prenez en charge la .item()méthode même si elle renvoie simplement la même valeur. De cette façon, je pourrais appliquer .item()sur des scalaires beaucoup plus engourdis sans boîtier spécial. En l'état, des concepts apparemment parallèles diffèrent en raison de la mise en œuvre sous-jacente. Je comprends parfaitement pourquoi cela a été fait. Mais c'est une gêne pour l'utilisateur de la bibliothèque.
Robert Lugg
45

me suis retrouvé avec un ensemble mixte de types numpy et de python standard. comme tous les types numpy dérivent de numpy.generic, voici comment vous pouvez tout convertir en types standard python:

if isinstance(obj, numpy.generic):
    return numpy.asscalar(obj)
tm_lv
la source
5
Comme le notent les réponses acceptées , NumPy 1.16 a déconseillé la np.asscalar()méthode. Pourquoi? Probablement sans raison évidente. Malgré une décennie de stabilité relative, l'API NumPy est désormais une cible mobile instable nécessitant une maintenance constante des applications en aval. Au moins, ils nous ont laissé la item()méthode ... pour l'instant.
Cecil Curry
La méthode asscalar s'est dépréciée depuis la v1.6 de numpy
Eswar
Vous pouvez facilement remplacer la réponse par if isinstance(o, numpy.generic): return o.item() raise TypeErroret elle se transforme à nouveau en une réponse non obsolète: D
Buggy
19

Si vous voulez convertir (numpy.array OU scalp numpy OU type natif OU numpy.darray) en type natif, vous pouvez simplement faire:

converted_value = getattr(value, "tolist", lambda: value)()

tolist convertira votre scalaire ou tableau en type natif python. La fonction lambda par défaut prend en charge le cas où la valeur est déjà native.

v.thorey
la source
2
Approche la plus propre pour les types mixtes (natifs et non natifs), bravo! Et pour ceux qui se demandent, oui, tolist ne renvoie qu'une seule valeur (le scalaire) lorsque vous l'appelez sur une seule valeur, pas une liste comme vous pourriez le penser. Il convient de noter que la façon la plus simple d'écrire le lambda est lambda: valueque nous ne voulons aucune entrée.
fgblomqvist
getattr+ le tolistcombo n'est pas seulement universel, mais même vectorisé! (unlinke .item ())
mirekphd
11

Que diriez-vous:

In [51]: dict([(d, type(np.zeros(1,d).tolist()[0])) for d in (np.float32,np.float64,np.uint32, np.int16)])
Out[51]: 
{<type 'numpy.int16'>: <type 'int'>,
 <type 'numpy.uint32'>: <type 'long'>,
 <type 'numpy.float32'>: <type 'float'>,
 <type 'numpy.float64'>: <type 'float'>}
unutbu
la source
1
Je mentionne ce type de solution comme possibilité à la fin de ma question. Mais je recherche une solution systématique plutôt qu'une solution codée en dur qui ne couvre que quelques-uns des cas. Par exemple, si numpy ajoute plus de dtypes à l'avenir, votre solution s'arrêtera. Je ne suis donc pas satisfait de cette solution.
conradlee
Le nombre de dtypes possibles est illimité. Considérez np.dtype('mint8')pour tout entier positif m. Il ne peut pas y avoir de cartographie exhaustive. (Je ne crois pas non plus qu'il existe une fonction intégrée pour effectuer cette conversion pour vous. Je peux me tromper, mais je ne pense pas :))
unutbu
2
Python mappe les dtypes numpy aux types python, je ne sais pas comment, mais j'aimerais utiliser la méthode qu'ils utilisent. Je pense que cela doit arriver pour permettre, par exemple, la multiplication (et d'autres opérations) entre les types dt numpy et les types python. Je suppose que leur méthode ne cartographie pas de manière exhaustive tous les types de numpy possibles, mais au moins les plus courants où cela a du sens.
conradlee
Cela ne fonctionne pas de manière cohérente: >>> print([numpy.asscalar(x) for x in numpy.linspace(1.0, 0.0, 21)]) [1.0, 0.95, 0.9, 0.85, 0.8, 0.75, 0.7, 0.6499999999999999, 0.6, 0.55, 0.5, 0.44999999999999996, 0.3999999999999999, 0.35, 0.29999999999999993, 0.25, 0.19999999999999996, 0.1499999999999999, 0.09999999999999998, 0.04999999999999993, 0.0]comme vous le voyez, toutes les valeurs n'ont pas été correctement converties.
Alex F
suite à mon commentaire précédent, étrangement celui-ci fonctionne, bien que j'aurais bien que vous deviez mettre le tour sur le type natif Python au lieu du type natif Numpy: >>> print([numpy.asscalar(round(x,2)) for x in numpy.linspace(1.0, 0.0, 21)]) [1.0, 0.95, 0.9, 0.85, 0.8, 0.75, 0.7, 0.65, 0.6, 0.55, 0.5, 0.45, 0.4, 0.35, 0.3, 0.25, 0.2, 0.15, 0.1, 0.05, 0.0]
Alex F
9

tolist()est une approche plus générale pour y parvenir. Il fonctionne dans n'importe quel type primitif et également dans des tableaux ou des matrices.

Je ne donne pas réellement de liste s'il est appelé à partir de types primitifs:

numpy == 1.15.2

>>> import numpy as np

>>> np_float = np.float64(1.23)
>>> print(type(np_float), np_float)
<class 'numpy.float64'> 1.23

>>> listed_np_float = np_float.tolist()
>>> print(type(listed_np_float), listed_np_float)
<class 'float'> 1.23

>>> np_array = np.array([[1,2,3.], [4,5,6.]])
>>> print(type(np_array), np_array)
<class 'numpy.ndarray'> [[1. 2. 3.]
 [4. 5. 6.]]

>>> listed_np_array = np_array.tolist()
>>> print(type(listed_np_array), listed_np_array)
<class 'list'> [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]
Carlos Santos
la source
8

Vous pouvez également appeler la item()méthode de l'objet que vous souhaitez convertir:

>>> from numpy import float32, uint32
>>> type(float32(0).item())
<type 'float'>
>>> type(uint32(0).item())
<type 'long'>
Aryeh Leib Taurog
la source
6

Je pense que vous pouvez simplement écrire une fonction de conversion de type général comme ceci:

import numpy as np

def get_type_convert(np_type):
   convert_type = type(np.zeros(1,np_type).tolist()[0])
   return (np_type, convert_type)

print get_type_convert(np.float32)
>> (<type 'numpy.float32'>, <type 'float'>)

print get_type_convert(np.float64)
>> (<type 'numpy.float64'>, <type 'float'>)

Cela signifie qu'il n'y a pas de listes fixes et que votre code évoluera avec plus de types.

Matt Alcock
la source
Savez-vous où se trouve le code source pour la partie de la méthode tolist () qui mappe les types numpy aux types python? J'ai jeté un coup d'œil rapide mais je ne l'ai pas trouvé.
conradlee
C'est un peu un hack ce que je fais est de générer un numpy.ndarrayavec 1 zéro en utilisant zeros()et d'appeler la ndarrays tolist()fonction à convertir en types natifs. Une fois dans les types natifs, je demande le type et le retourne. tolist()est une fonction dundarray
Matt Alcock
Oui, je vois que --- ça fonctionne pour ce que je veux et j'ai donc accepté votre solution. Mais je me demande comment tolist () fait son travail pour décider dans quel type diffuser, et je ne sais pas comment trouver la source.
conradlee
numpy.sourceforge.net/numdoc/HTML/numdoc.htm#pgfId-36588 est l'endroit où la fonction est documentée. Je pensais qu'inspect pourrait être en mesure d'aider à trouver plus d'informations mais pas de joie. L'étape suivante, j'ai essayé de cloner github.com/numpy/numpy.git et d'exécuter grep -r 'tolist' numpy. (toujours en cours, numpy est énorme!)
Matt Alcock
3

numpy contient ces informations dans un mappage exposées de typeDictmanière à ce que vous puissiez faire quelque chose comme ci-dessous:

>>> import __builtin__
>>> import numpy as np
>>> {v: k for k, v in np.typeDict.items() if k in dir(__builtin__)}
{numpy.object_: 'object',
 numpy.bool_: 'bool',
 numpy.string_: 'str',
 numpy.unicode_: 'unicode',
 numpy.int64: 'int',
 numpy.float64: 'float',
 numpy.complex128: 'complex'}

Si vous voulez les types python réels plutôt que leurs noms, vous pouvez faire:

>>> {v: getattr(__builtin__, k) for k, v in np.typeDict.items() if k in vars(__builtin__)}
{numpy.object_: object,
 numpy.bool_: bool,
 numpy.string_: str,
 numpy.unicode_: unicode,
 numpy.int64: int,
 numpy.float64: float,
 numpy.complex128: complex}
Meitham
la source
3

Désolé de venir tard en partie, mais je cherchais un problème de conversion numpy.float64en Python normal floatuniquement. J'ai vu 3 façons de le faire:

  1. npValue.item()
  2. npValue.astype(float)
  3. float(npValue)

Voici les horaires pertinents d'IPython:

In [1]: import numpy as np

In [2]: aa = np.random.uniform(0, 1, 1000000)

In [3]: %timeit map(float, aa)
10 loops, best of 3: 117 ms per loop

In [4]: %timeit map(lambda x: x.astype(float), aa)
1 loop, best of 3: 780 ms per loop

In [5]: %timeit map(lambda x: x.item(), aa)
1 loop, best of 3: 475 ms per loop

Cela float(npValue)semble beaucoup plus rapide.

gt6989b
la source
1

Mon approche est un peu énergique, mais semble bien jouer dans tous les cas:

def type_np2py(dtype=None, arr=None):
    '''Return the closest python type for a given numpy dtype'''

    if ((dtype is None and arr is None) or
        (dtype is not None and arr is not None)):
        raise ValueError(
            "Provide either keyword argument `dtype` or `arr`: a numpy dtype or a numpy array.")

    if dtype is None:
        dtype = arr.dtype

    #1) Make a single-entry numpy array of the same dtype
    #2) force the array into a python 'object' dtype
    #3) the array entry should now be the closest python type
    single_entry = np.empty([1], dtype=dtype).astype(object)

    return type(single_entry[0])

Usage:

>>> type_np2py(int)
<class 'int'>

>>> type_np2py(np.int)
<class 'int'>

>>> type_np2py(str)
<class 'str'>

>>> type_np2py(arr=np.array(['hello']))
<class 'str'>

>>> type_np2py(arr=np.array([1,2,3]))
<class 'int'>

>>> type_np2py(arr=np.array([1.,2.,3.]))
<class 'float'>
Simon Streicher
la source
Je vois que c'est essentiellement la même chose que la réponse de Matt Alcock.
Simon Streicher
1

Une note latérale sur les scalaires de tableau pour ceux qui n'ont pas besoin de conversion automatique et connaissent le type numpy de la valeur:

Les scalaires de tableau diffèrent des scalaires Python, mais pour la plupart, ils peuvent être utilisés de manière interchangeable (la principale exception concerne les versions de Python antérieures à v2.x, où les scalaires de tableau entier ne peuvent pas servir d'index pour les listes et les tuples). Il existe certaines exceptions, telles que lorsque le code nécessite des attributs très spécifiques d'un scalaire ou lorsqu'il vérifie spécifiquement si une valeur est un scalaire Python. Généralement, les problèmes sont facilement résolus en convertissant explicitement les scalaires de tableau en scalaires Python, en utilisant la fonction de type Python correspondante (par exemple, int, float, complex, str, unicode).

La source

Ainsi, dans la plupart des cas, la conversion peut ne pas être nécessaire du tout et le scalaire du tableau peut être utilisé directement. L'effet doit être identique à l'utilisation du scalaire Python:

>>> np.issubdtype(np.int64, int)
True
>>> np.int64(0) == 0
True
>>> np.issubdtype(np.float64, float)
True
>>> np.float64(1.1) == 1.1
True

Mais si, pour une raison quelconque, la conversion explicite est nécessaire, l'utilisation de la fonction intégrée Python correspondante est la solution. Comme indiqué dans l'autre réponse, il est également plus rapide que la item()méthode scalaire de tableau .

wombatonfire
la source
0

Traduisez le ndarray entier à la place d'un objet de données unitaire:

def trans(data):
"""
translate numpy.int/float into python native data type
"""
result = []
for i in data.index:
    # i = data.index[0]
    d0 = data.iloc[i].values
    d = []
    for j in d0:
        if 'int' in str(type(j)):
            res = j.item() if 'item' in dir(j) else j
        elif 'float' in str(type(j)):
            res = j.item() if 'item' in dir(j) else j
        else:
            res = j
        d.append(res)
    d = tuple(d)
    result.append(d)
result = tuple(result)
return result

Cependant, cela prend quelques minutes lors de la manipulation de grandes trames de données. Je recherche également une solution plus efficace. J'espère une meilleure réponse.

Qinhong Ma
la source