python - array - La forma más rápida de generar cadenas delimitadas a partir de una matriz numpy 1d
numpy reshape (7)
Tengo un programa que necesita convertir muchos arreglos de gran tamaño unidimensionales de flotadores en cadenas delimitadas. Estoy encontrando esta operación bastante lenta en relación con las operaciones matemáticas en mi programa y me pregunto si hay una manera de acelerarlo. Por ejemplo, considere el siguiente bucle, que toma 100,000 números aleatorios en una matriz numpy y une cada matriz en una cadena delimitada por comas.
import numpy as np
x = np.random.randn(100000)
for i in range(100):
",".join(map(str, x))
Este bucle tarda unos 20 segundos en completarse (total, no en cada ciclo). En contraste, considere que 100 ciclos de algo como la multiplicación de elementwise (x * x) tardarían más de 1/10 de segundo en completarse. Claramente, la operación de unión de cadena crea un gran cuello de botella de rendimiento; en mi aplicación real dominará el tiempo de ejecución total. Esto me hace preguntarme, ¿hay una manera más rápida que ",". Join (map (str, x))? Ya que map () es donde ocurre casi todo el tiempo de procesamiento, esto se reduce a la cuestión de si hay una forma más rápida de convertir un número muy grande de números en cadenas.
Convertir la matriz numpy en una lista primero. La operación de mapa parece ejecutarse más rápido en una lista que en una matriz numpy.
p.ej
import numpy as np
x = np.random.randn(100000).tolist()
for i in range(100):
",".join(map(str, x))
En las pruebas de tiempo encontré una aceleración constante del 15% para este ejemplo
Dejaré que otros expliquen por qué esto podría ser más rápido ya que no tengo idea.
Creo que podrías experimentar con numpy.savetxt
pasando un objeto cStringIO.StringIO
como un archivo falso ...
O tal vez usando str(x)
y haciendo una sustitución de los espacios en blanco por comas (edición: esto no funcionará bastante bien porque la str
hace una elipsis de matrices grandes: -s).
Como el propósito de esto era enviar la matriz a través de la red, quizás haya mejores alternativas (más eficientes tanto en la CPU como en el ancho de banda). El que señalé en un comentario a otra respuesta para codificar la representación binaria de la matriz como un bloque de texto Base64. El principal inconveniente para que esto sea óptimo es que el cliente que lee la porción de datos debería poder hacer cosas desagradables, como reinterpretar una matriz de bytes como una matriz flotante, y eso no suele permitirse en lenguajes de tipo seguro; pero se podría hacer rápidamente con una llamada de la biblioteca C (y la mayoría de los idiomas proporcionan medios para hacerlo).
En caso de que no pueda meterse con los bits, siempre existe la posibilidad de procesar los números uno por uno para convertir los bytes decodificados en flotantes.
Ah, y vigile las endiannes de las máquinas cuando envíe datos a través de la red: convierta a orden de la red -> base64encode -> envíe | recibir -> código base64 -> convertir a orden host
El uso de imap de itertools en lugar del mapa en el código del OP me está dando una mejora del 2-3% que no es mucho, pero es algo que podría combinarse con otras ideas para brindar más mejoras.
Personalmente, creo que si quieres mucho mejor que esto, tendrás que usar algo como Cython.
Muy buena información sobre el rendimiento de varias técnicas de concatenación de cadenas en Python: http://www.skymind.com/~ocrow/python_string/
Me sorprende un poco que algunos de los últimos enfoques funcionen tan bien como lo hacen, pero parece que ciertamente puedes encontrar algo que funcione mejor para ti que lo que estás haciendo allí.
El método más rápido mencionado en el sitio
Método 6: Listar las comprensiones
def method6(): return ''''.join([`num` for num in xrange(loop_count)])
Este método es el más corto. Voy a arruinar la sorpresa y te diré que también es la más rápida. Es extremadamente compacto, y también bastante comprensible. Cree una lista de números utilizando una lista de comprensión y luego únalos a todos. No podría ser más simple que eso. Esta es realmente una versión abreviada del Método 4, y consume casi la misma cantidad de memoria. Sin embargo, es más rápido porque no tenemos que llamar a la función list.append () cada vez que se realiza el ciclo.
Un poco tarde, pero esto es más rápido para mí:
#generate an array with strings
x_arrstr = np.char.mod(''%f'', x)
#combine to a string
x_str = ",".join(x_arrstr)
La velocidad está en mi máquina alrededor de 1.5x
numpy.savetxt es incluso más lento que string.join. ndarray.tofile () no parece funcionar con StringIO.
Pero sí encuentro un método más rápido (al menos aplicando al ejemplo de OP en python2.5 con una versión más baja de numpy):
import numpy as np
x = np.random.randn(100000)
for i in range(100):
(",%f"*100000)[1:] % tuple(x)
Parece que el formato de cadena es más rápido que la unión de cadena si tiene un formato bien definido, como en este caso particular. Pero me pregunto por qué OP necesita una cadena tan larga de números flotantes en la memoria.
Las nuevas versiones de numpy no muestran mejoras en la velocidad.
'',''.join(x.astype(str))
es aproximadamente un 10% más lento que como
x_arrstr = np.char.mod(''%f'', x)
x_str = ",".join(x_arrstr)
pero es más legible.