6. Learn how to use cython#
Source from http://consulting.behnel.de/ and https://www.youtube.com/watch?v=vl9La7wH7QI
%load_ext cython
import sys
import Cython
import numpy as np
print("Python %d.%d.%d %s %s" % sys.version_info)
print("Cython %s" % Cython.__version__)
print("NumPy %s" % np.__version__)
Python 3.9.13 final 0
Cython 0.29.33
NumPy 1.21.5
6.1. Cython Intro#
6.1.1. using Python:#
from math import sin
sin(5)
-0.9589242746631385
6.1.2. using Cython:#
%load_ext cython
%%cython
from math import sin
sin(5)
sin(5)
-0.9589242746631385
or, use “libc.math” from C instead of “math” from Python
%%cython
cimport libc.math
sin_func = libc.math.sin
sin_func(5)
-0.9589242746631385
or,
%%cython
cimport libc.math
def csin(double x):
return libc.math.sin(x)
csin(5)
-0.9589242746631385
6.2. Show Cython’s code analysis#
by passing the ‘–annotate’ option(or just ‘-a’):
%%cython -a
cimport libc.math
def csin(double x):
return libc.math.sin(x)
Generated by Cython 0.29.33
Yellow lines hint at Python interaction.
Click on a line that starts with a "+
" to see the C code that Cython generated for it.
1: cimport libc.math
2:
+3: def csin(double x):
/* Python wrapper */ static PyObject *__pyx_pw_46_cython_magic_cad4649a9d1a193ec6b2e960c1d586c3_1csin(PyObject *__pyx_self, PyObject *__pyx_arg_x); /*proto*/ static PyMethodDef __pyx_mdef_46_cython_magic_cad4649a9d1a193ec6b2e960c1d586c3_1csin = {"csin", (PyCFunction)__pyx_pw_46_cython_magic_cad4649a9d1a193ec6b2e960c1d586c3_1csin, METH_O, 0}; static PyObject *__pyx_pw_46_cython_magic_cad4649a9d1a193ec6b2e960c1d586c3_1csin(PyObject *__pyx_self, PyObject *__pyx_arg_x) { double __pyx_v_x; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("csin (wrapper)", 0); assert(__pyx_arg_x); { __pyx_v_x = __pyx_PyFloat_AsDouble(__pyx_arg_x); if (unlikely((__pyx_v_x == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 3, __pyx_L3_error) } goto __pyx_L4_argument_unpacking_done; __pyx_L3_error:; __Pyx_AddTraceback("_cython_magic_cad4649a9d1a193ec6b2e960c1d586c3.csin", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; __pyx_r = __pyx_pf_46_cython_magic_cad4649a9d1a193ec6b2e960c1d586c3_csin(__pyx_self, ((double)__pyx_v_x)); int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; /* function exit code */ __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_46_cython_magic_cad4649a9d1a193ec6b2e960c1d586c3_csin(CYTHON_UNUSED PyObject *__pyx_self, double __pyx_v_x) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("csin", 0); /* … */ /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_AddTraceback("_cython_magic_cad4649a9d1a193ec6b2e960c1d586c3.csin", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* … */ __pyx_tuple_ = PyTuple_Pack(2, __pyx_n_s_x, __pyx_n_s_x); if (unlikely(!__pyx_tuple_)) __PYX_ERR(0, 3, __pyx_L1_error) __Pyx_GOTREF(__pyx_tuple_); __Pyx_GIVEREF(__pyx_tuple_); /* … */ __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_cad4649a9d1a193ec6b2e960c1d586c3_1csin, NULL, __pyx_n_s_cython_magic_cad4649a9d1a193ec6); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 3, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); if (PyDict_SetItem(__pyx_d, __pyx_n_s_csin, __pyx_t_1) < 0) __PYX_ERR(0, 3, __pyx_L1_error) __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+4: return libc.math.sin(x)
__Pyx_XDECREF(__pyx_r); __pyx_t_1 = PyFloat_FromDouble(sin(__pyx_v_x)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 4, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __pyx_r = __pyx_t_1; __pyx_t_1 = 0; goto __pyx_L0;
This produces an HTML report of Cython code interleaved with the generated C code. Lines are colored according to the level of “typedness” – white lines translate to pure C, while lines that require the Python C-API are yellow (darker as they translate to more C-API interaction). Lines that translate to C code have a plus (+) in front and can be clicked to show the generated code.
This report is invaluable when optimizing a function for speed, and for determining when to release the GIL: in general, a nogil block may contain only “white” code.
%%cython --annotate
cimport libc.math
def square_sin(double x):
cdef double x_square = x * x
return libc.math.sin(x_square)
Generated by Cython 0.29.33
Yellow lines hint at Python interaction.
Click on a line that starts with a "+
" to see the C code that Cython generated for it.
1: cimport libc.math
2:
+3: def square_sin(double x):
/* Python wrapper */ static PyObject *__pyx_pw_46_cython_magic_eabc7642c83c38db29415342c79668b1_1square_sin(PyObject *__pyx_self, PyObject *__pyx_arg_x); /*proto*/ static PyMethodDef __pyx_mdef_46_cython_magic_eabc7642c83c38db29415342c79668b1_1square_sin = {"square_sin", (PyCFunction)__pyx_pw_46_cython_magic_eabc7642c83c38db29415342c79668b1_1square_sin, METH_O, 0}; static PyObject *__pyx_pw_46_cython_magic_eabc7642c83c38db29415342c79668b1_1square_sin(PyObject *__pyx_self, PyObject *__pyx_arg_x) { double __pyx_v_x; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("square_sin (wrapper)", 0); assert(__pyx_arg_x); { __pyx_v_x = __pyx_PyFloat_AsDouble(__pyx_arg_x); if (unlikely((__pyx_v_x == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 3, __pyx_L3_error) } goto __pyx_L4_argument_unpacking_done; __pyx_L3_error:; __Pyx_AddTraceback("_cython_magic_eabc7642c83c38db29415342c79668b1.square_sin", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; __pyx_r = __pyx_pf_46_cython_magic_eabc7642c83c38db29415342c79668b1_square_sin(__pyx_self, ((double)__pyx_v_x)); int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; /* function exit code */ __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_46_cython_magic_eabc7642c83c38db29415342c79668b1_square_sin(CYTHON_UNUSED PyObject *__pyx_self, double __pyx_v_x) { double __pyx_v_x_square; PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("square_sin", 0); /* … */ /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_AddTraceback("_cython_magic_eabc7642c83c38db29415342c79668b1.square_sin", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* … */ __pyx_tuple_ = PyTuple_Pack(3, __pyx_n_s_x, __pyx_n_s_x, __pyx_n_s_x_square); if (unlikely(!__pyx_tuple_)) __PYX_ERR(0, 3, __pyx_L1_error) __Pyx_GOTREF(__pyx_tuple_); __Pyx_GIVEREF(__pyx_tuple_); /* … */ __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_eabc7642c83c38db29415342c79668b1_1square_sin, NULL, __pyx_n_s_cython_magic_eabc7642c83c38db29); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 3, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); if (PyDict_SetItem(__pyx_d, __pyx_n_s_square_sin, __pyx_t_1) < 0) __PYX_ERR(0, 3, __pyx_L1_error) __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+4: cdef double x_square = x * x
__pyx_v_x_square = (__pyx_v_x * __pyx_v_x);
+5: return libc.math.sin(x_square)
__Pyx_XDECREF(__pyx_r); __pyx_t_1 = PyFloat_FromDouble(sin(__pyx_v_x_square)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 5, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __pyx_r = __pyx_t_1; __pyx_t_1 = 0; goto __pyx_L0;
square_sin(5)
-0.13235175009777303
6.3. Examples#
6.3.1. Create some data to use for this example#
people = 44_000_000
average = 3703*12
print("Average income of {:,d} earners, Deutschland 2016: {:,d} €".format(people, average))
Average income of 44,000,000 earners, Deutschland 2016: 44,436 €
lacking offical data, let’s create some alternative facts Draw samples from a log-normal distribution. Mean value ‘mu’ of the underlying normal distribution is 10.64, standard deviation ‘sigma’ of the underlying normal distribution is 0.35, and size is people // 20.
# lacking offical data, let's create some alternative facts
import numpy as np
mu, sigma = 10.64, .35
samples = np.random.lognormal(mu, sigma, people // 20)
['{:,.2f} €'.format(x) for x in (np.min(samples), np.mean(samples), np.max(samples))]
['7,075.37 €', '44,421.90 €', '216,314.02 €']
Present these data in the figure:
import matplotlib.pyplot as plt
count, bins, ignored = plt.hist(samples[samples < 110000], 100, density=True, align='mid')
x = np.linspace(min(bins), max(bins), 101)
pdf = (np.exp(-(np.log(x) - mu)**2 / (2 * sigma**2)) / (x * sigma * np.sqrt(2 * np.pi)))
plt.plot(bins, pdf, linewidth=2, color='r')
plt.axis('tight')
plt.show()

6.3.2. Operative part#
Let’s calculate everyone’s taxes.(https://de.wikipedia.org/wiki/Einkommensteuer_(Deutschland)#Tarif_2017)
# from Wikipedia:
# =WENN(A1>256303; A1*0,45-16164,53;
# WENN(A1>54057; A1*0,42-8475,44;
# WENN(A1>13769; (A1-13769)*((A1-13769)*0,0000022376+0,2397)+939,57;
# WENN(A1>8820; (A1-8820)*((A1-8820)*0,0000100727+0,14); 0))))
def calculate_tax(income):
if income > 256303:
return income * 0.45 - 16164.53
elif income > 54057:
return income * 0.42 - 8475.44
elif income > 13769:
return (income - 13769) * ((income - 13769) * 0.0000022376 + 0.2397) + 939.57
elif income > 8820:
return (income - 8820) * ((income - 8820) * 0.0000100727 + 0.14)
else:
return 0
def average_income(incomes):
return sum(incomes) / len(incomes)
def average_tax_rate(incomes):
return sum(calculate_tax(x) for x in incomes) / sum(incomes)
average income and tax are:
average, calculate_tax(average)
(44436, 10394.834135626399)
average income and average tax are:
incomes_np = samples
incomes = list(samples)
avg_in, avg_tax = average_income(incomes), average_tax_rate(incomes)
avg_in, avg_tax
(44421.90256771482, 0.24290608306517872)
%%timeit
average_tax_rate(incomes)
3.31 s ± 113 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Here we record the execution time of python, and create ratios function to compare the results afterwards
Tpython = 3.31
import operator
timings = {}
def ratios(**new):
assert len(new) == 1
timings.update(**new)
last = list(new.values())[0]
print('\n'.join('%10s: %7.2f' % (name, t / last)
for name, t in sorted(timings.items(), key=operator.itemgetter(1))))
ratios(python=Tpython)
python: 1.00
6.3.3. Numpy slicing#
Let’s use the following code instead of the above section.
# from Wikipedia:
# =WENN(A1>256303; A1*0,45-16164,53;
# WENN(A1>54057; A1*0,42-8475,44;
# WENN(A1>13769; (A1-13769)*((A1-13769)*0,0000022376+0,2397)+939,57;
# WENN(A1>8820; (A1-8820)*((A1-8820)*0,0000100727+0,14); 0))))
def calculate_tax_numpy_segments(d):
tax_seg1 = d[(d > 256303)] * 0.45 - 16164.53
tax_seg2 = d[(d > 54057) & (d <= 256303)] * 0.42 - 8475.44
seg3 = d[(d > 13769) & (d <= 54057)] - 13769
seg4 = d[(d > 8820) & (d <= 13769)] - 8820
prog_seg3 = seg3 * 0.0000022376 + 0.2397
prog_seg4 = seg4 * 0.0000100727 + 0.14
return (
tax_seg1.sum() +
tax_seg2.sum() +
(seg3 * prog_seg3 + 939.57).sum() +
(seg4 * prog_seg4).sum()
) / d.sum()
average income and average tax are:
incomes_np.mean(), calculate_tax_numpy_segments(incomes_np)
(44421.9025677147, 0.24290608306519038)
%%timeit
calculate_tax_numpy_segments(incomes_np)
79.4 ms ± 5.45 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
ratios(numpy=0.0794)
numpy: 1.00
python: 41.69
6.3.4. NumPy ufunc#
calculate_tax_numpy = np.frompyfunc(calculate_tax, 1, 1)
average tax is:
calculate_tax_numpy(incomes_np).sum() / incomes_np.sum()
0.24290608306517936
%%timeit
calculate_tax_numpy(incomes_np).sum() / incomes_np.sum()
911 ms ± 55.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
ratios(ufunc=0.911)
numpy: 0.09
ufunc: 1.00
python: 3.63
6.3.5. Cython#
%%cython -a
# plain copy from Python code above, only renamed functions
def calculate_tax_cy(income):
if income > 256303:
return income * 0.45 - 16164.53
elif income > 54057:
return income * 0.42 - 8475.44
elif income > 13769:
return (income - 13769) * ((income - 13769) * 0.0000022376 + 0.2397) + 939.57
elif income > 8820:
return (income - 8820) * ((income - 8820) * 0.0000100727 + 0.14)
else:
return 0
def average_income_cy(incomes):
return sum(incomes) / len(incomes)
def average_tax_rate_cy(incomes):
return sum(calculate_tax_cy(x) for x in incomes) / sum(incomes)
Generated by Cython 0.29.33
Yellow lines hint at Python interaction.
Click on a line that starts with a "+
" to see the C code that Cython generated for it.
01: # plain copy from Python code above, only renamed functions
02:
+03: def calculate_tax_cy(income):
/* Python wrapper */ static PyObject *__pyx_pw_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_1calculate_tax_cy(PyObject *__pyx_self, PyObject *__pyx_v_income); /*proto*/ static PyMethodDef __pyx_mdef_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_1calculate_tax_cy = {"calculate_tax_cy", (PyCFunction)__pyx_pw_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_1calculate_tax_cy, METH_O, 0}; static PyObject *__pyx_pw_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_1calculate_tax_cy(PyObject *__pyx_self, PyObject *__pyx_v_income) { PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("calculate_tax_cy (wrapper)", 0); __pyx_r = __pyx_pf_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_calculate_tax_cy(__pyx_self, ((PyObject *)__pyx_v_income)); /* function exit code */ __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_calculate_tax_cy(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_income) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("calculate_tax_cy", 0); /* … */ /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_XDECREF(__pyx_t_3); __Pyx_XDECREF(__pyx_t_4); __Pyx_AddTraceback("_cython_magic_823f81150a04040e0b5e1a44e74fe722.calculate_tax_cy", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* … */ __pyx_tuple_ = PyTuple_Pack(1, __pyx_n_s_income); if (unlikely(!__pyx_tuple_)) __PYX_ERR(0, 3, __pyx_L1_error) __Pyx_GOTREF(__pyx_tuple_); __Pyx_GIVEREF(__pyx_tuple_); /* … */ __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_1calculate_tax_cy, NULL, __pyx_n_s_cython_magic_823f81150a04040e0b); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 3, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); if (PyDict_SetItem(__pyx_d, __pyx_n_s_calculate_tax_cy, __pyx_t_1) < 0) __PYX_ERR(0, 3, __pyx_L1_error) __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __pyx_codeobj__2 = (PyObject*)__Pyx_PyCode_New(1, 0, 1, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple_, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_C_Users_DELL_ipython_cython__cyt, __pyx_n_s_calculate_tax_cy, 3, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__2)) __PYX_ERR(0, 3, __pyx_L1_error)
+04: if income > 256303:
__pyx_t_1 = PyObject_RichCompare(__pyx_v_income, __pyx_int_256303, Py_GT); __Pyx_XGOTREF(__pyx_t_1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 4, __pyx_L1_error) __pyx_t_2 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely(__pyx_t_2 < 0)) __PYX_ERR(0, 4, __pyx_L1_error) __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; if (__pyx_t_2) { /* … */ }
+05: return income * 0.45 - 16164.53
__Pyx_XDECREF(__pyx_r); __pyx_t_1 = PyNumber_Multiply(__pyx_v_income, __pyx_float_0_45); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 5, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __pyx_t_3 = __Pyx_PyFloat_SubtractObjC(__pyx_t_1, __pyx_float_16164_53, 16164.53, 0, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 5, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __pyx_r = __pyx_t_3; __pyx_t_3 = 0; goto __pyx_L0;
+06: elif income > 54057:
__pyx_t_3 = PyObject_RichCompare(__pyx_v_income, __pyx_int_54057, Py_GT); __Pyx_XGOTREF(__pyx_t_3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 6, __pyx_L1_error) __pyx_t_2 = __Pyx_PyObject_IsTrue(__pyx_t_3); if (unlikely(__pyx_t_2 < 0)) __PYX_ERR(0, 6, __pyx_L1_error) __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; if (__pyx_t_2) { /* … */ }
+07: return income * 0.42 - 8475.44
__Pyx_XDECREF(__pyx_r); __pyx_t_3 = PyNumber_Multiply(__pyx_v_income, __pyx_float_0_42); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 7, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); __pyx_t_1 = __Pyx_PyFloat_SubtractObjC(__pyx_t_3, __pyx_float_8475_44, 8475.44, 0, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 7, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __pyx_r = __pyx_t_1; __pyx_t_1 = 0; goto __pyx_L0;
+08: elif income > 13769:
__pyx_t_1 = PyObject_RichCompare(__pyx_v_income, __pyx_int_13769, Py_GT); __Pyx_XGOTREF(__pyx_t_1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 8, __pyx_L1_error) __pyx_t_2 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely(__pyx_t_2 < 0)) __PYX_ERR(0, 8, __pyx_L1_error) __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; if (__pyx_t_2) { /* … */ }
+09: return (income - 13769) * ((income - 13769) * 0.0000022376 + 0.2397) + 939.57
__Pyx_XDECREF(__pyx_r); __pyx_t_1 = __Pyx_PyInt_SubtractObjC(__pyx_v_income, __pyx_int_13769, 0x35C9, 0, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 9, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __pyx_t_3 = __Pyx_PyInt_SubtractObjC(__pyx_v_income, __pyx_int_13769, 0x35C9, 0, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 9, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); __pyx_t_4 = PyNumber_Multiply(__pyx_t_3, __pyx_float_0_0000022376); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 9, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __pyx_t_3 = __Pyx_PyFloat_AddObjC(__pyx_t_4, __pyx_float_0_2397, 0.2397, 0, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 9, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; __pyx_t_4 = PyNumber_Multiply(__pyx_t_1, __pyx_t_3); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 9, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __pyx_t_3 = __Pyx_PyFloat_AddObjC(__pyx_t_4, __pyx_float_939_57, 939.57, 0, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 9, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; __pyx_r = __pyx_t_3; __pyx_t_3 = 0; goto __pyx_L0;
+10: elif income > 8820:
__pyx_t_3 = PyObject_RichCompare(__pyx_v_income, __pyx_int_8820, Py_GT); __Pyx_XGOTREF(__pyx_t_3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 10, __pyx_L1_error) __pyx_t_2 = __Pyx_PyObject_IsTrue(__pyx_t_3); if (unlikely(__pyx_t_2 < 0)) __PYX_ERR(0, 10, __pyx_L1_error) __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; if (__pyx_t_2) { /* … */ }
+11: return (income - 8820) * ((income - 8820) * 0.0000100727 + 0.14)
__Pyx_XDECREF(__pyx_r); __pyx_t_3 = __Pyx_PyInt_SubtractObjC(__pyx_v_income, __pyx_int_8820, 0x2274, 0, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 11, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); __pyx_t_4 = __Pyx_PyInt_SubtractObjC(__pyx_v_income, __pyx_int_8820, 0x2274, 0, 0); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 11, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); __pyx_t_1 = PyNumber_Multiply(__pyx_t_4, __pyx_float_0_0000100727); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 11, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; __pyx_t_4 = __Pyx_PyFloat_AddObjC(__pyx_t_1, __pyx_float_0_14, 0.14, 0, 0); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 11, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __pyx_t_1 = PyNumber_Multiply(__pyx_t_3, __pyx_t_4); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 11, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; __pyx_r = __pyx_t_1; __pyx_t_1 = 0; goto __pyx_L0;
12: else:
+13: return 0
/*else*/ { __Pyx_XDECREF(__pyx_r); __Pyx_INCREF(__pyx_int_0); __pyx_r = __pyx_int_0; goto __pyx_L0; }
14:
+15: def average_income_cy(incomes):
/* Python wrapper */ static PyObject *__pyx_pw_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_3average_income_cy(PyObject *__pyx_self, PyObject *__pyx_v_incomes); /*proto*/ static PyMethodDef __pyx_mdef_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_3average_income_cy = {"average_income_cy", (PyCFunction)__pyx_pw_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_3average_income_cy, METH_O, 0}; static PyObject *__pyx_pw_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_3average_income_cy(PyObject *__pyx_self, PyObject *__pyx_v_incomes) { PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("average_income_cy (wrapper)", 0); __pyx_r = __pyx_pf_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_2average_income_cy(__pyx_self, ((PyObject *)__pyx_v_incomes)); /* function exit code */ __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_2average_income_cy(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_incomes) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("average_income_cy", 0); /* … */ /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_XDECREF(__pyx_t_3); __Pyx_XDECREF(__pyx_t_4); __Pyx_AddTraceback("_cython_magic_823f81150a04040e0b5e1a44e74fe722.average_income_cy", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* … */ __pyx_tuple__3 = PyTuple_Pack(1, __pyx_n_s_incomes); if (unlikely(!__pyx_tuple__3)) __PYX_ERR(0, 15, __pyx_L1_error) __Pyx_GOTREF(__pyx_tuple__3); __Pyx_GIVEREF(__pyx_tuple__3); /* … */ __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_3average_income_cy, NULL, __pyx_n_s_cython_magic_823f81150a04040e0b); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 15, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); if (PyDict_SetItem(__pyx_d, __pyx_n_s_average_income_cy, __pyx_t_1) < 0) __PYX_ERR(0, 15, __pyx_L1_error) __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __pyx_codeobj__4 = (PyObject*)__Pyx_PyCode_New(1, 0, 1, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__3, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_C_Users_DELL_ipython_cython__cyt, __pyx_n_s_average_income_cy, 15, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__4)) __PYX_ERR(0, 15, __pyx_L1_error)
+16: return sum(incomes) / len(incomes)
__Pyx_XDECREF(__pyx_r); __pyx_t_1 = __Pyx_PyObject_CallOneArg(__pyx_builtin_sum, __pyx_v_incomes); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 16, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __pyx_t_2 = PyObject_Length(__pyx_v_incomes); if (unlikely(__pyx_t_2 == ((Py_ssize_t)-1))) __PYX_ERR(0, 16, __pyx_L1_error) __pyx_t_3 = PyInt_FromSsize_t(__pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 16, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); __pyx_t_4 = __Pyx_PyNumber_Divide(__pyx_t_1, __pyx_t_3); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 16, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __pyx_r = __pyx_t_4; __pyx_t_4 = 0; goto __pyx_L0;
17:
+18: def average_tax_rate_cy(incomes):
/* Python wrapper */ static PyObject *__pyx_pw_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_5average_tax_rate_cy(PyObject *__pyx_self, PyObject *__pyx_v_incomes); /*proto*/ static PyMethodDef __pyx_mdef_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_5average_tax_rate_cy = {"average_tax_rate_cy", (PyCFunction)__pyx_pw_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_5average_tax_rate_cy, METH_O, 0}; static PyObject *__pyx_pw_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_5average_tax_rate_cy(PyObject *__pyx_self, PyObject *__pyx_v_incomes) { PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("average_tax_rate_cy (wrapper)", 0); __pyx_r = __pyx_pf_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_4average_tax_rate_cy(__pyx_self, ((PyObject *)__pyx_v_incomes)); /* function exit code */ __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_gb_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_19average_tax_rate_cy_2generator(__pyx_CoroutineObject *__pyx_generator, CYTHON_UNUSED PyThreadState *__pyx_tstate, PyObject *__pyx_sent_value); /* proto */ /* … */ static PyObject *__pyx_pf_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_4average_tax_rate_cy(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_incomes) { struct __pyx_obj_46_cython_magic_823f81150a04040e0b5e1a44e74fe722___pyx_scope_struct__average_tax_rate_cy *__pyx_cur_scope; PyObject *__pyx_gb_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_19average_tax_rate_cy_2generator = 0; PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("average_tax_rate_cy", 0); __pyx_cur_scope = (struct __pyx_obj_46_cython_magic_823f81150a04040e0b5e1a44e74fe722___pyx_scope_struct__average_tax_rate_cy *)__pyx_tp_new_46_cython_magic_823f81150a04040e0b5e1a44e74fe722___pyx_scope_struct__average_tax_rate_cy(__pyx_ptype_46_cython_magic_823f81150a04040e0b5e1a44e74fe722___pyx_scope_struct__average_tax_rate_cy, __pyx_empty_tuple, NULL); if (unlikely(!__pyx_cur_scope)) { __pyx_cur_scope = ((struct __pyx_obj_46_cython_magic_823f81150a04040e0b5e1a44e74fe722___pyx_scope_struct__average_tax_rate_cy *)Py_None); __Pyx_INCREF(Py_None); __PYX_ERR(0, 18, __pyx_L1_error) } else { __Pyx_GOTREF(__pyx_cur_scope); } __pyx_cur_scope->__pyx_v_incomes = __pyx_v_incomes; __Pyx_INCREF(__pyx_cur_scope->__pyx_v_incomes); __Pyx_GIVEREF(__pyx_cur_scope->__pyx_v_incomes); /* … */ /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_XDECREF(__pyx_t_2); __Pyx_XDECREF(__pyx_t_3); __Pyx_AddTraceback("_cython_magic_823f81150a04040e0b5e1a44e74fe722.average_tax_rate_cy", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __Pyx_XDECREF(__pyx_gb_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_19average_tax_rate_cy_2generator); __Pyx_DECREF(((PyObject *)__pyx_cur_scope)); __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* … */ __pyx_tuple__5 = PyTuple_Pack(3, __pyx_n_s_incomes, __pyx_n_s_genexpr, __pyx_n_s_genexpr); if (unlikely(!__pyx_tuple__5)) __PYX_ERR(0, 18, __pyx_L1_error) __Pyx_GOTREF(__pyx_tuple__5); __Pyx_GIVEREF(__pyx_tuple__5); /* … */ __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_5average_tax_rate_cy, NULL, __pyx_n_s_cython_magic_823f81150a04040e0b); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 18, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); if (PyDict_SetItem(__pyx_d, __pyx_n_s_average_tax_rate_cy, __pyx_t_1) < 0) __PYX_ERR(0, 18, __pyx_L1_error) __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; /* … */ struct __pyx_obj_46_cython_magic_823f81150a04040e0b5e1a44e74fe722___pyx_scope_struct__average_tax_rate_cy { PyObject_HEAD PyObject *__pyx_v_incomes; };
+19: return sum(calculate_tax_cy(x) for x in incomes) / sum(incomes)
static PyObject *__pyx_pf_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_19average_tax_rate_cy_genexpr(PyObject *__pyx_self) { struct __pyx_obj_46_cython_magic_823f81150a04040e0b5e1a44e74fe722___pyx_scope_struct_1_genexpr *__pyx_cur_scope; PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("genexpr", 0); __pyx_cur_scope = (struct __pyx_obj_46_cython_magic_823f81150a04040e0b5e1a44e74fe722___pyx_scope_struct_1_genexpr *)__pyx_tp_new_46_cython_magic_823f81150a04040e0b5e1a44e74fe722___pyx_scope_struct_1_genexpr(__pyx_ptype_46_cython_magic_823f81150a04040e0b5e1a44e74fe722___pyx_scope_struct_1_genexpr, __pyx_empty_tuple, NULL); if (unlikely(!__pyx_cur_scope)) { __pyx_cur_scope = ((struct __pyx_obj_46_cython_magic_823f81150a04040e0b5e1a44e74fe722___pyx_scope_struct_1_genexpr *)Py_None); __Pyx_INCREF(Py_None); __PYX_ERR(0, 19, __pyx_L1_error) } else { __Pyx_GOTREF(__pyx_cur_scope); } __pyx_cur_scope->__pyx_outer_scope = (struct __pyx_obj_46_cython_magic_823f81150a04040e0b5e1a44e74fe722___pyx_scope_struct__average_tax_rate_cy *) __pyx_self; __Pyx_INCREF(((PyObject *)__pyx_cur_scope->__pyx_outer_scope)); __Pyx_GIVEREF(__pyx_cur_scope->__pyx_outer_scope); { __pyx_CoroutineObject *gen = __Pyx_Generator_New((__pyx_coroutine_body_t) __pyx_gb_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_19average_tax_rate_cy_2generator, NULL, (PyObject *) __pyx_cur_scope, __pyx_n_s_genexpr, __pyx_n_s_average_tax_rate_cy_locals_genex, __pyx_n_s_cython_magic_823f81150a04040e0b); if (unlikely(!gen)) __PYX_ERR(0, 19, __pyx_L1_error) __Pyx_DECREF(__pyx_cur_scope); __Pyx_RefNannyFinishContext(); return (PyObject *) gen; } /* function exit code */ __pyx_L1_error:; __Pyx_AddTraceback("_cython_magic_823f81150a04040e0b5e1a44e74fe722.average_tax_rate_cy.genexpr", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __Pyx_DECREF(((PyObject *)__pyx_cur_scope)); __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_gb_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_19average_tax_rate_cy_2generator(__pyx_CoroutineObject *__pyx_generator, CYTHON_UNUSED PyThreadState *__pyx_tstate, PyObject *__pyx_sent_value) /* generator body */ { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("genexpr", 0); __pyx_L3_first_run:; if (unlikely(!__pyx_sent_value)) __PYX_ERR(0, 19, __pyx_L1_error) if (unlikely(!__pyx_cur_scope->__pyx_outer_scope->__pyx_v_incomes)) { __Pyx_RaiseClosureNameError("incomes"); __PYX_ERR(0, 19, __pyx_L1_error) } if (likely(PyList_CheckExact(__pyx_cur_scope->__pyx_outer_scope->__pyx_v_incomes)) || PyTuple_CheckExact(__pyx_cur_scope->__pyx_outer_scope->__pyx_v_incomes)) { __pyx_t_1 = __pyx_cur_scope->__pyx_outer_scope->__pyx_v_incomes; __Pyx_INCREF(__pyx_t_1); __pyx_t_2 = 0; __pyx_t_3 = NULL; } else { __pyx_t_2 = -1; __pyx_t_1 = PyObject_GetIter(__pyx_cur_scope->__pyx_outer_scope->__pyx_v_incomes); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 19, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __pyx_t_3 = Py_TYPE(__pyx_t_1)->tp_iternext; if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 19, __pyx_L1_error) } for (;;) { if (likely(!__pyx_t_3)) { if (likely(PyList_CheckExact(__pyx_t_1))) { if (__pyx_t_2 >= PyList_GET_SIZE(__pyx_t_1)) break; #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS __pyx_t_4 = PyList_GET_ITEM(__pyx_t_1, __pyx_t_2); __Pyx_INCREF(__pyx_t_4); __pyx_t_2++; if (unlikely(0 < 0)) __PYX_ERR(0, 19, __pyx_L1_error) #else __pyx_t_4 = PySequence_ITEM(__pyx_t_1, __pyx_t_2); __pyx_t_2++; if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 19, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); #endif } else { if (__pyx_t_2 >= PyTuple_GET_SIZE(__pyx_t_1)) break; #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS __pyx_t_4 = PyTuple_GET_ITEM(__pyx_t_1, __pyx_t_2); __Pyx_INCREF(__pyx_t_4); __pyx_t_2++; if (unlikely(0 < 0)) __PYX_ERR(0, 19, __pyx_L1_error) #else __pyx_t_4 = PySequence_ITEM(__pyx_t_1, __pyx_t_2); __pyx_t_2++; if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 19, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); #endif } } else { __pyx_t_4 = __pyx_t_3(__pyx_t_1); if (unlikely(!__pyx_t_4)) { PyObject* exc_type = PyErr_Occurred(); if (exc_type) { if (likely(__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) PyErr_Clear(); else __PYX_ERR(0, 19, __pyx_L1_error) } break; } __Pyx_GOTREF(__pyx_t_4); } __Pyx_XGOTREF(__pyx_cur_scope->__pyx_v_x); __Pyx_XDECREF_SET(__pyx_cur_scope->__pyx_v_x, __pyx_t_4); __Pyx_GIVEREF(__pyx_t_4); __pyx_t_4 = 0; __Pyx_GetModuleGlobalName(__pyx_t_5, __pyx_n_s_calculate_tax_cy); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 19, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_5); __pyx_t_6 = NULL; if (CYTHON_UNPACK_METHODS && unlikely(PyMethod_Check(__pyx_t_5))) { __pyx_t_6 = PyMethod_GET_SELF(__pyx_t_5); if (likely(__pyx_t_6)) { PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_5); __Pyx_INCREF(__pyx_t_6); __Pyx_INCREF(function); __Pyx_DECREF_SET(__pyx_t_5, function); } } __pyx_t_4 = (__pyx_t_6) ? __Pyx_PyObject_Call2Args(__pyx_t_5, __pyx_t_6, __pyx_cur_scope->__pyx_v_x) : __Pyx_PyObject_CallOneArg(__pyx_t_5, __pyx_cur_scope->__pyx_v_x); __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0; if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 19, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; __pyx_r = __pyx_t_4; __pyx_t_4 = 0; __Pyx_XGIVEREF(__pyx_t_1); __pyx_cur_scope->__pyx_t_0 = __pyx_t_1; __pyx_cur_scope->__pyx_t_1 = __pyx_t_2; __pyx_cur_scope->__pyx_t_2 = __pyx_t_3; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); __Pyx_Coroutine_ResetAndClearException(__pyx_generator); /* return from generator, yielding value */ __pyx_generator->resume_label = 1; return __pyx_r; __pyx_L6_resume_from_yield:; __pyx_t_1 = __pyx_cur_scope->__pyx_t_0; __pyx_cur_scope->__pyx_t_0 = 0; __Pyx_XGOTREF(__pyx_t_1); __pyx_t_2 = __pyx_cur_scope->__pyx_t_1; __pyx_t_3 = __pyx_cur_scope->__pyx_t_2; if (unlikely(!__pyx_sent_value)) __PYX_ERR(0, 19, __pyx_L1_error) } __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; CYTHON_MAYBE_UNUSED_VAR(__pyx_cur_scope); /* function exit code */ PyErr_SetNone(PyExc_StopIteration); goto __pyx_L0; __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_XDECREF(__pyx_t_4); __Pyx_XDECREF(__pyx_t_5); __Pyx_XDECREF(__pyx_t_6); __Pyx_AddTraceback("genexpr", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_L0:; __Pyx_XDECREF(__pyx_r); __pyx_r = 0; #if !CYTHON_USE_EXC_INFO_STACK __Pyx_Coroutine_ResetAndClearException(__pyx_generator); #endif __pyx_generator->resume_label = -1; __Pyx_Coroutine_clear((PyObject*)__pyx_generator); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* … */ __Pyx_XDECREF(__pyx_r); __pyx_t_1 = __pyx_pf_46_cython_magic_823f81150a04040e0b5e1a44e74fe722_19average_tax_rate_cy_genexpr(((PyObject*)__pyx_cur_scope)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 19, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __pyx_t_2 = __Pyx_PyObject_CallOneArg(__pyx_builtin_sum, __pyx_t_1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 19, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_2); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __pyx_t_1 = __Pyx_PyObject_CallOneArg(__pyx_builtin_sum, __pyx_cur_scope->__pyx_v_incomes); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 19, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __pyx_t_3 = __Pyx_PyNumber_Divide(__pyx_t_2, __pyx_t_1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 19, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __pyx_r = __pyx_t_3; __pyx_t_3 = 0; goto __pyx_L0;
average income and average tax are:
average_income_cy(incomes), average_tax_rate_cy(incomes)
(44421.90256771482, 0.24290608306517872)
%%timeit
average_tax_rate_cy(incomes)
2.75 s ± 49.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
ratios(cython_copy=2.75)
numpy: 0.03
ufunc: 0.33
cython_copy: 1.00
python: 1.20
6.3.6. Faster Cython: static types#
%%cython -a
# plain copy from Python code above, only renamed functions
cpdef double calculate_tax_cy(double income):
if income > 256303:
return income * 0.45 - 16164.53
elif income > 54057:
return income * 0.42 - 8475.44
elif income > 13769:
return (income - 13769) * ((income - 13769) * 0.0000022376 + 0.2397) + 939.57
elif income > 8820:
return (income - 8820) * ((income - 8820) * 0.0000100727 + 0.14)
else:
return 0
def average_income_cy(incomes):
return sum(incomes) / len(incomes)
def average_tax_rate_cy(list incomes not None):
cdef double tax_sum = 0, total = 0, income
for income in incomes:
tax_sum += calculate_tax_cy(income)
total += income
return tax_sum / total
Generated by Cython 0.29.33
Yellow lines hint at Python interaction.
Click on a line that starts with a "+
" to see the C code that Cython generated for it.
01: # plain copy from Python code above, only renamed functions
02:
+03: cpdef double calculate_tax_cy(double income):
static PyObject *__pyx_pw_46_cython_magic_0f4867f07ddf1678f65b653962aeb9a8_1calculate_tax_cy(PyObject *__pyx_self, PyObject *__pyx_arg_income); /*proto*/ static double __pyx_f_46_cython_magic_0f4867f07ddf1678f65b653962aeb9a8_calculate_tax_cy(double __pyx_v_income, CYTHON_UNUSED int __pyx_skip_dispatch) { double __pyx_r; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("calculate_tax_cy", 0); /* … */ /* function exit code */ __pyx_L0:; __Pyx_RefNannyFinishContext(); return __pyx_r; } /* Python wrapper */ static PyObject *__pyx_pw_46_cython_magic_0f4867f07ddf1678f65b653962aeb9a8_1calculate_tax_cy(PyObject *__pyx_self, PyObject *__pyx_arg_income); /*proto*/ static PyObject *__pyx_pw_46_cython_magic_0f4867f07ddf1678f65b653962aeb9a8_1calculate_tax_cy(PyObject *__pyx_self, PyObject *__pyx_arg_income) { double __pyx_v_income; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("calculate_tax_cy (wrapper)", 0); assert(__pyx_arg_income); { __pyx_v_income = __pyx_PyFloat_AsDouble(__pyx_arg_income); if (unlikely((__pyx_v_income == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 3, __pyx_L3_error) } goto __pyx_L4_argument_unpacking_done; __pyx_L3_error:; __Pyx_AddTraceback("_cython_magic_0f4867f07ddf1678f65b653962aeb9a8.calculate_tax_cy", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; __pyx_r = __pyx_pf_46_cython_magic_0f4867f07ddf1678f65b653962aeb9a8_calculate_tax_cy(__pyx_self, ((double)__pyx_v_income)); int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; /* function exit code */ __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_46_cython_magic_0f4867f07ddf1678f65b653962aeb9a8_calculate_tax_cy(CYTHON_UNUSED PyObject *__pyx_self, double __pyx_v_income) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("calculate_tax_cy", 0); __Pyx_XDECREF(__pyx_r); __pyx_t_1 = PyFloat_FromDouble(__pyx_f_46_cython_magic_0f4867f07ddf1678f65b653962aeb9a8_calculate_tax_cy(__pyx_v_income, 0)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 3, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __pyx_r = __pyx_t_1; __pyx_t_1 = 0; goto __pyx_L0; /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_AddTraceback("_cython_magic_0f4867f07ddf1678f65b653962aeb9a8.calculate_tax_cy", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; }
+04: if income > 256303:
__pyx_t_1 = ((__pyx_v_income > 256303.0) != 0); if (__pyx_t_1) { /* … */ }
+05: return income * 0.45 - 16164.53
__pyx_r = ((__pyx_v_income * 0.45) - 16164.53); goto __pyx_L0;
+06: elif income > 54057:
__pyx_t_1 = ((__pyx_v_income > 54057.0) != 0); if (__pyx_t_1) { /* … */ }
+07: return income * 0.42 - 8475.44
__pyx_r = ((__pyx_v_income * 0.42) - 8475.44); goto __pyx_L0;
+08: elif income > 13769:
__pyx_t_1 = ((__pyx_v_income > 13769.0) != 0); if (__pyx_t_1) { /* … */ }
+09: return (income - 13769) * ((income - 13769) * 0.0000022376 + 0.2397) + 939.57
__pyx_r = (((__pyx_v_income - 13769.0) * (((__pyx_v_income - 13769.0) * 0.0000022376) + 0.2397)) + 939.57); goto __pyx_L0;
+10: elif income > 8820:
__pyx_t_1 = ((__pyx_v_income > 8820.0) != 0); if (__pyx_t_1) { /* … */ }
+11: return (income - 8820) * ((income - 8820) * 0.0000100727 + 0.14)
__pyx_r = ((__pyx_v_income - 8820.0) * (((__pyx_v_income - 8820.0) * 0.0000100727) + 0.14)); goto __pyx_L0;
12: else:
+13: return 0
/*else*/ { __pyx_r = 0.0; goto __pyx_L0; }
14:
+15: def average_income_cy(incomes):
/* Python wrapper */ static PyObject *__pyx_pw_46_cython_magic_0f4867f07ddf1678f65b653962aeb9a8_3average_income_cy(PyObject *__pyx_self, PyObject *__pyx_v_incomes); /*proto*/ static PyMethodDef __pyx_mdef_46_cython_magic_0f4867f07ddf1678f65b653962aeb9a8_3average_income_cy = {"average_income_cy", (PyCFunction)__pyx_pw_46_cython_magic_0f4867f07ddf1678f65b653962aeb9a8_3average_income_cy, METH_O, 0}; static PyObject *__pyx_pw_46_cython_magic_0f4867f07ddf1678f65b653962aeb9a8_3average_income_cy(PyObject *__pyx_self, PyObject *__pyx_v_incomes) { PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("average_income_cy (wrapper)", 0); __pyx_r = __pyx_pf_46_cython_magic_0f4867f07ddf1678f65b653962aeb9a8_2average_income_cy(__pyx_self, ((PyObject *)__pyx_v_incomes)); /* function exit code */ __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_46_cython_magic_0f4867f07ddf1678f65b653962aeb9a8_2average_income_cy(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_incomes) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("average_income_cy", 0); /* … */ /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_XDECREF(__pyx_t_3); __Pyx_XDECREF(__pyx_t_4); __Pyx_AddTraceback("_cython_magic_0f4867f07ddf1678f65b653962aeb9a8.average_income_cy", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* … */ __pyx_tuple_ = PyTuple_Pack(1, __pyx_n_s_incomes); if (unlikely(!__pyx_tuple_)) __PYX_ERR(0, 15, __pyx_L1_error) __Pyx_GOTREF(__pyx_tuple_); __Pyx_GIVEREF(__pyx_tuple_); /* … */ __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_0f4867f07ddf1678f65b653962aeb9a8_3average_income_cy, NULL, __pyx_n_s_cython_magic_0f4867f07ddf1678f6); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 15, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); if (PyDict_SetItem(__pyx_d, __pyx_n_s_average_income_cy, __pyx_t_1) < 0) __PYX_ERR(0, 15, __pyx_L1_error) __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __pyx_codeobj__2 = (PyObject*)__Pyx_PyCode_New(1, 0, 1, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple_, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_C_Users_DELL_ipython_cython__cyt, __pyx_n_s_average_income_cy, 15, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__2)) __PYX_ERR(0, 15, __pyx_L1_error)
+16: return sum(incomes) / len(incomes)
__Pyx_XDECREF(__pyx_r); __pyx_t_1 = __Pyx_PyObject_CallOneArg(__pyx_builtin_sum, __pyx_v_incomes); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 16, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __pyx_t_2 = PyObject_Length(__pyx_v_incomes); if (unlikely(__pyx_t_2 == ((Py_ssize_t)-1))) __PYX_ERR(0, 16, __pyx_L1_error) __pyx_t_3 = PyInt_FromSsize_t(__pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 16, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); __pyx_t_4 = __Pyx_PyNumber_Divide(__pyx_t_1, __pyx_t_3); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 16, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __pyx_r = __pyx_t_4; __pyx_t_4 = 0; goto __pyx_L0;
17:
+18: def average_tax_rate_cy(list incomes not None):
/* Python wrapper */ static PyObject *__pyx_pw_46_cython_magic_0f4867f07ddf1678f65b653962aeb9a8_5average_tax_rate_cy(PyObject *__pyx_self, PyObject *__pyx_v_incomes); /*proto*/ static PyMethodDef __pyx_mdef_46_cython_magic_0f4867f07ddf1678f65b653962aeb9a8_5average_tax_rate_cy = {"average_tax_rate_cy", (PyCFunction)__pyx_pw_46_cython_magic_0f4867f07ddf1678f65b653962aeb9a8_5average_tax_rate_cy, METH_O, 0}; static PyObject *__pyx_pw_46_cython_magic_0f4867f07ddf1678f65b653962aeb9a8_5average_tax_rate_cy(PyObject *__pyx_self, PyObject *__pyx_v_incomes) { PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("average_tax_rate_cy (wrapper)", 0); if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_incomes), (&PyList_Type), 0, "incomes", 1))) __PYX_ERR(0, 18, __pyx_L1_error) __pyx_r = __pyx_pf_46_cython_magic_0f4867f07ddf1678f65b653962aeb9a8_4average_tax_rate_cy(__pyx_self, ((PyObject*)__pyx_v_incomes)); int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; /* function exit code */ goto __pyx_L0; __pyx_L1_error:; __pyx_r = NULL; __pyx_L0:; __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_46_cython_magic_0f4867f07ddf1678f65b653962aeb9a8_4average_tax_rate_cy(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_incomes) { double __pyx_v_tax_sum; double __pyx_v_total; double __pyx_v_income; PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("average_tax_rate_cy", 0); /* … */ /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_XDECREF(__pyx_t_3); __Pyx_AddTraceback("_cython_magic_0f4867f07ddf1678f65b653962aeb9a8.average_tax_rate_cy", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* … */ __pyx_tuple__3 = PyTuple_Pack(4, __pyx_n_s_incomes, __pyx_n_s_tax_sum, __pyx_n_s_total, __pyx_n_s_income); if (unlikely(!__pyx_tuple__3)) __PYX_ERR(0, 18, __pyx_L1_error) __Pyx_GOTREF(__pyx_tuple__3); __Pyx_GIVEREF(__pyx_tuple__3); /* … */ __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_0f4867f07ddf1678f65b653962aeb9a8_5average_tax_rate_cy, NULL, __pyx_n_s_cython_magic_0f4867f07ddf1678f6); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 18, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); if (PyDict_SetItem(__pyx_d, __pyx_n_s_average_tax_rate_cy, __pyx_t_1) < 0) __PYX_ERR(0, 18, __pyx_L1_error) __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+19: cdef double tax_sum = 0, total = 0, income
__pyx_v_tax_sum = 0.0; __pyx_v_total = 0.0;
+20: for income in incomes:
__pyx_t_1 = __pyx_v_incomes; __Pyx_INCREF(__pyx_t_1); __pyx_t_2 = 0; for (;;) { if (__pyx_t_2 >= PyList_GET_SIZE(__pyx_t_1)) break; #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS __pyx_t_3 = PyList_GET_ITEM(__pyx_t_1, __pyx_t_2); __Pyx_INCREF(__pyx_t_3); __pyx_t_2++; if (unlikely(0 < 0)) __PYX_ERR(0, 20, __pyx_L1_error) #else __pyx_t_3 = PySequence_ITEM(__pyx_t_1, __pyx_t_2); __pyx_t_2++; if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 20, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); #endif __pyx_t_4 = __pyx_PyFloat_AsDouble(__pyx_t_3); if (unlikely((__pyx_t_4 == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 20, __pyx_L1_error) __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __pyx_v_income = __pyx_t_4; /* … */ } __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+21: tax_sum += calculate_tax_cy(income)
__pyx_v_tax_sum = (__pyx_v_tax_sum + __pyx_f_46_cython_magic_0f4867f07ddf1678f65b653962aeb9a8_calculate_tax_cy(__pyx_v_income, 0));
+22: total += income
__pyx_v_total = (__pyx_v_total + __pyx_v_income);
+23: return tax_sum / total
__Pyx_XDECREF(__pyx_r); if (unlikely(__pyx_v_total == 0)) { PyErr_SetString(PyExc_ZeroDivisionError, "float division"); __PYX_ERR(0, 23, __pyx_L1_error) } __pyx_t_1 = PyFloat_FromDouble((__pyx_v_tax_sum / __pyx_v_total)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 23, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __pyx_r = __pyx_t_1; __pyx_t_1 = 0; goto __pyx_L0;
average_tax_rate_cy(incomes)
0.24290608306517872
%%timeit
average_tax_rate_cy(incomes)
32.6 ms ± 804 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
ratios(cython_typed=0.0326)
cython_typed: 1.00
numpy: 2.44
ufunc: 27.94
cython_copy: 84.36
python: 101.53
%%cython -a
# plain copy from Python code above, only renamed functions
cpdef double calculate_tax_cys(double income):
if income > 256303:
return income * 0.45 - 16164.53
elif income > 54057:
return income * 0.42 - 8475.44
elif income > 13769:
return (income - 13769) * ((income - 13769) * 0.0000022376 + 0.2397) + 939.57
elif income > 8820:
return (income - 8820) * ((income - 8820) * 0.0000100727 + 0.14)
else:
return 0
def average_tax_rate_cys(list incomes not None):
cdef double tax = 0, income = 0, x
for x in incomes:
income += x
tax += calculate_tax_cys(x)
return tax / income
Generated by Cython 0.29.33
Yellow lines hint at Python interaction.
Click on a line that starts with a "+
" to see the C code that Cython generated for it.
01: # plain copy from Python code above, only renamed functions
02:
+03: cpdef double calculate_tax_cys(double income):
static PyObject *__pyx_pw_46_cython_magic_5bd02f52af0119c2bd997e794f5ea94a_1calculate_tax_cys(PyObject *__pyx_self, PyObject *__pyx_arg_income); /*proto*/ static double __pyx_f_46_cython_magic_5bd02f52af0119c2bd997e794f5ea94a_calculate_tax_cys(double __pyx_v_income, CYTHON_UNUSED int __pyx_skip_dispatch) { double __pyx_r; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("calculate_tax_cys", 0); /* … */ /* function exit code */ __pyx_L0:; __Pyx_RefNannyFinishContext(); return __pyx_r; } /* Python wrapper */ static PyObject *__pyx_pw_46_cython_magic_5bd02f52af0119c2bd997e794f5ea94a_1calculate_tax_cys(PyObject *__pyx_self, PyObject *__pyx_arg_income); /*proto*/ static PyObject *__pyx_pw_46_cython_magic_5bd02f52af0119c2bd997e794f5ea94a_1calculate_tax_cys(PyObject *__pyx_self, PyObject *__pyx_arg_income) { double __pyx_v_income; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("calculate_tax_cys (wrapper)", 0); assert(__pyx_arg_income); { __pyx_v_income = __pyx_PyFloat_AsDouble(__pyx_arg_income); if (unlikely((__pyx_v_income == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 3, __pyx_L3_error) } goto __pyx_L4_argument_unpacking_done; __pyx_L3_error:; __Pyx_AddTraceback("_cython_magic_5bd02f52af0119c2bd997e794f5ea94a.calculate_tax_cys", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; __pyx_r = __pyx_pf_46_cython_magic_5bd02f52af0119c2bd997e794f5ea94a_calculate_tax_cys(__pyx_self, ((double)__pyx_v_income)); int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; /* function exit code */ __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_46_cython_magic_5bd02f52af0119c2bd997e794f5ea94a_calculate_tax_cys(CYTHON_UNUSED PyObject *__pyx_self, double __pyx_v_income) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("calculate_tax_cys", 0); __Pyx_XDECREF(__pyx_r); __pyx_t_1 = PyFloat_FromDouble(__pyx_f_46_cython_magic_5bd02f52af0119c2bd997e794f5ea94a_calculate_tax_cys(__pyx_v_income, 0)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 3, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __pyx_r = __pyx_t_1; __pyx_t_1 = 0; goto __pyx_L0; /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_AddTraceback("_cython_magic_5bd02f52af0119c2bd997e794f5ea94a.calculate_tax_cys", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; }
+04: if income > 256303:
__pyx_t_1 = ((__pyx_v_income > 256303.0) != 0); if (__pyx_t_1) { /* … */ }
+05: return income * 0.45 - 16164.53
__pyx_r = ((__pyx_v_income * 0.45) - 16164.53); goto __pyx_L0;
+06: elif income > 54057:
__pyx_t_1 = ((__pyx_v_income > 54057.0) != 0); if (__pyx_t_1) { /* … */ }
+07: return income * 0.42 - 8475.44
__pyx_r = ((__pyx_v_income * 0.42) - 8475.44); goto __pyx_L0;
+08: elif income > 13769:
__pyx_t_1 = ((__pyx_v_income > 13769.0) != 0); if (__pyx_t_1) { /* … */ }
+09: return (income - 13769) * ((income - 13769) * 0.0000022376 + 0.2397) + 939.57
__pyx_r = (((__pyx_v_income - 13769.0) * (((__pyx_v_income - 13769.0) * 0.0000022376) + 0.2397)) + 939.57); goto __pyx_L0;
+10: elif income > 8820:
__pyx_t_1 = ((__pyx_v_income > 8820.0) != 0); if (__pyx_t_1) { /* … */ }
+11: return (income - 8820) * ((income - 8820) * 0.0000100727 + 0.14)
__pyx_r = ((__pyx_v_income - 8820.0) * (((__pyx_v_income - 8820.0) * 0.0000100727) + 0.14)); goto __pyx_L0;
12: else:
+13: return 0
/*else*/ { __pyx_r = 0.0; goto __pyx_L0; }
14:
+15: def average_tax_rate_cys(list incomes not None):
/* Python wrapper */ static PyObject *__pyx_pw_46_cython_magic_5bd02f52af0119c2bd997e794f5ea94a_3average_tax_rate_cys(PyObject *__pyx_self, PyObject *__pyx_v_incomes); /*proto*/ static PyMethodDef __pyx_mdef_46_cython_magic_5bd02f52af0119c2bd997e794f5ea94a_3average_tax_rate_cys = {"average_tax_rate_cys", (PyCFunction)__pyx_pw_46_cython_magic_5bd02f52af0119c2bd997e794f5ea94a_3average_tax_rate_cys, METH_O, 0}; static PyObject *__pyx_pw_46_cython_magic_5bd02f52af0119c2bd997e794f5ea94a_3average_tax_rate_cys(PyObject *__pyx_self, PyObject *__pyx_v_incomes) { PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("average_tax_rate_cys (wrapper)", 0); if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_incomes), (&PyList_Type), 0, "incomes", 1))) __PYX_ERR(0, 15, __pyx_L1_error) __pyx_r = __pyx_pf_46_cython_magic_5bd02f52af0119c2bd997e794f5ea94a_2average_tax_rate_cys(__pyx_self, ((PyObject*)__pyx_v_incomes)); int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; /* function exit code */ goto __pyx_L0; __pyx_L1_error:; __pyx_r = NULL; __pyx_L0:; __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_46_cython_magic_5bd02f52af0119c2bd997e794f5ea94a_2average_tax_rate_cys(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_incomes) { double __pyx_v_tax; double __pyx_v_income; double __pyx_v_x; PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("average_tax_rate_cys", 0); /* … */ /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_XDECREF(__pyx_t_3); __Pyx_AddTraceback("_cython_magic_5bd02f52af0119c2bd997e794f5ea94a.average_tax_rate_cys", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* … */ __pyx_tuple_ = PyTuple_Pack(4, __pyx_n_s_incomes, __pyx_n_s_tax, __pyx_n_s_income, __pyx_n_s_x); if (unlikely(!__pyx_tuple_)) __PYX_ERR(0, 15, __pyx_L1_error) __Pyx_GOTREF(__pyx_tuple_); __Pyx_GIVEREF(__pyx_tuple_); /* … */ __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_5bd02f52af0119c2bd997e794f5ea94a_3average_tax_rate_cys, NULL, __pyx_n_s_cython_magic_5bd02f52af0119c2bd); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 15, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); if (PyDict_SetItem(__pyx_d, __pyx_n_s_average_tax_rate_cys, __pyx_t_1) < 0) __PYX_ERR(0, 15, __pyx_L1_error) __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+16: cdef double tax = 0, income = 0, x
__pyx_v_tax = 0.0; __pyx_v_income = 0.0;
+17: for x in incomes:
__pyx_t_1 = __pyx_v_incomes; __Pyx_INCREF(__pyx_t_1); __pyx_t_2 = 0; for (;;) { if (__pyx_t_2 >= PyList_GET_SIZE(__pyx_t_1)) break; #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS __pyx_t_3 = PyList_GET_ITEM(__pyx_t_1, __pyx_t_2); __Pyx_INCREF(__pyx_t_3); __pyx_t_2++; if (unlikely(0 < 0)) __PYX_ERR(0, 17, __pyx_L1_error) #else __pyx_t_3 = PySequence_ITEM(__pyx_t_1, __pyx_t_2); __pyx_t_2++; if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 17, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); #endif __pyx_t_4 = __pyx_PyFloat_AsDouble(__pyx_t_3); if (unlikely((__pyx_t_4 == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 17, __pyx_L1_error) __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __pyx_v_x = __pyx_t_4; /* … */ } __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+18: income += x
__pyx_v_income = (__pyx_v_income + __pyx_v_x);
+19: tax += calculate_tax_cys(x)
__pyx_v_tax = (__pyx_v_tax + __pyx_f_46_cython_magic_5bd02f52af0119c2bd997e794f5ea94a_calculate_tax_cys(__pyx_v_x, 0));
+20: return tax / income
__Pyx_XDECREF(__pyx_r); if (unlikely(__pyx_v_income == 0)) { PyErr_SetString(PyExc_ZeroDivisionError, "float division"); __PYX_ERR(0, 20, __pyx_L1_error) } __pyx_t_1 = PyFloat_FromDouble((__pyx_v_tax / __pyx_v_income)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 20, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __pyx_r = __pyx_t_1; __pyx_t_1 = 0; goto __pyx_L0;
average_tax_rate_cys(incomes)
0.24290608306517872
%%timeit
average_tax_rate_cys(incomes)
31.4 ms ± 416 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
ratios(cython_typed=0.0314)
cython_typed: 1.00
numpy: 2.53
ufunc: 29.01
cython_copy: 87.58
python: 105.41
6.4. Exercise: static typing for speed#
import math
def circular_distance(radius, lon1, lat1, lon2, lat2):
x = math.pi/180.0
a = (90.0-lat1) * x
b = (90.0-lat2) * x
theta = (lon2-lon1) * x
c = math.acos((math.cos(a)*math.cos(b)) + (math.sin(a)*math.sin(b)*math.cos(theta)))
return radius*c
print(circular_distance(10, 1.2, 2, 2, 4.3))
0.4249430879322785
%%timeit
circular_distance(10, 1.2, 2, 2, 4.3)
1.3 µs ± 46.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%%cython -a
# copy and optimise ...
# hint: use "libc.math" from C instead of "math" from Python
cimport libc.math
cpdef double circular_distance_cy(double radius, double lon1, double lat1, double lon2, double lat2):
cdef double x, a, b, theta, c
x = libc.math.pi/180.0
a = (90.0-lat1) * x
b = (90.0-lat2) * x
theta = (lon2-lon1) * x
c = libc.math.acos((libc.math.cos(a)*libc.math.cos(b)) + (libc.math.sin(a)*libc.math.sin(b)*libc.math.cos(theta)))
return radius*c
print(circular_distance_cy(10, 1.2, 2, 2, 4.3))
0.4249430879322785
Generated by Cython 0.29.33
Yellow lines hint at Python interaction.
Click on a line that starts with a "+
" to see the C code that Cython generated for it.
01: # copy and optimise ...
02: # hint: use "libc.math" from C instead of "math" from Python
03:
04: cimport libc.math
05:
+06: cpdef double circular_distance_cy(double radius, double lon1, double lat1, double lon2, double lat2):
static PyObject *__pyx_pw_46_cython_magic_05731be6528a27eca09a408da6a4fbb1_1circular_distance_cy(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ static double __pyx_f_46_cython_magic_05731be6528a27eca09a408da6a4fbb1_circular_distance_cy(double __pyx_v_radius, double __pyx_v_lon1, double __pyx_v_lat1, double __pyx_v_lon2, double __pyx_v_lat2, CYTHON_UNUSED int __pyx_skip_dispatch) { double __pyx_v_x; double __pyx_v_a; double __pyx_v_b; double __pyx_v_theta; double __pyx_v_c; double __pyx_r; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("circular_distance_cy", 0); /* … */ /* function exit code */ __pyx_L0:; __Pyx_RefNannyFinishContext(); return __pyx_r; } /* Python wrapper */ static PyObject *__pyx_pw_46_cython_magic_05731be6528a27eca09a408da6a4fbb1_1circular_distance_cy(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ static PyObject *__pyx_pw_46_cython_magic_05731be6528a27eca09a408da6a4fbb1_1circular_distance_cy(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { double __pyx_v_radius; double __pyx_v_lon1; double __pyx_v_lat1; double __pyx_v_lon2; double __pyx_v_lat2; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("circular_distance_cy (wrapper)", 0); { static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_radius,&__pyx_n_s_lon1,&__pyx_n_s_lat1,&__pyx_n_s_lon2,&__pyx_n_s_lat2,0}; PyObject* values[5] = {0,0,0,0,0}; if (unlikely(__pyx_kwds)) { Py_ssize_t kw_args; const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args); switch (pos_args) { case 5: values[4] = PyTuple_GET_ITEM(__pyx_args, 4); CYTHON_FALLTHROUGH; case 4: values[3] = PyTuple_GET_ITEM(__pyx_args, 3); CYTHON_FALLTHROUGH; case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2); CYTHON_FALLTHROUGH; case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1); CYTHON_FALLTHROUGH; case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0); CYTHON_FALLTHROUGH; case 0: break; default: goto __pyx_L5_argtuple_error; } kw_args = PyDict_Size(__pyx_kwds); switch (pos_args) { case 0: if (likely((values[0] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_radius)) != 0)) kw_args--; else goto __pyx_L5_argtuple_error; CYTHON_FALLTHROUGH; case 1: if (likely((values[1] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_lon1)) != 0)) kw_args--; else { __Pyx_RaiseArgtupleInvalid("circular_distance_cy", 1, 5, 5, 1); __PYX_ERR(0, 6, __pyx_L3_error) } CYTHON_FALLTHROUGH; case 2: if (likely((values[2] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_lat1)) != 0)) kw_args--; else { __Pyx_RaiseArgtupleInvalid("circular_distance_cy", 1, 5, 5, 2); __PYX_ERR(0, 6, __pyx_L3_error) } CYTHON_FALLTHROUGH; case 3: if (likely((values[3] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_lon2)) != 0)) kw_args--; else { __Pyx_RaiseArgtupleInvalid("circular_distance_cy", 1, 5, 5, 3); __PYX_ERR(0, 6, __pyx_L3_error) } CYTHON_FALLTHROUGH; case 4: if (likely((values[4] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_lat2)) != 0)) kw_args--; else { __Pyx_RaiseArgtupleInvalid("circular_distance_cy", 1, 5, 5, 4); __PYX_ERR(0, 6, __pyx_L3_error) } } if (unlikely(kw_args > 0)) { if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "circular_distance_cy") < 0)) __PYX_ERR(0, 6, __pyx_L3_error) } } else if (PyTuple_GET_SIZE(__pyx_args) != 5) { goto __pyx_L5_argtuple_error; } else { values[0] = PyTuple_GET_ITEM(__pyx_args, 0); values[1] = PyTuple_GET_ITEM(__pyx_args, 1); values[2] = PyTuple_GET_ITEM(__pyx_args, 2); values[3] = PyTuple_GET_ITEM(__pyx_args, 3); values[4] = PyTuple_GET_ITEM(__pyx_args, 4); } __pyx_v_radius = __pyx_PyFloat_AsDouble(values[0]); if (unlikely((__pyx_v_radius == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 6, __pyx_L3_error) __pyx_v_lon1 = __pyx_PyFloat_AsDouble(values[1]); if (unlikely((__pyx_v_lon1 == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 6, __pyx_L3_error) __pyx_v_lat1 = __pyx_PyFloat_AsDouble(values[2]); if (unlikely((__pyx_v_lat1 == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 6, __pyx_L3_error) __pyx_v_lon2 = __pyx_PyFloat_AsDouble(values[3]); if (unlikely((__pyx_v_lon2 == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 6, __pyx_L3_error) __pyx_v_lat2 = __pyx_PyFloat_AsDouble(values[4]); if (unlikely((__pyx_v_lat2 == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 6, __pyx_L3_error) } goto __pyx_L4_argument_unpacking_done; __pyx_L5_argtuple_error:; __Pyx_RaiseArgtupleInvalid("circular_distance_cy", 1, 5, 5, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 6, __pyx_L3_error) __pyx_L3_error:; __Pyx_AddTraceback("_cython_magic_05731be6528a27eca09a408da6a4fbb1.circular_distance_cy", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; __pyx_r = __pyx_pf_46_cython_magic_05731be6528a27eca09a408da6a4fbb1_circular_distance_cy(__pyx_self, __pyx_v_radius, __pyx_v_lon1, __pyx_v_lat1, __pyx_v_lon2, __pyx_v_lat2); int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; /* function exit code */ __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_46_cython_magic_05731be6528a27eca09a408da6a4fbb1_circular_distance_cy(CYTHON_UNUSED PyObject *__pyx_self, double __pyx_v_radius, double __pyx_v_lon1, double __pyx_v_lat1, double __pyx_v_lon2, double __pyx_v_lat2) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("circular_distance_cy", 0); __Pyx_XDECREF(__pyx_r); __pyx_t_1 = PyFloat_FromDouble(__pyx_f_46_cython_magic_05731be6528a27eca09a408da6a4fbb1_circular_distance_cy(__pyx_v_radius, __pyx_v_lon1, __pyx_v_lat1, __pyx_v_lon2, __pyx_v_lat2, 0)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 6, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __pyx_r = __pyx_t_1; __pyx_t_1 = 0; goto __pyx_L0; /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_AddTraceback("_cython_magic_05731be6528a27eca09a408da6a4fbb1.circular_distance_cy", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; }
07: cdef double x, a, b, theta, c
+08: x = libc.math.pi/180.0
__pyx_v_x = (((double)M_PI) / 180.0);
+09: a = (90.0-lat1) * x
__pyx_v_a = ((90.0 - __pyx_v_lat1) * __pyx_v_x);
+10: b = (90.0-lat2) * x
__pyx_v_b = ((90.0 - __pyx_v_lat2) * __pyx_v_x);
+11: theta = (lon2-lon1) * x
__pyx_v_theta = ((__pyx_v_lon2 - __pyx_v_lon1) * __pyx_v_x);
+12: c = libc.math.acos((libc.math.cos(a)*libc.math.cos(b)) + (libc.math.sin(a)*libc.math.sin(b)*libc.math.cos(theta)))
__pyx_v_c = acos(((cos(__pyx_v_a) * cos(__pyx_v_b)) + ((sin(__pyx_v_a) * sin(__pyx_v_b)) * cos(__pyx_v_theta))));
+13: return radius*c
__pyx_r = (__pyx_v_radius * __pyx_v_c); goto __pyx_L0;
14:
+15: print(circular_distance_cy(10, 1.2, 2, 2, 4.3))
__pyx_t_1 = PyFloat_FromDouble(__pyx_f_46_cython_magic_05731be6528a27eca09a408da6a4fbb1_circular_distance_cy(10.0, 1.2, 2.0, 2.0, 4.3, 0)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 15, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __pyx_t_2 = __Pyx_PyObject_CallOneArg(__pyx_builtin_print, __pyx_t_1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 15, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_2); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
%%timeit
circular_distance_cy(10, 1.2, 2, 2, 4.3)
227 ns ± 10.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
6.4.1. Faster Cython: processing memory views#
%%cython -a
cpdef double calculate_tax_memview(double income):
if income > 256303:
return income * 0.45 - 16164.53
elif income > 54057:
return income * 0.42 - 8475.44
elif income > 13769:
return (income - 13769) * ((income - 13769) * 0.0000022376 + 0.2397) + 939.57
elif income > 8820:
return (income - 8820) * ((income - 8820) * 0.0000100727 + 0.14)
else:
return 0
def average_tax_rate_memview(double[:] incomes):
cdef double tax = 0, income = 0, x
cdef size_t i
for i in range(incomes.shape[0]):
income += incomes[i]
tax += calculate_tax_memview(incomes[i])
return tax / income
Generated by Cython 0.29.33
Yellow lines hint at Python interaction.
Click on a line that starts with a "+
" to see the C code that Cython generated for it.
01:
+02: cpdef double calculate_tax_memview(double income):
static PyObject *__pyx_pw_46_cython_magic_7f4afb92afda6df9c3f40c5d1cc59a75_1calculate_tax_memview(PyObject *__pyx_self, PyObject *__pyx_arg_income); /*proto*/ static double __pyx_f_46_cython_magic_7f4afb92afda6df9c3f40c5d1cc59a75_calculate_tax_memview(double __pyx_v_income, CYTHON_UNUSED int __pyx_skip_dispatch) { double __pyx_r; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("calculate_tax_memview", 0); /* … */ /* function exit code */ __pyx_L0:; __Pyx_RefNannyFinishContext(); return __pyx_r; } /* Python wrapper */ static PyObject *__pyx_pw_46_cython_magic_7f4afb92afda6df9c3f40c5d1cc59a75_1calculate_tax_memview(PyObject *__pyx_self, PyObject *__pyx_arg_income); /*proto*/ static PyObject *__pyx_pw_46_cython_magic_7f4afb92afda6df9c3f40c5d1cc59a75_1calculate_tax_memview(PyObject *__pyx_self, PyObject *__pyx_arg_income) { double __pyx_v_income; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("calculate_tax_memview (wrapper)", 0); assert(__pyx_arg_income); { __pyx_v_income = __pyx_PyFloat_AsDouble(__pyx_arg_income); if (unlikely((__pyx_v_income == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 2, __pyx_L3_error) } goto __pyx_L4_argument_unpacking_done; __pyx_L3_error:; __Pyx_AddTraceback("_cython_magic_7f4afb92afda6df9c3f40c5d1cc59a75.calculate_tax_memview", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; __pyx_r = __pyx_pf_46_cython_magic_7f4afb92afda6df9c3f40c5d1cc59a75_calculate_tax_memview(__pyx_self, ((double)__pyx_v_income)); int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; /* function exit code */ __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_46_cython_magic_7f4afb92afda6df9c3f40c5d1cc59a75_calculate_tax_memview(CYTHON_UNUSED PyObject *__pyx_self, double __pyx_v_income) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("calculate_tax_memview", 0); __Pyx_XDECREF(__pyx_r); __pyx_t_1 = PyFloat_FromDouble(__pyx_f_46_cython_magic_7f4afb92afda6df9c3f40c5d1cc59a75_calculate_tax_memview(__pyx_v_income, 0)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 2, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __pyx_r = __pyx_t_1; __pyx_t_1 = 0; goto __pyx_L0; /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_AddTraceback("_cython_magic_7f4afb92afda6df9c3f40c5d1cc59a75.calculate_tax_memview", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* … */ __pyx_t_1 = __Pyx_PyDict_NewPresized(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 2, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); if (PyDict_SetItem(__pyx_d, __pyx_n_s_test, __pyx_t_1) < 0) __PYX_ERR(0, 2, __pyx_L1_error) __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+03: if income > 256303:
__pyx_t_1 = ((__pyx_v_income > 256303.0) != 0); if (__pyx_t_1) { /* … */ }
+04: return income * 0.45 - 16164.53
__pyx_r = ((__pyx_v_income * 0.45) - 16164.53); goto __pyx_L0;
+05: elif income > 54057:
__pyx_t_1 = ((__pyx_v_income > 54057.0) != 0); if (__pyx_t_1) { /* … */ }
+06: return income * 0.42 - 8475.44
__pyx_r = ((__pyx_v_income * 0.42) - 8475.44); goto __pyx_L0;
+07: elif income > 13769:
__pyx_t_1 = ((__pyx_v_income > 13769.0) != 0); if (__pyx_t_1) { /* … */ }
+08: return (income - 13769) * ((income - 13769) * 0.0000022376 + 0.2397) + 939.57
__pyx_r = (((__pyx_v_income - 13769.0) * (((__pyx_v_income - 13769.0) * 0.0000022376) + 0.2397)) + 939.57); goto __pyx_L0;
+09: elif income > 8820:
__pyx_t_1 = ((__pyx_v_income > 8820.0) != 0); if (__pyx_t_1) { /* … */ }
+10: return (income - 8820) * ((income - 8820) * 0.0000100727 + 0.14)
__pyx_r = ((__pyx_v_income - 8820.0) * (((__pyx_v_income - 8820.0) * 0.0000100727) + 0.14)); goto __pyx_L0;
11: else:
+12: return 0
/*else*/ { __pyx_r = 0.0; goto __pyx_L0; }
13:
+14: def average_tax_rate_memview(double[:] incomes):
/* Python wrapper */ static PyObject *__pyx_pw_46_cython_magic_7f4afb92afda6df9c3f40c5d1cc59a75_3average_tax_rate_memview(PyObject *__pyx_self, PyObject *__pyx_arg_incomes); /*proto*/ static PyMethodDef __pyx_mdef_46_cython_magic_7f4afb92afda6df9c3f40c5d1cc59a75_3average_tax_rate_memview = {"average_tax_rate_memview", (PyCFunction)__pyx_pw_46_cython_magic_7f4afb92afda6df9c3f40c5d1cc59a75_3average_tax_rate_memview, METH_O, 0}; static PyObject *__pyx_pw_46_cython_magic_7f4afb92afda6df9c3f40c5d1cc59a75_3average_tax_rate_memview(PyObject *__pyx_self, PyObject *__pyx_arg_incomes) { __Pyx_memviewslice __pyx_v_incomes = { 0, 0, { 0 }, { 0 }, { 0 } }; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("average_tax_rate_memview (wrapper)", 0); assert(__pyx_arg_incomes); { __pyx_v_incomes = __Pyx_PyObject_to_MemoryviewSlice_ds_double(__pyx_arg_incomes, PyBUF_WRITABLE); if (unlikely(!__pyx_v_incomes.memview)) __PYX_ERR(0, 14, __pyx_L3_error) } goto __pyx_L4_argument_unpacking_done; __pyx_L3_error:; __Pyx_AddTraceback("_cython_magic_7f4afb92afda6df9c3f40c5d1cc59a75.average_tax_rate_memview", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; __pyx_r = __pyx_pf_46_cython_magic_7f4afb92afda6df9c3f40c5d1cc59a75_2average_tax_rate_memview(__pyx_self, __pyx_v_incomes); int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; /* function exit code */ __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_46_cython_magic_7f4afb92afda6df9c3f40c5d1cc59a75_2average_tax_rate_memview(CYTHON_UNUSED PyObject *__pyx_self, __Pyx_memviewslice __pyx_v_incomes) { double __pyx_v_tax; double __pyx_v_income; size_t __pyx_v_i; PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("average_tax_rate_memview", 0); /* … */ /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_6); __Pyx_AddTraceback("_cython_magic_7f4afb92afda6df9c3f40c5d1cc59a75.average_tax_rate_memview", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __PYX_XDEC_MEMVIEW(&__pyx_v_incomes, 1); __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* … */ __pyx_tuple__20 = PyTuple_Pack(6, __pyx_n_s_incomes, __pyx_n_s_incomes, __pyx_n_s_tax, __pyx_n_s_income, __pyx_n_s_x, __pyx_n_s_i); if (unlikely(!__pyx_tuple__20)) __PYX_ERR(0, 14, __pyx_L1_error) __Pyx_GOTREF(__pyx_tuple__20); __Pyx_GIVEREF(__pyx_tuple__20); /* … */ __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_7f4afb92afda6df9c3f40c5d1cc59a75_3average_tax_rate_memview, NULL, __pyx_n_s_cython_magic_7f4afb92afda6df9c3); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 14, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); if (PyDict_SetItem(__pyx_d, __pyx_n_s_average_tax_rate_memview, __pyx_t_1) < 0) __PYX_ERR(0, 14, __pyx_L1_error) __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __pyx_codeobj__21 = (PyObject*)__Pyx_PyCode_New(1, 0, 6, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__20, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_C_Users_DELL_ipython_cython__cyt, __pyx_n_s_average_tax_rate_memview, 14, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__21)) __PYX_ERR(0, 14, __pyx_L1_error)
+15: cdef double tax = 0, income = 0, x
__pyx_v_tax = 0.0; __pyx_v_income = 0.0;
16: cdef size_t i
+17: for i in range(incomes.shape[0]):
__pyx_t_1 = (__pyx_v_incomes.shape[0]); __pyx_t_2 = __pyx_t_1; for (__pyx_t_3 = 0; __pyx_t_3 < __pyx_t_2; __pyx_t_3+=1) { __pyx_v_i = __pyx_t_3;
+18: income += incomes[i]
__pyx_t_4 = __pyx_v_i; __pyx_t_5 = -1; if (unlikely(__pyx_t_4 >= (size_t)__pyx_v_incomes.shape[0])) __pyx_t_5 = 0; if (unlikely(__pyx_t_5 != -1)) { __Pyx_RaiseBufferIndexError(__pyx_t_5); __PYX_ERR(0, 18, __pyx_L1_error) } __pyx_v_income = (__pyx_v_income + (*((double *) ( /* dim=0 */ (__pyx_v_incomes.data + __pyx_t_4 * __pyx_v_incomes.strides[0]) ))));
+19: tax += calculate_tax_memview(incomes[i])
__pyx_t_4 = __pyx_v_i; __pyx_t_5 = -1; if (unlikely(__pyx_t_4 >= (size_t)__pyx_v_incomes.shape[0])) __pyx_t_5 = 0; if (unlikely(__pyx_t_5 != -1)) { __Pyx_RaiseBufferIndexError(__pyx_t_5); __PYX_ERR(0, 19, __pyx_L1_error) } __pyx_v_tax = (__pyx_v_tax + __pyx_f_46_cython_magic_7f4afb92afda6df9c3f40c5d1cc59a75_calculate_tax_memview((*((double *) ( /* dim=0 */ (__pyx_v_incomes.data + __pyx_t_4 * __pyx_v_incomes.strides[0]) ))), 0)); }
+20: return tax / income
__Pyx_XDECREF(__pyx_r); if (unlikely(__pyx_v_income == 0)) { PyErr_SetString(PyExc_ZeroDivisionError, "float division"); __PYX_ERR(0, 20, __pyx_L1_error) } __pyx_t_6 = PyFloat_FromDouble((__pyx_v_tax / __pyx_v_income)); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 20, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_6); __pyx_r = __pyx_t_6; __pyx_t_6 = 0; goto __pyx_L0;
average_tax_rate_memview(incomes_np)
0.24290608306517872
%%timeit
average_tax_rate_memview(incomes_np)
11.9 ms ± 647 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
ratios(mviews=0.0119)
mviews: 1.00
cython_typed: 2.64
numpy: 6.67
ufunc: 76.55
cython_copy: 231.09
python: 278.15
6.4.2. bounds check#
%%cython -a
cimport cython
cpdef double calculate_tax_memviewb(double income):
if income > 256303:
return income * 0.45 - 16164.53
elif income > 54057:
return income * 0.42 - 8475.44
elif income > 13769:
return (income - 13769) * ((income - 13769) * 0.0000022376 + 0.2397) + 939.57
elif income > 8820:
return (income - 8820) * ((income - 8820) * 0.0000100727 + 0.14)
else:
return 0
@cython.boundscheck(False)
def average_tax_rate_memviewb(double[:] incomes):
cdef double tax = 0, income = 0, x
cdef size_t i
for i in range(incomes.shape[0]):
income += incomes[i]
tax += calculate_tax_memviewb(incomes[i])
return tax / income
Generated by Cython 0.29.33
Yellow lines hint at Python interaction.
Click on a line that starts with a "+
" to see the C code that Cython generated for it.
+01: cimport cython
__pyx_t_1 = __Pyx_PyDict_NewPresized(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); if (PyDict_SetItem(__pyx_d, __pyx_n_s_test, __pyx_t_1) < 0) __PYX_ERR(0, 1, __pyx_L1_error) __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
02:
+03: cpdef double calculate_tax_memviewb(double income):
static PyObject *__pyx_pw_46_cython_magic_ac0888f1c69d21b1f8db376f014adcfe_1calculate_tax_memviewb(PyObject *__pyx_self, PyObject *__pyx_arg_income); /*proto*/ static double __pyx_f_46_cython_magic_ac0888f1c69d21b1f8db376f014adcfe_calculate_tax_memviewb(double __pyx_v_income, CYTHON_UNUSED int __pyx_skip_dispatch) { double __pyx_r; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("calculate_tax_memviewb", 0); /* … */ /* function exit code */ __pyx_L0:; __Pyx_RefNannyFinishContext(); return __pyx_r; } /* Python wrapper */ static PyObject *__pyx_pw_46_cython_magic_ac0888f1c69d21b1f8db376f014adcfe_1calculate_tax_memviewb(PyObject *__pyx_self, PyObject *__pyx_arg_income); /*proto*/ static PyObject *__pyx_pw_46_cython_magic_ac0888f1c69d21b1f8db376f014adcfe_1calculate_tax_memviewb(PyObject *__pyx_self, PyObject *__pyx_arg_income) { double __pyx_v_income; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("calculate_tax_memviewb (wrapper)", 0); assert(__pyx_arg_income); { __pyx_v_income = __pyx_PyFloat_AsDouble(__pyx_arg_income); if (unlikely((__pyx_v_income == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 3, __pyx_L3_error) } goto __pyx_L4_argument_unpacking_done; __pyx_L3_error:; __Pyx_AddTraceback("_cython_magic_ac0888f1c69d21b1f8db376f014adcfe.calculate_tax_memviewb", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; __pyx_r = __pyx_pf_46_cython_magic_ac0888f1c69d21b1f8db376f014adcfe_calculate_tax_memviewb(__pyx_self, ((double)__pyx_v_income)); int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; /* function exit code */ __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_46_cython_magic_ac0888f1c69d21b1f8db376f014adcfe_calculate_tax_memviewb(CYTHON_UNUSED PyObject *__pyx_self, double __pyx_v_income) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("calculate_tax_memviewb", 0); __Pyx_XDECREF(__pyx_r); __pyx_t_1 = PyFloat_FromDouble(__pyx_f_46_cython_magic_ac0888f1c69d21b1f8db376f014adcfe_calculate_tax_memviewb(__pyx_v_income, 0)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 3, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __pyx_r = __pyx_t_1; __pyx_t_1 = 0; goto __pyx_L0; /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_AddTraceback("_cython_magic_ac0888f1c69d21b1f8db376f014adcfe.calculate_tax_memviewb", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; }
+04: if income > 256303:
__pyx_t_1 = ((__pyx_v_income > 256303.0) != 0); if (__pyx_t_1) { /* … */ }
+05: return income * 0.45 - 16164.53
__pyx_r = ((__pyx_v_income * 0.45) - 16164.53); goto __pyx_L0;
+06: elif income > 54057:
__pyx_t_1 = ((__pyx_v_income > 54057.0) != 0); if (__pyx_t_1) { /* … */ }
+07: return income * 0.42 - 8475.44
__pyx_r = ((__pyx_v_income * 0.42) - 8475.44); goto __pyx_L0;
+08: elif income > 13769:
__pyx_t_1 = ((__pyx_v_income > 13769.0) != 0); if (__pyx_t_1) { /* … */ }
+09: return (income - 13769) * ((income - 13769) * 0.0000022376 + 0.2397) + 939.57
__pyx_r = (((__pyx_v_income - 13769.0) * (((__pyx_v_income - 13769.0) * 0.0000022376) + 0.2397)) + 939.57); goto __pyx_L0;
+10: elif income > 8820:
__pyx_t_1 = ((__pyx_v_income > 8820.0) != 0); if (__pyx_t_1) { /* … */ }
+11: return (income - 8820) * ((income - 8820) * 0.0000100727 + 0.14)
__pyx_r = ((__pyx_v_income - 8820.0) * (((__pyx_v_income - 8820.0) * 0.0000100727) + 0.14)); goto __pyx_L0;
12: else:
+13: return 0
/*else*/ { __pyx_r = 0.0; goto __pyx_L0; }
14:
15: @cython.boundscheck(False)
+16: def average_tax_rate_memviewb(double[:] incomes):
/* Python wrapper */ static PyObject *__pyx_pw_46_cython_magic_ac0888f1c69d21b1f8db376f014adcfe_3average_tax_rate_memviewb(PyObject *__pyx_self, PyObject *__pyx_arg_incomes); /*proto*/ static PyMethodDef __pyx_mdef_46_cython_magic_ac0888f1c69d21b1f8db376f014adcfe_3average_tax_rate_memviewb = {"average_tax_rate_memviewb", (PyCFunction)__pyx_pw_46_cython_magic_ac0888f1c69d21b1f8db376f014adcfe_3average_tax_rate_memviewb, METH_O, 0}; static PyObject *__pyx_pw_46_cython_magic_ac0888f1c69d21b1f8db376f014adcfe_3average_tax_rate_memviewb(PyObject *__pyx_self, PyObject *__pyx_arg_incomes) { __Pyx_memviewslice __pyx_v_incomes = { 0, 0, { 0 }, { 0 }, { 0 } }; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("average_tax_rate_memviewb (wrapper)", 0); assert(__pyx_arg_incomes); { __pyx_v_incomes = __Pyx_PyObject_to_MemoryviewSlice_ds_double(__pyx_arg_incomes, PyBUF_WRITABLE); if (unlikely(!__pyx_v_incomes.memview)) __PYX_ERR(0, 16, __pyx_L3_error) } goto __pyx_L4_argument_unpacking_done; __pyx_L3_error:; __Pyx_AddTraceback("_cython_magic_ac0888f1c69d21b1f8db376f014adcfe.average_tax_rate_memviewb", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; __pyx_r = __pyx_pf_46_cython_magic_ac0888f1c69d21b1f8db376f014adcfe_2average_tax_rate_memviewb(__pyx_self, __pyx_v_incomes); int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; /* function exit code */ __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_46_cython_magic_ac0888f1c69d21b1f8db376f014adcfe_2average_tax_rate_memviewb(CYTHON_UNUSED PyObject *__pyx_self, __Pyx_memviewslice __pyx_v_incomes) { double __pyx_v_tax; double __pyx_v_income; size_t __pyx_v_i; PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("average_tax_rate_memviewb", 0); /* … */ /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_5); __Pyx_AddTraceback("_cython_magic_ac0888f1c69d21b1f8db376f014adcfe.average_tax_rate_memviewb", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __PYX_XDEC_MEMVIEW(&__pyx_v_incomes, 1); __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* … */ __pyx_tuple__20 = PyTuple_Pack(6, __pyx_n_s_incomes, __pyx_n_s_incomes, __pyx_n_s_tax, __pyx_n_s_income, __pyx_n_s_x, __pyx_n_s_i); if (unlikely(!__pyx_tuple__20)) __PYX_ERR(0, 16, __pyx_L1_error) __Pyx_GOTREF(__pyx_tuple__20); __Pyx_GIVEREF(__pyx_tuple__20); /* … */ __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_ac0888f1c69d21b1f8db376f014adcfe_3average_tax_rate_memviewb, NULL, __pyx_n_s_cython_magic_ac0888f1c69d21b1f8); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 16, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); if (PyDict_SetItem(__pyx_d, __pyx_n_s_average_tax_rate_memviewb, __pyx_t_1) < 0) __PYX_ERR(0, 16, __pyx_L1_error) __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __pyx_codeobj__21 = (PyObject*)__Pyx_PyCode_New(1, 0, 6, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__20, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_C_Users_DELL_ipython_cython__cyt, __pyx_n_s_average_tax_rate_memviewb, 16, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__21)) __PYX_ERR(0, 16, __pyx_L1_error)
+17: cdef double tax = 0, income = 0, x
__pyx_v_tax = 0.0; __pyx_v_income = 0.0;
18: cdef size_t i
+19: for i in range(incomes.shape[0]):
__pyx_t_1 = (__pyx_v_incomes.shape[0]); __pyx_t_2 = __pyx_t_1; for (__pyx_t_3 = 0; __pyx_t_3 < __pyx_t_2; __pyx_t_3+=1) { __pyx_v_i = __pyx_t_3;
+20: income += incomes[i]
__pyx_t_4 = __pyx_v_i; __pyx_v_income = (__pyx_v_income + (*((double *) ( /* dim=0 */ (__pyx_v_incomes.data + __pyx_t_4 * __pyx_v_incomes.strides[0]) ))));
+21: tax += calculate_tax_memviewb(incomes[i])
__pyx_t_4 = __pyx_v_i; __pyx_v_tax = (__pyx_v_tax + __pyx_f_46_cython_magic_ac0888f1c69d21b1f8db376f014adcfe_calculate_tax_memviewb((*((double *) ( /* dim=0 */ (__pyx_v_incomes.data + __pyx_t_4 * __pyx_v_incomes.strides[0]) ))), 0)); }
+22: return tax / income
__Pyx_XDECREF(__pyx_r); if (unlikely(__pyx_v_income == 0)) { PyErr_SetString(PyExc_ZeroDivisionError, "float division"); __PYX_ERR(0, 22, __pyx_L1_error) } __pyx_t_5 = PyFloat_FromDouble((__pyx_v_tax / __pyx_v_income)); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 22, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_5); __pyx_r = __pyx_t_5; __pyx_t_5 = 0; goto __pyx_L0;
%%timeit
average_tax_rate_memviewb(incomes_np)
11.1 ms ± 312 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
ratios(mviews_bcheck=0.0111)
mviews_bcheck: 1.00
mviews: 1.07
cython_typed: 2.83
numpy: 7.15
ufunc: 82.07
cython_copy: 247.75
python: 298.20
6.4.3. Faster Cython: prange#
%%cython -f -c=/openmp
# distutils: extra_compile_args=-fopenmp
# distutils: extra_link_args=-fopenmp
# SOLUTION
cpdef double calculate_tax_cy(double income) nogil:
if income > 256303:
return income * 0.45 - 16164.53
elif income > 54057:
return income * 0.42 - 8475.44
elif income > 13769:
return (income - 13769) * ((income - 13769) * 0.0000022376 + 0.2397) + 939.57
elif income > 8820:
return (income - 8820) * ((income - 8820) * 0.0000100727 + 0.14)
else:
return 0
cimport cython
from cython.parallel cimport prange
@cython.boundscheck(False)
def average_tax_rate_prange(double[:] incomes):
cdef unsigned long i
cdef double tax = 0, income = 0, x
for i in prange(incomes.shape[0], nogil=True, num_threads=4):
x = incomes[i]
income += x
tax += calculate_tax_cy(x)
return tax / income
average_tax_rate_prange(incomes_np)
0.24290608306519693
%%timeit
average_tax_rate_prange(incomes_np)
4.31 ms ± 95.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
ratios(prange=0.00431)
prange: 1.00
mviews_bcheck: 2.58
mviews: 2.76
cython_typed: 7.29
numpy: 18.42
ufunc: 211.37
cython_copy: 638.05
python: 767.98
6.5. more …#
%%cython
cpdef double calculate_tax_cy(double income):
if income > 256303:
return income * 0.45 - 16164.53
elif income > 54057:
return income * 0.42 - 8475.44
elif income > 13769:
return (income - 13769) * ((income - 13769) * 0.0000022376 + 0.2397) + 939.57
elif income > 8820:
return (income - 8820) * ((income - 8820) * 0.0000100727 + 0.14)
else:
return 0
def average_tax_cy(incomes):
# return sum(calculate_tax_cy(x) for x in incomes) / sum(incomes)
cdef double tax = 0, income = 0, x
for x in incomes:
income += x
tax += calculate_tax_cy(x)
return tax / income
cimport numpy as cnp
def average_tax_numcy(cnp.ndarray[double, ndim=1] incomes):
cdef long i
cdef double tax = 0, income = 0, x
for i in range(incomes.shape[0]):
x = incomes[i]
income += x
tax += calculate_tax_cy(x)
return income / tax
6.6. Pythran integration#
%%cython
# cython: language=c++
# cython: np_pythran=True
# distutils: extra_compile_args=-std=c++11
import numpy as np
cimport numpy as cnp
def calculate_tax_numpy_segments(cnp.ndarray[double, ndim=1] d):
tax_seg1 = d[(d > 256303)] * 0.45 - 16164.53
tax_seg2 = d[(d > 54057) & (d <= 256303)] * 0.42 - 8475.44
seg3 = d[(d > 13769) & (d <= 54057)] - 13769
seg4 = d[(d > 8820) & (d <= 13769)] - 8820
prog_seg3 = seg3 * 0.0000022376 + 0.2397
prog_seg4 = seg4 * 0.0000100727 + 0.14
return (
np.sum(tax_seg1) +
np.sum(tax_seg2) +
np.sum(seg3 * prog_seg3 + 939.57) +
np.sum(seg4 * prog_seg4)
) / np.sum(d)