Самый быстрый способ создания строки с разделителями из массива 1d - PullRequest
22 голосов
/ 27 апреля 2010

У меня есть программа, которая должна превратить многие большие одномерные массивы чисел с плавающей точкой в ​​строки с разделителями. Я нахожу эту операцию довольно медленной по отношению к математическим операциям в моей программе, и мне интересно, есть ли способ ускорить ее. Например, рассмотрим следующий цикл, который принимает 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 () - это место, где происходит почти все время обработки, вопрос сводится к тому, существует ли более быстрый способ преобразования очень большого числа чисел в строки.

Ответы [ 7 ]

26 голосов
/ 13 декабря 2012

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

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

Ускорение на моей машине примерно в 1,5 раза

5 голосов
/ 27 апреля 2010

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

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

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

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

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

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

3 голосов
/ 10 ноября 2010

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

, например

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

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

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

2 голосов
/ 27 апреля 2010

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

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

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

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

Да, и следите за порядком номера машин при отправке данных по сети: преобразовать в сетевой порядок -> base64encode -> send | получить -> base64decode -> преобразовать в порядок хостов

1 голос
/ 27 апреля 2010

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)

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

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

0 голосов
/ 15 мая 2018
','.join(x.astype(str))

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

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

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

0 голосов
/ 27 апреля 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...