Преобразование типа массива 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.