@@ -73,7 +73,7 @@ four calls) using the `prun ipython magic function <http://ipython.org/ipython-d
73
73
74
74
.. ipython :: python
75
75
76
- % prun - l 4 df.apply(lambda x : integrate_f(x[' a' ], x[' b' ], x[' N' ]), axis = 1 )
76
+ % prun - l 4 df.apply(lambda x : integrate_f(x[' a' ], x[' b' ], x[' N' ]), axis = 1 ) # noqa E999
77
77
78
78
By far the majority of time is spend inside either ``integrate_f `` or ``f ``,
79
79
hence we'll concentrate our efforts cythonizing these two functions.
@@ -189,8 +189,10 @@ in Python, so maybe we could minimize these by cythonizing the apply part.
189
189
...: for i in range(N):
190
190
...: s += f_typed(a + i * dx)
191
191
...: return s * dx
192
- ...: cpdef np.ndarray[double] apply_integrate_f(np.ndarray col_a, np.ndarray col_b, np.ndarray col_N):
193
- ...: assert (col_a.dtype == np.float and col_b.dtype == np.float and col_N.dtype == np.int)
192
+ ...: cpdef np.ndarray[double] apply_integrate_f(np.ndarray col_a, np.ndarray col_b,
193
+ ...: np.ndarray col_N):
194
+ ...: assert (col_a.dtype == np.float
195
+ ...: and col_b.dtype == np.float and col_N.dtype == np.int)
194
196
...: cdef Py_ssize_t i, n = len(col_N)
195
197
...: assert (len(col_a) == len(col_b) == n)
196
198
...: cdef np.ndarray[double] res = np.empty(n)
@@ -271,7 +273,9 @@ advanced Cython techniques:
271
273
...: return s * dx
272
274
...: @cython.boundscheck(False)
273
275
...: @cython.wraparound(False)
274
- ...: cpdef np.ndarray[double] apply_integrate_f_wrap(np.ndarray[double] col_a, np.ndarray[double] col_b, np.ndarray[int] col_N):
276
+ ...: cpdef np.ndarray[double] apply_integrate_f_wrap(np.ndarray[double] col_a,
277
+ ...: np.ndarray[double] col_b,
278
+ ...: np.ndarray[int] col_N):
275
279
...: cdef int i, n = len(col_N)
276
280
...: assert len(col_a) == len(col_b) == n
277
281
...: cdef np.ndarray[double] res = np.empty(n)
@@ -317,45 +321,45 @@ take the plain Python code from above and annotate with the ``@jit`` decorator.
317
321
318
322
.. code-block :: python
319
323
320
- import numba
324
+ import numba
321
325
322
326
323
- @numba.jit
324
- def f_plain (x ):
325
- return x * (x - 1 )
327
+ @numba.jit
328
+ def f_plain (x ):
329
+ return x * (x - 1 )
326
330
327
331
328
- @numba.jit
329
- def integrate_f_numba (a , b , N ):
330
- s = 0
331
- dx = (b - a) / N
332
- for i in range (N):
333
- s += f_plain(a + i * dx)
334
- return s * dx
332
+ @numba.jit
333
+ def integrate_f_numba (a , b , N ):
334
+ s = 0
335
+ dx = (b - a) / N
336
+ for i in range (N):
337
+ s += f_plain(a + i * dx)
338
+ return s * dx
335
339
336
340
337
- @numba.jit
338
- def apply_integrate_f_numba (col_a , col_b , col_N ):
339
- n = len (col_N)
340
- result = np.empty(n, dtype = ' float64' )
341
- assert len (col_a) == len (col_b) == n
342
- for i in range (n):
343
- result[i] = integrate_f_numba(col_a[i], col_b[i], col_N[i])
344
- return result
341
+ @numba.jit
342
+ def apply_integrate_f_numba (col_a , col_b , col_N ):
343
+ n = len (col_N)
344
+ result = np.empty(n, dtype = ' float64' )
345
+ assert len (col_a) == len (col_b) == n
346
+ for i in range (n):
347
+ result[i] = integrate_f_numba(col_a[i], col_b[i], col_N[i])
348
+ return result
345
349
346
350
347
- def compute_numba (df ):
348
- result = apply_integrate_f_numba(df[' a' ].values, df[' b' ].values,
349
- df[' N' ].values)
350
- return pd.Series(result, index = df.index, name = ' result' )
351
+ def compute_numba (df ):
352
+ result = apply_integrate_f_numba(df[' a' ].values, df[' b' ].values,
353
+ df[' N' ].values)
354
+ return pd.Series(result, index = df.index, name = ' result' )
351
355
352
356
Note that we directly pass NumPy arrays to the Numba function. ``compute_numba `` is just a wrapper that provides a
353
357
nicer interface by passing/returning pandas objects.
354
358
355
359
.. code-block :: ipython
356
360
357
- In [4]: %timeit compute_numba(df)
358
- 1000 loops, best of 3: 798 us per loop
361
+ In [4]: %timeit compute_numba(df)
362
+ 1000 loops, best of 3: 798 us per loop
359
363
360
364
In this example, using Numba was faster than Cython.
361
365
@@ -368,30 +372,30 @@ Consider the following toy example of doubling each observation:
368
372
369
373
.. code-block :: python
370
374
371
- import numba
375
+ import numba
372
376
373
377
374
- def double_every_value_nonumba (x ):
375
- return x * 2
378
+ def double_every_value_nonumba (x ):
379
+ return x * 2
376
380
377
381
378
- @numba.vectorize
379
- def double_every_value_withnumba (x ):
380
- return x * 2
382
+ @numba.vectorize
383
+ def double_every_value_withnumba (x ): # noqa E501
384
+ return x * 2
381
385
382
386
.. code-block :: ipython
383
387
384
- # Custom function without numba
385
- In [5]: %timeit df['col1_doubled'] = df.a.apply(double_every_value_nonumba)
386
- 1000 loops, best of 3: 797 us per loop
388
+ # Custom function without numba
389
+ In [5]: %timeit df['col1_doubled'] = df.a.apply(double_every_value_nonumba) # noqa E501
390
+ 1000 loops, best of 3: 797 us per loop
387
391
388
- # Standard implementation (faster than a custom function)
389
- In [6]: %timeit df['col1_doubled'] = df.a* 2
390
- 1000 loops, best of 3: 233 us per loop
392
+ # Standard implementation (faster than a custom function)
393
+ In [6]: %timeit df['col1_doubled'] = df.a * 2
394
+ 1000 loops, best of 3: 233 us per loop
391
395
392
- # Custom function with numba
393
- In [7]: %timeit df['col1_doubled'] = double_every_value_withnumba(df.a.values)
394
- 1000 loops, best of 3: 145 us per loop
396
+ # Custom function with numba
397
+ In [7]: %timeit ( df['col1_doubled'] = double_every_value_withnumba(df.a.values)
398
+ 1000 loops, best of 3: 145 us per loop
395
399
396
400
Caveats
397
401
~~~~~~~
0 commit comments