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)
Cython: _cython_magic_cad4649a9d1a193ec6b2e960c1d586c3.pyx

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)
Cython: _cython_magic_eabc7642c83c38db29415342c79668b1.pyx

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()
_images/a6bc840e2d9979a176527ecc587997f06c5beb88212cdc85ebdd8bdea60b8712.png

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)
Cython: _cython_magic_823f81150a04040e0b5e1a44e74fe722.pyx

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
Cython: _cython_magic_0f4867f07ddf1678f65b653962aeb9a8.pyx

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
Cython: _cython_magic_5bd02f52af0119c2bd997e794f5ea94a.pyx

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
Cython: _cython_magic_05731be6528a27eca09a408da6a4fbb1.pyx

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
Cython: _cython_magic_7f4afb92afda6df9c3f40c5d1cc59a75.pyx

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
Cython: _cython_magic_ac0888f1c69d21b1f8db376f014adcfe.pyx

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)