#python #string #numpy #count
#python #строка #numpy #количество
Вопрос:
Я сравнивал производительность подсчета количества букв «C» в очень длинной строке, используя набор numpy array
символов и метод string count
.
геном — очень длинная строка.
g1 = genome
g2 = np.array([i for i in genome])
%timeit np.sum(g2=='C')
4.43 s ± 230 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit g1.count('C')
955 ms ± 6.42 ms per loop (mean ± std. dev. of 7 runs, 1 loop each).
Я ожидал, что массив numpy вычислит его быстрее, но я ошибаюсь.
Может кто-нибудь объяснить мне, как работает метод count и что это быстрее, чем использование массива numpy?
Спасибо!
Комментарии:
1. Как это работает? Вы проверили исходный код?
2. Я думаю, что ваш тест несправедлив, потому
g2=='C'
что создаст новый массив, который содержит[True, False...]
, в то времяg1.count('C')
как нужно только проследить число.3. Ну,
np.sum(g2=='C')
нужно дважды повторить массив создать новый массив. Один раз для создания маски индекса, гдеg2=='C'
и затем суммирования.g1.count('C')
требуется выполнить итерацию только один раз. Даже если4. Более справедливым тестом было бы, вместо
g1.count
,sum(i=='C' for i in genome)
5. Ну, numpy — это числовой python — если вы имеете дело со строками, стандартные операции python лучше оптимизированы и, вероятно, являются лучшим выбором.
Ответ №1:
Давайте рассмотрим некоторые варианты решения проблемы. Я не буду пытаться создавать такую большую строку, как ваша.
In [393]: astr = 'ABCDEF'*10000
Сначала количество строк:
In [394]: astr.count('C')
Out[394]: 10000
In [395]: timeit astr.count('C')
70.2 µs ± 115 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Теперь попробуйте создать массив из 1 элемента с этой строкой:
In [396]: arr = np.array(astr)
In [397]: arr.shape
Out[397]: ()
In [398]: np.char.count(arr, 'C')
Out[398]: array(10000)
In [399]: timeit np.char.count(arr, 'C')
200 µs ± 2.97 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [400]: arr.dtype
Out[400]: dtype('<U60000')
Мой опыт других применений char
заключается в том, что он выполняет итерации по элементам массива и применяет строковый метод. Так что это не может быть быстрее, чем прямое применение метода string. Я полагаю, что остальное время — это какие-то накладные расходы.
Создайте список из строки — по одному символу на элемент списка:
In [402]: alist = list(astr)
In [403]: alist.count('C')
Out[403]: 10000
In [404]: timeit alist.count('C')
955 µs ± 18.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Количество списков должно перебирать элементы и выполнять проверку C
каждый раз. Тем не менее, это быстрее, чем sum(i=='C' for i in alist)
(и варианты).
Теперь создайте массив из этого списка — односимвольные элементы:
In [405]: arr1 = np.array(alist)
In [406]: arr1.shape
Out[406]: (60000,)
In [407]: timeit arr1=='C'
634 µs ± 12.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [408]: timeit np.sum(arr1=='C')
740 µs ± 23.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Это np.sum
относительно быстро. Это проверка на ‘C’, которая занимает больше всего времени.
Если я создам числовой массив того же размера, время подсчета будет немного быстрее. Проверка равенства по отношению к числу выполняется быстрее, чем эквивалентная проверка строки.
In [431]: arr2 = np.resize(np.array([1,2,3,4,5,6]),arr1.shape[0])
In [432]: np.sum(arr2==3)
Out[432]: 10000
In [433]: timeit np.sum(arr2==3)
155 µs ± 1.66 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
numpy
не обещает быть быстрее для всех операций Python. По большей части при работе со строковыми элементами это сильно зависит от собственного строкового кода Python.