{"id":349,"date":"2022-06-06T19:17:13","date_gmt":"2022-06-06T18:17:13","guid":{"rendered":"https:\/\/www.maidaccordo.net\/blog\/?p=349"},"modified":"2023-04-11T19:36:50","modified_gmt":"2023-04-11T18:36:50","slug":"pregiudizi-su-python-e-numpy","status":"publish","type":"post","link":"https:\/\/www.maidaccordo.net\/blog\/2022\/06\/pregiudizi-su-python-e-numpy\/","title":{"rendered":"Pregiudizi su python e numpy"},"content":{"rendered":"\n<p>A volte le nostre convinzioni sono basate sul nulla e alla prova dei fatti sono sbagliate.<\/p>\n\n\n\n<p>Il python \u00e8 un linguaggio interpretato e come tale \u00e8 lento: \u00e8 vero ma a volte a complicare<br \/>le cose ci si mettono anche i nostri<strong> <\/strong>pregiudizi<strong>.<\/strong><\/p>\n\n\n\n<!--more-->\n\n\n\n<p>Dovevo produrre un vettore e per farlo, nella notte dei tempi, avevo scritto una funzioncina stupida:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-code-table\"><mark class='shcb-loc'><span><span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">old_grid_generator<\/span><span class=\"hljs-params\">()<\/span>:<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>      Psealevel    = <span class=\"hljs-number\">1000.0<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>      scale_height = <span class=\"hljs-number\">6.0<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>      Zdummy       = np.concatenate((np.arange(<span class=\"hljs-number\">80<\/span>,<span class=\"hljs-number\">55<\/span>,<span class=\"hljs-number\">-2<\/span>),np.arange(<span class=\"hljs-number\">54<\/span>,<span class=\"hljs-number\">4<\/span>,<span class=\"hljs-number\">-1<\/span>)))\n<\/span><\/mark><mark class='shcb-loc'><span>      <span class=\"hljs-keyword\">return<\/span> Psealevel * np.exp(-Zdummy \/ scale_height)\n<\/span><\/mark><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Come si intuisce ho usato la libreria numpy nella convinzione che la libreria, compilata dal C, facesse un buon lavoro. Ma siccome il mio cervello funziona male, qualche tempo fa, riguardandola, mi sono chiesto se questa cosa fosse vera. Allora mi sono messo a pensare 5 minuti a come avrei potuto scrivere del codice &#8220;pure python&#8221; ragionevolmente veloce, il primo risultato \u00e8 stato questo:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-code-table\"><mark class='shcb-loc'><span><span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">old_grid_generator_2<\/span><span class=\"hljs-params\">()<\/span>:<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>      Psealevel    = <span class=\"hljs-number\">1000.0<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>      scale_height = <span class=\"hljs-number\">6.0<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>      <span class=\"hljs-keyword\">from<\/span> math <span class=\"hljs-keyword\">import<\/span> exp\n<\/span><\/mark><mark class='shcb-loc'><span>      <span class=\"hljs-keyword\">return<\/span> ( Psealevel * exp(-zz \/ scale_height) <span class=\"hljs-keyword\">for<\/span> zz <span class=\"hljs-keyword\">in<\/span> (*range(<span class=\"hljs-number\">80<\/span>,<span class=\"hljs-number\">55<\/span>,<span class=\"hljs-number\">-2<\/span>),*range(<span class=\"hljs-number\">54<\/span>,<span class=\"hljs-number\">4<\/span>,<span class=\"hljs-number\">-1<\/span>)) )\n<\/span><\/mark><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>confrontando i tempi che impiegano a girare:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">In<\/span> <span class=\"hljs-selector-attr\">&#91;302]<\/span>: %<span class=\"hljs-selector-tag\">timeit<\/span> <span class=\"hljs-selector-tag\">old_grid_generator<\/span>()\n5<span class=\"hljs-selector-class\">.38<\/span> \u00b5<span class=\"hljs-selector-tag\">s<\/span> \u00b1 40 <span class=\"hljs-selector-tag\">ns<\/span> <span class=\"hljs-selector-tag\">per<\/span> <span class=\"hljs-selector-tag\">loop<\/span> (<span class=\"hljs-selector-tag\">mean<\/span> \u00b1 <span class=\"hljs-selector-tag\">std<\/span>. <span class=\"hljs-selector-tag\">dev<\/span>. <span class=\"hljs-selector-tag\">of<\/span> 7 <span class=\"hljs-selector-tag\">runs<\/span>, 100000 <span class=\"hljs-selector-tag\">loops<\/span> <span class=\"hljs-selector-tag\">each<\/span>)\n\n<span class=\"hljs-selector-tag\">In<\/span> <span class=\"hljs-selector-attr\">&#91;333]<\/span>: %<span class=\"hljs-selector-tag\">timeit<\/span> <span class=\"hljs-selector-tag\">old_grid_generator_2<\/span>()\n1<span class=\"hljs-selector-class\">.39<\/span> \u00b5<span class=\"hljs-selector-tag\">s<\/span> \u00b1 28 <span class=\"hljs-selector-tag\">ns<\/span> <span class=\"hljs-selector-tag\">per<\/span> <span class=\"hljs-selector-tag\">loop<\/span> (<span class=\"hljs-selector-tag\">mean<\/span> \u00b1 <span class=\"hljs-selector-tag\">std<\/span>. <span class=\"hljs-selector-tag\">dev<\/span>. <span class=\"hljs-selector-tag\">of<\/span> 7 <span class=\"hljs-selector-tag\">runs<\/span>, 1000000 <span class=\"hljs-selector-tag\">loops<\/span> <span class=\"hljs-selector-tag\">each<\/span>)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Meglio se  importo exp fuori dalla funzione<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">In<\/span> <span class=\"hljs-selector-attr\">&#91;32]<\/span>: %<span class=\"hljs-selector-tag\">timeit<\/span> <span class=\"hljs-selector-tag\">old_grid_generator_2<\/span>()\n925 <span class=\"hljs-selector-tag\">ns<\/span> \u00b1 9<span class=\"hljs-selector-class\">.11<\/span> <span class=\"hljs-selector-tag\">ns<\/span> <span class=\"hljs-selector-tag\">per<\/span> <span class=\"hljs-selector-tag\">loop<\/span> (<span class=\"hljs-selector-tag\">mean<\/span> \u00b1 <span class=\"hljs-selector-tag\">std<\/span>. <span class=\"hljs-selector-tag\">dev<\/span>. <span class=\"hljs-selector-tag\">of<\/span> 7 <span class=\"hljs-selector-tag\">runs<\/span>, 1000000 <span class=\"hljs-selector-tag\">loops<\/span> <span class=\"hljs-selector-tag\">each<\/span>)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Ancora meglio se uso la funzione itertools.chain per concatenare 2 generatori senza passare per la tupla, per convenienza ne ho fatto una terza versione.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">from<\/span> itertools <span class=\"hljs-keyword\">import<\/span> chain\ndef old_grid_generator_3():\n    Psealevel    = <span class=\"hljs-number\">1000.0<\/span>\n    scale_height = <span class=\"hljs-number\">6.0<\/span>\n    <span class=\"hljs-keyword\">return<\/span> ( Psealevel * exp(-zz \/ scale_height) <span class=\"hljs-keyword\">for<\/span> zz <span class=\"hljs-keyword\">in<\/span> chain(range(<span class=\"hljs-number\">80<\/span>,<span class=\"hljs-number\">55<\/span>,<span class=\"hljs-number\">-2<\/span>),range(<span class=\"hljs-number\">54<\/span>,<span class=\"hljs-number\">4<\/span>,<span class=\"hljs-number\">-1<\/span>)))\n\nIn &#91;<span class=\"hljs-number\">114<\/span>]: %timeit old_grid_generator_3()\n<span class=\"hljs-number\">516<\/span> ns \u00b1 <span class=\"hljs-number\">2<\/span> ns per loop (mean \u00b1 std. dev. of <span class=\"hljs-number\">7<\/span> runs, <span class=\"hljs-number\">1000000<\/span> loops each)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Ah, quindi in questo caso particolare il python da solo fa 10 volte meglio di numpy.<br \/>Vabb\u00e8, mi sono detto, sto barando: la prima versione tira fuori un utilissimo<br \/>numpy.array mentre la seconda un banalissimo generatore. Allora:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">In<\/span> <span class=\"hljs-selector-attr\">&#91;116]<\/span>: %<span class=\"hljs-selector-tag\">timeit<\/span> <span class=\"hljs-selector-tag\">np<\/span><span class=\"hljs-selector-class\">.fromiter<\/span>(<span class=\"hljs-selector-tag\">old_grid_generator_3<\/span>(),<span class=\"hljs-selector-tag\">float<\/span>)\n9<span class=\"hljs-selector-class\">.47<\/span> \u00b5<span class=\"hljs-selector-tag\">s<\/span> \u00b1 152 <span class=\"hljs-selector-tag\">ns<\/span> <span class=\"hljs-selector-tag\">per<\/span> <span class=\"hljs-selector-tag\">loop<\/span> (<span class=\"hljs-selector-tag\">mean<\/span> \u00b1 <span class=\"hljs-selector-tag\">std<\/span>. <span class=\"hljs-selector-tag\">dev<\/span>. <span class=\"hljs-selector-tag\">of<\/span> 7 <span class=\"hljs-selector-tag\">runs<\/span>, 100000 <span class=\"hljs-selector-tag\">loops<\/span> <span class=\"hljs-selector-tag\">each<\/span>)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Viene fuori che se gli faccio costruire il vettore numpy il tempo \u00e8 2 volte quello che ci mette la versione nativa numpy: se lo riprende con gli interessi. Quindi scrivere codice che usa le numpy nativamente \u00e8 pi\u00f9 efficiente che scrivere codice veloce in puro python e poi convertire i risultati in numpy.<\/p>\n\n\n\n<p>Allora mi chiedo se a richiedere tempo \u00e8 il vettore numpy o il fatto di voler &#8220;mettere a terra&#8221; il risultato immagazzinandolo in memoria.<\/p>\n\n\n\n<p>Per testare questa ipotesi genero una tupla, oggetto decisamente pi\u00f9 snello di un array numpy:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">In<\/span> <span class=\"hljs-selector-attr\">&#91;138]<\/span>: %<span class=\"hljs-selector-tag\">timeit<\/span> <span class=\"hljs-selector-tag\">tuple<\/span>(<span class=\"hljs-selector-tag\">old_grid_generator_3<\/span>())\n7<span class=\"hljs-selector-class\">.49<\/span> \u00b5<span class=\"hljs-selector-tag\">s<\/span> \u00b1 29<span class=\"hljs-selector-class\">.1<\/span> <span class=\"hljs-selector-tag\">ns<\/span> <span class=\"hljs-selector-tag\">per<\/span> <span class=\"hljs-selector-tag\">loop<\/span> (<span class=\"hljs-selector-tag\">mean<\/span> \u00b1 <span class=\"hljs-selector-tag\">std<\/span>. <span class=\"hljs-selector-tag\">dev<\/span>. <span class=\"hljs-selector-tag\">of<\/span> 7 <span class=\"hljs-selector-tag\">runs<\/span>, 100000 <span class=\"hljs-selector-tag\">loops<\/span> <span class=\"hljs-selector-tag\">each<\/span>)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Ottengo risultati simili se la tupla la genero all&#8217;interno della funzione.<\/p>\n\n\n\n<p>Evidentemente l&#8217;operazione che prende tempo \u00e8 chiedere che i valori vengano immagazzinati in un oggetto in memoria. <\/p>\n\n\n\n<p>Ne deduco che se riuscissimo a ottenere tutti i nostri risultati usando soltanto generatori, senza mai appoggiarli in memoria, se non in ultima istanza, probabilmente otterremmo del codice rapido (confrontabile o quasi coi risultati delle numpy) e molto meno pesante in memoria. Per dirla in modo pi\u00f9 sintetico, se scrivessimo il codice in modo un po&#8217; pi\u00f9 &#8220;funzionale&#8221;. <\/p>\n\n\n\n<p>Non sono sicuro che sia sempre fattibile&#8230;<\/p>\n\n\n\n<p>Quel che resterebbe da capire \u00e8 come questo comportamento scali all&#8217;aumentare del numero di elementi del vettore.<\/p>\n\n\n\n<p>Questo semplice esempio fa vedere \u00e8 come in alcuni casi (attenzione a generalizzare) il design progettuale del &#8220;nuovo&#8221; python3 e quello delle numpy sono in aperto disaccordo.<\/p>\n\n\n\n<p>Ma l&#8217;insegnamento pi\u00f9 importante \u00e8 quello di fermarsi un attimo a pensare prima di scrivere del codice, anche quello che svolge lavori banali.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A volte le nostre convinzioni sono basate sul nulla e alla prova dei fatti sono sbagliate. Il python \u00e8 un linguaggio interpretato e come tale \u00e8 lento: \u00e8 vero ma a volte a complicarele cose ci si mettono anche i &hellip; <a href=\"https:\/\/www.maidaccordo.net\/blog\/2022\/06\/pregiudizi-su-python-e-numpy\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-349","post","type-post","status-publish","format-standard","hentry","category-hardsoft"],"_links":{"self":[{"href":"https:\/\/www.maidaccordo.net\/blog\/wp-json\/wp\/v2\/posts\/349","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.maidaccordo.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.maidaccordo.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.maidaccordo.net\/blog\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/www.maidaccordo.net\/blog\/wp-json\/wp\/v2\/comments?post=349"}],"version-history":[{"count":22,"href":"https:\/\/www.maidaccordo.net\/blog\/wp-json\/wp\/v2\/posts\/349\/revisions"}],"predecessor-version":[{"id":400,"href":"https:\/\/www.maidaccordo.net\/blog\/wp-json\/wp\/v2\/posts\/349\/revisions\/400"}],"wp:attachment":[{"href":"https:\/\/www.maidaccordo.net\/blog\/wp-json\/wp\/v2\/media?parent=349"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.maidaccordo.net\/blog\/wp-json\/wp\/v2\/categories?post=349"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.maidaccordo.net\/blog\/wp-json\/wp\/v2\/tags?post=349"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}