Journal PoC : Transformer les tableaux associatifs (dict/map) en vecteur algébrique

Posté par  (site web personnel) . Licence CC By‑SA.
4
13
avr.
2026

La PoC (Preuve de concept) dont il est ici question est en python, mais elle est généralisable à tout langage objet.

Le projet

Aujourd'hui à 99% de couverture de code on peut considérer la PoC complète. Le code source sur github est ici.

Le projet vise à transformer de manière portable les tableaux associatifs en vecteur algébrique.

Pour ça on part d'un développement initialement piloté par les propriétés algébriques pour obtenir du code chez moi ça marche©.

Pourquoi ? Parce que c'est drôle, mais incidemment, et on va explorer plus tard comment : c'est utile.

La présentation FOSDEM 2017 est ici, la version vidéo est ici.

Comment ?

En utilisant des traits ou mixins (votre éclairage sur le sujet est bienvenu) par surcharge des opérateurs (à droite et à gauche) on obtient des comportements qui sont compatibles avec n'importe quoi qui a l'interface d'une table associative.

En composant les traits, on obtient deux familles de tableaux associatifs algébriques

Voir Schéma

Les tableaux associatifs algébriques qui supportent la logique de combinaison linéaire.

Et les tableaux associatifs vectoriels qui supportent les opérations sur la norme, le produit scalaire et le cosinus.

Utilité

Avoir des vecteurs de dimensions n et fractal

Autant la fractalité des vecteurs me laisse de marbre et je ne vois pas l'utilité, autant avoir des vecteurs indéfinis est utile. Un cas simple est le fameux « word counter » (compte mots) utilisé dans les exemples d'indexation textuelle.

Plus un cosinus est proche de 1 plus vos deux vecteurs pointent dans la même direction ce qui permet de mesurer la similarité.

Ce qui est vrai avec des comptes mots est vrai avec d'autres vecteurs.

Utiliser python comme démonstrateur de pseudo code qui « marche » pour de l'objet

Foncièrement, les ficelles utilisées dans ce projet sont les mêmes que pour du C++/ruby/php?/C# : de la bonne vieille surcharge d'opérateur et des utilisations d'interfaces.

Il est donc possible de porter le code en utilisant les algorithmes pas très sioux utilisés et les tests vers d'autres langages.

  • # Il dit qu'il n'a plus de genou

    Posté par  (site web personnel) . Évalué à 9 (+6/-0).

    Je n'ai rien compris. Enfin, presque rien. Et pourtant, les tableaux associatifs et les vecteurs, j'ai une assez bonne idée de ce que c'est et ce qu'on peut faire avec.

    La PoC (Preuve de concept) dont il est ici question est en python, mais elle est >généralisable à tout langage objet.

    Aujourd'hui à 99% de couverture de code on peut considérer la PoC complète.

    Ça ne donne aucune idée de ce dont il s'agit concrètement, mais bravo.

    En utilisant des traits ou mixins (votre éclairage sur le sujet est bienvenu)

    Euh, tu as utilisé des traits et des mixins et tu aimerais avoir des explications à ce sujet ‽

    Je m'arrête là, c'est trop mal expliqué pour moi. Je vais aller voir la présentation et le code pour voir si j'en tire quelque chose de compréhensible, parce que c'est quand même intriguant cette histoire.

    • [^] # Re: Il dit qu'il n'a plus de genou

      Posté par  (site web personnel) . Évalué à 10 (+8/-0).

      Super le code, ça s'appelle l'archerie et les fichiers Python s'appellent la caserne, l'arc, le carquois et la flèche. Je vois que c'est optimisé pour le divertissement de l'auteur, pas pour la relecture.

      Sinon, visiblement il s'agit de classes qui implémentent des dictionnaires avec des trucs dignes d'un espace vectoriel, à savoir :

      • un dictionnaire nul ;
      • l'opposé d'un dictionnaire ;
      • l'addition de deux dictionnaires ;
      • la multiplication d'un dictionnaire par un scalaire ;
      • le produit scalaire de deux dictionnaires ;
      • d'autres opérations annexes qui peuvent être déduites des précédentes ;

      le tout en respectant suffisamment bien les axiomes d'un espace vectoriel.

      Voilà, est-ce que ce n'est pas plus clair en présentant les choses comme ça ?

      • [^] # Re: Il dit qu'il n'a plus de genou

        Posté par  (site web personnel) . Évalué à 4 (+3/-0).

        Super le code, ça s'appelle l'archerie et les fichiers Python s'appellent la caserne, l'arc, le carquois et la flèche. Je vois que c'est optimisé pour le divertissement de l'auteur, pas pour la relecture.

        J'étais parti de l'idée que j'utilisais des traits, et après, j'ai filé la métaphore pour tenter d'être cohérent.

        Après tout, un ensemble de traits (flèches en anglais) c'est un carquois …

        Et une implémentation concrète d'un ensemble de trait ça paraissait « logique » d'appeler ça un arc (car c'est avec ça qu'on lance des « traits »).

        Après, avec le temps, je me suis aperçu que c'était un poil foireux, mais j'ai pas eu l'imagination nécessaire à trouver des meilleurs noms. J'ai juste changé les arcs japonais initiaux (hankyus et daikyus) par mdict (dict qui multiplie) et vdict (dict vectoriel)…

        Nommer c'est dur. Ça m'a servi de leçon, depuis, j'essaie d'éviter la créativité.

        Voilà, est-ce que ce n'est pas plus clair en présentant les choses comme ça ?

        si si.

        Il faut croire que la communication n'est pas mon fort. _^

        Merci pour ce résumé qui à mon avis est plus éclairant que ma présentation.

      • [^] # Re: Il dit qu'il n'a plus de genou

        Posté par  (site web personnel) . Évalué à 5 (+2/-0).

        Bon, j'ai regardé un peu le code. C'est assez étrange à mes yeux, il faut dire que je ne comprends pour le moment rien du tout aux traits et aux mixins, donc accessoirement ce n'est pas moi qui pourrai t'éclairer sur les concepts que tu as utilisés. Personnellement, je m'amuserais bien à faire ce genre de truc sans m'encombrer de ces concepts : après tout, ça devrait être faisable en définissant simplement quelques classes héritant de dict.

        Quoi qu'il en soit, j'ai l'explication que je cherchais : l'addition et la multiplication par un scalaire, qui sont au cœur d'un espace vectoriel, fonctionnent en travaillant coordonnée par coordonnée, en considérant chaque clef comme un indice de coordonnée.

        Par conséquent, cela ne fonctionne que pour les dictionnaires à valeurs susceptibles d'être additionnées ou multipliées par un scalaire. Forcément, sinon ce ne serait pas des maths mais de la magie.

        À mon sens, cela consiste à considérer un espace vectoriel de dimension infinie à coordonnées indexées par n'importe quels objets immuables. C'est intéressant.

        • [^] # Re: Il dit qu'il n'a plus de genou

          Posté par  (site web personnel) . Évalué à 2 (+1/-0).

          après tout, ça devrait être faisable en définissant simplement quelques classes héritant de dict.

          L'avantage du duck typing (juste s'appuyer sur la présence de __setitem__ et __getitem__) c'est que ça permet de faire un truc générique.

          Ce qui permet de faire hériter des classes qui ne sont pas des dict des méthodes nécessaires à l'implémentation de l'algèbre linéaire (ou plus), mais ont une interface de dict en lui faisant hériter des traits (ou mixins) par composition.

          Ex (peut être que du code c'est plus clair) :

              from collections import Counter
              from archery import VectorDict
              class CWCos(VectorDict, Counter):
                   pass
          
              CWCos(["mot", "wut", "wut", "bla"]).cos(CWCos(["mot","wut", "bla"]))
              # OUT: 0.942809041582
          • [^] # Re: Il dit qu'il n'a plus de genou

            Posté par  . Évalué à 3 (+1/-0).

            Je rejoins l'avis que je comprends rien à tes descriptions et je pense que le fait de ne pas distinguer les concepts (la partie vecteur) de l'implémentation (les traits) joue beaucoup là dessus.

            Tu dis que ça peut être implémenté dans n'importe quel langage objet, donc tu ne devrais pas avoir besoin de parler de traits pour en parler.

            Je n'ai pas lu le code, mais déjà je suis pas clair de s'il s'agit d'utiliser un dictionnaire qui est implémenter par un vecteur ou si l'utilisateur sait qu'il utilise un vecteur. Le code que tu montre semble parler du second mais plus haut il est question d'hériter des dictionnaires.

            Je ne comprends pas le schémas que tu nous as donné. Les couleurs indiquent quoi ? À première vu je me suis dis que c'était un schéma d'opération arithmétique comme on en voit souvent pour les fonctions de hashage, mais ça n'a pas l'air d'être le cas vu qu'il est mentionné des classes.

            Je suis certain que c'est super intéressant et j'aimerais bien comprendre mais j'entrave rien

            https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

            • [^] # Re: Il dit qu'il n'a plus de genou

              Posté par  (site web personnel) . Évalué à 4 (+3/-0).

              La preuve de concept part d'un classique des langages de programmations : les tableaux associatifs aussi appelés dictionnaires.

              Ensuite, on surcharge les opérateurs +, -, /, x, de manière à passer les règles de l'algèbre linéaire définissant un vecteur, c'est-à-dire outre les régles de distributivité, associativité, il est défini :

              • un dictionnaire nul ;
              • l'opposé d'un dictionnaire ;
              • l'addition de deux dictionnaires ;
              • la multiplication d'un dictionnaire par un scalaire ;
              • la multiplication de 2 dictionnaires l'un par l'autre ;
              • le produit scalaire de deux dictionnaires ;
              • d'autres opérations annexes qui peuvent être déduites des précédentes (comme le cosinus) ;

              C'est plus clair ?

              Oui, j'ai repompé ma copie sur messire Ortotlo :)

              Tu dis que ça peut être implémenté dans n'importe quel langage objet, donc tu ne devrais pas avoir besoin de parler de traits pour en parler.

              En effet.

              • [^] # Re: Il dit qu'il n'a plus de genou

                Posté par  . Évalué à 3 (+1/-0).

                C'est quoi la sémantique de ces opérations ? Pourquoi est-ce que je voudrais faire { "foo" : "bar" } ÷ { "linux" : 7} par exemple ?

                https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

                • [^] # Re: Il dit qu'il n'a plus de genou

                  Posté par  (site web personnel) . Évalué à 2 (+1/-0).

                      from archery import mdict as d
                      d({ "foo" : "bar" }) / d({ "linux" : 7})
                      # {}

                  Là, je ne vois pas :)

                  Par contre si j'ai

                      from archery import mdict as Point
                      Point(x=1, y=1) / 7
                      # {'x': 0.14285714285714285, 'y': 0.14285714285714285}

                  J'ai bien une homothétie d'un facteur 1/7 sur un vecteur :)

                  • [^] # Re: Il dit qu'il n'a plus de genou

                    Posté par  . Évalué à 2 (+0/-0). Dernière modification le 16 avril 2026 à 20:38.

                    OK ça se compare comment face aux compréhensions, etiertools et autres numpy/panda ?

                    Ah et ton schéma, il représente quoi ?

                    https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

                    • [^] # Re: Il dit qu'il n'a plus de genou

                      Posté par  (site web personnel) . Évalué à 2 (+1/-0).

                      OK ça se compare comment face aux compréhensions, etiertools et autres numpy/panda ?

                      Pour etiertools j'ai rien trouvé https://pypi.org/search/?q=etiertools&o=
                      Pour itertools ça n'a rien à voir.

                      Pour numpy/panda, ça se comporte grosso modo selon le même mode : celui du principe de moindre surprise.

                      Ah et ton schéma, il représente quoi ?

                      Chaque cases dans un rectangle de couleur est supposé être un trait.

                      Ex :
                      - un adder est un additionneur
                      - un suber un soustractionneur (kof kof)
                      - un muler un multiplicateur
                      - un diver un divisionneur (kof)

                      et l'ensemble de mul/div/add/sub est supposé faire un tout cohérent d'algèbre linéaire.

                      C'est pas très fin, mais c'est dans les grandes lignes ce que j'ai appris à faire en microélectronique que j'ai appliqué à des concepts de plus haut niveau.

                      Et j'ai appelé un emsemble de traits cohérents un carquois. On peut discuter longtemps sur mon talent de nommage …. Je ne suis pas convaincu moi même par mes conventions.

                      Mais ça paraissait intelligent quand je l'ai fait.

                      • [^] # Re: Il dit qu'il n'a plus de genou

                        Posté par  . Évalué à 2 (+0/-0).

                        Pour etiertools j'ai rien trouvé https://pypi.org/search/?q=etiertools&o=
                        Pour itertools ça n'a rien à voir.

                        Pardon pour la faute de frappe. Comme tu fais principalement des iterations ça me parait tout de même proche. Je connais pas bien mais la multiplication ça donnerait ça :

                        d = {'a': 1, 'b': 2, 'c': 3}
                        n = 10
                        
                        resultat = dict(zip(d.keys(), map(lambda x, y: x * y, d.values(), repeat(n))))

                        je suis d’accord que c’est pas fou, mais avec une compréhention c’est pas mal:

                        d = {'a': 1, 'b': 2, 'c': 3}
                        n = 10
                        
                        
                        resultat = {k: v * n for k, v in d.items()}
                        • un adder est un additionneur
                        • un suber un soustractionneur (kof kof)
                        • un muler un multiplicateur
                        • un diver un divisionneur (kof)

                        du coup tu as défini un adder et un suber ? c’est un peu bizarre à l’usage non ? il y a des cas où tu voudrais qu’un seul des 2 ? en plus tu peut définir l’un à partir de l’autre. De même pour muler et Skully.

                        Et du coup le muler hérite de l’adder (de l’héritage de mixin) ? L’héritage me parait surprenant. Si tu applique le mixin muler alors tu peut faire aussi l’addition mais pas la soustraction ? par contre si tu veut la soustraction tu aura la multiplication ?

                        Désolé si je pose beaucoup de question, mais ça m’intéresse.

                        Mais ça paraissait intelligent quand je l'ai fait.

                        Ne te met pas la rate au cour-bouillon pour ça. Faut juste l’expliquer.

                        https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

                        • [^] # Re: Il dit qu'il n'a plus de genou

                          Posté par  (site web personnel) . Évalué à 3 (+2/-0).

                          Pardon pour la faute de frappe. Comme tu fais principalement des iterations ça me parait tout de même proche. Je connais pas bien mais la multiplication ça donnerait ça :

                          Genre :

                          mdict(a="b") * 10 
                          # {'x': 'bbbbbbbbbb'}

                          Et bien, comme je propage le + et le x selon les types natifs, ça marche comme tu le penserais avec des types pour lesquels x veut dire répéter (les strings et les tableaux).

                          On a bien sans surprise

                          resultat = {k: v * n for k, v in d.items()}

                          C'est un peu l'algo de base pour les 4 opérations en récursif.

                          Et du coup le muler hérite de l’adder (de l’héritage de mixin) ? L’héritage me parait surprenant. Si tu applique le mixin muler alors tu peut faire aussi l’addition mais pas la soustraction ? par contre si tu veut la soustraction tu aura la multiplication ?

                          Yep, J'ai galéré à faire un truc cohérent et le développement a convergé à lier les mixins les un aux autres.

                          Par exemple a - b est en fait a + ( -1 x b )

                          C'est pour ça que pour avoir la soustraction, il faut d'abord avoir la multiplication et l'addition.

                          Et c'est pour ça que je « livre » les mixins dans des ensembles cohérents (les carquois) car de mon point de vue les livrer indépendamment les uns des autres est un terrain miné.

                          +, x, -, / forme un ensemble appelé LinearAlgebra. Là,pour le nommage j'ai été carré.

                          Désolé si je pose beaucoup de question, mais ça m’intéresse.

                          Ne soit pas désolé, ça me fait plaisir d'exposer ce que j'ai fait.

                          Pour info, j'ai tenté de le proposer à python-ideas, mais mes nombreux manques en communication et peut-être aussi le fait que personne ne voit l'intérêt de transformer les dict en vecteur n dimensions, a fait que l'idée n'a pas été retenue.

                          Je me tâte à me fallucher l'énorme travail de rédiger une PEP pour re proposer l'idée.

                        • [^] # Re: Il dit qu'il n'a plus de genou

                          Posté par  (site web personnel) . Évalué à 3 (+2/-0).

                          PS : Ce projet est une réécriture complète d'un autre projet (vectordict) qui incluait les opérations booléennes (désormais impossible car | est utilisé pour autre chose).

                          La doc est plus complète et contient une API plus étendue qui inclut « les matrices creuses » qui permettent de transformer les dict en dict.

                          La ré-écriture, m'a permis de pouvoir livrer le cœur des fonctionnalités et d'atteindre la couverture de 99% du code.

                  • [^] # Re: Il dit qu'il n'a plus de genou

                    Posté par  . Évalué à 5 (+2/-0). Dernière modification le 16 avril 2026 à 20:52.

                    Sinon les opérations algébriques sur des vecteurs avec des "noms de colonne" ça fait aussi penser aux "DataFrame" de pandas pour les stats : https://pandas.pydata.org/docs/reference/frame.html

                    Ça implémente pas tout à fait tes opérations et c'est très très largement plus étendu en fonctionnalité et en ambition par contre. Apparemment multiplier des "Series" calcule un produit scalaire.

                    Comment calculer une matrice de similarité d'un ensemble de vecteurs avec cette librairie combinée avec d'autres en quelques ligne : https://stackoverflow.com/questions/45387476/cosine-similarity-between-each-row-in-a-dataframe-in-python

        • [^] # Re: Il dit qu'il n'a plus de genou

          Posté par  . Évalué à 6 (+3/-0).

          Par un espace de dimension infinie, ça c'est les espaces de fonctions (voir Espace Lp). Si l'ensemble des clés est effectivement infini, c'est pas vraiment calculable de manière générique avec une boucle "for" en tout cas, il faut des opérations d'intégrations spécifiques pour un certain espace vectoriel.

          C'est plus des opérations génériques sur des espaces vectoriels de dimensions arbitraires, mais finie, sans regarder le code.

Envoyer un commentaire

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.