Mengubah dtypes numpy ke tipe python asli

238

Jika saya memiliki tipe numpy, bagaimana cara mengubahnya secara otomatis ke tipe data python terdekat? Sebagai contoh,

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

Saya bisa mencoba memetakan semua kasus ini, tetapi apakah numpy menyediakan beberapa cara otomatis untuk mengubah dtypesnya menjadi tipe python asli terdekat? Pemetaan ini tidak perlu lengkap, tetapi harus mengkonversi dtypes umum yang memiliki analog python dekat. Saya pikir ini sudah terjadi di suatu tempat di numpy.

conradlee
sumber

Jawaban:

325

Gunakan val.item()untuk mengonversi sebagian besar nilai NumPy ke tipe Python asli:

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'>
...

(Metode lain adalah np.asscalar(val), namun sudah ditinggalkan sejak NumPy 1.16).


Bagi yang penasaran, untuk membuat tabel konversi skalar array NumPy untuk sistem Anda:

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

Ada beberapa jenis NumPy yang tidak memiliki setara Python asli pada beberapa sistem, termasuk: clongdouble, clongfloat, complex192, complex256, float128, longcomplex, longdoubledan longfloat. Ini perlu dikonversi ke setara NumPy terdekat sebelum menggunakan .item().

Mike T
sumber
Saya menggunakan panda (0.23.0). Paling tidak untuk versi itu, np.str tidak memiliki metode .item () sehingga satu-satunya cara yang saya lihat adalah membungkus .item () di dalam blok percobaan.
Robert Lugg
3
@RobertLugg np.strbukan tipe Numpy, yaitu np.str is str, jadi itu hanya alias untuk tipe Python standar. Sama dengan np.float, np.int, np.bool, np.complex, dan np.object. Tipe Numpy memiliki trailing _, mis np.str_.
Mike T
Saya mengerti. Jadi masalahnya adalah "alangkah baiknya jika" saya bisa melakukannya: np.float64(0).item()dan juga np.float(0).item(). Dengan kata lain, untuk kasus-kasus di mana diketahui apa yang harus dilakukan, dukung .item()metode tersebut meskipun hanya mengembalikan nilai yang sama. Dengan begitu saya bisa menerapkan .item()pada skalar yang jauh lebih numpy tanpa casing khusus. Karena itu, konsep paralel tampaknya berbeda karena implementasi yang mendasarinya. Saya benar-benar mengerti mengapa ini dilakukan. Tapi itu menjengkelkan bagi pengguna perpustakaan.
Robert Lugg
45

menemukan diri saya memiliki set campuran tipe numpy dan python standar. karena semua jenis numpy berasal dari numpy.generic, inilah cara Anda dapat mengubah segalanya menjadi tipe standar python:

if isinstance(obj, numpy.generic):
    return numpy.asscalar(obj)
tm_lv
sumber
5
Seperti catatan jawaban yang diterima , NumPy 1.16 tidak lagi menggunakan np.asscalar()metode ini. Mengapa? Mungkin tanpa alasan yang jelas. Meskipun memiliki stabilitas relatif selama satu dekade, API NumPy sekarang menjadi target bergerak yang tidak stabil yang mengharuskan pemeliharaan konstan dari aplikasi hilir. Setidaknya mereka meninggalkan kita item()metode ... untuk saat ini.
Cecil Curry
Metode asscalar telah terdepresiasi sejak v1.6 dari numpy
Eswar
Anda dapat dengan mudah mengganti jawabannya dengan if isinstance(o, numpy.generic): return o.item() raise TypeErrordan itu berubah menjadi jawaban yang tidak usang lagi: D
Buggy
19

Jika Anda ingin mengonversi (numpy.array ATAU numpy skalar ATAU tipe asli ATAU numpy.darray) UNTUK tipe asli Anda cukup lakukan:

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

tolist akan mengonversi skalar atau array Anda menjadi tipe asli python. Fungsi lambda default menangani kasus di mana nilai sudah asli.

v.thorey
sumber
2
Pendekatan terbersih untuk tipe campuran (asli dan non-pribumi), bagus sekali! Dan bagi mereka yang heran, ya, tolist hanya mengembalikan nilai tunggal (skalar) ketika Anda menyebutnya pada nilai tunggal, bukan daftar seperti yang Anda pikirkan. Perlu dicatat adalah bahwa cara sederhana untuk menulis lambda adalah lambda: valuekarena kita tidak menginginkan input apa pun.
fgblomqvist
getattr+ tolistkombo tidak hanya universal, tetapi bahkan vektor! (batalkan tautan .item ())
mirekphd
11

Bagaimana tentang:

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
sumber
1
Saya menyebutkan jenis solusi sebagai kemungkinan di akhir pertanyaan saya. Tapi saya mencari solusi yang sistematis daripada yang hard-coded yang hanya mencakup beberapa kasus. Misalnya, jika numpy menambahkan lebih banyak dtypes di masa depan, solusi Anda akan pecah. Jadi saya tidak senang dengan solusi itu.
conradlee
Jumlah kemungkinan tipe yang tidak dibatasi. Pertimbangkan np.dtype('mint8')untuk bilangan bulat positif m. Tidak mungkin ada pemetaan yang lengkap. (Saya juga tidak percaya ada fungsi bawaan untuk melakukan konversi ini untuk Anda. Saya bisa saja salah, tapi saya rasa tidak :))
unutbu
2
Python memetakan dtypes numpy ke tipe python, saya tidak yakin bagaimana, tapi saya ingin menggunakan metode apa pun yang mereka lakukan. Saya pikir ini harus terjadi untuk memungkinkan, misalnya, perkalian (dan operasi lainnya) antara tipe numpy dan tipe python. Saya kira metode mereka tidak memetakan semua tipe numpy, tetapi setidaknya yang paling umum di mana masuk akal.
conradlee
Itu tidak bekerja secara konsisten: >>> 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]Seperti yang Anda lihat tidak semua nilai dikonversi dengan benar.
Alex F
mengikuti komentar saya sebelumnya, anehnya yang ini bekerja, meskipun saya akan berpikir Anda perlu menempatkan putaran pada tipe asli Python bukan tipe asli 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()adalah pendekatan yang lebih umum untuk mencapai hal ini. Ia bekerja di semua tipe primitif dan juga dalam array atau matriks.

Saya sebenarnya tidak menghasilkan daftar jika dipanggil dari tipe primitif:

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
sumber
8

Anda juga dapat memanggil item()metode objek yang ingin Anda konversi:

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

Saya pikir Anda bisa menulis fungsi convert tipe umum seperti:

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'>)

Ini berarti tidak ada daftar tetap dan kode Anda akan diskalakan dengan lebih banyak jenis.

Matt Alcock
sumber
Apakah Anda tahu di mana kode sumber adalah bagian dari metode tolist () yang memetakan tipe numpy ke tipe python? Saya melihat sekilas tetapi tidak bisa menemukannya.
conradlee
Ini sedikit hack yang saya lakukan adalah menghasilkan numpy.ndarray1 nol di dalamnya menggunakan zeros()dan memanggil ndarrays tolist()fungsi untuk mengkonversi ke tipe asli. Setelah dalam jenis asli saya meminta jenis mengembalikannya. tolist()adalah fucntion darindarray
Matt Alcock
Ya saya melihat itu --- itu bekerja untuk apa yang saya inginkan dan jadi saya menerima solusi Anda. Tapi saya bertanya-tanya bagaimana cara mendaftar () melakukan tugasnya memutuskan jenis apa yang akan dimasukkan, dan saya tidak yakin bagaimana menemukan sumbernya.
conradlee
numpy.sourceforge.net/numdoc/HTML/numdoc.htm#pgfId-36588 adalah tempat fungsinya didokumentasikan. Saya pikir inspeksi mungkin dapat membantu menemukan lebih banyak informasi tetapi tidak ada sukacita. Langkah selanjutnya saya mencoba untuk mengkloning github.com/numpy/numpy.git dan jalankan grep -r 'tolist' numpy. (masih dalam proses, numpy sangat besar!)
Matt Alcock
3

numpy berpendapat bahwa informasi dalam pemetaan terbuka typeDictsehingga Anda dapat melakukan sesuatu seperti di bawah ini ::

>>> 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'}

Jika Anda menginginkan tipe python yang sebenarnya daripada namanya, Anda dapat melakukan ::

>>> {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
sumber
3

Maaf terlambat datang ke sebagian, tapi saya melihat masalah konversi numpy.float64ke Python biasa floatsaja. Saya melihat 3 cara melakukan itu:

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

Berikut ini adalah timing yang relevan dari 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

Kedengarannya seperti float(npValue)tampaknya jauh lebih cepat.

gt6989b
sumber
1

Pendekatan saya agak kuat, tetapi tampaknya bermain bagus untuk semua kasus:

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])

Pemakaian:

>>> 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
sumber
Saya melihat ini pada dasarnya sama dengan jawaban Matt Alcock.
Simon Streicher
1

Catatan tambahan tentang skalar array untuk mereka yang tidak memerlukan konversi otomatis dan mengetahui jenis numpy nilainya:

Skrip array berbeda dari skrip Python, tetapi untuk sebagian besar skrip dapat digunakan secara bergantian (pengecualian utama adalah untuk versi Python yang lebih lama dari v2.x, di mana skalars array integer tidak dapat bertindak sebagai indeks untuk daftar dan tupel). Ada beberapa pengecualian, seperti ketika kode membutuhkan atribut skalar yang sangat spesifik atau ketika memeriksa secara spesifik apakah suatu nilai adalah skalar Python. Secara umum, masalah mudah diperbaiki dengan secara eksplisit mengkonversi skalar array ke skalar Python, menggunakan fungsi tipe Python yang sesuai (misalnya, int, float, complex, str, unicode).

Sumber

Jadi, untuk sebagian besar kasus konversi mungkin tidak diperlukan sama sekali, dan skalar array dapat digunakan secara langsung. Efeknya harus identik dengan menggunakan skalar 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

Tetapi jika, karena alasan tertentu, konversi eksplisit diperlukan, menggunakan fungsi bawaan Python yang sesuai adalah cara yang harus dilakukan. Seperti yang ditunjukkan pada jawaban lain, ini juga lebih cepat daripada item()metode skalar array .

wombatonfire
sumber
0

Menerjemahkan seluruh ndarray sebagai satu objek data unit:

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

Namun, dibutuhkan beberapa menit saat menangani kerangka data yang besar. Saya juga mencari solusi yang lebih efisien. Semoga jawaban yang lebih baik.

Qinhong Ma
sumber