Самый быстрый способ сгенерировать строку с разделителем из массива 1d numpy

У меня есть программа, которая должна превращать много больших одномерных массивов numpy поплавков в строки с разделителями. Я нахожу эту операцию довольно медленной относительно математических операций в моей программе, и мне интересно, есть ли способ ускорить ее. Например, рассмотрим следующий цикл, который принимает 100 000 случайных чисел в массиве numpy и объединяет каждый массив в строку с разделителями-запятыми.

import numpy as np
x = np.random.randn(100000)
for i in range(100):
    ",".join(map(str, x))

Этот цикл занимает около 20 секунд (полный, а не каждый цикл). Напротив, рассмотрим, что 100 циклов чего-то типа элементарного умножения (x * x) будут занимать не более 1/10 секунды для завершения. Очевидно, что операция объединения строк создает узкое место в производительности; в моем фактическом приложении он будет доминировать в общей продолжительности выполнения. Это заставляет меня задуматься, есть ли более быстрый способ, чем ",". Join (map (str, x))? Поскольку map() - это то место, где происходит почти все время обработки, это сводится к вопросу о том, будет ли быстрее преобразовывать очень большое количество чисел в строки.

Ответы

Ответ 1

Очень хорошая рецензия на производительность различных методов конкатенации строк в Python: http://www.skymind.com/~ocrow/python_string/

Я немного удивлен, что некоторые из последних подходов работают так же хорошо, как и они, но похоже, что вы наверняка найдете там что-то, что будет работать лучше для вас, чем то, что вы делаете там.

Самый быстрый метод, упомянутый на сайте

Метод 6: Список понимания

def method6():
  return ''.join(['num' for num in xrange(loop_count)])

Этот метод самый короткий. Я испорчу сюрприз и скажу вам, что он также самый быстрый. Это чрезвычайно компактно, а также довольно понятно. Создайте список чисел, используя понимание списка, а затем объедините их все вместе. Не может быть проще, чем это. На самом деле это просто сокращенная версия метода 4, и она потребляет примерно столько же памяти. Это быстрее, хотя, потому что нам не нужно вызывать функцию list.append() каждый раз вокруг цикла.

Ответ 2

Немного поздно, но для меня это быстрее:

#generate an array with strings
x_arrstr = np.char.mod('%f', x)
#combine to a string
x_str = ",".join(x_arrstr)

Ускорение на моей машине около 1.5x

Ответ 3

Сначала преобразуйте массив numpy в список. Операция карты работает быстрее в списке, чем в массиве numpy.

например.

import numpy as np
x = np.random.randn(100000).tolist()
for i in range(100):
    ",".join(map(str, x))

В тестах времени я нашел последовательное ускорение на 15% для этого примера

Я оставлю других объяснить, почему это может быть быстрее, поскольку я понятия не имею!

Ответ 4

Я думаю, вы могли бы экспериментировать с numpy.savetxt, передавая объект cStringIO.StringIO в качестве поддельного файла...

Или, возможно, используя str(x) и выполнив замену пробелов запятыми (edit: это не сработает, потому что str выполняет многоточие больших массивов: -s).

Целью этого было отправить массив по сети, возможно, есть лучшие альтернативы (более эффективные как в процессоре, так и в полосе пропускания). Тот, который я указал в комментарии к другому ответу, чтобы закодировать двоичное представление массива как текстового блока Base64. Главным неудобством для этого является то, что клиент, читающий кусок данных, должен иметь возможность делать неприятные вещи, такие как переинтерпретировать байтовый массив как массив с плавающей запятой и который обычно не разрешается на безопасных языках типов; но это можно сделать быстро с помощью вызова библиотеки C (и большинство языков предоставляют средства для этого).

Если вы не можете общаться с битами, всегда есть возможность обрабатывать номера один за другим, чтобы преобразовать декодированные байты в float.

О, и следите за эндиантами машин при отправке данных через сеть: конвертировать в сетевой порядок → base64encode → отправить | receive → base64decode → конвертировать в хост-порядок

Ответ 5

numpy.savetxt еще медленнее, чем string.join. ndarray.tofile(), похоже, не работает с StringIO.

Но я нахожу более быстрый метод (по крайней мере, применяя пример OP на python2.5 с более низкой версией numpy):

import numpy as np
x = np.random.randn(100000)
for i in range(100):
    (",%f"*100000)[1:] % tuple(x)

Похоже, что строковый формат быстрее, чем объединение строк, если у вас есть четко определенный формат, например, в этом конкретном случае. Но мне интересно, почему OP нуждается в такой длинной строке плавающих чисел в памяти.

Новые версии numpy не показывают улучшения скорости.

Ответ 6

Использование imap из itertools вместо карты в коде OP дает мне 2-3% -ное улучшение, которое не так много, но что-то, что может сочетаться с другими идеями, чтобы улучшить их.

Лично я считаю, что если вы хотите гораздо лучше этого, вам придется использовать что-то вроде Cython.

Ответ 7

','.join(x.astype(str))

примерно на 10% медленнее, чем как

x_arrstr = np.char.mod('%f', x)
x_str = ",".join(x_arrstr)

но более читабельно.