การแปลงชนิด In-place ของอาร์เรย์ NumPy


127

ด้วยอาร์เรย์ NumPy ของint32ฉันจะแปลงให้float32 เข้าที่ได้อย่างไร โดยพื้นฐานแล้วฉันอยากจะทำ

a = a.astype(numpy.float32)

โดยไม่ต้องคัดลอกอาร์เรย์ มันใหญ่.

aเหตุผลสำหรับการทำเช่นนี้ก็คือว่าฉันมีสองขั้นตอนวิธีการในการคำนวณของ หนึ่งในนั้นส่งคืนอาร์เรย์ของint32อีกอันส่งคืนอาร์เรย์ของfloat32(และสิ่งนี้มีอยู่ในอัลกอริทึมที่แตกต่างกันสองแบบ) ทั้งหมดคำนวณต่อไปคิดว่าเป็นอาเรย์ของafloat32

ขณะนี้ฉันทำแปลงในฟังก์ชัน C ctypesเรียกว่าผ่าน มีวิธีทำใน Python หรือไม่?


ใช้ctypesเป็นมาก "ในงูใหญ่" numpyกับการใช้ :)
Karl Knechtel

3
@ คาร์ล: ไม่เพราะฉันต้องเขียนโค้ดและคอมไพล์ฟังก์ชัน C ด้วยตัวเอง
Sven Marnach

อ้อเข้าใจแล้ว. ฉันคิดว่าคุณน่าจะเป็น SOL ในเรื่องนี้
Karl Knechtel

3
@ แอนดรูว์: มีหลายวิธีที่จะบอกได้ว่าจะส่งคืนสำเนาหรือไม่ หนึ่งในนั้นคือการอ่านเอกสาร
Sven Marnach

1
In-place แปลว่า "ใช้หน่วยความจำเดียวกันกับอาร์เรย์เดิม" ดูคำตอบที่ยอมรับ - ส่วนสุดท้ายแสดงให้เห็นว่าค่าใหม่ได้เขียนทับหน่วยความจำเดียวกันแล้ว
Sven Marnach

คำตอบ:


110

คุณสามารถสร้างมุมมองด้วย dtype อื่นจากนั้นคัดลอกในตำแหน่งลงในมุมมอง:

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

print(y)

อัตราผลตอบแทน

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

หากต้องการแสดงการแปลงที่เกิดขึ้นโปรดทราบว่าการคัดลอกจาก xเป็นyแก้ไขx:

print(x)

พิมพ์

array([         0, 1065353216, 1073741824, 1077936128, 1082130432,
       1084227584, 1086324736, 1088421888, 1090519040, 1091567616])

26
หมายเหตุสำหรับผู้ที่ (เช่นฉัน) ที่ต้องการการแปลงระหว่าง dtype ของขนาดไบต์ที่ต่างกัน (เช่น 32 ถึง 16 บิต): วิธีนี้ล้มเหลวเนื่องจาก y.size <> x.size ตรรกะเมื่อคุณคิดเกี่ยวกับมัน :-(
Juh_

โซลูชันนี้ใช้ได้กับ Numpy เวอร์ชันเก่าหรือไม่ เมื่อฉันทำnp.arange(10, dtype=np.int32).view(np.float32)ใน Numpy 1.8.2 array([ 0.00000000e+00, 1.40129846e-45, ... [snip] ... 1.26116862e-44], dtype=float32)ฉันได้รับ
Bas Swinckels

3
@BasSwinckels: นั่นคือสิ่งที่คาดหวัง y[:] = xแปลงที่เกิดขึ้นเมื่อคุณกำหนด
unutbu

เพื่อชี้แจงประเด็นที่ทำเกี่ยวกับขนาดรายการ (จำนวนบิต) ที่อ้างถึงโดยคำตอบเดิมและ @Juh_ เช่น: a = np.arange(10, dtype='float32'); b = a[::-1]; c = np.vstack((a,b)); d = c.view('float64')รหัสนี้ใช้เวลา 10 + 10 float32 และให้ผลลัพธ์เป็น 10 แทนที่จะเป็น 20 float64
dcanelhas

1
การเปลี่ยนแปลงในสถานที่นี้อาจช่วยประหยัดการใช้หน่วยความจำ แต่จะช้ากว่าการx.astype(float)แปลงแบบธรรมดา ฉันจะไม่แนะนำมันเว้นแต่ว่าสคริปต์ของคุณอยู่ติดกับ MemoryError
hpaulj

158

อัปเดต: ฟังก์ชันนี้จะหลีกเลี่ยงการคัดลอกเท่านั้นหากทำได้ดังนั้นนี่ไม่ใช่คำตอบที่ถูกต้องสำหรับคำถามนี้ คำตอบของ unutbu คือคำตอบที่ถูกต้อง


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

numpy astype มีแฟล็กสำเนา ทำไมเราถึงไม่ควรใช้?


14
เมื่อพารามิเตอร์นี้ได้รับการสนับสนุนใน NumPy รีลีสแน่นอนว่าเราสามารถใช้งานได้ แต่ปัจจุบันมีให้ใช้งานในสาขาการพัฒนาเท่านั้น และในเวลาที่ฉันถามคำถามนี้มันไม่มีเลย
Sven Marnach

2
@SvenMarnach ตอนนี้รองรับแล้วอย่างน้อยก็ในเวอร์ชันของฉัน (1.7.1)
PhilMacKay

ดูเหมือนว่าจะทำงานได้อย่างสมบูรณ์ใน python3.3 ด้วยเวอร์ชัน numpy ล่าสุด
CHM

1
ฉันพบว่าสิ่งนี้ช้ากว่า a = a.view ประมาณ 700 เท่า ((float, len (a.dtype.names)))
JJ

14
แฟล็กการคัดลอกบอกเพียงว่าหากการเปลี่ยนแปลงสามารถทำได้โดยไม่ต้องคัดลอกจะทำได้โดยไม่ต้องคัดลอก อย่างไรก็ตามประเภทที่แตกต่างกันก็จะยังคงคัดลอกอยู่เสมอ
coderforlife

14

คุณสามารถเปลี่ยนประเภทอาร์เรย์ได้โดยไม่ต้องแปลงดังนี้:

a.dtype = numpy.float32

แต่ก่อนอื่นคุณต้องเปลี่ยนจำนวนเต็มทั้งหมดให้เป็นค่าที่จะตีความว่าเป็นจำนวนลอยที่สอดคล้องกัน วิธีที่ช้ามากคือใช้structโมดูลของ python ดังนี้:

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

... นำไปใช้กับสมาชิกแต่ละคนในอาร์เรย์ของคุณ

แต่บางทีวิธีที่เร็วกว่าคือการใช้เครื่องมือ ctypeslib ของ numpy (ซึ่งฉันไม่คุ้นเคย)

- แก้ไข -

เนื่องจาก ctypeslib ดูเหมือนจะไม่ทำงานฉันจึงดำเนินการแปลงต่อด้วยnumpy.astypeวิธีการทั่วไปแต่ดำเนินการต่อในขนาดบล็อกที่อยู่ในขีด จำกัด หน่วยความจำของคุณ:

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

... จากนั้นเปลี่ยน dtype เมื่อเสร็จแล้ว

นี่คือฟังก์ชั่นที่ทำงานให้สำเร็จสำหรับ dtypes ที่เข้ากันได้ (ใช้ได้เฉพาะกับ dtypes ที่มีไอเท็มขนาดเดียวกัน) และจัดการอาร์เรย์ที่มีรูปร่างตามอำเภอใจโดยผู้ใช้ควบคุมขนาดบล็อก:

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

1
ขอบคุณสำหรับคำตอบ. จริงๆแล้วฉันไม่คิดว่านี่มีประโยชน์มากสำหรับอาร์เรย์ขนาดใหญ่ - มันช้าเกินไป Reinterpreting ข้อมูลของอาร์เรย์เป็นชนิดที่แตกต่างกันเป็นเรื่องง่าย - a.view(numpy.float32)ตัวอย่างเช่นโดยการเรียก ส่วนที่ยากคือการแปลงข้อมูล numpy.ctypeslibช่วยในการตีความข้อมูลซ้ำเท่านั้นไม่ใช่การแปลงข้อมูลจริง
Sven Marnach

ตกลง. ฉันไม่แน่ใจว่าข้อ จำกัด ของหน่วยความจำ / โปรเซสเซอร์ของคุณคืออะไร ดูการแก้ไขของฉัน
พอล

ขอบคุณสำหรับการอัพเดท. การทำแบบบล็อกเป็นความคิดที่ดี - อาจดีที่สุดที่คุณจะได้รับจากอินเทอร์เฟซ NumPy ปัจจุบัน แต่ในกรณีนี้ฉันอาจจะยึดติดกับโซลูชัน ctypes ปัจจุบันของฉัน
Sven Marnach

-1
import numpy as np
arr_float = np.arange(10, dtype=np.float32)
arr_int = arr_float.view(np.float32)

ใช้ view () และพารามิเตอร์ 'dtype' เพื่อเปลี่ยนอาร์เรย์ในตำแหน่ง


เป้าหมายของคำถามคือการแปลงข้อมูลในสถานที่จริง หลังจากแก้ไขประเภทในบรรทัดสุดท้ายเป็นintคำตอบนี้จะตีความข้อมูลที่มีอยู่ใหม่เป็นประเภทอื่นเท่านั้นซึ่งไม่ใช่สิ่งที่ฉันขอ
Sven Marnach

คุณหมายถึงอะไร? dtype เป็นเพียงลักษณะของข้อมูลในหน่วยความจำซึ่งใช้งานได้จริงอย่างไรก็ตามใน np.astype พารามิเตอร์ 'การหล่อ' สามารถควบคุมวิธีการแปลงค่าเริ่มต้น 'ไม่ปลอดภัย' ได้
蒋志强

ใช่ฉันเห็นด้วยกับคำตอบแรกที่ยอมรับ อย่างไรก็ตาม arr_.astype (new_dtype, copy = False) ยังคงส่งคืนอาร์เรย์ที่จัดสรรใหม่ วิธีการความพึงพอใจdtype, orderและsubokความต้องการที่จะกลับสำเนาของอาร์เรย์หรือไม่? ฉันไม่แก้มัน
蒋志强

-5

ใช้สิ่งนี้:

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)

5
แน่ใจหรือว่าไม่ใช่สำเนา คุณช่วยตรวจสอบและอธิบายเพิ่มเติมเล็กน้อยได้ไหม
Michele d'Amico

-5

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


1
แม้ว่าข้อมูลโค้ดนี้อาจเป็นวิธีแก้ปัญหา แต่การมีคำอธิบายจะช่วยปรับปรุงคุณภาพของโพสต์ของคุณได้มาก โปรดจำไว้ว่าคุณกำลังตอบคำถามสำหรับผู้อ่านในอนาคตและบุคคลเหล่านั้นอาจไม่ทราบสาเหตุของการแนะนำโค้ดของคุณ
Sebastialonso

เหตุใดจึงควรเป็นConversion ในสถานที่ numpy.subtractกำลังส่งคืนสำเนาใช่หรือไม่ เฉพาะชื่อที่aใช้ซ้ำสำหรับข้อมูลอื่น ... โปรดอธิบายถ้าฉันผิดเกี่ยวกับเรื่องนี้
koffein

ขอบคุณที่ชี้ให้เห็นดูเหมือนว่าคุณถูกต้อง - มีการสร้างสำเนา
MIO
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.