Conversão de tipo no local de uma matriz NumPy

127

Dada uma matriz NumPy de int32, como eu a converto float32 no local ? Então, basicamente, eu gostaria de fazer

a = a.astype(numpy.float32)

sem copiar a matriz. É grande.

A razão para fazer isso é que eu tenho dois algoritmos para o cálculo de a. Um deles retorna uma matriz de int32, o outro retorna uma matriz de float32(e isso é inerente aos dois algoritmos diferentes). Todos os cálculos adicionais assumem que aé uma matriz de float32.

Atualmente eu faço a conversão em uma função C chamada via ctypes. Existe uma maneira de fazer isso em Python?

Sven Marnach
fonte
Usar ctypesé tanto "em Python" quanto usar numpy. :)
Karl Knechtel
3
@ Karl: Não, porque eu tenho que codificar e compilar a função C.
Sven Marnach
Ah eu vejo. Eu acho que você provavelmente está SOL neste.
Karl Knechtel
3
@ Andrew: Existem muitas maneiras de saber se ele retorna uma cópia. Um deles é ler a documentação .
Sven Marnach
1
No local, significa simplesmente "usando a mesma memória que a matriz original". Veja a resposta aceita - a última parte mostra que os novos valores realmente substituíram a mesma memória.
Sven Marnach 13/02/2012

Respostas:

110

Você pode fazer uma exibição com um tipo diferente e copiar no local para a exibição:

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

print(y)

rendimentos

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

Para mostrar que a conversão ocorreu, observe que copiar de x para yalterado x:

print(x)

impressões

array([         0, 1065353216, 1073741824, 1077936128, 1082130432,
       1084227584, 1086324736, 1088421888, 1090519040, 1091567616])
unutbu
fonte
26
Nota para aqueles (como eu) que desejam conversão entre tipos de tamanhos diferentes de bytes (por exemplo, 32 a 16 bits): Esse método falha porque y.size <> x.size. Lógico quando você pensa sobre isso :-(
Juh_
Esta solução estava funcionando para uma versão mais antiga do Numpy? Quando eu faço o np.arange(10, dtype=np.int32).view(np.float32)Numpy 1.8.2, eu recebo array([ 0.00000000e+00, 1.40129846e-45, ... [snip] ... 1.26116862e-44], dtype=float32).
Bas Swinckels
3
@BasSwinckels: Isso é esperado. A conversão ocorre quando você atribui y[:] = x.
unutbu
para esclarecer a questão levantada sobre o ItemSize (número de bits) a que se refere a resposta original e @Juh_ por exemplo: a = np.arange(10, dtype='float32'); b = a[::-1]; c = np.vstack((a,b)); d = c.view('float64')Este código tira 10 + 10 float32 e resulta em 10, em vez de 20 float64
dcanelhas
1
Essa alteração no local pode economizar no uso da memória, mas é mais lenta que uma simples x.astype(float)conversão. Eu não o recomendaria, a menos que seu script esteja próximo ao MemoryError.
hpaulj
158

Atualização: Esta função evita a cópia apenas se puder, portanto, essa não é a resposta correta para esta pergunta. a resposta de unutbu é a certa.


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

numpy astype tem um sinalizador de cópia. Por que não devemos usá-lo?

Vikas
fonte
14
Uma vez que esse parâmetro é suportado em uma versão do NumPy, é claro que poderíamos usá-lo, mas atualmente ele está disponível apenas no ramo de desenvolvimento. E no momento em que fiz essa pergunta, ela não existia.
Sven Marnach
2
@SvenMarnach Agora é suportado, pelo menos na minha versão (1.7.1).
PhilMacKay
Parece funcionar perfeitamente em python3.3 com a versão numpy mais recente.
CHM
1
Eu acho que isso seja em torno de 700x mais lento do que a = a.view ((flutuador, len (a.dtype.names)))
JJ
14
O sinalizador de cópia diz apenas que, se a alteração puder ser feita sem uma cópia, será feita sem uma cópia. No entanto, o tipo é diferente, mas sempre será copiado.
Coderforlife
14

Você pode alterar o tipo de matriz sem converter da seguinte maneira:

a.dtype = numpy.float32

mas primeiro você precisa alterar todos os números inteiros para algo que será interpretado como a flutuação correspondente. Uma maneira muito lenta de fazer isso seria usar o structmódulo python como este:

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

... aplicado a cada membro da sua matriz.

Mas talvez uma maneira mais rápida seria utilizar as ferramentas ctypeslib de numpy (com as quais não estou familiarizado)

- editar -

Como o ctypeslib parece não funcionar, eu prosseguiria com a conversão com o numpy.astypemétodo típico , mas prosseguiria em tamanhos de bloco dentro dos limites da sua memória:

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

... altere o dtype quando terminar.

Aqui está uma função que realiza a tarefa para qualquer tipo compatível (que funciona apenas para tipos com itens do mesmo tamanho) e lida com matrizes de forma arbitrária com controle do usuário sobre o tamanho do bloco:

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
Paulo
fonte
1
Obrigado pela sua resposta. Honestamente, não acho que isso seja muito útil para grandes matrizes - é muito lento. Reinterpretar os dados da matriz como um tipo diferente é fácil - por exemplo, chamando a.view(numpy.float32). A parte difícil é realmente converter os dados. numpy.ctypeslibapenas ajuda a reinterpretar os dados, não a convertê-los.
Sven Marnach
Está bem. Não sabia ao certo quais eram as suas limitações de memória / processador. Veja minha edição.
Paul
Obrigado pela atualização. Fazê-lo em blocos é uma boa idéia - provavelmente o melhor que você pode obter com a atual interface NumPy. Mas, neste caso, provavelmente vou me ater à minha solução atual de ctypes.
Sven Marnach
-1
import numpy as np
arr_float = np.arange(10, dtype=np.float32)
arr_int = arr_float.view(np.float32)

use view () e o parâmetro 'dtype' para alterar a matriz no lugar.

蒋志强
fonte
O objetivo da pergunta era realmente converter os dados no local. Depois de corrigir o tipo na última linha para int, essa resposta só reinterpretaria os dados existentes como um tipo diferente, o que não era o que eu estava pedindo.
Sven Marnach 5/08/19
O que você quer dizer? dtype é apenas a aparência dos dados na memória, ele realmente funciona. No entanto, no parâmetro np.astype, o parâmetro 'casting' pode controlar o padrão de conversão do método 'não seguro'.
蒋志强
Sim, concordo com a primeira resposta aceita. No entanto, arr_.astype (new_dtype, copy = False) ainda retorna uma matriz alocada recentemente. Como satisfez as dtype, ordere subokrequisitos para retornar uma cópia da matriz? Eu não resolvo isso.
蒋志强
-5

Usa isto:

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)
Ankit Barik
fonte
5
Tem certeza de que não é uma cópia? Você pode conferir e explicar um pouco mais?
Michele d'Amico
-5

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

MIO
fonte
1
Embora esse snippet de código possa ser a solução, incluir uma explicação realmente ajuda a melhorar a qualidade da sua postagem. Lembre-se de que você está respondendo à pergunta dos leitores no futuro e essas pessoas podem não saber os motivos da sua sugestão de código.
Sebastialonso
Por que essa conversão deve ser feita no local ? numpy.subtractestá retornando uma cópia, não é? Somente o nome foi areutilizado para outro pedaço de dados ... Por favor, explique se estou errado sobre isso.
Koffein
Obrigado por apontar isso, parece que você está correto - uma cópia é produzida.
MIO