Skip to content

Instantly share code, notes, and snippets.

@lagru
Last active March 22, 2018 19:47
Show Gist options
  • Save lagru/add10aa2ecb62966297ca0d4a7e21b92 to your computer and use it in GitHub Desktop.
Save lagru/add10aa2ecb62966297ca0d4a7e21b92 to your computer and use it in GitHub Desktop.
Evaluation of peak_prominences
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Comparison: peak_prominences"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"%load_ext line_profiler\n",
"%load_ext Cython\n",
"\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import line_profiler\n",
"\n",
"from scipy.signal import find_peaks, peak_prominences # Old version\n",
"\n",
"np.random.seed(20180309)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Cythonized Version"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<!DOCTYPE html>\n",
"<!-- Generated by Cython 0.27.3 -->\n",
"<html>\n",
"<head>\n",
" <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n",
" <title>Cython: _cython_magic_0fc3700ef1e0d2858154a206c2a8964e.pyx</title>\n",
" <style type=\"text/css\">\n",
" \n",
"body.cython { font-family: courier; font-size: 12; }\n",
"\n",
".cython.tag { }\n",
".cython.line { margin: 0em }\n",
".cython.code { font-size: 9; color: #444444; display: none; margin: 0px 0px 0px 8px; border-left: 8px none; }\n",
"\n",
".cython.line .run { background-color: #B0FFB0; }\n",
".cython.line .mis { background-color: #FFB0B0; }\n",
".cython.code.run { border-left: 8px solid #B0FFB0; }\n",
".cython.code.mis { border-left: 8px solid #FFB0B0; }\n",
"\n",
".cython.code .py_c_api { color: red; }\n",
".cython.code .py_macro_api { color: #FF7000; }\n",
".cython.code .pyx_c_api { color: #FF3000; }\n",
".cython.code .pyx_macro_api { color: #FF7000; }\n",
".cython.code .refnanny { color: #FFA000; }\n",
".cython.code .trace { color: #FFA000; }\n",
".cython.code .error_goto { color: #FFA000; }\n",
"\n",
".cython.code .coerce { color: #008000; border: 1px dotted #008000 }\n",
".cython.code .py_attr { color: #FF0000; font-weight: bold; }\n",
".cython.code .c_attr { color: #0000FF; }\n",
".cython.code .py_call { color: #FF0000; font-weight: bold; }\n",
".cython.code .c_call { color: #0000FF; }\n",
"\n",
".cython.score-0 {background-color: #FFFFff;}\n",
".cython.score-1 {background-color: #FFFFe7;}\n",
".cython.score-2 {background-color: #FFFFd4;}\n",
".cython.score-3 {background-color: #FFFFc4;}\n",
".cython.score-4 {background-color: #FFFFb6;}\n",
".cython.score-5 {background-color: #FFFFaa;}\n",
".cython.score-6 {background-color: #FFFF9f;}\n",
".cython.score-7 {background-color: #FFFF96;}\n",
".cython.score-8 {background-color: #FFFF8d;}\n",
".cython.score-9 {background-color: #FFFF86;}\n",
".cython.score-10 {background-color: #FFFF7f;}\n",
".cython.score-11 {background-color: #FFFF79;}\n",
".cython.score-12 {background-color: #FFFF73;}\n",
".cython.score-13 {background-color: #FFFF6e;}\n",
".cython.score-14 {background-color: #FFFF6a;}\n",
".cython.score-15 {background-color: #FFFF66;}\n",
".cython.score-16 {background-color: #FFFF62;}\n",
".cython.score-17 {background-color: #FFFF5e;}\n",
".cython.score-18 {background-color: #FFFF5b;}\n",
".cython.score-19 {background-color: #FFFF57;}\n",
".cython.score-20 {background-color: #FFFF55;}\n",
".cython.score-21 {background-color: #FFFF52;}\n",
".cython.score-22 {background-color: #FFFF4f;}\n",
".cython.score-23 {background-color: #FFFF4d;}\n",
".cython.score-24 {background-color: #FFFF4b;}\n",
".cython.score-25 {background-color: #FFFF48;}\n",
".cython.score-26 {background-color: #FFFF46;}\n",
".cython.score-27 {background-color: #FFFF44;}\n",
".cython.score-28 {background-color: #FFFF43;}\n",
".cython.score-29 {background-color: #FFFF41;}\n",
".cython.score-30 {background-color: #FFFF3f;}\n",
".cython.score-31 {background-color: #FFFF3e;}\n",
".cython.score-32 {background-color: #FFFF3c;}\n",
".cython.score-33 {background-color: #FFFF3b;}\n",
".cython.score-34 {background-color: #FFFF39;}\n",
".cython.score-35 {background-color: #FFFF38;}\n",
".cython.score-36 {background-color: #FFFF37;}\n",
".cython.score-37 {background-color: #FFFF36;}\n",
".cython.score-38 {background-color: #FFFF35;}\n",
".cython.score-39 {background-color: #FFFF34;}\n",
".cython.score-40 {background-color: #FFFF33;}\n",
".cython.score-41 {background-color: #FFFF32;}\n",
".cython.score-42 {background-color: #FFFF31;}\n",
".cython.score-43 {background-color: #FFFF30;}\n",
".cython.score-44 {background-color: #FFFF2f;}\n",
".cython.score-45 {background-color: #FFFF2e;}\n",
".cython.score-46 {background-color: #FFFF2d;}\n",
".cython.score-47 {background-color: #FFFF2c;}\n",
".cython.score-48 {background-color: #FFFF2b;}\n",
".cython.score-49 {background-color: #FFFF2b;}\n",
".cython.score-50 {background-color: #FFFF2a;}\n",
".cython.score-51 {background-color: #FFFF29;}\n",
".cython.score-52 {background-color: #FFFF29;}\n",
".cython.score-53 {background-color: #FFFF28;}\n",
".cython.score-54 {background-color: #FFFF27;}\n",
".cython.score-55 {background-color: #FFFF27;}\n",
".cython.score-56 {background-color: #FFFF26;}\n",
".cython.score-57 {background-color: #FFFF26;}\n",
".cython.score-58 {background-color: #FFFF25;}\n",
".cython.score-59 {background-color: #FFFF24;}\n",
".cython.score-60 {background-color: #FFFF24;}\n",
".cython.score-61 {background-color: #FFFF23;}\n",
".cython.score-62 {background-color: #FFFF23;}\n",
".cython.score-63 {background-color: #FFFF22;}\n",
".cython.score-64 {background-color: #FFFF22;}\n",
".cython.score-65 {background-color: #FFFF22;}\n",
".cython.score-66 {background-color: #FFFF21;}\n",
".cython.score-67 {background-color: #FFFF21;}\n",
".cython.score-68 {background-color: #FFFF20;}\n",
".cython.score-69 {background-color: #FFFF20;}\n",
".cython.score-70 {background-color: #FFFF1f;}\n",
".cython.score-71 {background-color: #FFFF1f;}\n",
".cython.score-72 {background-color: #FFFF1f;}\n",
".cython.score-73 {background-color: #FFFF1e;}\n",
".cython.score-74 {background-color: #FFFF1e;}\n",
".cython.score-75 {background-color: #FFFF1e;}\n",
".cython.score-76 {background-color: #FFFF1d;}\n",
".cython.score-77 {background-color: #FFFF1d;}\n",
".cython.score-78 {background-color: #FFFF1c;}\n",
".cython.score-79 {background-color: #FFFF1c;}\n",
".cython.score-80 {background-color: #FFFF1c;}\n",
".cython.score-81 {background-color: #FFFF1c;}\n",
".cython.score-82 {background-color: #FFFF1b;}\n",
".cython.score-83 {background-color: #FFFF1b;}\n",
".cython.score-84 {background-color: #FFFF1b;}\n",
".cython.score-85 {background-color: #FFFF1a;}\n",
".cython.score-86 {background-color: #FFFF1a;}\n",
".cython.score-87 {background-color: #FFFF1a;}\n",
".cython.score-88 {background-color: #FFFF1a;}\n",
".cython.score-89 {background-color: #FFFF19;}\n",
".cython.score-90 {background-color: #FFFF19;}\n",
".cython.score-91 {background-color: #FFFF19;}\n",
".cython.score-92 {background-color: #FFFF19;}\n",
".cython.score-93 {background-color: #FFFF18;}\n",
".cython.score-94 {background-color: #FFFF18;}\n",
".cython.score-95 {background-color: #FFFF18;}\n",
".cython.score-96 {background-color: #FFFF18;}\n",
".cython.score-97 {background-color: #FFFF17;}\n",
".cython.score-98 {background-color: #FFFF17;}\n",
".cython.score-99 {background-color: #FFFF17;}\n",
".cython.score-100 {background-color: #FFFF17;}\n",
".cython.score-101 {background-color: #FFFF16;}\n",
".cython.score-102 {background-color: #FFFF16;}\n",
".cython.score-103 {background-color: #FFFF16;}\n",
".cython.score-104 {background-color: #FFFF16;}\n",
".cython.score-105 {background-color: #FFFF16;}\n",
".cython.score-106 {background-color: #FFFF15;}\n",
".cython.score-107 {background-color: #FFFF15;}\n",
".cython.score-108 {background-color: #FFFF15;}\n",
".cython.score-109 {background-color: #FFFF15;}\n",
".cython.score-110 {background-color: #FFFF15;}\n",
".cython.score-111 {background-color: #FFFF15;}\n",
".cython.score-112 {background-color: #FFFF14;}\n",
".cython.score-113 {background-color: #FFFF14;}\n",
".cython.score-114 {background-color: #FFFF14;}\n",
".cython.score-115 {background-color: #FFFF14;}\n",
".cython.score-116 {background-color: #FFFF14;}\n",
".cython.score-117 {background-color: #FFFF14;}\n",
".cython.score-118 {background-color: #FFFF13;}\n",
".cython.score-119 {background-color: #FFFF13;}\n",
".cython.score-120 {background-color: #FFFF13;}\n",
".cython.score-121 {background-color: #FFFF13;}\n",
".cython.score-122 {background-color: #FFFF13;}\n",
".cython.score-123 {background-color: #FFFF13;}\n",
".cython.score-124 {background-color: #FFFF13;}\n",
".cython.score-125 {background-color: #FFFF12;}\n",
".cython.score-126 {background-color: #FFFF12;}\n",
".cython.score-127 {background-color: #FFFF12;}\n",
".cython.score-128 {background-color: #FFFF12;}\n",
".cython.score-129 {background-color: #FFFF12;}\n",
".cython.score-130 {background-color: #FFFF12;}\n",
".cython.score-131 {background-color: #FFFF12;}\n",
".cython.score-132 {background-color: #FFFF11;}\n",
".cython.score-133 {background-color: #FFFF11;}\n",
".cython.score-134 {background-color: #FFFF11;}\n",
".cython.score-135 {background-color: #FFFF11;}\n",
".cython.score-136 {background-color: #FFFF11;}\n",
".cython.score-137 {background-color: #FFFF11;}\n",
".cython.score-138 {background-color: #FFFF11;}\n",
".cython.score-139 {background-color: #FFFF11;}\n",
".cython.score-140 {background-color: #FFFF11;}\n",
".cython.score-141 {background-color: #FFFF10;}\n",
".cython.score-142 {background-color: #FFFF10;}\n",
".cython.score-143 {background-color: #FFFF10;}\n",
".cython.score-144 {background-color: #FFFF10;}\n",
".cython.score-145 {background-color: #FFFF10;}\n",
".cython.score-146 {background-color: #FFFF10;}\n",
".cython.score-147 {background-color: #FFFF10;}\n",
".cython.score-148 {background-color: #FFFF10;}\n",
".cython.score-149 {background-color: #FFFF10;}\n",
".cython.score-150 {background-color: #FFFF0f;}\n",
".cython.score-151 {background-color: #FFFF0f;}\n",
".cython.score-152 {background-color: #FFFF0f;}\n",
".cython.score-153 {background-color: #FFFF0f;}\n",
".cython.score-154 {background-color: #FFFF0f;}\n",
".cython.score-155 {background-color: #FFFF0f;}\n",
".cython.score-156 {background-color: #FFFF0f;}\n",
".cython.score-157 {background-color: #FFFF0f;}\n",
".cython.score-158 {background-color: #FFFF0f;}\n",
".cython.score-159 {background-color: #FFFF0f;}\n",
".cython.score-160 {background-color: #FFFF0f;}\n",
".cython.score-161 {background-color: #FFFF0e;}\n",
".cython.score-162 {background-color: #FFFF0e;}\n",
".cython.score-163 {background-color: #FFFF0e;}\n",
".cython.score-164 {background-color: #FFFF0e;}\n",
".cython.score-165 {background-color: #FFFF0e;}\n",
".cython.score-166 {background-color: #FFFF0e;}\n",
".cython.score-167 {background-color: #FFFF0e;}\n",
".cython.score-168 {background-color: #FFFF0e;}\n",
".cython.score-169 {background-color: #FFFF0e;}\n",
".cython.score-170 {background-color: #FFFF0e;}\n",
".cython.score-171 {background-color: #FFFF0e;}\n",
".cython.score-172 {background-color: #FFFF0e;}\n",
".cython.score-173 {background-color: #FFFF0d;}\n",
".cython.score-174 {background-color: #FFFF0d;}\n",
".cython.score-175 {background-color: #FFFF0d;}\n",
".cython.score-176 {background-color: #FFFF0d;}\n",
".cython.score-177 {background-color: #FFFF0d;}\n",
".cython.score-178 {background-color: #FFFF0d;}\n",
".cython.score-179 {background-color: #FFFF0d;}\n",
".cython.score-180 {background-color: #FFFF0d;}\n",
".cython.score-181 {background-color: #FFFF0d;}\n",
".cython.score-182 {background-color: #FFFF0d;}\n",
".cython.score-183 {background-color: #FFFF0d;}\n",
".cython.score-184 {background-color: #FFFF0d;}\n",
".cython.score-185 {background-color: #FFFF0d;}\n",
".cython.score-186 {background-color: #FFFF0d;}\n",
".cython.score-187 {background-color: #FFFF0c;}\n",
".cython.score-188 {background-color: #FFFF0c;}\n",
".cython.score-189 {background-color: #FFFF0c;}\n",
".cython.score-190 {background-color: #FFFF0c;}\n",
".cython.score-191 {background-color: #FFFF0c;}\n",
".cython.score-192 {background-color: #FFFF0c;}\n",
".cython.score-193 {background-color: #FFFF0c;}\n",
".cython.score-194 {background-color: #FFFF0c;}\n",
".cython.score-195 {background-color: #FFFF0c;}\n",
".cython.score-196 {background-color: #FFFF0c;}\n",
".cython.score-197 {background-color: #FFFF0c;}\n",
".cython.score-198 {background-color: #FFFF0c;}\n",
".cython.score-199 {background-color: #FFFF0c;}\n",
".cython.score-200 {background-color: #FFFF0c;}\n",
".cython.score-201 {background-color: #FFFF0c;}\n",
".cython.score-202 {background-color: #FFFF0c;}\n",
".cython.score-203 {background-color: #FFFF0b;}\n",
".cython.score-204 {background-color: #FFFF0b;}\n",
".cython.score-205 {background-color: #FFFF0b;}\n",
".cython.score-206 {background-color: #FFFF0b;}\n",
".cython.score-207 {background-color: #FFFF0b;}\n",
".cython.score-208 {background-color: #FFFF0b;}\n",
".cython.score-209 {background-color: #FFFF0b;}\n",
".cython.score-210 {background-color: #FFFF0b;}\n",
".cython.score-211 {background-color: #FFFF0b;}\n",
".cython.score-212 {background-color: #FFFF0b;}\n",
".cython.score-213 {background-color: #FFFF0b;}\n",
".cython.score-214 {background-color: #FFFF0b;}\n",
".cython.score-215 {background-color: #FFFF0b;}\n",
".cython.score-216 {background-color: #FFFF0b;}\n",
".cython.score-217 {background-color: #FFFF0b;}\n",
".cython.score-218 {background-color: #FFFF0b;}\n",
".cython.score-219 {background-color: #FFFF0b;}\n",
".cython.score-220 {background-color: #FFFF0b;}\n",
".cython.score-221 {background-color: #FFFF0b;}\n",
".cython.score-222 {background-color: #FFFF0a;}\n",
".cython.score-223 {background-color: #FFFF0a;}\n",
".cython.score-224 {background-color: #FFFF0a;}\n",
".cython.score-225 {background-color: #FFFF0a;}\n",
".cython.score-226 {background-color: #FFFF0a;}\n",
".cython.score-227 {background-color: #FFFF0a;}\n",
".cython.score-228 {background-color: #FFFF0a;}\n",
".cython.score-229 {background-color: #FFFF0a;}\n",
".cython.score-230 {background-color: #FFFF0a;}\n",
".cython.score-231 {background-color: #FFFF0a;}\n",
".cython.score-232 {background-color: #FFFF0a;}\n",
".cython.score-233 {background-color: #FFFF0a;}\n",
".cython.score-234 {background-color: #FFFF0a;}\n",
".cython.score-235 {background-color: #FFFF0a;}\n",
".cython.score-236 {background-color: #FFFF0a;}\n",
".cython.score-237 {background-color: #FFFF0a;}\n",
".cython.score-238 {background-color: #FFFF0a;}\n",
".cython.score-239 {background-color: #FFFF0a;}\n",
".cython.score-240 {background-color: #FFFF0a;}\n",
".cython.score-241 {background-color: #FFFF0a;}\n",
".cython.score-242 {background-color: #FFFF0a;}\n",
".cython.score-243 {background-color: #FFFF0a;}\n",
".cython.score-244 {background-color: #FFFF0a;}\n",
".cython.score-245 {background-color: #FFFF0a;}\n",
".cython.score-246 {background-color: #FFFF09;}\n",
".cython.score-247 {background-color: #FFFF09;}\n",
".cython.score-248 {background-color: #FFFF09;}\n",
".cython.score-249 {background-color: #FFFF09;}\n",
".cython.score-250 {background-color: #FFFF09;}\n",
".cython.score-251 {background-color: #FFFF09;}\n",
".cython.score-252 {background-color: #FFFF09;}\n",
".cython.score-253 {background-color: #FFFF09;}\n",
".cython.score-254 {background-color: #FFFF09;}\n",
".cython .hll { background-color: #ffffcc }\n",
".cython { background: #f8f8f8; }\n",
".cython .c { color: #408080; font-style: italic } /* Comment */\n",
".cython .err { border: 1px solid #FF0000 } /* Error */\n",
".cython .k { color: #008000; font-weight: bold } /* Keyword */\n",
".cython .o { color: #666666 } /* Operator */\n",
".cython .ch { color: #408080; font-style: italic } /* Comment.Hashbang */\n",
".cython .cm { color: #408080; font-style: italic } /* Comment.Multiline */\n",
".cython .cp { color: #BC7A00 } /* Comment.Preproc */\n",
".cython .cpf { color: #408080; font-style: italic } /* Comment.PreprocFile */\n",
".cython .c1 { color: #408080; font-style: italic } /* Comment.Single */\n",
".cython .cs { color: #408080; font-style: italic } /* Comment.Special */\n",
".cython .gd { color: #A00000 } /* Generic.Deleted */\n",
".cython .ge { font-style: italic } /* Generic.Emph */\n",
".cython .gr { color: #FF0000 } /* Generic.Error */\n",
".cython .gh { color: #000080; font-weight: bold } /* Generic.Heading */\n",
".cython .gi { color: #00A000 } /* Generic.Inserted */\n",
".cython .go { color: #888888 } /* Generic.Output */\n",
".cython .gp { color: #000080; font-weight: bold } /* Generic.Prompt */\n",
".cython .gs { font-weight: bold } /* Generic.Strong */\n",
".cython .gu { color: #800080; font-weight: bold } /* Generic.Subheading */\n",
".cython .gt { color: #0044DD } /* Generic.Traceback */\n",
".cython .kc { color: #008000; font-weight: bold } /* Keyword.Constant */\n",
".cython .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */\n",
".cython .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */\n",
".cython .kp { color: #008000 } /* Keyword.Pseudo */\n",
".cython .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */\n",
".cython .kt { color: #B00040 } /* Keyword.Type */\n",
".cython .m { color: #666666 } /* Literal.Number */\n",
".cython .s { color: #BA2121 } /* Literal.String */\n",
".cython .na { color: #7D9029 } /* Name.Attribute */\n",
".cython .nb { color: #008000 } /* Name.Builtin */\n",
".cython .nc { color: #0000FF; font-weight: bold } /* Name.Class */\n",
".cython .no { color: #880000 } /* Name.Constant */\n",
".cython .nd { color: #AA22FF } /* Name.Decorator */\n",
".cython .ni { color: #999999; font-weight: bold } /* Name.Entity */\n",
".cython .ne { color: #D2413A; font-weight: bold } /* Name.Exception */\n",
".cython .nf { color: #0000FF } /* Name.Function */\n",
".cython .nl { color: #A0A000 } /* Name.Label */\n",
".cython .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */\n",
".cython .nt { color: #008000; font-weight: bold } /* Name.Tag */\n",
".cython .nv { color: #19177C } /* Name.Variable */\n",
".cython .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */\n",
".cython .w { color: #bbbbbb } /* Text.Whitespace */\n",
".cython .mb { color: #666666 } /* Literal.Number.Bin */\n",
".cython .mf { color: #666666 } /* Literal.Number.Float */\n",
".cython .mh { color: #666666 } /* Literal.Number.Hex */\n",
".cython .mi { color: #666666 } /* Literal.Number.Integer */\n",
".cython .mo { color: #666666 } /* Literal.Number.Oct */\n",
".cython .sa { color: #BA2121 } /* Literal.String.Affix */\n",
".cython .sb { color: #BA2121 } /* Literal.String.Backtick */\n",
".cython .sc { color: #BA2121 } /* Literal.String.Char */\n",
".cython .dl { color: #BA2121 } /* Literal.String.Delimiter */\n",
".cython .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */\n",
".cython .s2 { color: #BA2121 } /* Literal.String.Double */\n",
".cython .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */\n",
".cython .sh { color: #BA2121 } /* Literal.String.Heredoc */\n",
".cython .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */\n",
".cython .sx { color: #008000 } /* Literal.String.Other */\n",
".cython .sr { color: #BB6688 } /* Literal.String.Regex */\n",
".cython .s1 { color: #BA2121 } /* Literal.String.Single */\n",
".cython .ss { color: #19177C } /* Literal.String.Symbol */\n",
".cython .bp { color: #008000 } /* Name.Builtin.Pseudo */\n",
".cython .fm { color: #0000FF } /* Name.Function.Magic */\n",
".cython .vc { color: #19177C } /* Name.Variable.Class */\n",
".cython .vg { color: #19177C } /* Name.Variable.Global */\n",
".cython .vi { color: #19177C } /* Name.Variable.Instance */\n",
".cython .vm { color: #19177C } /* Name.Variable.Magic */\n",
".cython .il { color: #666666 } /* Literal.Number.Integer.Long */\n",
" </style>\n",
" <script>\n",
" function toggleDiv(id) {\n",
" theDiv = id.nextElementSibling\n",
" if (theDiv.style.display != 'block') theDiv.style.display = 'block';\n",
" else theDiv.style.display = 'none';\n",
" }\n",
" </script>\n",
"</head>\n",
"<body class=\"cython\">\n",
"<p><span style=\"border-bottom: solid 1px grey;\">Generated by Cython 0.27.3</span></p>\n",
"<p>\n",
" <span style=\"background-color: #FFFF00\">Yellow lines</span> hint at Python interaction.<br />\n",
" Click on a line that starts with a \"<code>+</code>\" to see the C code that Cython generated for it.\n",
"</p>\n",
"<div class=\"cython\"><pre class=\"cython line score-0\">&#xA0;<span class=\"\">01</span>: </pre>\n",
"<pre class=\"cython line score-16\" onclick='toggleDiv(this)'>+<span class=\"\">02</span>: <span class=\"k\">import</span> <span class=\"nn\">numpy</span> <span class=\"k\">as</span> <span class=\"nn\">np</span></pre>\n",
"<pre class='cython code score-16 '> __pyx_t_1 = <span class='pyx_c_api'>__Pyx_Import</span>(__pyx_n_s_numpy, 0, 0);<span class='error_goto'> if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 2, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_1);\n",
" if (<span class='py_c_api'>PyDict_SetItem</span>(__pyx_d, __pyx_n_s_np, __pyx_t_1) &lt; 0) <span class='error_goto'>__PYX_ERR(0, 2, __pyx_L1_error)</span>\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_1); __pyx_t_1 = 0;\n",
"/* … */\n",
" __pyx_t_1 = <span class='pyx_c_api'>__Pyx_PyDict_NewPresized</span>(0);<span class='error_goto'> if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 2, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_1);\n",
" if (<span class='py_c_api'>PyDict_SetItem</span>(__pyx_d, __pyx_n_s_test, __pyx_t_1) &lt; 0) <span class='error_goto'>__PYX_ERR(0, 2, __pyx_L1_error)</span>\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_1); __pyx_t_1 = 0;\n",
"</pre><pre class=\"cython line score-0\">&#xA0;<span class=\"\">03</span>: <span class=\"k\">cimport</span> <span class=\"nn\">numpy</span> <span class=\"k\">as</span> <span class=\"nn\">np</span></pre>\n",
"<pre class=\"cython line score-0\">&#xA0;<span class=\"\">04</span>: <span class=\"k\">import</span> <span class=\"nn\">cython</span></pre>\n",
"<pre class=\"cython line score-0\">&#xA0;<span class=\"\">05</span>: </pre>\n",
"<pre class=\"cython line score-0\">&#xA0;<span class=\"\">06</span>: </pre>\n",
"<pre class=\"cython line score-0\">&#xA0;<span class=\"\">07</span>: <span class=\"nd\">@cython</span><span class=\"o\">.</span><span class=\"n\">wraparound</span><span class=\"p\">(</span><span class=\"bp\">False</span><span class=\"p\">)</span></pre>\n",
"<pre class=\"cython line score-0\">&#xA0;<span class=\"\">08</span>: <span class=\"nd\">@cython</span><span class=\"o\">.</span><span class=\"n\">boundscheck</span><span class=\"p\">(</span><span class=\"bp\">False</span><span class=\"p\">)</span></pre>\n",
"<pre class=\"cython line score-76\" onclick='toggleDiv(this)'>+<span class=\"\">09</span>: <span class=\"k\">def</span> <span class=\"nf\">inner</span><span class=\"p\">(</span><span class=\"n\">np</span><span class=\"o\">.</span><span class=\"n\">float64_t</span><span class=\"p\">[::</span><span class=\"mf\">1</span><span class=\"p\">]</span> <span class=\"n\">x</span> <span class=\"ow\">not</span> <span class=\"bp\">None</span><span class=\"p\">,</span></pre>\n",
"<pre class='cython code score-76 '>/* Python wrapper */\n",
"static PyObject *__pyx_pw_46_cython_magic_0fc3700ef1e0d2858154a206c2a8964e_1inner(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/\n",
"static PyMethodDef __pyx_mdef_46_cython_magic_0fc3700ef1e0d2858154a206c2a8964e_1inner = {\"inner\", (PyCFunction)__pyx_pw_46_cython_magic_0fc3700ef1e0d2858154a206c2a8964e_1inner, METH_VARARGS|METH_KEYWORDS, 0};\n",
"static PyObject *__pyx_pw_46_cython_magic_0fc3700ef1e0d2858154a206c2a8964e_1inner(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {\n",
" __Pyx_memviewslice __pyx_v_x = { 0, 0, { 0 }, { 0 }, { 0 } };\n",
" __Pyx_memviewslice __pyx_v_peaks = { 0, 0, { 0 }, { 0 }, { 0 } };\n",
" __pyx_t_5numpy_intp_t __pyx_v_wlen;\n",
" PyObject *__pyx_r = 0;\n",
" <span class='refnanny'>__Pyx_RefNannyDeclarations</span>\n",
" <span class='refnanny'>__Pyx_RefNannySetupContext</span>(\"inner (wrapper)\", 0);\n",
" {\n",
" static PyObject **__pyx_pyargnames[] = {&amp;__pyx_n_s_x,&amp;__pyx_n_s_peaks,&amp;__pyx_n_s_wlen,0};\n",
" PyObject* values[3] = {0,0,0};\n",
" if (unlikely(__pyx_kwds)) {\n",
" Py_ssize_t kw_args;\n",
" const Py_ssize_t pos_args = <span class='py_macro_api'>PyTuple_GET_SIZE</span>(__pyx_args);\n",
" switch (pos_args) {\n",
" case 3: values[2] = <span class='py_macro_api'>PyTuple_GET_ITEM</span>(__pyx_args, 2);\n",
" CYTHON_FALLTHROUGH;\n",
" case 2: values[1] = <span class='py_macro_api'>PyTuple_GET_ITEM</span>(__pyx_args, 1);\n",
" CYTHON_FALLTHROUGH;\n",
" case 1: values[0] = <span class='py_macro_api'>PyTuple_GET_ITEM</span>(__pyx_args, 0);\n",
" CYTHON_FALLTHROUGH;\n",
" case 0: break;\n",
" default: goto __pyx_L5_argtuple_error;\n",
" }\n",
" kw_args = <span class='py_c_api'>PyDict_Size</span>(__pyx_kwds);\n",
" switch (pos_args) {\n",
" case 0:\n",
" if (likely((values[0] = <span class='py_c_api'>PyDict_GetItem</span>(__pyx_kwds, __pyx_n_s_x)) != 0)) kw_args--;\n",
" else goto __pyx_L5_argtuple_error;\n",
" CYTHON_FALLTHROUGH;\n",
" case 1:\n",
" if (likely((values[1] = <span class='py_c_api'>PyDict_GetItem</span>(__pyx_kwds, __pyx_n_s_peaks)) != 0)) kw_args--;\n",
" else {\n",
" <span class='pyx_c_api'>__Pyx_RaiseArgtupleInvalid</span>(\"inner\", 1, 3, 3, 1); <span class='error_goto'>__PYX_ERR(0, 9, __pyx_L3_error)</span>\n",
" }\n",
" CYTHON_FALLTHROUGH;\n",
" case 2:\n",
" if (likely((values[2] = <span class='py_c_api'>PyDict_GetItem</span>(__pyx_kwds, __pyx_n_s_wlen)) != 0)) kw_args--;\n",
" else {\n",
" <span class='pyx_c_api'>__Pyx_RaiseArgtupleInvalid</span>(\"inner\", 1, 3, 3, 2); <span class='error_goto'>__PYX_ERR(0, 9, __pyx_L3_error)</span>\n",
" }\n",
" }\n",
" if (unlikely(kw_args &gt; 0)) {\n",
" if (unlikely(<span class='pyx_c_api'>__Pyx_ParseOptionalKeywords</span>(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, \"inner\") &lt; 0)) <span class='error_goto'>__PYX_ERR(0, 9, __pyx_L3_error)</span>\n",
" }\n",
" } else if (<span class='py_macro_api'>PyTuple_GET_SIZE</span>(__pyx_args) != 3) {\n",
" goto __pyx_L5_argtuple_error;\n",
" } else {\n",
" values[0] = <span class='py_macro_api'>PyTuple_GET_ITEM</span>(__pyx_args, 0);\n",
" values[1] = <span class='py_macro_api'>PyTuple_GET_ITEM</span>(__pyx_args, 1);\n",
" values[2] = <span class='py_macro_api'>PyTuple_GET_ITEM</span>(__pyx_args, 2);\n",
" }\n",
" __pyx_v_x = __Pyx_PyObject_to_MemoryviewSlice_dc_nn___pyx_t_5numpy_float64_t(values[0]);<span class='error_goto'> if (unlikely(!__pyx_v_x.memview)) __PYX_ERR(0, 9, __pyx_L3_error)</span>\n",
" __pyx_v_peaks = __Pyx_PyObject_to_MemoryviewSlice_dc_nn___pyx_t_5numpy_intp_t(values[1]);<span class='error_goto'> if (unlikely(!__pyx_v_peaks.memview)) __PYX_ERR(0, 10, __pyx_L3_error)</span>\n",
" __pyx_v_wlen = <span class='pyx_c_api'>__Pyx_PyInt_As_Py_intptr_t</span>(values[2]); if (unlikely((__pyx_v_wlen == ((npy_intp)-1)) &amp;&amp; <span class='py_c_api'>PyErr_Occurred</span>())) <span class='error_goto'>__PYX_ERR(0, 11, __pyx_L3_error)</span>\n",
" }\n",
" goto __pyx_L4_argument_unpacking_done;\n",
" __pyx_L5_argtuple_error:;\n",
" <span class='pyx_c_api'>__Pyx_RaiseArgtupleInvalid</span>(\"inner\", 1, 3, 3, <span class='py_macro_api'>PyTuple_GET_SIZE</span>(__pyx_args)); <span class='error_goto'>__PYX_ERR(0, 9, __pyx_L3_error)</span>\n",
" __pyx_L3_error:;\n",
" <span class='pyx_c_api'>__Pyx_AddTraceback</span>(\"_cython_magic_0fc3700ef1e0d2858154a206c2a8964e.inner\", __pyx_clineno, __pyx_lineno, __pyx_filename);\n",
" <span class='refnanny'>__Pyx_RefNannyFinishContext</span>();\n",
" return NULL;\n",
" __pyx_L4_argument_unpacking_done:;\n",
" if (unlikely(((PyObject *)__pyx_v_x.memview) == Py_None)) {\n",
" <span class='py_c_api'>PyErr_Format</span>(PyExc_TypeError, \"Argument '%.200s' must not be None\", \"x\"); <span class='error_goto'>__PYX_ERR(0, 9, __pyx_L1_error)</span>\n",
" }\n",
" if (unlikely(((PyObject *)__pyx_v_peaks.memview) == Py_None)) {\n",
" <span class='py_c_api'>PyErr_Format</span>(PyExc_TypeError, \"Argument '%.200s' must not be None\", \"peaks\"); <span class='error_goto'>__PYX_ERR(0, 10, __pyx_L1_error)</span>\n",
" }\n",
" __pyx_r = __pyx_pf_46_cython_magic_0fc3700ef1e0d2858154a206c2a8964e_inner(__pyx_self, __pyx_v_x, __pyx_v_peaks, __pyx_v_wlen);\n",
"\n",
" /* function exit code */\n",
" goto __pyx_L0;\n",
" __pyx_L1_error:;\n",
" __pyx_r = NULL;\n",
" __pyx_L0:;\n",
" <span class='refnanny'>__Pyx_RefNannyFinishContext</span>();\n",
" return __pyx_r;\n",
"}\n",
"\n",
"static PyObject *__pyx_pf_46_cython_magic_0fc3700ef1e0d2858154a206c2a8964e_inner(CYTHON_UNUSED PyObject *__pyx_self, __Pyx_memviewslice __pyx_v_x, __Pyx_memviewslice __pyx_v_peaks, __pyx_t_5numpy_intp_t __pyx_v_wlen) {\n",
" __Pyx_memviewslice __pyx_v_prominences = { 0, 0, { 0 }, { 0 }, { 0 } };\n",
" __Pyx_memviewslice __pyx_v_left_bases = { 0, 0, { 0 }, { 0 }, { 0 } };\n",
" __Pyx_memviewslice __pyx_v_right_bases = { 0, 0, { 0 }, { 0 }, { 0 } };\n",
" __pyx_t_5numpy_float64_t __pyx_v_left_min;\n",
" __pyx_t_5numpy_float64_t __pyx_v_right_min;\n",
" __pyx_t_5numpy_intp_t __pyx_v_peak_nr;\n",
" __pyx_t_5numpy_intp_t __pyx_v_peak;\n",
" __pyx_t_5numpy_intp_t __pyx_v_i_min;\n",
" __pyx_t_5numpy_intp_t __pyx_v_i_max;\n",
" __pyx_t_5numpy_intp_t __pyx_v_i;\n",
" int __pyx_v_raise_error;\n",
" PyObject *__pyx_r = NULL;\n",
" <span class='refnanny'>__Pyx_RefNannyDeclarations</span>\n",
" <span class='refnanny'>__Pyx_RefNannySetupContext</span>(\"inner\", 0);\n",
"/* … */\n",
" /* function exit code */\n",
" __pyx_L1_error:;\n",
" <span class='pyx_macro_api'>__Pyx_XDECREF</span>(__pyx_t_1);\n",
" <span class='pyx_macro_api'>__Pyx_XDECREF</span>(__pyx_t_2);\n",
" <span class='pyx_macro_api'>__Pyx_XDECREF</span>(__pyx_t_3);\n",
" <span class='pyx_macro_api'>__Pyx_XDECREF</span>(__pyx_t_4);\n",
" <span class='pyx_macro_api'>__Pyx_XDECREF</span>(__pyx_t_5);\n",
" __PYX_XDEC_MEMVIEW(&amp;__pyx_t_6, 1);\n",
" __PYX_XDEC_MEMVIEW(&amp;__pyx_t_7, 1);\n",
" <span class='pyx_c_api'>__Pyx_AddTraceback</span>(\"_cython_magic_0fc3700ef1e0d2858154a206c2a8964e.inner\", __pyx_clineno, __pyx_lineno, __pyx_filename);\n",
" __pyx_r = NULL;\n",
" __pyx_L0:;\n",
" __PYX_XDEC_MEMVIEW(&amp;__pyx_v_prominences, 1);\n",
" __PYX_XDEC_MEMVIEW(&amp;__pyx_v_left_bases, 1);\n",
" __PYX_XDEC_MEMVIEW(&amp;__pyx_v_right_bases, 1);\n",
" __PYX_XDEC_MEMVIEW(&amp;__pyx_v_x, 1);\n",
" __PYX_XDEC_MEMVIEW(&amp;__pyx_v_peaks, 1);\n",
" <span class='refnanny'>__Pyx_XGIVEREF</span>(__pyx_r);\n",
" <span class='refnanny'>__Pyx_RefNannyFinishContext</span>();\n",
" return __pyx_r;\n",
"}\n",
"/* … */\n",
" __pyx_tuple__29 = <span class='py_c_api'>PyTuple_Pack</span>(14, __pyx_n_s_x, __pyx_n_s_peaks, __pyx_n_s_wlen, __pyx_n_s_prominences, __pyx_n_s_left_bases, __pyx_n_s_right_bases, __pyx_n_s_left_min, __pyx_n_s_right_min, __pyx_n_s_peak_nr, __pyx_n_s_peak, __pyx_n_s_i_min, __pyx_n_s_i_max, __pyx_n_s_i, __pyx_n_s_raise_error);<span class='error_goto'> if (unlikely(!__pyx_tuple__29)) __PYX_ERR(0, 9, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_tuple__29);\n",
" <span class='refnanny'>__Pyx_GIVEREF</span>(__pyx_tuple__29);\n",
"/* … */\n",
" __pyx_t_1 = PyCFunction_NewEx(&amp;__pyx_mdef_46_cython_magic_0fc3700ef1e0d2858154a206c2a8964e_1inner, NULL, __pyx_n_s_cython_magic_0fc3700ef1e0d28581);<span class='error_goto'> if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 9, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_1);\n",
" if (<span class='py_c_api'>PyDict_SetItem</span>(__pyx_d, __pyx_n_s_inner, __pyx_t_1) &lt; 0) <span class='error_goto'>__PYX_ERR(0, 9, __pyx_L1_error)</span>\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_1); __pyx_t_1 = 0;\n",
" __pyx_codeobj__30 = (PyObject*)<span class='pyx_c_api'>__Pyx_PyCode_New</span>(3, 0, 14, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__29, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_home_lg_cache_ipython_cython__c, __pyx_n_s_inner, 9, __pyx_empty_bytes);<span class='error_goto'> if (unlikely(!__pyx_codeobj__30)) __PYX_ERR(0, 9, __pyx_L1_error)</span>\n",
"</pre><pre class=\"cython line score-0\">&#xA0;<span class=\"\">10</span>: <span class=\"n\">np</span><span class=\"o\">.</span><span class=\"n\">intp_t</span><span class=\"p\">[::</span><span class=\"mf\">1</span><span class=\"p\">]</span> <span class=\"n\">peaks</span> <span class=\"ow\">not</span> <span class=\"bp\">None</span><span class=\"p\">,</span></pre>\n",
"<pre class=\"cython line score-0\">&#xA0;<span class=\"\">11</span>: <span class=\"n\">np</span><span class=\"o\">.</span><span class=\"n\">intp_t</span> <span class=\"n\">wlen</span><span class=\"p\">):</span></pre>\n",
"<pre class=\"cython line score-0\">&#xA0;<span class=\"\">12</span>: <span class=\"k\">cdef</span><span class=\"p\">:</span></pre>\n",
"<pre class=\"cython line score-0\">&#xA0;<span class=\"\">13</span>: <span class=\"n\">np</span><span class=\"o\">.</span><span class=\"n\">float64_t</span><span class=\"p\">[::</span><span class=\"mf\">1</span><span class=\"p\">]</span> <span class=\"n\">prominences</span></pre>\n",
"<pre class=\"cython line score-0\">&#xA0;<span class=\"\">14</span>: <span class=\"n\">np</span><span class=\"o\">.</span><span class=\"n\">intp_t</span><span class=\"p\">[::</span><span class=\"mf\">1</span><span class=\"p\">]</span> <span class=\"n\">left_bases</span><span class=\"p\">,</span> <span class=\"n\">right_bases</span></pre>\n",
"<pre class=\"cython line score-0\">&#xA0;<span class=\"\">15</span>: <span class=\"n\">np</span><span class=\"o\">.</span><span class=\"n\">float64_t</span> <span class=\"n\">left_min</span><span class=\"p\">,</span> <span class=\"n\">right_min</span></pre>\n",
"<pre class=\"cython line score-0\">&#xA0;<span class=\"\">16</span>: <span class=\"n\">np</span><span class=\"o\">.</span><span class=\"n\">intp_t</span> <span class=\"n\">peak_nr</span><span class=\"p\">,</span> <span class=\"n\">peak</span><span class=\"p\">,</span> <span class=\"n\">i_min</span><span class=\"p\">,</span> <span class=\"n\">i_max</span><span class=\"p\">,</span> <span class=\"n\">i</span></pre>\n",
"<pre class=\"cython line score-0\">&#xA0;<span class=\"\">17</span>: <span class=\"n\">bint</span> <span class=\"n\">raise_error</span></pre>\n",
"<pre class=\"cython line score-0\">&#xA0;<span class=\"\">18</span>: </pre>\n",
"<pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">19</span>: <span class=\"n\">raise_error</span> <span class=\"o\">=</span> <span class=\"bp\">False</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_v_raise_error = 0;\n",
"</pre><pre class=\"cython line score-35\" onclick='toggleDiv(this)'>+<span class=\"\">20</span>: <span class=\"n\">prominences</span> <span class=\"o\">=</span> <span class=\"n\">np</span><span class=\"o\">.</span><span class=\"n\">empty</span><span class=\"p\">(</span><span class=\"n\">peaks</span><span class=\"o\">.</span><span class=\"n\">shape</span><span class=\"p\">[</span><span class=\"mf\">0</span><span class=\"p\">],</span> <span class=\"n\">dtype</span><span class=\"o\">=</span><span class=\"n\">np</span><span class=\"o\">.</span><span class=\"n\">float64</span><span class=\"p\">)</span></pre>\n",
"<pre class='cython code score-35 '> __pyx_t_1 = <span class='pyx_c_api'>__Pyx_GetModuleGlobalName</span>(__pyx_n_s_np);<span class='error_goto'> if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 20, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_1);\n",
" __pyx_t_2 = <span class='pyx_c_api'>__Pyx_PyObject_GetAttrStr</span>(__pyx_t_1, __pyx_n_s_empty);<span class='error_goto'> if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 20, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_2);\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_1); __pyx_t_1 = 0;\n",
" __pyx_t_1 = <span class='py_c_api'>PyInt_FromSsize_t</span>((__pyx_v_peaks.shape[0]));<span class='error_goto'> if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 20, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_1);\n",
" __pyx_t_3 = <span class='py_c_api'>PyTuple_New</span>(1);<span class='error_goto'> if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 20, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_3);\n",
" <span class='refnanny'>__Pyx_GIVEREF</span>(__pyx_t_1);\n",
" <span class='py_macro_api'>PyTuple_SET_ITEM</span>(__pyx_t_3, 0, __pyx_t_1);\n",
" __pyx_t_1 = 0;\n",
" __pyx_t_1 = <span class='pyx_c_api'>__Pyx_PyDict_NewPresized</span>(1);<span class='error_goto'> if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 20, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_1);\n",
" __pyx_t_4 = <span class='pyx_c_api'>__Pyx_GetModuleGlobalName</span>(__pyx_n_s_np);<span class='error_goto'> if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 20, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_4);\n",
" __pyx_t_5 = <span class='pyx_c_api'>__Pyx_PyObject_GetAttrStr</span>(__pyx_t_4, __pyx_n_s_float64);<span class='error_goto'> if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 20, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_5);\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_4); __pyx_t_4 = 0;\n",
" if (<span class='py_c_api'>PyDict_SetItem</span>(__pyx_t_1, __pyx_n_s_dtype, __pyx_t_5) &lt; 0) <span class='error_goto'>__PYX_ERR(0, 20, __pyx_L1_error)</span>\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_5); __pyx_t_5 = 0;\n",
" __pyx_t_5 = <span class='pyx_c_api'>__Pyx_PyObject_Call</span>(__pyx_t_2, __pyx_t_3, __pyx_t_1);<span class='error_goto'> if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 20, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_5);\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_2); __pyx_t_2 = 0;\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_3); __pyx_t_3 = 0;\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_1); __pyx_t_1 = 0;\n",
" __pyx_t_6 = __Pyx_PyObject_to_MemoryviewSlice_dc_nn___pyx_t_5numpy_float64_t(__pyx_t_5);\n",
" if (unlikely(!__pyx_t_6.memview)) <span class='error_goto'>__PYX_ERR(0, 20, __pyx_L1_error)</span>\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_5); __pyx_t_5 = 0;\n",
" __pyx_v_prominences = __pyx_t_6;\n",
" __pyx_t_6.memview = NULL;\n",
" __pyx_t_6.data = NULL;\n",
"</pre><pre class=\"cython line score-35\" onclick='toggleDiv(this)'>+<span class=\"\">21</span>: <span class=\"n\">left_bases</span> <span class=\"o\">=</span> <span class=\"n\">np</span><span class=\"o\">.</span><span class=\"n\">empty</span><span class=\"p\">(</span><span class=\"n\">peaks</span><span class=\"o\">.</span><span class=\"n\">shape</span><span class=\"p\">[</span><span class=\"mf\">0</span><span class=\"p\">],</span> <span class=\"n\">dtype</span><span class=\"o\">=</span><span class=\"n\">np</span><span class=\"o\">.</span><span class=\"n\">intp</span><span class=\"p\">)</span></pre>\n",
"<pre class='cython code score-35 '> __pyx_t_5 = <span class='pyx_c_api'>__Pyx_GetModuleGlobalName</span>(__pyx_n_s_np);<span class='error_goto'> if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 21, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_5);\n",
" __pyx_t_1 = <span class='pyx_c_api'>__Pyx_PyObject_GetAttrStr</span>(__pyx_t_5, __pyx_n_s_empty);<span class='error_goto'> if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 21, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_1);\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_5); __pyx_t_5 = 0;\n",
" __pyx_t_5 = <span class='py_c_api'>PyInt_FromSsize_t</span>((__pyx_v_peaks.shape[0]));<span class='error_goto'> if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 21, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_5);\n",
" __pyx_t_3 = <span class='py_c_api'>PyTuple_New</span>(1);<span class='error_goto'> if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 21, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_3);\n",
" <span class='refnanny'>__Pyx_GIVEREF</span>(__pyx_t_5);\n",
" <span class='py_macro_api'>PyTuple_SET_ITEM</span>(__pyx_t_3, 0, __pyx_t_5);\n",
" __pyx_t_5 = 0;\n",
" __pyx_t_5 = <span class='pyx_c_api'>__Pyx_PyDict_NewPresized</span>(1);<span class='error_goto'> if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 21, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_5);\n",
" __pyx_t_2 = <span class='pyx_c_api'>__Pyx_GetModuleGlobalName</span>(__pyx_n_s_np);<span class='error_goto'> if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 21, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_2);\n",
" __pyx_t_4 = <span class='pyx_c_api'>__Pyx_PyObject_GetAttrStr</span>(__pyx_t_2, __pyx_n_s_intp);<span class='error_goto'> if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 21, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_4);\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_2); __pyx_t_2 = 0;\n",
" if (<span class='py_c_api'>PyDict_SetItem</span>(__pyx_t_5, __pyx_n_s_dtype, __pyx_t_4) &lt; 0) <span class='error_goto'>__PYX_ERR(0, 21, __pyx_L1_error)</span>\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_4); __pyx_t_4 = 0;\n",
" __pyx_t_4 = <span class='pyx_c_api'>__Pyx_PyObject_Call</span>(__pyx_t_1, __pyx_t_3, __pyx_t_5);<span class='error_goto'> if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 21, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_4);\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_1); __pyx_t_1 = 0;\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_3); __pyx_t_3 = 0;\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_5); __pyx_t_5 = 0;\n",
" __pyx_t_7 = __Pyx_PyObject_to_MemoryviewSlice_dc_nn___pyx_t_5numpy_intp_t(__pyx_t_4);\n",
" if (unlikely(!__pyx_t_7.memview)) <span class='error_goto'>__PYX_ERR(0, 21, __pyx_L1_error)</span>\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_4); __pyx_t_4 = 0;\n",
" __pyx_v_left_bases = __pyx_t_7;\n",
" __pyx_t_7.memview = NULL;\n",
" __pyx_t_7.data = NULL;\n",
"</pre><pre class=\"cython line score-35\" onclick='toggleDiv(this)'>+<span class=\"\">22</span>: <span class=\"n\">right_bases</span> <span class=\"o\">=</span> <span class=\"n\">np</span><span class=\"o\">.</span><span class=\"n\">empty</span><span class=\"p\">(</span><span class=\"n\">peaks</span><span class=\"o\">.</span><span class=\"n\">shape</span><span class=\"p\">[</span><span class=\"mf\">0</span><span class=\"p\">],</span> <span class=\"n\">dtype</span><span class=\"o\">=</span><span class=\"n\">np</span><span class=\"o\">.</span><span class=\"n\">intp</span><span class=\"p\">)</span></pre>\n",
"<pre class='cython code score-35 '> __pyx_t_4 = <span class='pyx_c_api'>__Pyx_GetModuleGlobalName</span>(__pyx_n_s_np);<span class='error_goto'> if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 22, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_4);\n",
" __pyx_t_5 = <span class='pyx_c_api'>__Pyx_PyObject_GetAttrStr</span>(__pyx_t_4, __pyx_n_s_empty);<span class='error_goto'> if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 22, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_5);\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_4); __pyx_t_4 = 0;\n",
" __pyx_t_4 = <span class='py_c_api'>PyInt_FromSsize_t</span>((__pyx_v_peaks.shape[0]));<span class='error_goto'> if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 22, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_4);\n",
" __pyx_t_3 = <span class='py_c_api'>PyTuple_New</span>(1);<span class='error_goto'> if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 22, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_3);\n",
" <span class='refnanny'>__Pyx_GIVEREF</span>(__pyx_t_4);\n",
" <span class='py_macro_api'>PyTuple_SET_ITEM</span>(__pyx_t_3, 0, __pyx_t_4);\n",
" __pyx_t_4 = 0;\n",
" __pyx_t_4 = <span class='pyx_c_api'>__Pyx_PyDict_NewPresized</span>(1);<span class='error_goto'> if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 22, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_4);\n",
" __pyx_t_1 = <span class='pyx_c_api'>__Pyx_GetModuleGlobalName</span>(__pyx_n_s_np);<span class='error_goto'> if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 22, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_1);\n",
" __pyx_t_2 = <span class='pyx_c_api'>__Pyx_PyObject_GetAttrStr</span>(__pyx_t_1, __pyx_n_s_intp);<span class='error_goto'> if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 22, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_2);\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_1); __pyx_t_1 = 0;\n",
" if (<span class='py_c_api'>PyDict_SetItem</span>(__pyx_t_4, __pyx_n_s_dtype, __pyx_t_2) &lt; 0) <span class='error_goto'>__PYX_ERR(0, 22, __pyx_L1_error)</span>\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_2); __pyx_t_2 = 0;\n",
" __pyx_t_2 = <span class='pyx_c_api'>__Pyx_PyObject_Call</span>(__pyx_t_5, __pyx_t_3, __pyx_t_4);<span class='error_goto'> if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 22, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_2);\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_5); __pyx_t_5 = 0;\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_3); __pyx_t_3 = 0;\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_4); __pyx_t_4 = 0;\n",
" __pyx_t_7 = __Pyx_PyObject_to_MemoryviewSlice_dc_nn___pyx_t_5numpy_intp_t(__pyx_t_2);\n",
" if (unlikely(!__pyx_t_7.memview)) <span class='error_goto'>__PYX_ERR(0, 22, __pyx_L1_error)</span>\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_2); __pyx_t_2 = 0;\n",
" __pyx_v_right_bases = __pyx_t_7;\n",
" __pyx_t_7.memview = NULL;\n",
" __pyx_t_7.data = NULL;\n",
"</pre><pre class=\"cython line score-0\">&#xA0;<span class=\"\">23</span>: </pre>\n",
"<pre class=\"cython line score-4\" onclick='toggleDiv(this)'>+<span class=\"\">24</span>: <span class=\"k\">with</span> <span class=\"k\">nogil</span><span class=\"p\">:</span></pre>\n",
"<pre class='cython code score-4 '> {\n",
" #ifdef WITH_THREAD\n",
" PyThreadState *_save;\n",
" Py_UNBLOCK_THREADS\n",
" <span class='pyx_c_api'>__Pyx_FastGIL_Remember</span>();\n",
" #endif\n",
" /*try:*/ {\n",
"/* … */\n",
" /*finally:*/ {\n",
" /*normal exit:*/{\n",
" #ifdef WITH_THREAD\n",
" <span class='pyx_c_api'>__Pyx_FastGIL_Forget</span>();\n",
" Py_BLOCK_THREADS\n",
" #endif\n",
" goto __pyx_L5;\n",
" }\n",
" __pyx_L5:;\n",
" }\n",
" }\n",
"</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">25</span>: <span class=\"k\">for</span> <span class=\"n\">peak_nr</span> <span class=\"ow\">in</span> <span class=\"nb\">range</span><span class=\"p\">(</span><span class=\"n\">peaks</span><span class=\"o\">.</span><span class=\"n\">shape</span><span class=\"p\">[</span><span class=\"mf\">0</span><span class=\"p\">]):</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_t_8 = (__pyx_v_peaks.shape[0]);\n",
" for (__pyx_t_9 = 0; __pyx_t_9 &lt; __pyx_t_8; __pyx_t_9+=1) {\n",
" __pyx_v_peak_nr = __pyx_t_9;\n",
"</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">26</span>: <span class=\"n\">peak</span> <span class=\"o\">=</span> <span class=\"n\">peaks</span><span class=\"p\">[</span><span class=\"n\">peak_nr</span><span class=\"p\">]</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_t_10 = __pyx_v_peak_nr;\n",
" __pyx_v_peak = (*((__pyx_t_5numpy_intp_t *) ( /* dim=0 */ ((char *) (((__pyx_t_5numpy_intp_t *) __pyx_v_peaks.data) + __pyx_t_10)) )));\n",
"</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">27</span>: <span class=\"n\">i_min</span> <span class=\"o\">=</span> <span class=\"mf\">0</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_v_i_min = 0;\n",
"</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">28</span>: <span class=\"n\">i_max</span> <span class=\"o\">=</span> <span class=\"n\">x</span><span class=\"o\">.</span><span class=\"n\">shape</span><span class=\"p\">[</span><span class=\"mf\">0</span><span class=\"p\">]</span> <span class=\"o\">-</span> <span class=\"mf\">1</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_v_i_max = ((__pyx_v_x.shape[0]) - 1);\n",
"</pre><pre class=\"cython line score-0\">&#xA0;<span class=\"\">29</span>: </pre>\n",
"<pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">30</span>: <span class=\"k\">if</span> <span class=\"mf\">2</span> <span class=\"o\">&lt;=</span> <span class=\"n\">wlen</span><span class=\"p\">:</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_t_11 = ((2 &lt;= __pyx_v_wlen) != 0);\n",
" if (__pyx_t_11) {\n",
"/* … */\n",
" }\n",
"</pre><pre class=\"cython line score-0\">&#xA0;<span class=\"\">31</span>: <span class=\"c\"># Adjust window around the evaluated peak (within bounds);</span></pre>\n",
"<pre class=\"cython line score-0\">&#xA0;<span class=\"\">32</span>: <span class=\"c\"># if wlen is even the resulting window length is is implicitly</span></pre>\n",
"<pre class=\"cython line score-0\">&#xA0;<span class=\"\">33</span>: <span class=\"c\"># rounded to next odd integer</span></pre>\n",
"<pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">34</span>: <span class=\"n\">i_min</span> <span class=\"o\">=</span> <span class=\"nb\">max</span><span class=\"p\">(</span><span class=\"n\">peak</span> <span class=\"o\">-</span> <span class=\"n\">wlen</span> <span class=\"o\">//</span> <span class=\"mf\">2</span><span class=\"p\">,</span> <span class=\"n\">i_min</span><span class=\"p\">)</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_t_12 = __pyx_v_i_min;\n",
" __pyx_t_13 = (__pyx_v_peak - __Pyx_div_long(__pyx_v_wlen, 2));\n",
" if (((__pyx_t_12 &gt; __pyx_t_13) != 0)) {\n",
" __pyx_t_14 = __pyx_t_12;\n",
" } else {\n",
" __pyx_t_14 = __pyx_t_13;\n",
" }\n",
" __pyx_v_i_min = __pyx_t_14;\n",
"</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">35</span>: <span class=\"n\">i_max</span> <span class=\"o\">=</span> <span class=\"nb\">min</span><span class=\"p\">(</span><span class=\"n\">peak</span> <span class=\"o\">+</span> <span class=\"n\">wlen</span> <span class=\"o\">//</span> <span class=\"mf\">2</span><span class=\"p\">,</span> <span class=\"n\">i_max</span><span class=\"p\">)</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_t_12 = __pyx_v_i_max;\n",
" __pyx_t_14 = (__pyx_v_peak + __Pyx_div_long(__pyx_v_wlen, 2));\n",
" if (((__pyx_t_12 &lt; __pyx_t_14) != 0)) {\n",
" __pyx_t_13 = __pyx_t_12;\n",
" } else {\n",
" __pyx_t_13 = __pyx_t_14;\n",
" }\n",
" __pyx_v_i_max = __pyx_t_13;\n",
"</pre><pre class=\"cython line score-0\">&#xA0;<span class=\"\">36</span>: </pre>\n",
"<pre class=\"cython line score-0\">&#xA0;<span class=\"\">37</span>: <span class=\"c\"># Find the left base in interval [i_min, peak]</span></pre>\n",
"<pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">38</span>: <span class=\"n\">i</span> <span class=\"o\">=</span> <span class=\"n\">peak</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_v_i = __pyx_v_peak;\n",
"</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">39</span>: <span class=\"n\">left_min</span> <span class=\"o\">=</span> <span class=\"n\">x</span><span class=\"p\">[</span><span class=\"n\">peak</span><span class=\"p\">]</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_t_15 = __pyx_v_peak;\n",
" __pyx_v_left_min = (*((__pyx_t_5numpy_float64_t *) ( /* dim=0 */ ((char *) (((__pyx_t_5numpy_float64_t *) __pyx_v_x.data) + __pyx_t_15)) )));\n",
"</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">40</span>: <span class=\"k\">while</span> <span class=\"n\">i_min</span> <span class=\"o\">&lt;=</span> <span class=\"n\">i</span> <span class=\"ow\">and</span> <span class=\"n\">x</span><span class=\"p\">[</span><span class=\"n\">i</span><span class=\"p\">]</span> <span class=\"o\">&lt;=</span> <span class=\"n\">x</span><span class=\"p\">[</span><span class=\"n\">peak</span><span class=\"p\">]:</span></pre>\n",
"<pre class='cython code score-0 '> while (1) {\n",
" __pyx_t_16 = ((__pyx_v_i_min &lt;= __pyx_v_i) != 0);\n",
" if (__pyx_t_16) {\n",
" } else {\n",
" __pyx_t_11 = __pyx_t_16;\n",
" goto __pyx_L11_bool_binop_done;\n",
" }\n",
" __pyx_t_17 = __pyx_v_i;\n",
" __pyx_t_18 = __pyx_v_peak;\n",
" __pyx_t_16 = (((*((__pyx_t_5numpy_float64_t *) ( /* dim=0 */ ((char *) (((__pyx_t_5numpy_float64_t *) __pyx_v_x.data) + __pyx_t_17)) ))) &lt;= (*((__pyx_t_5numpy_float64_t *) ( /* dim=0 */ ((char *) (((__pyx_t_5numpy_float64_t *) __pyx_v_x.data) + __pyx_t_18)) )))) != 0);\n",
" __pyx_t_11 = __pyx_t_16;\n",
" __pyx_L11_bool_binop_done:;\n",
" if (!__pyx_t_11) break;\n",
"</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">41</span>: <span class=\"k\">if</span> <span class=\"n\">x</span><span class=\"p\">[</span><span class=\"n\">i</span><span class=\"p\">]</span> <span class=\"o\">&lt;</span> <span class=\"n\">left_min</span><span class=\"p\">:</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_t_19 = __pyx_v_i;\n",
" __pyx_t_11 = (((*((__pyx_t_5numpy_float64_t *) ( /* dim=0 */ ((char *) (((__pyx_t_5numpy_float64_t *) __pyx_v_x.data) + __pyx_t_19)) ))) &lt; __pyx_v_left_min) != 0);\n",
" if (__pyx_t_11) {\n",
"/* … */\n",
" }\n",
"</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">42</span>: <span class=\"n\">left_min</span> <span class=\"o\">=</span> <span class=\"n\">x</span><span class=\"p\">[</span><span class=\"n\">i</span><span class=\"p\">]</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_t_20 = __pyx_v_i;\n",
" __pyx_v_left_min = (*((__pyx_t_5numpy_float64_t *) ( /* dim=0 */ ((char *) (((__pyx_t_5numpy_float64_t *) __pyx_v_x.data) + __pyx_t_20)) )));\n",
"</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">43</span>: <span class=\"n\">left_bases</span><span class=\"p\">[</span><span class=\"n\">peak_nr</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"n\">i</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_t_21 = __pyx_v_peak_nr;\n",
" *((__pyx_t_5numpy_intp_t *) ( /* dim=0 */ ((char *) (((__pyx_t_5numpy_intp_t *) __pyx_v_left_bases.data) + __pyx_t_21)) )) = __pyx_v_i;\n",
"</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">44</span>: <span class=\"n\">i</span> <span class=\"o\">-=</span> <span class=\"mf\">1</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_v_i = (__pyx_v_i - 1);\n",
" }\n",
"</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">45</span>: <span class=\"k\">if</span> <span class=\"ow\">not</span> <span class=\"n\">left_min</span> <span class=\"o\">&lt;</span> <span class=\"n\">x</span><span class=\"p\">[</span><span class=\"n\">peak</span><span class=\"p\">]:</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_t_22 = __pyx_v_peak;\n",
" __pyx_t_11 = ((!((__pyx_v_left_min &lt; (*((__pyx_t_5numpy_float64_t *) ( /* dim=0 */ ((char *) (((__pyx_t_5numpy_float64_t *) __pyx_v_x.data) + __pyx_t_22)) )))) != 0)) != 0);\n",
" if (__pyx_t_11) {\n",
"/* … */\n",
" }\n",
"</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">46</span>: <span class=\"n\">raise_error</span> <span class=\"o\">=</span> <span class=\"bp\">True</span> <span class=\"c\"># Raise error outside nogil statement</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_v_raise_error = 1;\n",
"</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">47</span>: <span class=\"k\">break</span></pre>\n",
"<pre class='cython code score-0 '> goto __pyx_L7_break;\n",
"</pre><pre class=\"cython line score-0\">&#xA0;<span class=\"\">48</span>: </pre>\n",
"<pre class=\"cython line score-0\">&#xA0;<span class=\"\">49</span>: <span class=\"c\"># Find the right base in interval [peak, i_max]</span></pre>\n",
"<pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">50</span>: <span class=\"n\">i</span> <span class=\"o\">=</span> <span class=\"n\">peak</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_v_i = __pyx_v_peak;\n",
"</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">51</span>: <span class=\"n\">right_min</span> <span class=\"o\">=</span> <span class=\"n\">x</span><span class=\"p\">[</span><span class=\"n\">peak</span><span class=\"p\">]</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_t_23 = __pyx_v_peak;\n",
" __pyx_v_right_min = (*((__pyx_t_5numpy_float64_t *) ( /* dim=0 */ ((char *) (((__pyx_t_5numpy_float64_t *) __pyx_v_x.data) + __pyx_t_23)) )));\n",
"</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">52</span>: <span class=\"k\">while</span> <span class=\"n\">i</span> <span class=\"o\">&lt;=</span> <span class=\"n\">i_max</span> <span class=\"ow\">and</span> <span class=\"n\">x</span><span class=\"p\">[</span><span class=\"n\">i</span><span class=\"p\">]</span> <span class=\"o\">&lt;=</span> <span class=\"n\">x</span><span class=\"p\">[</span><span class=\"n\">peak</span><span class=\"p\">]:</span></pre>\n",
"<pre class='cython code score-0 '> while (1) {\n",
" __pyx_t_16 = ((__pyx_v_i &lt;= __pyx_v_i_max) != 0);\n",
" if (__pyx_t_16) {\n",
" } else {\n",
" __pyx_t_11 = __pyx_t_16;\n",
" goto __pyx_L17_bool_binop_done;\n",
" }\n",
" __pyx_t_24 = __pyx_v_i;\n",
" __pyx_t_25 = __pyx_v_peak;\n",
" __pyx_t_16 = (((*((__pyx_t_5numpy_float64_t *) ( /* dim=0 */ ((char *) (((__pyx_t_5numpy_float64_t *) __pyx_v_x.data) + __pyx_t_24)) ))) &lt;= (*((__pyx_t_5numpy_float64_t *) ( /* dim=0 */ ((char *) (((__pyx_t_5numpy_float64_t *) __pyx_v_x.data) + __pyx_t_25)) )))) != 0);\n",
" __pyx_t_11 = __pyx_t_16;\n",
" __pyx_L17_bool_binop_done:;\n",
" if (!__pyx_t_11) break;\n",
"</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">53</span>: <span class=\"k\">if</span> <span class=\"n\">x</span><span class=\"p\">[</span><span class=\"n\">i</span><span class=\"p\">]</span> <span class=\"o\">&lt;</span> <span class=\"n\">right_min</span><span class=\"p\">:</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_t_26 = __pyx_v_i;\n",
" __pyx_t_11 = (((*((__pyx_t_5numpy_float64_t *) ( /* dim=0 */ ((char *) (((__pyx_t_5numpy_float64_t *) __pyx_v_x.data) + __pyx_t_26)) ))) &lt; __pyx_v_right_min) != 0);\n",
" if (__pyx_t_11) {\n",
"/* … */\n",
" }\n",
"</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">54</span>: <span class=\"n\">right_min</span> <span class=\"o\">=</span> <span class=\"n\">x</span><span class=\"p\">[</span><span class=\"n\">i</span><span class=\"p\">]</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_t_27 = __pyx_v_i;\n",
" __pyx_v_right_min = (*((__pyx_t_5numpy_float64_t *) ( /* dim=0 */ ((char *) (((__pyx_t_5numpy_float64_t *) __pyx_v_x.data) + __pyx_t_27)) )));\n",
"</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">55</span>: <span class=\"n\">right_bases</span><span class=\"p\">[</span><span class=\"n\">peak_nr</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"n\">i</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_t_28 = __pyx_v_peak_nr;\n",
" *((__pyx_t_5numpy_intp_t *) ( /* dim=0 */ ((char *) (((__pyx_t_5numpy_intp_t *) __pyx_v_right_bases.data) + __pyx_t_28)) )) = __pyx_v_i;\n",
"</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">56</span>: <span class=\"n\">i</span> <span class=\"o\">+=</span> <span class=\"mf\">1</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_v_i = (__pyx_v_i + 1);\n",
" }\n",
"</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">57</span>: <span class=\"k\">if</span> <span class=\"ow\">not</span> <span class=\"n\">right_min</span> <span class=\"o\">&lt;</span> <span class=\"n\">x</span><span class=\"p\">[</span><span class=\"n\">peak</span><span class=\"p\">]:</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_t_29 = __pyx_v_peak;\n",
" __pyx_t_11 = ((!((__pyx_v_right_min &lt; (*((__pyx_t_5numpy_float64_t *) ( /* dim=0 */ ((char *) (((__pyx_t_5numpy_float64_t *) __pyx_v_x.data) + __pyx_t_29)) )))) != 0)) != 0);\n",
" if (__pyx_t_11) {\n",
"/* … */\n",
" }\n",
"</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">58</span>: <span class=\"n\">raise_error</span> <span class=\"o\">=</span> <span class=\"bp\">True</span> <span class=\"c\"># Raise error outside nogil statement</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_v_raise_error = 1;\n",
"</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">59</span>: <span class=\"k\">break</span></pre>\n",
"<pre class='cython code score-0 '> goto __pyx_L7_break;\n",
"</pre><pre class=\"cython line score-0\">&#xA0;<span class=\"\">60</span>: </pre>\n",
"<pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">61</span>: <span class=\"n\">prominences</span><span class=\"p\">[</span><span class=\"n\">peak_nr</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"n\">x</span><span class=\"p\">[</span><span class=\"n\">peak</span><span class=\"p\">]</span> <span class=\"o\">-</span> <span class=\"nb\">max</span><span class=\"p\">(</span><span class=\"n\">left_min</span><span class=\"p\">,</span> <span class=\"n\">right_min</span><span class=\"p\">)</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_t_30 = __pyx_v_peak;\n",
" __pyx_t_31 = __pyx_v_right_min;\n",
" __pyx_t_32 = __pyx_v_left_min;\n",
" if (((__pyx_t_31 &gt; __pyx_t_32) != 0)) {\n",
" __pyx_t_33 = __pyx_t_31;\n",
" } else {\n",
" __pyx_t_33 = __pyx_t_32;\n",
" }\n",
" __pyx_t_34 = __pyx_v_peak_nr;\n",
" *((__pyx_t_5numpy_float64_t *) ( /* dim=0 */ ((char *) (((__pyx_t_5numpy_float64_t *) __pyx_v_prominences.data) + __pyx_t_34)) )) = ((*((__pyx_t_5numpy_float64_t *) ( /* dim=0 */ ((char *) (((__pyx_t_5numpy_float64_t *) __pyx_v_x.data) + __pyx_t_30)) ))) - __pyx_t_33);\n",
" }\n",
" __pyx_L7_break:;\n",
" }\n",
"</pre><pre class=\"cython line score-0\">&#xA0;<span class=\"\">62</span>: </pre>\n",
"<pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">63</span>: <span class=\"k\">if</span> <span class=\"n\">raise_error</span> <span class=\"ow\">is</span> <span class=\"bp\">True</span><span class=\"p\">:</span></pre>\n",
"<pre class='cython code score-0 '> __pyx_t_11 = ((__pyx_v_raise_error == 1) != 0);\n",
" if (__pyx_t_11) {\n",
"/* … */\n",
" }\n",
"</pre><pre class=\"cython line score-26\" onclick='toggleDiv(this)'>+<span class=\"\">64</span>: <span class=\"k\">raise</span> <span class=\"ne\">ValueError</span><span class=\"p\">(</span><span class=\"nb\">str</span><span class=\"p\">(</span><span class=\"n\">peak</span><span class=\"p\">)</span> <span class=\"o\">+</span> <span class=\"s\">&#39; is not a valid peak&#39;</span><span class=\"p\">)</span></pre>\n",
"<pre class='cython code score-26 '> __pyx_t_2 = <span class='pyx_c_api'>__Pyx_PyInt_From_Py_intptr_t</span>(__pyx_v_peak);<span class='error_goto'> if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 64, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_2);\n",
" __pyx_t_4 = <span class='py_c_api'>PyTuple_New</span>(1);<span class='error_goto'> if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 64, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_4);\n",
" <span class='refnanny'>__Pyx_GIVEREF</span>(__pyx_t_2);\n",
" <span class='py_macro_api'>PyTuple_SET_ITEM</span>(__pyx_t_4, 0, __pyx_t_2);\n",
" __pyx_t_2 = 0;\n",
" __pyx_t_2 = <span class='pyx_c_api'>__Pyx_PyObject_Call</span>(((PyObject *)(&amp;PyUnicode_Type)), __pyx_t_4, NULL);<span class='error_goto'> if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 64, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_2);\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_4); __pyx_t_4 = 0;\n",
" __pyx_t_4 = <span class='pyx_c_api'>__Pyx_PyUnicode_Concat</span>(__pyx_t_2, __pyx_kp_u_is_not_a_valid_peak);<span class='error_goto'> if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 64, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_4);\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_2); __pyx_t_2 = 0;\n",
" __pyx_t_2 = <span class='py_c_api'>PyTuple_New</span>(1);<span class='error_goto'> if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 64, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_2);\n",
" <span class='refnanny'>__Pyx_GIVEREF</span>(__pyx_t_4);\n",
" <span class='py_macro_api'>PyTuple_SET_ITEM</span>(__pyx_t_2, 0, __pyx_t_4);\n",
" __pyx_t_4 = 0;\n",
" __pyx_t_4 = <span class='pyx_c_api'>__Pyx_PyObject_Call</span>(__pyx_builtin_ValueError, __pyx_t_2, NULL);<span class='error_goto'> if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 64, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_4);\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_2); __pyx_t_2 = 0;\n",
" <span class='pyx_c_api'>__Pyx_Raise</span>(__pyx_t_4, 0, 0, 0);\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_4); __pyx_t_4 = 0;\n",
" <span class='error_goto'>__PYX_ERR(0, 64, __pyx_L1_error)</span>\n",
"</pre><pre class=\"cython line score-0\">&#xA0;<span class=\"\">65</span>: </pre>\n",
"<pre class=\"cython line score-0\">&#xA0;<span class=\"\">66</span>: <span class=\"c\"># Return memoryviews as ndarrays</span></pre>\n",
"<pre class=\"cython line score-18\" onclick='toggleDiv(this)'>+<span class=\"\">67</span>: <span class=\"k\">return</span> <span class=\"n\">prominences</span><span class=\"o\">.</span><span class=\"n\">base</span><span class=\"p\">,</span> <span class=\"n\">left_bases</span><span class=\"o\">.</span><span class=\"n\">base</span><span class=\"p\">,</span> <span class=\"n\">right_bases</span><span class=\"o\">.</span><span class=\"n\">base</span></pre>\n",
"<pre class='cython code score-18 '> <span class='pyx_macro_api'>__Pyx_XDECREF</span>(__pyx_r);\n",
" __pyx_t_4 = __pyx_memoryview_fromslice(__pyx_v_prominences, 1, (PyObject *(*)(char *)) __pyx_memview_get_nn___pyx_t_5numpy_float64_t, (int (*)(char *, PyObject *)) __pyx_memview_set_nn___pyx_t_5numpy_float64_t, 0);;<span class='error_goto'> if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 67, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_4);\n",
" __pyx_t_2 = <span class='pyx_c_api'>__Pyx_PyObject_GetAttrStr</span>(__pyx_t_4, __pyx_n_s_base);<span class='error_goto'> if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 67, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_2);\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_4); __pyx_t_4 = 0;\n",
" __pyx_t_4 = __pyx_memoryview_fromslice(__pyx_v_left_bases, 1, (PyObject *(*)(char *)) __pyx_memview_get_nn___pyx_t_5numpy_intp_t, (int (*)(char *, PyObject *)) __pyx_memview_set_nn___pyx_t_5numpy_intp_t, 0);;<span class='error_goto'> if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 67, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_4);\n",
" __pyx_t_3 = <span class='pyx_c_api'>__Pyx_PyObject_GetAttrStr</span>(__pyx_t_4, __pyx_n_s_base);<span class='error_goto'> if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 67, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_3);\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_4); __pyx_t_4 = 0;\n",
" __pyx_t_4 = __pyx_memoryview_fromslice(__pyx_v_right_bases, 1, (PyObject *(*)(char *)) __pyx_memview_get_nn___pyx_t_5numpy_intp_t, (int (*)(char *, PyObject *)) __pyx_memview_set_nn___pyx_t_5numpy_intp_t, 0);;<span class='error_goto'> if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 67, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_4);\n",
" __pyx_t_5 = <span class='pyx_c_api'>__Pyx_PyObject_GetAttrStr</span>(__pyx_t_4, __pyx_n_s_base);<span class='error_goto'> if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 67, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_5);\n",
" <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_4); __pyx_t_4 = 0;\n",
" __pyx_t_4 = <span class='py_c_api'>PyTuple_New</span>(3);<span class='error_goto'> if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 67, __pyx_L1_error)</span>\n",
" <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_4);\n",
" <span class='refnanny'>__Pyx_GIVEREF</span>(__pyx_t_2);\n",
" <span class='py_macro_api'>PyTuple_SET_ITEM</span>(__pyx_t_4, 0, __pyx_t_2);\n",
" <span class='refnanny'>__Pyx_GIVEREF</span>(__pyx_t_3);\n",
" <span class='py_macro_api'>PyTuple_SET_ITEM</span>(__pyx_t_4, 1, __pyx_t_3);\n",
" <span class='refnanny'>__Pyx_GIVEREF</span>(__pyx_t_5);\n",
" <span class='py_macro_api'>PyTuple_SET_ITEM</span>(__pyx_t_4, 2, __pyx_t_5);\n",
" __pyx_t_2 = 0;\n",
" __pyx_t_3 = 0;\n",
" __pyx_t_5 = 0;\n",
" __pyx_r = __pyx_t_4;\n",
" __pyx_t_4 = 0;\n",
" goto __pyx_L0;\n",
"</pre></div></body></html>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"%%cython -a\n",
"\n",
"import numpy as np\n",
"cimport numpy as np\n",
"import cython\n",
"\n",
"\n",
"@cython.wraparound(False)\n",
"@cython.boundscheck(False)\n",
"def inner(np.float64_t[::1] x not None,\n",
" np.intp_t[::1] peaks not None,\n",
" np.intp_t wlen):\n",
" cdef:\n",
" np.float64_t[::1] prominences\n",
" np.intp_t[::1] left_bases, right_bases\n",
" np.float64_t left_min, right_min\n",
" np.intp_t peak_nr, peak, i_min, i_max, i\n",
" bint raise_error\n",
"\n",
" raise_error = False\n",
" prominences = np.empty(peaks.shape[0], dtype=np.float64)\n",
" left_bases = np.empty(peaks.shape[0], dtype=np.intp)\n",
" right_bases = np.empty(peaks.shape[0], dtype=np.intp)\n",
"\n",
" with nogil:\n",
" for peak_nr in range(peaks.shape[0]):\n",
" peak = peaks[peak_nr]\n",
" i_min = 0\n",
" i_max = x.shape[0] - 1\n",
"\n",
" if 2 <= wlen:\n",
" # Adjust window around the evaluated peak (within bounds);\n",
" # if wlen is even the resulting window length is is implicitly\n",
" # rounded to next odd integer\n",
" i_min = max(peak - wlen // 2, i_min)\n",
" i_max = min(peak + wlen // 2, i_max)\n",
"\n",
" # Find the left base in interval [i_min, peak]\n",
" i = peak\n",
" left_min = x[peak]\n",
" while i_min <= i and x[i] <= x[peak]:\n",
" if x[i] < left_min:\n",
" left_min = x[i]\n",
" left_bases[peak_nr] = i\n",
" i -= 1\n",
" if not left_min < x[peak]:\n",
" raise_error = True # Raise error outside nogil statement\n",
" break\n",
"\n",
" # Find the right base in interval [peak, i_max]\n",
" i = peak\n",
" right_min = x[peak]\n",
" while i <= i_max and x[i] <= x[peak]:\n",
" if x[i] < right_min:\n",
" right_min = x[i]\n",
" right_bases[peak_nr] = i\n",
" i += 1\n",
" if not right_min < x[peak]:\n",
" raise_error = True # Raise error outside nogil statement\n",
" break\n",
"\n",
" prominences[peak_nr] = x[peak] - max(left_min, right_min)\n",
"\n",
" if raise_error is True:\n",
" raise ValueError(str(peak) + ' is not a valid peak')\n",
"\n",
" # Return memoryviews as ndarrays\n",
" return prominences.base, left_bases.base, right_bases.base"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"import math\n",
"\n",
"\n",
"def peak_prominences_cy(x, peaks, wlen=None):\n",
" # Inner function expects `x` to be C-contiguous\n",
" x = np.asarray(x, order='C', dtype=np.float64)\n",
" if x.ndim != 1:\n",
" raise ValueError('`x` must have exactly one dimension')\n",
"\n",
" peaks = np.asarray(peaks)\n",
" try:\n",
" # Safely convert to C-contiguous array of type np.intp\n",
" peaks = peaks.astype(np.intp, order='C', casting='safe',\n",
" subok=False, copy=False)\n",
" except TypeError:\n",
" if peaks.size == 0:\n",
" # Empty arrays default to np.float64 but are valid input\n",
" peaks = np.array([], dtype=np.intp)\n",
" else:\n",
" raise TypeError(\"Cannot safely cast `peaks` to dtype('intp')\")\n",
" if peaks.ndim != 1:\n",
" raise ValueError('`peaks` must have exactly one dimension')\n",
"\n",
" if wlen is None:\n",
" wlen = -1 # Inner function expects int -> None == -1\n",
" elif not 1 < wlen:\n",
" # Give feedback if wlen has illogical value\n",
" raise ValueError('`wlen` must be at larger than 1, was ' + str(wlen))\n",
" else:\n",
" # Round up to next positive integer; rounding up to next odd integer\n",
" # happens implicitly inside the inner function\n",
" wlen = math.ceil(wlen)\n",
"\n",
" return inner(x, peaks, wlen)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Timing different use cases\n",
"\n",
"### ECG signal"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"x_ecg.size = 15000\n"
]
},
{
"data": {
"text/plain": [
"(0, 2000)"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from urllib.request import urlopen\n",
"\n",
"# A natural ecg signal (provided by the package BioSPPy)\n",
"ecg_txt = urlopen(\"https://raw.githubusercontent.com/PIA-Group/BioSPPy/e71ab177c4c8bdc7e644d962210cc40b1bdb41fb/examples/ecg.txt\")\n",
"x_ecg = np.loadtxt(ecg_txt)\n",
"print(\"x_ecg.size =\", x_ecg.size)\n",
"\n",
"peaks_ecg = find_peaks(x_ecg)[0]\n",
"\n",
"plt.plot(x_ecg)\n",
"plt.plot(peaks_ecg, x_ecg[peaks_ecg], \"x\")\n",
"plt.xlim(0, 2000)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"np.testing.assert_equal(peak_prominences(x_ecg, peaks_ecg)[0],\n",
" peak_prominences_cy(x_ecg, peaks_ecg)[0])\n",
"np.testing.assert_equal(peak_prominences(x_ecg, peaks_ecg, 1000)[0],\n",
" peak_prominences_cy(x_ecg, peaks_ecg, 1000)[0])"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"60.4 ms ± 4.44 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"402 µs ± 1.62 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n"
]
}
],
"source": [
"%timeit peak_prominences(x_ecg, peaks_ecg)\n",
"%timeit peak_prominences_cy(x_ecg, peaks_ecg)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"21.6 ms ± 2.18 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"349 µs ± 6.88 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n"
]
}
],
"source": [
"%timeit peak_prominences(x_ecg, peaks_ecg, 1000)\n",
"%timeit peak_prominences_cy(x_ecg, peaks_ecg, 1000)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Compare harmonic signal"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(0, 500)"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Two overlayed harmonic waves with different frequencies\n",
"t = np.linspace(0, 500 * np.pi, 2 ** 15)\n",
"x_harmonic = np.sin(t) + np.sin(7.2 * t)\n",
"\n",
"peaks_harmonic = find_peaks(x_harmonic)[0]\n",
"\n",
"plt.plot(x_harmonic)\n",
"plt.plot(peaks_harmonic, x_harmonic[peaks_harmonic], \"x\")\n",
"plt.xlim(0, 500)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"for a, b in zip(peak_prominences(x_harmonic, peaks_harmonic),\n",
" peak_prominences_cy(x_harmonic, peaks_harmonic)):\n",
" np.testing.assert_equal(a, b)\n",
"for a, b in zip(peak_prominences(x_harmonic, peaks_harmonic, 1000),\n",
" peak_prominences_cy(x_harmonic, peaks_harmonic, 1000)):\n",
" np.testing.assert_equal(a, b)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can see that the bug isn't visible for signals were minima aren't flat."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"170 ms ± 626 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"534 µs ± 8.22 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n"
]
}
],
"source": [
"%timeit peak_prominences(x_harmonic, peaks_harmonic)\n",
"%timeit peak_prominences_cy(x_harmonic, peaks_harmonic)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"44.8 ms ± 4.34 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"369 µs ± 6.58 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n"
]
}
],
"source": [
"%timeit peak_prominences(x_harmonic, peaks_harmonic, 1000)\n",
"%timeit peak_prominences_cy(x_harmonic, peaks_harmonic, 1000)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Peaks with constant height and spacing"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(0, 50)"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"x_const = np.zeros(2 ** 14)\n",
"x_const[2::4] += 1\n",
"\n",
"peaks_const = find_peaks(x_const)[0]\n",
"\n",
"plt.plot(x_const)\n",
"plt.plot(peaks_const, x_const[peaks_const], \"x\")\n",
"plt.xlim(0, 50)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Expected: \n",
"Arrays are not equal\n",
"\n",
"(mismatch 100.0%)\n",
" x: array([0, 0, 0, ..., 0, 0, 0])\n",
" y: array([ 1, 5, 9, ..., 16373, 16377, 16381])\n",
"Expected: \n",
"Arrays are not equal\n",
"\n",
"(mismatch 100.0%)\n",
" x: array([ 0, 0, 0, ..., 15875, 15879, 15883])\n",
" y: array([ 1, 5, 9, ..., 16373, 16377, 16381])\n"
]
}
],
"source": [
"p1, l1, r1 = peak_prominences(x_const, peaks_const)\n",
"p2, l2, r2 = peak_prominences_cy(x_const, peaks_const)\n",
"\n",
"np.testing.assert_equal(p1, p2)\n",
"np.testing.assert_equal(r1, r2)\n",
"try:\n",
" np.testing.assert_equal(l1, l2) # Explanation below\n",
"except AssertionError as e:\n",
" print(\"Expected:\", e)\n",
"\n",
"p1, l1, r1 = peak_prominences(x_const, peaks_const, 1000)\n",
"p2, l2, r2 = peak_prominences_cy(x_const, peaks_const, 1000)\n",
"\n",
"np.testing.assert_equal(p1, p2)\n",
"np.testing.assert_equal(r1, r2)\n",
"try:\n",
" np.testing.assert_equal(l1, l2) # Explanation below\n",
"except AssertionError as e:\n",
" print(\"Expected:\", e)"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"165 ms ± 6.31 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"53 ms ± 160 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
]
}
],
"source": [
"%timeit peak_prominences(x_const, peaks_const)\n",
"%timeit peak_prominences_cy(x_const, peaks_const)"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"78.5 ms ± 2.64 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"3.36 ms ± 15 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
]
}
],
"source": [
"%timeit peak_prominences(x_const, peaks_const, 1000)\n",
"%timeit peak_prominences_cy(x_const, peaks_const, 1000)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This synthetic signal reveals an interesting edge case that is encountered when neighbouring peaks have the same height. Usually a peaks surrounding bases are searched for in the interval \\[a, b\\] where a and b are signal edges or peaks with a higher amplitude than the one currently evaluated. This is the most calculation intensive case for this function. Usually, the iterative solution can break the evaluation of `x` when the interval borders are found (not the case with vectorized approach). In this case however the interval always covers the whole signal and the time difference is not as significant as in more \"natural\" use cases.\n",
"\n",
"Furthermore a potential bug is revealed for the current / old implementation. Lets examine the bases surounding the 50th peak as calculated by both functions:"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"peak = 202\n",
"bases = 0, 203\n",
"bases_cy = 201, 203\n"
]
}
],
"source": [
"_, lbases, rbases = peak_prominences(x_const, peaks_const)\n",
"_, lbases_cy, rbases_cy = peak_prominences_cy(x_const, peaks_const)\n",
"\n",
"i = 50\n",
"print(\"peak = {}\\nbases = {}, {}\\nbases_cy = {}, {}\"\n",
" .format(peaks_const[i], lbases[i], rbases[i], lbases_cy[i], rbases_cy[i]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As we can see the original function returns the signal edge as the position of the left base. This holds true for all peaks in `lbases`.\n",
"However if multiple candidates (with the same minimal height) for the peaks base are possible it would make sense to choose the one nearest to the evaluated peak. This is the case for the cythonized version."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Flat peaks with constant spacing but different height (same base height)"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(0, 50)"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"x_flat = np.zeros(2 ** 15)\n",
"heights = abs(np.random.rand(x_flat.size // 4))\n",
"x_flat[2::4] += heights\n",
"x_flat[3::4] += heights\n",
"\n",
"peaks_flat = find_peaks(x_flat)[0]\n",
"\n",
"plt.plot(x_flat)\n",
"plt.plot(peaks_flat, x_flat[peaks_flat], \"x\")\n",
"plt.xlim(0, 50)"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Expected: \n",
"Arrays are not equal\n",
"\n",
"(mismatch 100.0%)\n",
" x: array([ 0, 4, 8, ..., 32740, 32756, 32740])\n",
" y: array([ 1, 5, 9, ..., 32753, 32757, 32761])\n",
"Expected: \n",
"Arrays are not equal\n",
"\n",
"(mismatch 100.0%)\n",
" x: array([ 0, 4, 8, ..., 32740, 32756, 32740])\n",
" y: array([ 1, 5, 9, ..., 32753, 32757, 32761])\n"
]
}
],
"source": [
"p1, l1, r1 = peak_prominences(x_flat, peaks_flat)\n",
"p2, l2, r2 = peak_prominences_cy(x_flat, peaks_flat)\n",
"\n",
"np.testing.assert_equal(p1, p2)\n",
"np.testing.assert_equal(r1, r2)\n",
"try:\n",
" np.testing.assert_equal(l1, l2) # Same bug for old implementation\n",
"except AssertionError as e:\n",
" print(\"Expected:\", e)\n",
"\n",
" \n",
"p1, l1, r1 = peak_prominences(x_flat, peaks_flat, 1000)\n",
"p2, l2, r2 = peak_prominences_cy(x_flat, peaks_flat,1000)\n",
"\n",
"np.testing.assert_equal(p1, p2)\n",
"np.testing.assert_equal(r1, r2)\n",
"try:\n",
" np.testing.assert_equal(l1, l2) # Same bug for old implementation\n",
"except AssertionError as e:\n",
" print(\"Expected:\", e)"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"998 ms ± 45.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
"632 µs ± 5.94 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n"
]
}
],
"source": [
"%timeit peak_prominences(x_flat, peaks_flat)\n",
"%timeit peak_prominences_cy(x_flat, peaks_flat)"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"190 ms ± 5.68 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
"482 µs ± 1.71 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n"
]
}
],
"source": [
"%timeit peak_prominences(x_flat, peaks_flat, 1000)\n",
"%timeit peak_prominences_cy(x_flat, peaks_flat, 1000)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment