На місці перетворення типу масиву 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.