numpy sum медленнее, чем количество строк

#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.