Numpy: apply_along_axis corta strings

Criado em 7 dez. 2016  ·  16Comentários  ·  Fonte: numpy/numpy

Estou tentando concatenar todos os elementos de uma linha em uma string da seguinte maneira:

np.apply_along_axis(lambda x: " ".join(map(str, x)), 1, b)

b é

[[111,111,0,0,0], [111,111,111,111,111]]

No entanto, o resultado da linha é:

['111 111 0 0 0', '111 111 111 1']

Parece que np.apply_along_axis está cortando a segunda string para ter o mesmo comprimento da primeira. Se eu colocar uma sequência mais longa primeiro, o resultado está correto:

['111 111 111 111 111', '111 111 0 0 0']

Acho que isso é um bug?


Resumo 30/04/2019 por @seberg

np.apply_along_axis infere o dtype de saída da primeira passagem. Que pode ser contornado, por exemplo, mas a função retornando um array de um tipo correto.

Ações:

  • np.apply_along_axis poderia / deveria obter um dtype kwarg (ou similar, compare também np.vectorize ).
01 - Enhancement numpy.lib Intermediate

Comentários muito úteis

Talvez devêssemos apenas adicionar um argumento dtype= a apply_along_axis

Todos 16 comentários

(encontrado isso através de # 8363) @lukovnikov - o código falha porque foi escrito com matrizes numéricas em mente, para as quais o cálculo em qualquer parte de uma matriz pode retornar o mesmo tipo de saída que qualquer outra parte. Devo observar de forma mais geral que matrizes numpy não são particularmente boas ou eficientes em strings e, a menos que você tenha uma matriz muito complicada, meu palpite é que seria muito melhor trabalhar apenas com listas e funções python, especialmente porque você já está usando as funções de string python para fazer a concatenação.

@mhvk : Discordo totalmente de sua afirmação. numpy pode ser escrito para cálculos numéricos, mas isso não significa que omitimos a funcionalidade com str arrays. Afinal, é por isso que temos dtypes específicos para strings, ao contrário de bibliotecas como pandas .

Este é um bug e deve ser corrigido, a menos que você possa apresentar um argumento mais convincente do que o que você forneceu.

@gfyoung - Não estou dizendo que não se deve tentar resolver o bug (embora eu ache que é óbvio qualquer solução melhor não causar uma grande regressão de desempenho para mais típico), apenas explicando porque o bug existe e sugerindo que para strings um realmente é melhor não usar ndarray . De qualquer forma, esses são meus 2 centavos.

@mhvk : soado como se isso não fosse realmente uma preocupação de numpy . É por isso que eu queria enfatizar fortemente que isso é algo que devemos tentar corrigir.

Eu tive o mesmo problema:

você pode ver que corta o 'g' de jpg simplesmente referenciando-o. Achei que tem algo a ver com a mudança de forma. acabou usando listas + mapa em vez disso.

capture d ecran 2017-05-27 a 11 54 25

Estou assumindo que é um exemplo deliberadamente inventado, porque você não deveria usar apply_along_axis para uma indexação simples como essa.

np.ma.apply_along_axis funcionará corretamente aqui (por enquanto - veja # 8511). Outra opção (travou até 1.13) é uma conversão manual para dtype object:

np.apply_along_axis(lambda x: np.array(x[0], object), 1, fnames)

@ eric-wieser, seu comentário acima me ajudou a resolver um problema que venho tendo há alguns meses com operações numpy e string. 👍

Estou assumindo que é um exemplo deliberadamente planejado, porque você não deveria usar o apply_along_axis para uma indexação simples como essa.

np.ma.apply_along_axis funcionará corretamente aqui (por enquanto - consulte # 8511). Outra opção (travou até 1.13) é uma conversão manual para o objeto dtype:

np.apply_along_axis (lambda x: np.array (x [0], objeto), 1, fnames)

Obrigado

Talvez devêssemos apenas adicionar um argumento dtype= a apply_along_axis

@ eric-wieser

Concordo que você deve adicionar esse argumento, se houver diferenças de desempenho entre a versão do array de máscara e a versão regular. Acabei de fazer um pequeno teste (não tenho certeza se isso significa alguma coisa) e aqui está uma foto dos resultados:

screen shot 2017-12-29 at 9 58 56 am

Meu caso de uso : estou armazenando dados PNL analisados ​​(strings) em matrizes numpy e tentando me livrar de todas as cláusulas for loops e if-else ; Eu aplico algumas funções analíticas de texto usando apply_along_axis . Qualquer benefício de velocidade seria incrível, pois cada extração de informações poderia ter de 10 a 20 variações (de um documento que poderia ter dezenas a centenas de extrações de informações que vêm de um corpus de milhares de documentos ... por dia).

EDITAR

Para qualquer outra pessoa que tenha esse problema com operações numpy string :
Acabei de definir explicitamente dtype no apply_along_axis vice normal usando a abordagem de matriz mascarada com é mais lento do que o normal apply_along_axis .

Detalhes sobre minhas operações de string para que você possa ver se isso se aplica a você : Minha matriz numpy (definida como nump no código abaixo) tem uma forma de (26,1) e cada elemento na matriz é uma extração de informação de uma frase em um documento. Cada extração de informações é uma lista de pares de chave / valor, e estou extraindo os pares de chave / valor que representam os triplos da PNL (sujeito, relação, objeto). A função lambda é passada pelo array para combinar o triplo em uma única sentença que testarei para a facilidade de leitura de carne kincaid, uma vez que são sentenças geradas por computador; as sentenças estavam sendo truncadas ou definidas para algum comprimento padrão com base no dtype . O código original que estava truncando as strings era:

%timeit -n 500 np.apply_along_axis(lambda x: ("{} {} {}".format(x[0]['subject'],x[0]['relation'],x[0]['object'])),1,np.array(nump[0][0])[:,np.newaxis])

Minha velocidade era de 116 µs ± 5,42 µs por loop (média ± desvio padrão de 7 execuções, 500 loops cada)

Solução

Usando o comentário de @eric-wieser acima , costumava seguir para corrigir meu problema e manter as velocidades próximas do original:

  • Transmitindo explicitamente o argumento dtype
%timeit -n 500 np.apply_along_axis(lambda x: np.array("{} {} {}".format(x[0]['subject'],x[0]['relation'],x[0]['object']),dtype='S255'),1,np.array(nump[0][0])[:,np.newaxis])

Minha velocidade era de 152 µs ± 12,6 µs por loop (média ± desvio padrão de 7 execuções, 500 loops cada)

A abordagem de matriz mascarada funciona sem alterar seu código, mas é mais lenta.

  • Matriz mascarada sem argumento dtype
%timeit -n 500 np.ma.apply_along_axis(lambda x: ("{} {} {}".format(x[0]['subject'],x[0]['relation'],x[0]['object'])),1,np.array(nump[0][0])[:,np.newaxis])

Minha velocidade era de 925 µs ± 33,6 µs por loop (média ± desvio padrão de 7 execuções, 500 loops cada)

e tentando se livrar de todos os loops for

Observe que apply_along_axis é apenas um loop for python, com uma pequena ajuda na alocação do array de saída para você - não é improvável que seja mais lento do que o loop que substitui

Ah, que pena; Eu estava fazendo entorpecimento porque sempre pensei que levava a velocidades mais rápidas. Sheesh.

fml

Para evitar o corte ao unir string com np.apply_along_axis:

a = np.array(['sssssssss','ffffffffffffff'])
b = np.array(['cccccccccccc','iiiiiiiiiiiiiiiii'])

def join_txt(text): return np.asarray(" ".join(text),dtype=object)

np.apply_along_axis(join_txt,0,[a,b])

o resultado é

array(['sssssssss cccccccccccc', 'ffffffffffffff iiiiiiiiiiiiiiiii'], dtype=object)

@cerlymarco , como é a abordagem que você propõe em relação à solução de @ linwoodc3 em termos de velocidade?

Talvez devêssemos apenas adicionar um argumento dtype= a apply_along_axis

Infelizmente, isso não é possível sem quebrar alguém. A assinatura atual é:

def apply_along_axis(func1d, axis, arr, *args, **kwargs):

Hoje, os usuários podem chamá-lo de:

def f1(x):
    return x
np.apply_along_axis(f1, 0, my_arr)
def f2(x, *, dtype):
    return x.astype(dtype)
np.apply_along_axis(f2, 0, my_arr, dtype=int)

Se fizermos um apply_along_axis pegar um argumento dtype e não passá-lo para f , então f2 falhará. Se o fizermos pegar um argumento dtype e passá-lo para f , então f1 falhará.

O que podemos fazer é:

  • Emita um FutureWarning if 'dtype' in kwargs dizendo às pessoas para renomearem seus argumentos
  • Espere 2 anos
  • Divida todos os usuários que ainda usam algo como f2 acima

Apenas um comentário que eu estava tentando adicionar um prefixo e sufixo a um nome de arquivo usando Numpy em uma série de Pandas e tive o mesmo problema (eu acho); ou seja, que a segunda corda foi cortada no mesmo comprimento que a primeira.

filenames = Series(['S1/C03/C03_R1/S1_C03_R1_PICT0239.JPG','S1/C03/C03_R1/S1_C03_R1_PICT0239.JPG'])
prefix = 'somepath'
np.char.add(prefix, filenames.astype(str))

array (['somepathS1 / C03 / C', 'somepathS1 / C03 / C'], dtype = '

Esta página foi útil?
0 / 5 - 0 avaliações