Преобразование типа массива NumPy на месте


127

Учитывая массив NumPy int32, как мне преобразовать его на float32 место ? В общем, я хотел бы сделать

a = a.astype(numpy.float32)

без копирования массива. Оно большое.

Причина этого в том, что у меня есть два алгоритма вычисления a. Один из них возвращает массив int32, другой - массив float32(и это присуще двум различным алгоритмам). Все дальнейшие вычисления предполагают, что aэто массив float32.

В настоящее время я выполняю преобразование в функции C, вызываемой через ctypes. Есть ли способ сделать это в Python?


Использование ctypes- это столько же «в Python», сколько и использование numpy. :)
Карл Кнехтель

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

О, я вижу. Я думаю, что вы, вероятно, СОЛНЕЧНО на этом.
Карл Кнехтель

3
@ Андрей: Есть много способов узнать, возвращает ли он копию. Один из них - прочитать документацию .
Sven Marnach

1
На месте просто означает «использование той же памяти, что и исходный массив». Посмотрите на принятый ответ - последняя часть показывает, что новые значения действительно перезаписали ту же память.
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.
Логично,

Работало ли это решение для какой-то старой версии 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, мы, конечно, могли бы его использовать, но в настоящее время он доступен только в ветке разработки. И в то время, когда я задал этот вопрос, его вообще не существовало.
Свен Марнах

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]

... применяется к каждому члену вашего массива.

Но, возможно, более быстрым способом было бы использовать инструменты numpy ctypeslib (с которыми я не знаком)

- редактировать -

Поскольку 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
Спасибо за Ваш ответ. Честно говоря, я не думаю, что это очень полезно для больших массивов - это слишком медленно. Переинтерпретировать данные массива как другой тип легко - например, вызвав a.view(numpy.float32). Самая сложная часть - это преобразование данных. numpy.ctypeslibпомогает только переосмыслить данные, но не преобразовать их.
Свен Марнах

хорошо. Я не был уверен, какие у вас ограничения памяти / процессора. Смотрите мою правку.
Пол

Спасибо за обновление. Поблочно - хорошая идея - вероятно, лучшее, что вы можете получить с текущим интерфейсом 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 05

что вы имеете в виду? dtype - это просто появление данных в памяти, он действительно работает. Однако в np.astype параметр casting может управлять небезопасным методом преобразования по умолчанию.
蒋志强 05

Да, я согласен с первым принятым ответом. Однако arr_.astype (new_dtype, copy = False) по-прежнему возвращает вновь выделенный массив. Как удовлетворил dtype, orderи subokтребование вернуть копию массива? Я не решаю это.
蒋志强 05

-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
Хотя этот фрагмент кода может быть решением, включение объяснения действительно помогает улучшить качество вашего сообщения. Помните, что вы отвечаете на вопрос читателей в будущем, и эти люди могут не знать причины вашего предложения кода.
Sebastialonso

Почему это должно быть преобразование на месте ? numpy.subtractвозвращает копию, не так ли? Только имя aповторно используется для другого блока данных ... Пожалуйста, объясните, если я ошибаюсь.
koffein

Спасибо, что указали на это, кажется, вы правы - копия сделана.
MIO
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.