Трансляция пользовательских функций - PullRequest
0 голосов
/ 12 апреля 2019

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

В частности, у меня есть два массива, один из которых датируется, а другой - раз, и я хочу объединить их, как в 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), и у меня есть два массива x,y. Есть ли способ применить правила вещания и получить f(x,y)?

Ответы [ 2 ]

0 голосов
/ 12 апреля 2019

Пользовательский ufuncs хорошо, если вы хотите копаться в c коде. Но ваш иллюстративный пример работает с datetime объектами. np.frompyfunc может быть весьма полезным для этого. С массивами dtype объектов numpy должен выполнять итерацию на (почти) уровне Python, выполняя код Python для каждого из объектов. Если вы вызываете 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 комбайна немного быстрее

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)
0 голосов
/ 12 апреля 2019

Если вы ищете что-то большее, чем 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

...