Derniers messages sur Zeste de Savoirhttps://zestedesavoir.com/forums/2017-01-13T10:10:39+01:00Les derniers messages parus sur le forum de Zeste de Savoir.Pyexcel - comparaison de deux fichiers xls, message #1385362017-01-13T10:10:39+01:00leir/@leirhttps://zestedesavoir.com/forums/sujet/7773/pyexcel-comparaison-de-deux-fichiers-xls/?page=1#p138536<p>Merci à vous deux pour ces explications très complètes ! Cela va grandement m’aider, je peux avoir à traiter des fichiers de 200k à 500k lignes parfois, j’avais besoin d’une bonne méthode (la mienne fonctionnait mais j’imagine qu’avec deux fichiers de 500k lignes, ça aurait été super long ^^).</p>
<p>Et j’ai appris plein de trucs, merci encore ! <img alt=":)" src="/static/smileys/smile.png"> Décidément, j’adore python !</p>Pyexcel - comparaison de deux fichiers xls, message #1385282017-01-13T09:39:00+01:00Kje/@Kjehttps://zestedesavoir.com/forums/sujet/7773/pyexcel-comparaison-de-deux-fichiers-xls/?page=1#p138528<p>Au passage, leir, ta fonction <code>define_files_to_check</code> n’est pas très pythonic. Quand tu as 2 valeurs a renvoyés (et toujours 2), pas besoin de passer par un dico. En python on peut renvoyer plusieurs variables (en vrai on renvoie un <code>tuple</code> mais peut importe). </p>
<p>Utilise plutôt un truc comme ça :</p>
<div><table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11</pre></div></td><td class="code"><div class="codehilite"><pre><span></span><span class="k">def</span> <span class="nf">define_files_to_check</span><span class="p">():</span>
<span class="n">file_name1</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s1">'Entrez le nom du 1er fichier :'</span><span class="p">)</span>
<span class="n">file_name2</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s1">'Entrez le nom du 2ème fichier :'</span><span class="p">)</span>
<span class="hll"> <span class="n">sheet1</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">get_array</span><span class="p">(</span><span class="n">file_name</span><span class="o">=</span><span class="n">file_name1</span><span class="p">)</span>
</span><span class="hll"> <span class="n">sheet2</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">get_array</span><span class="p">(</span><span class="n">file_name</span><span class="o">=</span><span class="n">file_name2</span><span class="p">)</span>
</span><span class="hll"> <span class="k">return</span> <span class="n">sheet1</span><span class="p">,</span> <span class="n">sheet2</span>
</span>
<span class="c1"># ...</span>
<span class="hll"><span class="n">sheet1</span><span class="p">,</span> <span class="n">sheet2</span> <span class="o">=</span> <span class="n">define_files_to_check</span><span class="p">()</span>
</span><span class="n">browse_spreadsheet_rows</span><span class="p">(</span><span class="n">sheet1</span><span class="p">,</span> <span class="n">sheet2</span><span class="p">)</span>
</pre></div>
</td></tr></table></div>
<p>C’est bien plus clair.</p>Pyexcel - comparaison de deux fichiers xls, message #1385262017-01-13T09:32:26+01:00adri1/@adri1https://zestedesavoir.com/forums/sujet/7773/pyexcel-comparaison-de-deux-fichiers-xls/?page=1#p138526<blockquote>
<p>Ce que je ne comprends pas bien c’est qu’à l’intérieur des set(), tu parcours chaque ’sheet’, puis chaque ’row’ pour ajouter un ’elt’ (le contenu d’une cellule ?) non ?</p>
</blockquote>
<p>C’est exactement ça. En fait, <code>(elt for row in sheet1 for elt in row)</code> est un <em>generator</em> écrit en compréhension qui est équivalent à celui-ci :</p>
<div><table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4</pre></div></td><td class="code"><div class="codehilite"><pre><span></span>def browse(sheet):
for row in sheet:
for elt in row:
yield elt
</pre></div>
</td></tr></table></div>
<p>La construction du set pourrait être écrite <code>set1 = set(browse(sheet1))</code>.</p>
<p>C’est un objet qui va donc parcourir une fois les données dans <code>sheet1</code> et les donner une à une à <code>set</code>, qui va se charger de construire un ensemble avec ces données.</p>
<blockquote>
<p>Comment est-ce que j’arrive à une telle différence ?</p>
</blockquote>
<p>Il y a plein de raisons différente.</p>
<p>La première, c’est que <code>set</code> est énormément optimisé pour construire un ensemble unique à partir de données puis calculer des intersections entre deux sets. Tu auras (et moi aussi je te rassure <img alt=":)" src="/static/smileys/smile.png"> ) déjà du mal à venir avec un algorithme meilleur que celui qui est implémenté dans <code>set</code>. Si ça t’intéresse, <code>set</code> marche avec une <code>hashtable</code>, c’est à dire que pour chaque élément que tu lui donnes, <code>set</code> calcul un numéro qui lui sert à identifier cet élément, pour chaque nouveau élément que tu ajoutes, il suffit de calculer le hash de cet élément et vérifier si il est présent ou non (ce qui est rapide puisque les hash sont ordonnables) pour savoir si il faut l’ajouter au set ou pas. Le même mécanisme rend le calcul de l’intersection beaucoup plus efficace qu’une comparaison élément par élément entre les deux feuilles puis élément par élément entre les éléments identiques et l’intersection déjà trouvée comme tu le faisais. Ton code nécessite 50K*50K comparaisons environ (en comptant un élément par ligne), soit 2.5 <em>milliards</em> contre 100K calculs de hash et environ 200K recherches dans les hashtables.</p>
<p>Ça, ça explique partiellement que mon code soit beaucoup plus rapide que le premier que tu as fait. Une deuxième raison, c’est que <code>set</code> est implémenté en C alors que ton code est implémenté en Python. Ceci implique un surcoût pour des opérations qui ont l’air simples (et le sont en C mais pas en Python) comme la comparaison de deux éléments. Ça n’a pas d’incidence visible sur un faible nombre de comparaisons, mais sur 2.5 milliards, ça va se voir. Même problème dans ton second code, la construction de listes et la comparaison avec les set déjà trouvé sera lente.</p>
<blockquote>
<p>Il y a tout de même une petite différence entre les deux codes. Le tiens était totalement itératif. Celui d’adri1 va mettre, en gros, tout le contenu des deux feuilles en mémoires. Sur des (très) gros tableaux ça peut coûter cher. Le siens sera toujours bien plus rapide mais consomme plus de mémoire a priori. Cependant, d’ici que tu remplisse ta mémoire avec des feuilles Excel…</p>
</blockquote>
<p>Même pas parce que les <code>arrays</code> de <code>pyexcel</code> <em>sont</em> chargés en mémoire. Et même dans le cas contraire, ce que mon code charge en mémoire serait seulement les ensembles obtenus.</p>Pyexcel - comparaison de deux fichiers xls, message #1385252017-01-13T09:29:04+01:00Kje/@Kjehttps://zestedesavoir.com/forums/sujet/7773/pyexcel-comparaison-de-deux-fichiers-xls/?page=1#p138525<p>C’est bien plus rapide car :</p>
<ol>
<li>Dans ton code pour chaque cellule de la feuille 1 tu va re-parcourir entièrement tout l’autre feuille. Ça fait des tonnes d’itérations alors que dans son code il ne passe qu’une fois sur chaque cellule (pour récupérer le contenu).</li>
<li>Une fois les feuilles parcourus, beaucoup moins de comparaisons seront effectués. En effet en créant des sets tu as déjà viré toutes les cellules identiques. Imagine si ta première feuille a 100 cellules qui contiennent le chiffre "0". Dans ton code, pour chacune d’elles tu va aller parcourir tout l’autre feuille pour le chercher. Si il est a la fin, ça va être long et en plus tu va le faire 100 fois. Dans sa solution, quand le set est créé, il n’y a déjà plus qu’un seul "0". Donc quand il va le rechercher dans le deuxième il ne va le faire qu’une seule fois.</li>
<li>Enfin dernière raisons, les set sont un des conteneurs de bases. Ils sont codés en C et sont très optimisés. Les recherche/comparaison effectués par l’intersection sont réalisés de manière bien plus optimale.</li>
</ol>
<div class="warning ico-after">
<p>Il y a tout de même une petite différence entre les deux codes. Le tiens était totalement itératif. Celui d’adri1 va mettre, en gros, tout le contenu des deux feuilles en mémoires. Sur des (très) gros tableaux ça peut coûter cher. Le siens sera toujours bien plus rapide mais consomme plus de mémoire a priori. Cependant, d’ici que tu remplisse ta mémoire avec des feuilles Excel… </p>
</div>Pyexcel - comparaison de deux fichiers xls, message #1385142017-01-13T07:50:21+01:00leir/@leirhttps://zestedesavoir.com/forums/sujet/7773/pyexcel-comparaison-de-deux-fichiers-xls/?page=1#p138514<p>Ton dernier code est effectivement bien plus rapide. Ce que je ne comprends pas bien c’est qu’à l’intérieur des set(), tu parcours chaque ’sheet’, puis chaque ’row’ pour ajouter un ’elt’ (le contenu d’une cellule ?) non ? Ton code produit un résultat équivalent au mien mais en 2 à 3 secondes seulement. Comment est-ce que j’arrive à une telle différence ?</p>
<p>En tout cas merci pour ton aide ! Je suis en train de créer une petite interface graphique avec tkinter et la première version de mon code faisait planter le soft à tous les coups <img alt="^^" src="/static/smileys/hihi.png"></p>Pyexcel - comparaison de deux fichiers xls, message #1385042017-01-12T23:19:48+01:00adri1/@adri1https://zestedesavoir.com/forums/sujet/7773/pyexcel-comparaison-de-deux-fichiers-xls/?page=1#p138504<p>Tu peux faire plus simple, il suffit de laisser <code>set</code> faire tout le boulot. Ce sera forcément plus efficace que de boucler à la main ou encore de passer des filter qui vont parcourir toutes tes données une fois pour pas grand chose.</p>
<div><table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4</pre></div></td><td class="code"><div class="codehilite"><pre><span></span><span class="k">def</span> <span class="nf">browse_rows</span><span class="p">(</span><span class="n">sheet1</span><span class="p">,</span> <span class="n">sheet2</span><span class="p">):</span>
<span class="n">set1</span> <span class="o">=</span> <span class="nb">set</span><span class="p">(</span><span class="n">elt</span> <span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">sheet1</span> <span class="k">for</span> <span class="n">elt</span> <span class="ow">in</span> <span class="n">row</span><span class="p">)</span>
<span class="n">set2</span> <span class="o">=</span> <span class="nb">set</span><span class="p">(</span><span class="n">elt</span> <span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">sheet2</span> <span class="k">for</span> <span class="n">elt</span> <span class="ow">in</span> <span class="n">row</span><span class="p">)</span>
<span class="k">return</span> <span class="n">set1</span> <span class="o">&</span> <span class="n">set2</span> <span class="o">-</span> <span class="p">{</span><span class="s1">''</span><span class="p">,</span> <span class="bp">None</span><span class="p">}</span>
</pre></div>
</td></tr></table></div>
<p>Le <code>&</code> effectue une intersection et le <code>- {'', None}</code> permet de virer les cases vides.</p>Pyexcel - comparaison de deux fichiers xls, message #1385002017-01-12T23:02:48+01:00leir/@leirhttps://zestedesavoir.com/forums/sujet/7773/pyexcel-comparaison-de-deux-fichiers-xls/?page=1#p138500<p>Salut, merci pour ta réponse ! J’ai creusé dans ce sens, et j’ai écrit le code suivant :</p>
<div><table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13</pre></div></td><td class="code"><div class="codehilite"><pre><span></span><span class="k">def</span> <span class="nf">browse_rows</span><span class="p">(</span><span class="n">sheet1</span><span class="p">,</span> <span class="n">sheet2</span><span class="p">):</span>
<span class="n">matches</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">row_first_sheet</span> <span class="ow">in</span> <span class="n">sheet1</span><span class="p">:</span>
<span class="n">row_first_sheet</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">filter</span><span class="p">(</span><span class="bp">None</span><span class="p">,</span> <span class="n">row_first_sheet</span><span class="p">))</span>
<span class="k">for</span> <span class="n">row_second_sheet</span> <span class="ow">in</span> <span class="n">sheet2</span><span class="p">:</span>
<span class="n">row_second_sheet</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">filter</span><span class="p">(</span><span class="bp">None</span><span class="p">,</span> <span class="n">row_second_sheet</span><span class="p">))</span>
<span class="n">result</span> <span class="o">=</span> <span class="nb">set</span><span class="p">(</span><span class="n">row_first_sheet</span><span class="p">)</span><span class="o">.</span><span class="n">intersection</span><span class="p">(</span><span class="n">row_second_sheet</span><span class="p">)</span>
<span class="k">if</span> <span class="n">result</span> <span class="ow">and</span> <span class="n">result</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">matches</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
<span class="n">matches</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s1">'FINISHED'</span><span class="p">)</span>
</pre></div>
</td></tr></table></div>
<p>Je ne sais pas s’il y a moyen d’optimiser encore, là je suis passé de plusieurs minutes à environ 12-13 secondes pour deux fichiers de 50k lignes. Un grand merci en tout cas !</p>Pyexcel - comparaison de deux fichiers xls, message #1384952017-01-12T21:50:28+01:00adri1/@adri1https://zestedesavoir.com/forums/sujet/7773/pyexcel-comparaison-de-deux-fichiers-xls/?page=1#p138495<p>Salut,</p>
<p>L’outil le plus rapide en Python pour ça sera effectivement l’intersection de <code>set</code>. J’ai un peu de mal à comprendre ce qui peut te poser problème là-dessus, l’idée est juste de construire deux sets avec le contenu de tes deux fichiers et d’en prendre l’intersection, c’est l’affaire de trois lignes pour définir <code>browse_spreadsheet_rows</code>.</p>Pyexcel - comparaison de deux fichiers xls, message #1384832017-01-12T20:36:46+01:00leir/@leirhttps://zestedesavoir.com/forums/sujet/7773/pyexcel-comparaison-de-deux-fichiers-xls/?page=1#p138483<p>Bonjour, voilà je me frotte depuis quelques temps à python et j’essaie de me créer quelques utilitaires. J’en ai un qui va parcourir un fichier Excel et qui va regarder, pour chaque cellule, s’il y a une occurence dans le deuxième. Le truc fonctionne, mais c’est lent (il me faut plusieurs minutes pour deux fichiers de 50k cellules occupées chacun). Je me demandais s’il y avait moyen d’optimiser ça. J’ai tenté d’utiliser filter() et intersection(), mais je ne m’en sors pas. Je ne demande pas un code tout fait, mais des pistes seraient bienvenues.</p>
<p>Voilà le code qui fonctionne :</p>
<div><table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42</pre></div></td><td class="code"><div class="codehilite"><pre><span></span><span class="c1"># coding: utf-8</span>
<span class="kn">import</span> <span class="nn">pyexcel</span> <span class="kn">as</span> <span class="nn">p</span>
<span class="k">def</span> <span class="nf">define_files_to_check</span><span class="p">():</span>
<span class="n">file_name1</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s1">'Entrez le nom du 1er fichier :'</span><span class="p">)</span>
<span class="n">file_name2</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s1">'Entrez le nom du 2ème fichier :'</span><span class="p">)</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s1">'sheet1'</span><span class="p">:</span> <span class="n">p</span><span class="o">.</span><span class="n">get_array</span><span class="p">(</span><span class="n">file_name</span><span class="o">=</span><span class="n">file_name1</span><span class="p">),</span>
<span class="s1">'sheet2'</span><span class="p">:</span> <span class="n">p</span><span class="o">.</span><span class="n">get_array</span><span class="p">(</span><span class="n">file_name</span><span class="o">=</span><span class="n">file_name2</span><span class="p">),</span>
<span class="p">}</span>
<span class="k">def</span> <span class="nf">check_if_cells_match</span><span class="p">(</span><span class="n">cell1</span><span class="p">,</span> <span class="n">cell2</span><span class="p">):</span>
<span class="sd">""" Return True if 2 cells have the same content (not type) """</span>
<span class="k">if</span> <span class="n">cell1</span> <span class="o">==</span> <span class="n">cell2</span> <span class="ow">and</span> <span class="n">cell1</span> <span class="o">!=</span> <span class="s1">''</span> <span class="ow">and</span> <span class="n">cell2</span> <span class="o">!=</span> <span class="s1">''</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">True</span>
<span class="k">def</span> <span class="nf">browse_spreadsheet_rows</span><span class="p">(</span><span class="n">sheet1</span><span class="p">,</span> <span class="n">sheet2</span><span class="p">):</span>
<span class="sd">""" Loop trough 2 worksheets to compare all cells """</span>
<span class="n">cells_checked_count</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">matches</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">row_first_sheet</span> <span class="ow">in</span> <span class="n">sheet1</span><span class="p">:</span>
<span class="k">for</span> <span class="n">cell_first_sheet</span> <span class="ow">in</span> <span class="n">row_first_sheet</span><span class="p">:</span>
<span class="n">cells_checked_count</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">if</span> <span class="n">cells_checked_count</span><span class="o">%</span><span class="mi">250</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s1">'({} cells checked)'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">cells_checked_count</span><span class="p">))</span>
<span class="k">for</span> <span class="n">row_second_sheet</span> <span class="ow">in</span> <span class="n">sheet2</span><span class="p">:</span>
<span class="k">for</span> <span class="n">cell_second_sheet</span> <span class="ow">in</span> <span class="n">row_second_sheet</span><span class="p">:</span>
<span class="k">if</span> <span class="n">check_if_cells_match</span><span class="p">(</span><span class="n">cell_first_sheet</span><span class="p">,</span> <span class="n">cell_second_sheet</span><span class="p">):</span>
<span class="k">if</span> <span class="n">cell_first_sheet</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">matches</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s1">'MATCH : {} ({} cells checked)'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">cell_first_sheet</span><span class="p">,</span> <span class="n">cells_checked_count</span><span class="p">))</span>
<span class="n">matches</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">cell_first_sheet</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s1">'FINISHED : {} cells checked'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">cells_checked_count</span><span class="p">))</span>
<span class="n">sheets</span> <span class="o">=</span> <span class="n">define_files_to_check</span><span class="p">()</span>
<span class="n">browse_spreadsheet_rows</span><span class="p">(</span><span class="n">sheets</span><span class="p">[</span><span class="s1">'sheet1'</span><span class="p">],</span> <span class="n">sheets</span><span class="p">[</span><span class="s1">'sheet2'</span><span class="p">])</span>
</pre></div>
</td></tr></table></div>
<p>Merci pour vos conseils !</p>