MakeMake The Dwarf Planet is a feed agregator.

Panda vs Numpy

by Olivier Pons from Olivier Pons

Ce qu’il faut retenir

Numpy et Pandas n’ont pas exactement les mêmes objectifs.

Dans la plupart des cas, NumPy peut être légèrement plus rapide que pandas, car NumPy est plus bas niveau et a moins de surcharge. Cependant, pandas offre des structures de données et des fonctionnalités plus avancées, ce qui peut faciliter le travail avec des ensembles de données complexes. Les performances relatives de NumPy et pandas dépendent également des opérations spécifiques effectuées sur les données, de sorte que les différences de performances peuvent varier en fonction des tâches spécifiques. Certaines fonctions n’existent qu’avec pandas, et qui n’ont pas d’équivalents NumPy sont : read_csv, read_excel, groupby, pivot_table, merge, concat, melt, crosstab, cut, qcut, get_dummies et applymap.

Résultats

Résultat : image générée : notez bien que j’ai appelé des fonctions « bas niveau » pour qu’on voie ce que NumPy a dans le ventre et des fonctions qui n’existent que dans pandas, que ré-implémentées en Python pur + NumPy.

Résultats pandas vs NumPy

Code source

Voici le code source que j’ai fait, qui appelle quelques fonctions connues de NumPy et de pandas.

import numpy as np
import pandas as pd
import time
import matplotlib.pyplot as plt

# Générer un grand ensemble de données
data_np = np.random.rand(30_000_000)
data_pd = pd.DataFrame({"values": data_np})

operations = (
    "sum",
    "mean",
    "filter",
    "cum_sum",
    "sort",
    "complex",
    "pivot",
    "group_by",
    "rolling",
)
time_np = []
time_pd = []


# Définir une fonction pour chronométrer et stocker les temps d'exécution
def measure_time(start_time, end_time, time_list):
    time_list.append(end_time - start_time)


# Effectuer les différentes opérations et mesurer les temps d'exécution
for operation in operations:
    # print(f"operation: {operation}")
    print(f"{operation}")
    if operation == "sum":
        start_time_np = time.time()
        result_np = np.sum(data_np)
        end_time_np = time.time()
        measure_time(start_time_np, end_time_np, time_np)

        start_time_pd = time.time()
        result_pd = data_pd["values"].sum()
        end_time_pd = time.time()
        measure_time(start_time_pd, end_time_pd, time_pd)

    elif operation == "mean":
        start_time_np = time.time()
        mean_np = np.mean(data_np)
        end_time_np = time.time()
        measure_time(start_time_np, end_time_np, time_np)

        start_time_pd = time.time()
        mean_pd = data_pd["values"].mean()
        end_time_pd = time.time()
        measure_time(start_time_pd, end_time_pd, time_pd)

    elif operation == "filter":
        start_time_np = time.time()
        filtered_np = data_np[data_np > 0.5]
        end_time_np = time.time()
        measure_time(start_time_np, end_time_np, time_np)

        start_time_pd = time.time()
        filtered_pd = data_pd[data_pd["values"] > 0.5]
        end_time_pd = time.time()
        measure_time(start_time_pd, end_time_pd, time_pd)

    elif operation == "cum_sum":
        start_time_np = time.time()
        cum_sum_np = np.cumsum(data_np)
        end_time_np = time.time()
        measure_time(start_time_np, end_time_np, time_np)

        start_time_pd = time.time()
        cum_sum_pd = data_pd["values"].cumsum()
        end_time_pd = time.time()
        measure_time(start_time_pd, end_time_pd, time_pd)

    elif operation == "sort":
        start_time_np = time.time()
        sorted_np = np.sort(data_np)
        end_time_np = time.time()
        measure_time(start_time_np, end_time_np, time_np)

        start_time_pd = time.time()
        sorted_pd = data_pd["values"].sort_values()
        end_time_pd = time.time()
        measure_time(start_time_pd, end_time_pd, time_pd)
    elif operation == "complex":
        # Générer des données structurées
        data_1 = np.random.randint(0, 1_000_000, (2_000, 2))
        data_2 = np.random.randint(0, 1_000_000, (2_000, 2))

        # Créer des DataFrames pandas
        df_1 = pd.DataFrame(data_1, columns=["id", "value_1"])
        df_2 = pd.DataFrame(data_2, columns=["id", "value_2"])

        # Créer des arrays structurés NumPy
        d_type = np.dtype([("id", int), ("value", int)])
        numpy_data_1 = np.array(
            list(map(tuple, data_1)), dtype=d_type
        )
        numpy_data_2 = np.array(
            list(map(tuple, data_2)), dtype=d_type
        )

        # Jointure avec NumPy
        def numpy_join(data1, data2):
            result = []
            for row1 in data1:
                for row2 in data2:
                    if row1["id"] == row2["id"]:
                        result.append(
                            (row1["id"], row1["value"], row2["value"])
                        )
            return np.array(
                result,
                dtype=[
                    ("id", int),
                    ("value_1", int),
                    ("value_2", int),
                ],
            )

        start_time_np = time.time()
        numpy_result = numpy_join(numpy_data_1, numpy_data_2)
        end_time_np = time.time()
        measure_time(
            start_time_np, end_time_np, time_np
        )  # Ajoutez cette ligne

        # Jointure avec pandas
        start_time_pd = time.time()
        pandas_result = df_1.merge(df_2, on="id")
        end_time_pd = time.time()

        measure_time(start_time_pd, end_time_pd, time_pd)
    elif operation == "pivot":
        # Générer des données structurées
        unique_ids = np.arange(0, 60_000)
        unique_groups = np.arange(0, 3)
        id_col = np.repeat(unique_ids, len(unique_groups))
        group_col = np.tile(unique_groups, len(unique_ids))
        value_col = np.random.randint(0, 100, len(id_col))
        data = np.column_stack((id_col, group_col, value_col))

        # Créer des DataFrames pandas
        df = pd.DataFrame(data, columns=["id", "group", "value"])

        # Créer des arrays structurés NumPy
        d_type = np.dtype(
            [("id", int), ("group", int), ("value", int)]
        )
        numpy_data = np.array(list(map(tuple, data)), dtype=d_type)

        # Pivot avec NumPy
        def numpy_pivot(_data, _id_col, _group_col, _value_col):
            _unique_ids = np.unique(_data[_id_col])
            _unique_groups = np.unique(_data[_group_col])

            pivot_table = np.zeros(
                (len(_unique_ids), len(_unique_groups))
            )


            for row in _data:
                id_index = np.where(_unique_ids == row[_id_col])[0][0]
                group_index = np.where(
                    _unique_groups == row[_group_col]
                )[0][0]
                pivot_table[id_index, group_index] = row[_value_col]

            return pivot_table

        start_time_np = time.time()
        numpy_pivot_table = numpy_pivot(
            numpy_data, "id", "group", "value"
        )
        end_time_np = time.time()
        measure_time(start_time_np, end_time_np, time_np)

        # Pivot avec pandas
        start_time_pd = time.time()
        pandas_pivot_table = df.pivot(
            index="id", columns="group", values="value"
        )
        end_time_pd = time.time()
        measure_time(start_time_pd, end_time_pd, time_pd)

    elif operation == "group_by":
        # Générer des données structurées
        data = np.random.randint(0, 10_000_000, (100_000, 2))

        # Créer des DataFrames pandas
        df = pd.DataFrame(data, columns=["id", "value"])

        # Créer des arrays structurés NumPy
        d_type = np.dtype([("id", int), ("value", int)])
        numpy_data = np.array(list(map(tuple, data)), dtype=d_type)

        # Group_by avec NumPy
        def numpy_group_by_mean(_data):
            _unique_ids, counts = np.unique(
                _data["id"], return_counts=True
            )
            sums = np.zeros_like(_unique_ids, dtype=float)
            for row in _data:
                sums[np.where(_unique_ids == row["id"])[0][0]] += row[
                    "value"
                ]
            return _unique_ids, sums / counts

        start_time_np = time.time()
        numpy_result = numpy_group_by_mean(numpy_data)
        end_time_np = time.time()
        measure_time(start_time_np, end_time_np, time_np)

        # Group_by avec pandas
        start_time_pd = time.time()
        pandas_result = df.groupby("id")["value"].mean()
        end_time_pd = time.time()
        measure_time(start_time_pd, end_time_pd, time_pd)

    elif operation == "rolling":
        # Générer un grand ensemble de données
        data_np = np.random.rand(100_000_000)
        data_pd = pd.DataFrame({"values": data_np})

        window = 100

        def numpy_rolling_mean(arr, _window):
            _cum_sum = np.cumsum(np.insert(arr, 0, 0))
            return (
                _cum_sum[_window:] - _cum_sum[:-_window]
            ) / _window

        start_time_np = time.time()
        numpy_result = numpy_rolling_mean(data_np, window)
        end_time_np = time.time()
        measure_time(start_time_np, end_time_np, time_np)

        # Rolling avec pandas
        start_time_pd = time.time()
        pandas_result = (
            data_pd["values"].rolling(window=window).mean()
        )
        end_time_pd = time.time()
        measure_time(start_time_pd, end_time_pd, time_pd)

# Créer un graphique de comparaison
x = np.arange(len(operations))
width = 0.35

fig, ax = plt.subplots()

rects1 = ax.bar(
    x - width / 2,
    time_np,
    width,
    label="NumPy",
    color="#c9daf8",
    edgecolor="black",
    hatch="//",
    linewidth=1,
)
rects2 = ax.bar(
    x + width / 2,
    time_pd,
    width,
    label="pandas",
    color="#c2e8b8",
    edgecolor="black",
    hatch=".",
    linewidth=1,
    alpha=0.5,
)


# Modification de la taille des marqueurs dans rects2
for rect in rects2:
    rect.set_linewidth(2)

ax.set_yscale("log")
ax.set_ylabel("Temps d'exécution (s) - Échelle logarithmique")
ax.set_title(
    "Comparaison des temps d'exécution entre NumPy et pandas"
)
ax.set_xticks(x)
ax.set_xticklabels(operations)
ax.legend()


def autolabel(rects):
    for _rect in rects:
        height = _rect.get_height()
        ax.annotate(
            "{:.2f}".format(height),
            xy=(_rect.get_x() + _rect.get_width() / 2, height),
            xytext=(0, 3),  # 3 points vertical offset
            textcoords="offset points",
            ha="center",
            va="bottom",
        )


autolabel(rects1)
autolabel(rects2)

fig.tight_layout()
plt.savefig("pandas_vs_numpy.png")

Lire la suite…

Visite à la PyConFR 2023

by Stéphane Blondon <stephane@yaal.coop> from Yaal

La conférence PyConFR est constituée de deux jours de sprints puis deux jours de conférences. Cette année elle a eu lieu à Bordeaux, l'occasion pour Yaal Coop de s'y rendre à plusieurs et de sponsoriser l'évènement pour soutenir l'organisation.

Les sprints sont l'occasion de découvrir des projets écrits en Python, mettre le pied à l'étrier pour les utiliser, idéalement, réussir à faire quelques contributions dessus. Ce fut l'occasion de tester zou ainsi qu'avoir une Pull Request acceptée sur cpython (merci à Julien Pallard) et d'en faire une autre, suite à la précédente. :)

C'est aussi l'occasion de rencontrer et discuter dans les allées avec d'autres pythonistes.

Lors de la seconde partie de la PyCon, plusieurs conférences se déroulaient en même temps, dans les amphithéatres différents. Parmi celles que nous avons vues:

Django Admin comme framework pour développer des outils internes

Il est possible d'adapter l'interface d'admin de django pour créer des applications CRUD pour un usage interne. L'intérêt est de gagner du temps en utilisant la capacité de django-admin à produire des interfaces listant et modifiant des objets rapidemment. Parmi les astuces et personnalisation, on notera :

  • la possibilité de modifier le nom 'Django admin' dans l'interface de connexion pour rassurer les utilisateurs
  • l'utilisation de 'create views' dans un script de migration permettant de faire des visualisations en lecture seule.

Le présentateur indique que, si le besoin devient plus complexe par la suite, la solution est de passer au developpement classique d'un service django.

Uncovering Python’s surprises: a deep dive into gotchas

Une présentation en anglais montrant des curiosités plus ou moins connues du langage. Une partie des exemples sont issus de wtfpython.

Faire du Python professionnel

Typer ou ne pas typer en python, telle est la question... Plusieurs conférences ont abordé le sujet, dont celle-ci. Globalement ses avantages semblent faire de plus en plus consensus au sein de la communauté. Une référence d'article de blog intéressante néanmoins, avec des arguments contre les annotations de type.

Et un conseil pertinent : ne pas faire d'annotation sans mettre en place un outil (type mypy) dans la CI pour les vérifier. 😇

Portage Python sur Webassembly

WebAssembly est un langage fonctionnant dans les navigateurs. Il est possible d'écrire du code Python et de le convertir en WebAssembly. Arfang3d est un moteur 3D qui fonctionne ainsi.

python -m asyncio -> pour avoir un shell python asynchone

C'est aussi un moyen de convertir des jeux écrits avec pygame pour les exécuter dans un navigateur. Une liste de jeu compatible est disponible sur pygame-web.github.io.

Fear the mutants. Love the mutants.

Comment être sûr que les tests vérifient bien ce qu'ils sont censés vérifier ? mutmut modifie le code source à tester et vérifie que les tests sont en erreur après la modification. La commande principale est mutmut run. Pour changer le comportement du code, mutmut accède et modifie l'AST (Abstact Syntax Tree) grâce à la bibliothèque parso.

Python moderne et fonctionnel pour des logiciels robustes

Il s'agissait ici de s'inspirer de quelques principes et règles souvent utilisées dans le fonctionnel pour pouvoir coder en python de façon plus propre des services plus résistants et moins endettés.

Il a été question de typage avec les hints de Mypy mais aussi avec Pyright, moins permissif et donc contraignant à des règles de propreté. Avec Python3.8 sont arrivés les Protocols, un cas spécifique d'utilisation des Abstract Base Classes sans héritage où il suffit de reproduire une signature.

Faire remonter les impuretés et les effets de bord a également été abordé avec l'architecture en oignon (comme la connexion à la persistance, les modifications à sauvegarder, les configurations, etc.) avec l'idée de pouvoir tester le cœur de son code sans dépendre de tout un environnement.

Le paramètre frozen du décorateur @dataclass(frozen=True) permet de rendre les instances immutables.

La classe typing.Protocol, décrite dans la PEP 544 et introduite dans Python 3.8 permet de définir des interfaces.

Une recommandation de vidéo : Functional architecture - The pits of success - Mark Seemann

Accessibilité numérique : faire sa part quand on est développeur·euse backend

Une introduction aux problématiques de l'accessibilité avec une démo percutante en vidéo de l'expérience que peut avoir une personne aveugle en naviguant sur internet. Saviez vous qu'aujourd'hui, 1 personne sur 5 était en situation de handicap ? L'objectif était ici de sensibiliser le public, en majorité des développeurs back-end, aux questions d'accessibilité, et d'appuyer le fait que ce n'était pas qu'une question réservée aux design ou au front.

Quelques petites choses glanées :

  • quand on construit une plateforme diffusant du contenu utilisateur, prévoir l'ajout possible d'un texte alternatif/de sous titres à stocker avec l'image/la vidéo de l'utilisateur
  • se méfier des inputs utilisateurs que certains arriveront à détourner pour mettre du contenu en forme (des émojis, des caractères mathématiques...) qui sera ensuite illisible pour les lecteurs d'écrans
  • l'attribut html lang peut être utilisé avec n'importe quelle balise, pas seulement dans l'en-tête de la page, pour signaler une citation dans une langue étrangère par exemple ! Cela permet aux logiciels de lecture d'écran d'adopter la bonne prononciation 🤯
  • préferer le server side rendering et faire de la mise en cache pour accélerer l'affichage : un loader à l'écran n'est pas forcément explicite pour tous les utilisateurs (ou lecteurs...)
  • FALC (Facile à lire et à comprendre) est une méthode/un ensemble de règles ayant pour finalité de rendre l'information facile à lire et à comprendre, notamment pour les personnes en situation de handicap mental

Sensibiliser les producteurs d'une part significative des mediums d'information est toujours une bonne chose.

Interactive web pages with Django or Flask, without writing JavaScript

htmx est une bibliothèque javascript dont le but est de permettre la fourniture de code HTML qui sera intégré dans le DOM. L'idée est de remplacer le code javascript as-hoc et les transferts en JSON (ou autre). Si le principe semble adapté à certains cas, il ne remplacera pas de gros framework produisant des Single Page App.

pyScript est un projet encore très jeune qui permet l'exécution de code Python dans le navigateur.

Merci à l'équipe de bénévoles pour l'organisation de la conférence. À l'année prochaine !

Lire la suite…

Contributions à des logiciels libres par l'équipe Yaal Coop durant l'hiver 2023

by Éloi Rivard <eloi@yaal.coop> from Yaal

Cet hiver nous avons surtout travaillé sur Canaille pour préparer l'implémentation de fonctionnalités sponsorisées par la fondation NLNet.

Documentation

Esup-Pod

Plateforme de partage vidéo pensée pour l'enseignement et la recherche

Contributions financées par la Direction du Numérique pour l'Éducation (pôle Formation Ouverte et A Distance)

DSFR

Système de design de l’État

canaille

Serveur OpenID Connect simpliste, basé sur OpenLDAP

nextcloud-oidc-login

Plugin OpenID Connect pour nextcloud

flask-shell-ptpython

Utilisation de l'interpréteur de commandes ptpython dans flask

authlib

Bibliothèque python de gestion des identités et des accès

wtforms

Bibliothèque python de gestion de formulaires web

flask-wtf

Intégration de WTForms dans Flask

python-caldav

Bibliothèque python pour le protocole CalDAV

aioimaplib

Bibliothèque python asyncio IMAP4rev1

simple-svelte-autocomplete

Composant svelte de suggestion d'éléments dans une liste déroulante

python-slapd

Interface pythonique pour contrôler un serveur OpenLDAP

supervisord

Un gestionnaire de processus écrit en Python

Lire la suite…

FOSS contributions from the Yaal Coop team during winter 2023

by Éloi Rivard <eloi@yaal.coop> from Yaal

This winter we have mainly worked on Canaille in order to prepare the implementation of features sponsored by the NLNet foundation.

Documentation

Esup-Pod

Video sharing website aimed at education and research

Contributions funded by the Direction du Numérique pour l'Éducation (Formation Ouverte et A Distance service)

DSFR

French State design system

canaille

Simplistic OpenID Connect provider over OpenLDAP

nextcloud-oidc-login

Nextcloud login via a single OpenID Connect 1.0 provider

flask-shell-ptpython

Ptpython shell for flask

authlib

Identity and Access management library for python

wtforms

A flexible forms validation and rendering library for Python.

flask-wtf

Simple integration of Flask and WTForms, including CSRF, file upload and Recaptcha integration.

python-caldav

Python CalDAV library

aioimaplib

Python asyncio IMAP4rev1 client library

simple-svelte-autocomplete

Simple Autocomplete / typeahead component for Svelte

python-slapd

Controls a slapd process in a pythonic way

supervisord

A Process Control System written in Python

Lire la suite…

IUT alternants : projet Django / Python à rendre le 12 février minuit au plus tard

by Olivier Pons from Olivier Pons

A rendre le dimanche 12 février 2023 minuit au plus tard


Projet individuel


Comment le rendre

Faites un fichier README.txt et déposez-le ici
Dans le fichier README.txt, précisez :

  • le sujet choisi
  • l’adresse de votre site
  • un nom d’utilisateur
  • un mot de passe
  • (et plusieurs nom/mot de passe, s’il y a plusieurs niveaux de droits (administrateur/visiteur etc.))
  • si vous avez utilisé des librairies spécifiques que je vous ai autorisées, merci de le re-préciser

Sujet

Ce que vous voulez tant que c’est dans le cadre de ce que l’on a vu. Vous avez tout le Web comme inspiration !
N’oubliez pas de me donner le nom et le mot de passe pour se connecter !
Si vous gérez des profils différents (admin / user ou autre), donnez moi les noms et mots de passe de différents profils !


Fonctionnalités obligatoires

  • Nouveaux modèles
  • Nouvelles relations à mettre en oeuvre : ForeignKey, ManyToMany, OneToOne
  • Au moins un formulaire
  • Connexion + déconnexion (vu en cours)
  • Visualisation de tout dans l’interface d’administration

Sujets possibles

  1. Site de partage de photos
  2. Site de cocktails (cf ci-dessus)
  3. e-rated : site d’appréciations (selon des sujets, à définir)
  4. Ask-a-question : site où l’on pose des questions sur des sujets divers, et des gens répondent
  5. Write-a-book-together : site où l’on se connecte et où on peut écrire un livre à plusieurs
  6. Wedding-couple-site : site où l’on uploade + partage des photos de mariage + livre de commandes
  7. Playing-cards-collection : site où on scanne + échange des cartes (Magic the gathering)
  8. Polls-and-surveys : site de création de sondages (= QCM, exemple très beau ici : quipoquiz)
  9. Poems-generator : faire un cadavre exquis qui génère des poèmes + possibilité pour les utilisateurs de les noter / d’ajouter des mots
  10. The-future-of-post-it : faire un carnet de choses à faire pour les utilisateurs, qui envoie des mails de rappels de ces choses à des dates données
  11. Gift-ideas : un site où l’on va faire des idées de cadeaux / suggérer des idées de cadeaux + les noter (les meilleurs ressortent en premier)
  12. Le-bon-recoin : refaire le bon coin en plus simple
  13. Suggest-crawlers : site de suggestions : on clique sur un mot, il en suggère plein d’autres avec + définitions / liens de sites pour chacuns
  14. Tv-fans : site de présentations + notes d’émissions télé
  15. Faire le jeu SokoBan vu en cours, avec la possibilité de login, enregistrement. Pour les appels JSON supplémentaires, lorsque l’utilisateur choisit un tableau, s’en souvenir (= AJAX) et lorsqu’il se reconnecte, le remettre directement. Puis enregistrer son score lorsqu’il a terminé un niveau + montrer les meilleurs scores.

Pour les sujets qui suivent, ils sont possibles mais plutôt complexes et demandent plus d’investissement. Si vous êtes motivés, demandez-moi plus d’informations, je vous expliquerai les difficultés que vous allez rencontrer.

  1. Turn-by-turn : faire un jeu multijoueurs en tour par tour (jeu de cartes, de poker, ou de plateau etc)
  2. Chat-with-someone : site de chat/discussion
  3. A-maze-ing : site où l’on peut se ballader dans un labyrinthe et essayer d’en trouver la sortie

Sujet imposé si vous n’avez pas d’idée

Cocktails : on se connecte, on a une liste d’éléments (récupérés en JSON) disponibles, on coche ceux qui nous intéressent, on valide, c’est envoyé, et le retour en JSON affiche les cocktails qu’il est possible de faire avec ce que l’on a coché.


Ce que vous devez rendre

Idéalement

Si vous n’avez pas le choix

Les fichiers source de votre projet


Pour favoriser votre organisation

Utilisez ce que l’on a vu en cours (Google boilerplate)


Librairies autorisées


React autorisé

Note pour ceux qui connaissent / font / du React : la librairie est autorisée, mais il me faut le code d’origine, et non pas le code minifié / de production.


Interdiction d’utiliser une librairie JavaScript qui ne vienne pas des sites autorisés précédemment


Retard

Après la date et heure limite

Passé ce délai ce sera 1 pt par 2 heures de retard (mon robot qui analyse les mails prend en compte la date de réception du mail, tout est fait automatiquement).
Pour ceux qui essaient vraiment d’aller jusqu’à la dernière minute, toute heure entamée est comptée comme une heure complète.
Exemple : un point en moins si je le reçois un jour après à la minute près, soit date limite plus 00:01 minute.

N’oubliez pas de me donner le nom et le mot de passe pour se connecter !


Copier-coller

  • Copie sur une autre personne (« je se savais pas comment implémenter telle ou telle fonctionnalité dont j’avais besoin pour aller plus loin, je l’ai copiée sur un autre ») :
    • si la personne est clairement nommée : note pour la fonctionnalité divisée par 2 (uniquement la moitié du travail a été faite) ;
    • 0 aux deux personnes sinon ;
  • Si je m’aperçois que vous avez bêtement copié collé des sources Internet, je vous convoquerai pour vous demander de m’expliquer la fonctionnalité, et :
    • si vous ne savez pas m’expliquer le code alors 0 ;
    • si vous savez m’expliquer tout le code alors votre note totale sera divisée par vous + le nombre de contributeurs à ce projet, ce qui se rapprochera certainement de 0 aussi.

Voici un exemple de ce que vous pouvez faire, si vous choisissez le projet cocktails.


PDFs

Python Django

Lire la suite…

Nombres décimaux et Python

by ascendances from ascendances

Python, comme de nombreux autres langages ainsi que des implémentations matérielles, suit la norme IEEE 754 pour manipuler les nombres à virgule (le type float en Python). Cette norme définit les tailles possibles de mémoire allouée pour contenir le nombre. La taille étant fixe, certains nombres ne sont pas représentables et la valeur enregistrée peut être légèrement erronée.

Cette situation n’est donc pas spécifique à Python. L’écart entre la valeur saisie et la valeur en mémoire est visible avec un interpréteur Python :

$ python3 -q
>>> 1.9999999999999943e+71
1.9999999999999942e+71

ou un calcul qui devrait valoir 0 si les mathématiques étaient une science exacte :

$ python3 -q
>>> 0.1 + 0.1 + 0.1 - 0.3
5.551115123125783e-17

Ce type d’erreur ne se rencontre pas uniquement dans les domaines spatial ou scientifique. Par exemple, des calculs de TVA et TTC peuvent produire des erreurs visibles pour l’utilisateur.

Pour éviter ces erreurs, il est possible d’utiliser la bibliothèque decimal incluse dans la bibliothèque standard :

$ python3 -q
>>> from decimal import Decimal
>>> decimal.Decimal('1.9999999999999943e+71')
Decimal('1.9999999999999943E+71')
>>> Decimal(1) / Decimal(10) + \
... Decimal(1) / Decimal(10) + \
... Decimal(1) / Decimal(10) - \
... Decimal(3) / Decimal(10)
Decimal('0.0')

Un autre moyen est de faire des calculs en n’utilisant que des entiers et faire des conversions au dernier moment. Dans le cas de la TVA, cela signifie de ne travailler qu’en centimes et de ne convertir en euro que lors de l’affichage à l’utilisateur (avec l’arrondi adapté, limité à deux décimales).

Références

Lire la suite…

Dernières contributions à des logiciels libres par l'équipe Yaal Coop (automne 2022)

by Éloi Rivard <eloi@yaal.coop> from Yaal

pytest-httpserver

Serveur HTTP pour pytest

dnserver

Serveur DNS simpliste pour le développement

canaille

Serveur OpenID Connect simpliste, basé sur OpenLDAP

ZEO

Serveur de base de données pour ZODB

nextcloud-oidc-login

Plugin OpenID Connect pour nextcloud

authlib

Bibliothèque python de gestion des identités et des accès

aioimaplib

Bibliothèque python asyncio IMAP4rev1

MessagePack

Format de sérialisation binaire efficace (bibliothèque python)

simple-svelte-autocomplete

Composant svelte de suggestion d'éléments dans une liste déroulante

dnspython

outils DNS pour Python

Lire la suite…