Chuyển đổi loại tại chỗ của một mảng NumPy


127

Đưa ra một mảng NumPy int32, làm thế nào để tôi chuyển đổi nó thành float32 tại chỗ ? Về cơ bản, tôi muốn làm

a = a.astype(numpy.float32)

mà không sao chép mảng. Nó to.

Lý do để làm điều này là tôi có hai thuật toán cho việc tính toán a. Một trong số chúng trả về một mảng int32, cái còn lại trả về một mảng float32(và điều này vốn có của hai thuật toán khác nhau). Tất cả các tính toán tiếp theo cho rằng đó alà một mảng của float32.

Hiện tại tôi thực hiện chuyển đổi trong một hàm C được gọi thông qua ctypes. Có cách nào để làm điều này trong Python không?


Sử dụng ctypesnhiều như "trong Python" như sử dụng numpy. :)
Karl Knechtel

3
@Karl: Không, vì tôi phải tự viết mã và tự biên dịch hàm C.
Sven Marnach

Ồ, tôi hiểu rồi. Tôi nghĩ rằng bạn có thể là SOL về điều này.
Karl Knechtel

3
@Andrew: Có nhiều cách để biết nếu nó trả về một bản sao. Một trong số đó là đọc tài liệu .
Sven Marnach

1
Tại chỗ đơn giản có nghĩa là "sử dụng cùng bộ nhớ với mảng ban đầu". Hãy xem câu trả lời được chấp nhận - phần cuối cùng cho thấy rằng các giá trị mới thực sự đã ghi đè lên cùng một bộ nhớ.
Sven Marnach

Câu trả lời:


110

Bạn có thể tạo chế độ xem với một loại khác nhau, sau đó sao chép tại chỗ vào chế độ xem:

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

print(y)

sản lượng

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

Để hiển thị chuyển đổi đã được thực hiện, lưu ý rằng sao chép từ x để ythay đổi x:

print(x)

in

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

26
Lưu ý đối với những người (như tôi) muốn chuyển đổi giữa các loại có kích thước byte khác nhau (ví dụ 32 đến 16 bit): Phương pháp này không thành công vì y.size <> x.size. Hợp lý khi bạn nghĩ về nó :-(
Juh_

Giải pháp này có hiệu quả với một số phiên bản cũ của Numpy không? Khi tôi làm np.arange(10, dtype=np.int32).view(np.float32)trên Numpy 1.8.2, tôi nhận được array([ 0.00000000e+00, 1.40129846e-45, ... [snip] ... 1.26116862e-44], dtype=float32).
Bas Swinckels

3
@BasSwinckels: Đó là mong đợi. Việc chuyển đổi xảy ra khi bạn chỉ định y[:] = x.
unutbu

để làm rõ điểm được thực hiện về kích thước vật phẩm (số bit) được đề cập bởi câu trả lời ban đầu và @Juh_ ví dụ: a = np.arange(10, dtype='float32'); b = a[::-1]; c = np.vstack((a,b)); d = c.view('float64')Mã này mất 10 + 10 float32 và kết quả là 10, thay vì 20 float64
dcanelhas

1
Thay đổi tại chỗ này có thể tiết kiệm sử dụng bộ nhớ, nhưng chậm hơn so với x.astype(float)chuyển đổi đơn giản . Tôi sẽ không đề xuất nó trừ khi tập lệnh của bạn giáp với MemoryError.
hpaulj

158

Cập nhật: Chức năng này chỉ tránh sao chép nếu có thể, do đó đây không phải là câu trả lời chính xác cho câu hỏi này. câu trả lời của unutbu là đúng.


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

astype numpy có một cờ sao chép. Tại sao chúng ta không nên sử dụng nó?


14
Khi tham số này được hỗ trợ trong bản phát hành NumPy, tất nhiên chúng ta có thể sử dụng nó, nhưng hiện tại nó chỉ có sẵn trong nhánh phát triển. Và tại thời điểm tôi hỏi câu hỏi này, nó hoàn toàn không tồn tại.
Sven Marnach

2
@SvenMarnach Hiện tại nó đã được hỗ trợ, ít nhất là trong phiên bản của tôi (1.7.1).
PhilMacKay

Nó dường như hoạt động hoàn hảo trong python3.3 với phiên bản numpy mới nhất.
CHM

1
Tôi thấy tốc độ này chậm hơn khoảng 700 lần so với a = a.view ((float, len (a.dtype.names)))
JJ

14
Cờ sao chép chỉ nói rằng nếu thay đổi có thể được thực hiện mà không có bản sao, nó sẽ được thực hiện mà không cần bản sao. Tuy nhiên, loại này là khác nhau, nó vẫn sẽ luôn luôn sao chép.
coderforlife

14

Bạn có thể thay đổi kiểu mảng mà không cần chuyển đổi như thế này:

a.dtype = numpy.float32

nhưng trước tiên, bạn phải thay đổi tất cả các số nguyên thành một số sẽ được hiểu là số float tương ứng. Một cách rất chậm để làm điều này là sử dụng structmô-đun của python như thế này:

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

... áp dụng cho từng thành viên trong mảng của bạn.

Nhưng có lẽ cách nhanh hơn là sử dụng các công cụ ctypeslib của numpy (mà tôi không quen thuộc)

- biên tập -

Vì ctypeslib dường như không hoạt động, nên tôi sẽ tiến hành chuyển đổi với numpy.astypephương thức điển hình , nhưng tiến hành kích thước khối nằm trong giới hạn bộ nhớ của bạn:

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

... Sau đó thay đổi dtype khi hoàn thành.

Đây là một chức năng hoàn thành nhiệm vụ cho bất kỳ loại hình tương thích nào (chỉ hoạt động đối với các loại hình có cùng kích thước) và xử lý các mảng có hình dạng tùy ý với sự kiểm soát của người dùng đối với kích thước khối:

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
Cảm ơn câu trả lời của bạn. Thành thật mà nói, tôi không nghĩ rằng điều này rất hữu ích cho các mảng lớn - nó quá chậm. Giải thích lại dữ liệu của mảng dưới dạng khác là dễ dàng - ví dụ bằng cách gọi a.view(numpy.float32). Phần khó là thực sự chuyển đổi dữ liệu. numpy.ctypeslibchỉ giúp giải thích lại dữ liệu, không thực sự chuyển đổi nó.
Sven Marnach

đồng ý. Tôi không chắc những hạn chế về bộ nhớ / bộ xử lý của bạn là gì. Xem chỉnh sửa của tôi.
Paul

Cảm ơn các cập nhật. Thực hiện theo từng khối là một ý tưởng hay - có lẽ là cách tốt nhất bạn có thể có với giao diện NumPy hiện tại. Nhưng trong trường hợp này, tôi có thể sẽ sử dụng giải pháp ctypes hiện tại của mình.
Sven Marnach

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

sử dụng view () và tham số 'dtype' để thay đổi mảng tại chỗ.


Mục tiêu của câu hỏi là thực sự chuyển đổi dữ liệu tại chỗ. Sau khi sửa loại trong dòng cuối cùng thành int, câu trả lời này sẽ chỉ diễn giải lại dữ liệu hiện có thành một loại khác, đó không phải là điều tôi đang yêu cầu.
Sven Marnach

Ý anh là gì? dtype chỉ là sự xuất hiện của dữ liệu trong bộ nhớ, nó thực sự hoạt động. Tuy nhiên, trong np.astype, tham số 'truyền' có thể kiểm soát phương thức chuyển đổi mặc định 'không an toàn'.
蒋志强

Vâng, tôi đồng ý với câu trả lời đầu tiên được chấp nhận. Tuy nhiên, Array_.astype (new_dtype, copy = false) vẫn trả về một mảng mới được phân bổ. Làm thế nào để đáp ứng được dtype, ordersubokyêu cầu quay trở lại một bản sao của mảng? Tôi không giải quyết nó.
蒋志强

-5

Dùng cái này:

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
Bạn có chắc chắn đó không phải là một bản sao? Bạn có thể kiểm tra nó và giải thích thêm một chút không?
Michele Keyboardmico

-5

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


1
Mặc dù đoạn mã này có thể là giải pháp, bao gồm một lời giải thích thực sự giúp cải thiện chất lượng bài đăng của bạn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho độc giả trong tương lai và những người đó có thể không biết lý do cho đề xuất mã của bạn.
Sebastialonso

Tại sao điều này nên là một chuyển đổi tại chỗ ? numpy.subtractđang trả lại một bản sao, phải không? Chỉ tên được asử dụng lại cho một đoạn dữ liệu khác ... Hãy giải thích, nếu tôi sai về điều này.
koffein

Cảm ơn bạn đã chỉ ra điều này, có vẻ như bạn đã đúng - một bản sao được sản xuất.
MIO
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.