Широковещательная пользовательская функция

#python #numpy #broadcast

#python #numpy #широковещательная

Вопрос:

Я хочу создать пользовательскую функцию, которая поддерживается широковещательной передачей.

В частности, у меня есть два массива, один из дат, а другой — времени, и я хочу объединить их, как в datetime.datetime.combine .

Я хотел бы иметь что-то подобное (это значения, которые у меня есть, но проблема более общая):

 x = array([datetime.date(2019, 1, 21), datetime.date(2019, 1, 21),
           datetime.date(2019, 1, 21)])
y = array([datetime.time(0, 0), datetime.time(0, 15), datetime.time(0, 30)]
  

И я хотел бы сделать что-то вроде этого:

 datetime.combine(out[:,0], out[:,1])
  

Чтобы получить тот же результат:

 np.asarray([datetime.combine(i,j) for i,j in zip(x,y)])
  

В более общем плане:

Предположим, у меня есть функция f(a,b) , и у меня есть два numpy-массива x,y . Есть ли способ применить правила широковещательной передачи и получить f(x,y) ?

Комментарии:

1. Вы пытаетесь векторизовать функцию python в массиве numpy?

2. Кроме того, я не думаю, что широковещательная передача означает то, что вы думаете, что это значит…

3. Чтобы ответить на ваш последний вопрос: это зависит от f .

4. @MadPhysicist что касается первого ответа, да, и я хотел бы избежать np.vectorize . Более общая часть является обобщением первой: я неправильно ее написал. В моем примере есть проблема с векторизацией, и, если я правильно понял значение широковещательной передачи, в последней части я предполагаю, что два массива могут быть разных размеров (но пригодны для широковещательной передачи).

5. Если вы не работаете с типами numpy, вы не получите много пользы от массивов numpy, содержащих ссылки на полномасштабные объекты python

Ответ №1:

Пользовательский ufuncs интерфейс подойдет, если вы хотите покопаться в c коде. Но ваш иллюстративный пример работает с datetime объектами. np.frompyfunc может быть весьма полезным для этого. С массивами объектов dtype numpy необходимо выполнять итерации на (близком) уровне Python, запуская код Python для каждого из объектов. Если вы вызываете a ufunc для массива объектов, он делегирует задачу соответствующему методу каждого объекта (и не выполняет его, такого метода не существует).

Давайте создадим ваши массивы дат:

 In [20]: from datetime import datetime   

In [35]: alist = [datetime(2019,1,21,0,0), datetime(2019,1,21,0,10),datetime(2020,1,21,0,0)]                                                           
In [36]: x = np.array([a.date() for a in alist])                                
In [37]: y = np.array([a.time() for a in alist])                                
In [38]: x                                                                      
Out[38]: 
array([datetime.date(2019, 1, 21), datetime.date(2019, 1, 21),
       datetime.date(2020, 1, 21)], dtype=object)
In [39]: y                                                                      
Out[39]: 
array([datetime.time(0, 0), datetime.time(0, 10), datetime.time(0, 0)],
      dtype=object)
  

И выполните объединение с пониманием списка:

 In [41]: np.array([datetime.combine(i,j) for i, j in zip(x,y)])                 
Out[41]: 
array([datetime.datetime(2019, 1, 21, 0, 0),
       datetime.datetime(2019, 1, 21, 0, 10),
       datetime.datetime(2020, 1, 21, 0, 0)], dtype=object)
  

и с frompyfunc :

 In [43]: np.frompyfunc(datetime.combine, 2,1)(x,y)                              
Out[43]: 
array([datetime.datetime(2019, 1, 21, 0, 0),
       datetime.datetime(2019, 1, 21, 0, 10),
       datetime.datetime(2020, 1, 21, 0, 0)], dtype=object)
  

С frompyfunc помощью мы можем применить широковещательную передачу

 In [44]: np.frompyfunc(datetime.combine, 2,1)(x,y[:,None])                      
Out[44]: 
array([[datetime.datetime(2019, 1, 21, 0, 0),
        datetime.datetime(2019, 1, 21, 0, 0),
        datetime.datetime(2020, 1, 21, 0, 0)],
       [datetime.datetime(2019, 1, 21, 0, 10),
        datetime.datetime(2019, 1, 21, 0, 10),
        datetime.datetime(2020, 1, 21, 0, 10)],
       [datetime.datetime(2019, 1, 21, 0, 0),
        datetime.datetime(2019, 1, 21, 0, 0),
        datetime.datetime(2020, 1, 21, 0, 0)]], dtype=object)
  

x можно было бы создать с frompyfunc :

 In [46]: np.frompyfunc(lambda a: a.date(),1,1)(alist)                           
Out[46]: 
array([datetime.date(2019, 1, 21), datetime.date(2019, 1, 21),
       datetime.date(2020, 1, 21)], dtype=object)
  

frompyfunc Версия combine немного быстрее

 In [47]: timeit np.frompyfunc(datetime.combine, 2,1)(x,y)                       
5.39 µs ± 181 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [48]: timeit np.array([datetime.combine(i,j) for i, j in zip(x,y)])          
11.8 µs ± 66.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
  

хотя значительная часть времени [48] поступает из интерфейса массива:

 In [51]: timeit [datetime.combine(i,j) for i, j in zip(x,y)]                    
3.91 µs ± 41.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
  

combine из списка версий x и y еще быстрее.

 In [52]: %%timeit xy=zip(x.tolist(),y.tolist()) 
    ...: [datetime.combine(i,j) for i,j in xy] 
190 ns ± 0.579 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
  

Ответ №2:

Если вы ищете что-то большее, чем numpy.vectorize, вы можете захотеть проверить numpy ufuncs:

https://docs.scipy.org/doc/numpy-1.16.1/reference/ufuncs.html

и вы можете попытаться создать свой собственный пользовательский ufunc https://docs.scipy.org/doc/numpy/user/c-info.ufunc-tutorial.html