На місці перетворення типу масиву NumPy


127

З огляду на масив NumPy int32, як перетворити його на float32 місце ? Так в основному я хотів би зробити

a = a.astype(numpy.float32)

без копіювання масиву. Це велике.

Причиною цього є те, що у мене є два алгоритми для обчислення a. Один з них повертає масив int32, інший повертає масив float32(і це притаманне двом різним алгоритмам). Усі подальші обчислення припускають, що aце масив float32.

В даний час я роблю перетворення у функцію C, що називається via ctypes. Чи є спосіб це зробити в Python?


Використовувати ctypesстільки "в Python", скільки і використовувати numpy. :)
Карл Кнечтел

3
@Karl: Ні, тому що я повинен сам кодувати і компілювати функцію C.
Свен Марнах

О Я бачу. Я думаю, ти, мабуть, СОЛ на цьому.
Карл Кнечтел

3
@Andrew: Є багато способів сказати, чи повертається копія. Один з них - це прочитати документацію .
Свен Марнах

1
На місці просто означає "використання тієї самої пам'яті, що і вихідний масив". Погляньте на прийняту відповідь - остання частина показує, що нові значення справді перезаписали ту саму пам’ять.
Свен Марнах

Відповіді:


110

Ви можете зробити перегляд з іншим типом, а потім скопіювати його на місце:

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

Оновлення: Ця функція дозволяє уникнути копіювання, лише якщо це можливо, отже, це не правильна відповідь на це питання. Відповідь Юнутбу - правильна.


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

numpy astype має прапор копії. Чому ми не повинні його використовувати?


14
Як тільки цей параметр підтримується у випуску NumPy, ми, звичайно, можемо його використовувати, але на даний момент він доступний лише у галузі розробки. І коли я задав це питання, його взагалі не було.
Свен Марнах

2
@SvenMarnach Зараз це підтримується, принаймні в моїй версії (1.7.1).
PhilMacKay

Здається, він ідеально працює в python3.3 з останньою версією numpy.
CHM

1
Я вважаю, що це приблизно на 700 разів повільніше, ніж a = a.view ((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')

... потім змінити тип, коли буде зроблено.

Ось функція, яка виконує завдання для будь-яких сумісних 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
Дякую за вашу відповідь. Чесно кажучи, я не думаю, що це дуже корисно для великих масивів - це занадто повільно. Інтерпретувати дані масиву як різного типу легко - наприклад, зателефонувавши a.view(numpy.float32). Важка частина - це фактично перетворення даних. numpy.ctypeslibдопомагає лише при реінтерпретації даних, а не при їх фактичному перетворенні.
Свен Марнах

гаразд. Я не був впевнений, які обмеження у вашій пам'яті / процесорі. Дивіться мою редакцію.
Павло

Дякуємо за оновлення. Робити це блочно - це гарна ідея - можливо, найкраще, що ви можете отримати із поточним інтерфейсом NumPy. Але в цьому випадку я, мабуть, дотримуватимусь свого поточного рішення щодо типів.
Свен Марнах

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

використовувати view () та параметр 'dtype' для зміни масиву на місці.


Метою питання було фактично перетворити дані на місце. Після виправлення типу в останньому рядку до intцього відповіді буде лише повторно інтерпретовано наявні дані як іншого типу, що не те, про що я просив.
Свен Марнах

Що ви маєте на увазі? dtype - це лише поява даних у пам'яті, вона справді працює. Однак у np.astype параметр "casting" може керувати методом перетворення за замовчуванням у "небезпечний".
蒋志强

Так, я згоден з першою прийнятою відповіддю. Однак 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
Ви впевнені, що це не копія? Ви можете це перевірити і пояснити ще трохи?
Мішель д'Аміко,

-5

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


1
Хоча цей фрагмент коду може бути вирішенням, зокрема пояснення дійсно допомагає покращити якість вашої публікації. Пам'ятайте, що ви відповідаєте на запитання читачів у майбутньому, і ці люди можуть не знати причини вашої пропозиції щодо коду.
Себастіалонсо

Чому це має бути конверсія на місці ? numpy.subtractповертає копію, чи не так? Тільки ім'я aповторно використовувалося для чергової частини даних ... Будь ласка, поясніть, якщо я помиляюся з цього приводу.
кофеїн

Дякую, що вказали на це, здається, ви праві - копія створюється.
MIO
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.