> [Site index](https://perfectvector.com/llms.txt) — available pages and descriptions.

# Marqueurs SVG dans Matplotlib : vérifier le chemin

> Utilise un chemin SVG simple comme marqueur de nuage de points Matplotlib. Reproduis les contrôles de centrage, d’orientation, de trou et de taille dans les véritables exports SVG et PDF.

Source: https://perfectvector.com/fr/blog/matplotlib-svg-scatter-marker

Language: fr
Author: Irene Kim
Published: 2026-10-11T08:00:00+09:00
Updated: 2026-10-11T08:00:00+09:00

Pour utiliser un SVG comme marqueur personnalisé de nuage de points Matplotlib, convertis la géométrie prise en charge en `matplotlib.path.Path`, centre-la autour de `(0, 0)` et passe cet objet à `scatter(marker=...)`. La couleur et la taille du marqueur proviennent alors du graphique. L’[API de marqueurs de Matplotlib accepte un Path](https://matplotlib.org/3.10.7/api/markers_api.html) ; elle ne prend pas un document SVG quelconque comme nom de fichier de marqueur.

Ce tutoriel utilise un polygone asymétrique original avec un trou rectangulaire. Le script complet ci-dessous gère un sous-ensemble SVG volontairement limité et rejette les fonctions qu’il ne peut pas préserver. Nous l’avons exécuté avec Matplotlib 3.10.7, inspecté les chemins SVG et PDF enregistrés et vérifié les deux rendus.

Si tu souhaites une illustration à côté d’un graphique exporté, utilise le [guide distinct de composition d’illustrations SVG avec Matplotlib](https://perfectvector.com/fr/blog/matplotlib-svg-artwork). Un marqueur de nuage de points se répète aux coordonnées des données ; son centre, son orientation et sa taille nécessitent donc un autre contrôle.

## 1. Pars d’une forme prise en charge

Il te faut Python avec Matplotlib et NumPy disponibles, ainsi que l’autorisation d’écrire dans un dossier de sortie. L’exemple inclut son SVG original ; tu n’as donc pas besoin de télécharger une icône. Les trois coordonnées tracées sont illustratives, pas des observations de recherche.

Le parseur accepte exactement un `path` à fond noir dans une racine SVG possédant un `viewBox`. Chaque contour doit se fermer par `Z` ; chaque paire de coordonnées exige un `M` ou un `L` majuscule explicite. Il accepte un chemin composé avec un contour extérieur et un trou utilisant des sens opposés avec `fill-rule="nonzero"`.

Il rejette les courbes, commandes relatives, transformations, contours, CSS, éléments de chemin multiples, texte, découpages, dégradés et autres couleurs. Il rejette aussi les attributs de racine supplémentaires comme `width` et `height`. Cette restriction rend l’exemple inspectable ; ce n’est pas un importateur SVG général. Garde une copie intacte de l’illustration avant de préparer une entrée compatible.

Un Path Matplotlib stocke des sommets et des codes de dessin plutôt que tout le modèle de peinture SVG. L’[API Path](https://matplotlib.org/stable/api/path_api.html) définit les opérations de déplacement, de ligne et de fermeture utilisées ici. Si ton symbole exige plusieurs couleurs ou des effets, choisis un processus qui les préserve au lieu de les supprimer silencieusement pour satisfaire ce parseur.

## 2. Exécute l’exemple complet de marqueur

Enregistre ceci sous `scatter_marker.py`, puis exécute `python scatter_marker.py` dans le même dossier. Cela crée `marker-output/marker.svg`, `scatter.svg`, `scatter.pdf` et un aperçu PNG. La source SVG générée et tous les réglages du graphique sont inclus ici.

```python
from pathlib import Path as FilePath
import re
import xml.etree.ElementTree as ET

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.path import Path
import numpy as np

SVG = '''<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 80">
  <path fill="#000000" fill-rule="nonzero"
        d="M10 10 L70 10 L70 30 L90 30 L90 70 L10 70 Z
           M25 25 L25 55 L45 55 L45 25 Z"/>
</svg>'''


def svg_marker(svg_text):
    """One black, nonzero-filled path; explicit absolute M/L/Z only."""
    namespace = "{http://www.w3.org/2000/svg}"
    root = ET.fromstring(svg_text)
    if root.tag != namespace + "svg" or set(root.attrib) != {"viewBox"}:
        raise ValueError("Use only an SVG root with a viewBox")
    view = np.array([float(value) for value in root.attrib["viewBox"].split()])
    if view.shape != (4,) or not np.isfinite(view).all() or (view[2:] <= 0).any():
        raise ValueError("Invalid viewBox")
    if len(root) != 1 or root[0].tag != namespace + "path":
        raise ValueError("Use exactly one path, with no groups or other elements")
    node = root[0]
    if any((text or "").strip() for text in [root.text, node.text, node.tail]):
        raise ValueError("Text content is not supported")
    if set(node.attrib) - {"d", "fill", "fill-rule"} or len(node):
        raise ValueError("Unsupported path attribute or child element")
    if node.get("fill", "black") not in {"black", "#000", "#000000"}:
        raise ValueError("Use one solid black fill")
    if node.get("fill-rule", "nonzero") != "nonzero":
        raise ValueError("Use nonzero winding; evenodd is not converted here")
    data = node.get("d", "")
    pattern = r"[MLZ]|[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?"
    tokens = re.findall(pattern, data)
    if re.sub(pattern, "", data).strip(" ,\n\r\t"):
        raise ValueError("Only explicit absolute M, L and Z commands are supported")
    vertices, codes = [], []
    index, start, open_contour, lines = 0, None, False, 0
    while index < len(tokens):
        command = tokens[index]
        if command in {"M", "L"}:
            if index + 2 >= len(tokens) or tokens[index + 1] in {"M", "L", "Z"} or tokens[index + 2] in {"M", "L", "Z"}:
                raise ValueError("Each M/L needs one explicit coordinate pair")
            point = tuple(float(value) for value in tokens[index + 1:index + 3])
            if not np.isfinite(point).all():
                raise ValueError("Coordinates must be finite")
            if command == "M":
                if open_contour:
                    raise ValueError("Close each contour with Z")
                start, open_contour, lines = point, True, 0
                codes.append(Path.MOVETO)
            else:
                if not open_contour:
                    raise ValueError("Start each contour with M")
                lines += 1
                codes.append(Path.LINETO)
            vertices.append(point)
            index += 3
        elif command == "Z":
            if not open_contour or lines < 2:
                raise ValueError("A closed contour needs at least three vertices")
            vertices.append(start)
            codes.append(Path.CLOSEPOLY)
            open_contour = False
            index += 1
        else:
            raise ValueError("Repeat M/L explicitly before coordinate pairs")
    if open_contour or not vertices:
        raise ValueError("Supply nonempty closed contours")
    path = Path(np.array(vertices), codes)
    box = path.get_extents()
    if box.width <= 0 or box.height <= 0:
        raise ValueError("Path needs nonzero width and height")
    if box.x0 < view[0] or box.y0 < view[1] or box.x1 > view[0] + view[2] or box.y1 > view[1] + view[3]:
        raise ValueError("Path outside viewBox: resolve clipping before import")
    center = np.array([(box.x0 + box.x1) / 2, (box.y0 + box.y1) / 2])
    centered = (path.vertices - center) * np.array([1, -1])
    return Path(centered, path.codes)


if __name__ == "__main__":
    output = FilePath("marker-output")
    output.mkdir(exist_ok=True)
    (output / "marker.svg").write_text(SVG)
    marker = svg_marker(SVG)
    plt.rcParams.update({"svg.fonttype": "none", "pdf.fonttype": 42,
                         "font.family": "DejaVu Sans", "font.size": 11})
    fig, ax = plt.subplots(figsize=(8, 3), dpi=144)
    fig.subplots_adjust(left=.1, right=.96, bottom=.24, top=.8)
    ax.scatter([1, 2, 3], [1.3, 2.4, 1.6], s=[400, 1600, 3600],
               marker=marker, color="#2f6977", linewidths=0, edgecolors="none")
    ax.set(xlim=(0, 4), ylim=(0, 4), xlabel="Illustrative x", ylabel="Illustrative y",
           title="Original SVG marker: s = 400, 1600, 3600 points squared")
    ax.spines[["top", "right"]].set_visible(False)
    fig.savefig(output / "scatter.svg", format="svg")
    fig.savefig(output / "scatter.pdf", format="pdf")
    fig.savefig(output / "scatter.png", format="png")
    plt.close(fig)
```

Ouvre `scatter.svg` et `scatter.pdf`. Tu dois voir trois versions bleu-vert de la même forme : un trou rectangulaire à gauche et un décrochement s’étendant en bas à droite. Compare le plus petit marqueur à sa taille d’affichage prévue aussi bien qu’en vue agrandie.

![Véritables exports SVG et PDF Matplotlib montrant le marqueur asymétrique original, son trou et trois tailles vérifiées](https://perfectvector.com/blog/matplotlib-svg-scatter-marker/scatter-proof.webp)

*Exemple original et véritables exports enregistrés de Matplotlib 3.10.7. La vue PDF est un rendu du fichier vectoriel enregistré ; les valeurs de données sont illustratives.*

## 3. Vérifie le centre et inverse Y une seule fois

L’illustration source occupe `x=10…90` et `y=10…70`. Le centre de sa boîte englobante est `(50, 40)`. Soustraire ce centre donne des limites de `−40…40` horizontalement et `−30…30` verticalement. C’est un centrage sur la boîte englobante, pas un centroïde ni une estimation de l’équilibre visuel.

Le décrochement en bas à droite de la source est utile, car un cercle symétrique masquerait une inversion verticale. Dans le [système de coordonnées SVG initial](https://www.w3.org/TR/SVG2/coords.html), Y positif pointe vers le bas. Le script prend l’opposé de Y une seule fois pour conserver l’orientation affichée de la source dans le graphique habituel où Y monte.

Les deux contours reçoivent la même transformation. Leurs sens relatifs restent opposés ; l’ouverture rectangulaire reste donc un trou. La [règle de remplissage nonzero de SVG](https://www.w3.org/TR/SVG2/painting.html#FillRuleProperty) dépend du sens des contours ; une source `evenodd` est explicitement rejetée ici au lieu d’être considérée comme équivalente.

Matplotlib normalise ensuite ce Path centré. Dans la version testée, le résultat s’étend de `−0.5…0.5` en X et de `−0.375…0.375` en Y. Le point fourni à `scatter` se place au centre de ces limites. Tu peux choisir volontairement un autre ancrage pour un symbole particulier, mais ne suppose pas que le coin supérieur gauche d’un SVG source est un ancrage de données adapté.

## 4. Lis la taille de scatter en points carrés

L’[API scatter](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.scatter.html) définit `s` en points carrés, un point typographique valant 1/72 de pouce. Un bord de marqueur non nul peut agrandir son étendue visible. L’exemple définit `linewidths=0` et `edgecolors="none"` pour que le contrôle de taille porte sur la géométrie remplie.

Pour ce marqueur normalisé, la largeur est d’une unité de marqueur et la hauteur de 0.75 unité. Sa largeur enregistrée est donc de `sqrt(s)` points et sa hauteur de `0.75 * sqrt(s)` points.

| `s` (points carrés) | Largeur attendue et mesurée | Hauteur attendue et mesurée |
| ------------------- | --------------------------- | --------------------------- |
| 400                 | 20 pt                       | 15 pt                       |
| 1600                | 40 pt                       | 30 pt                       |
| 3600                | 60 pt                       | 45 pt                       |

Ce sont les dimensions de cette forme normalisée précise, pas sa surface peinte. Un autre rapport d’aspect, ancrage ou contour de bord change la relation entre le paramètre de taille et le symbole visible. Multiplier `s` par quatre double les dimensions dans cet exemple.

Nous avons inspecté les véritables coordonnées SVG enregistrées et les opérations de chemins remplis du PDF avec une tolérance numérique de 0.000001 point. Les trois marqueurs avaient les limites, le placement centré sur les données et les sommets transformés attendus. Le SVG enregistré ne contenait aucun élément d’image, et le PDF aucun objet image.

Nous avons aussi rendu le PDF enregistré à 144 ppp et vérifié trois positions intérieures déclarées : le trou était blanc, l’encoche supérieure droite blanche et le prolongement inférieur droit bleu-vert. Le rendu natif du véritable SVG dans le navigateur a réussi les mêmes contrôles intérieurs. Cela vérifie l’orientation et l’ouverture choisies ; cela ne prétend pas obtenir des bords anticrénelés identiques entre moteurs ni prendre en charge toute illustration SVG.

## 5. Corrige la cause avant d’ajuster la taille

| Symptôme                                            | Contrôle et correction                                                                                                                                                                                          |
| --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Le parseur signale une fonction non prise en charge | Inspecte la fonction nommée. Prépare une entrée polygonale compatible distincte ou utilise une conversion plus complète et teste-la. Retirer un attribut sans préserver son effet peut modifier l’illustration. |
| La forme est à l’envers                             | Vérifie si Y était déjà inversé avant cette fonction. Applique le changement de coordonnées une fois, puis compare un détail asymétrique.                                                                       |
| Le symbole est décalé du point de données           | Inspecte ses limites et l’ancrage choisi. Cette fonction centre les limites de l’illustration, indépendamment des espaces vides dans le `viewBox`.                                                              |
| Le trou se remplit                                  | Inspecte les sens des contours et la règle de remplissage. Conserve les sens opposés pour cet exemple nonzero ; le parseur ne convertit pas la géométrie evenodd.                                               |
| Le fichier enregistré contient un symbole matriciel | Inspecte les éléments ou ressources exportés. La présence d’un nom de fichier SVG ou PDF ne prouve pas à elle seule que chaque élément tracé est vectoriel.                                                     |
| Les petits marqueurs perdent des détails utiles     | Examine la figure à sa taille finale. Agrandis le marqueur, simplifie le symbole ou utilise un marqueur intégré s’il communique plus clairement la catégorie.                                                   |

Pour inspecter les fichiers au-delà de cet exemple, consulte [comment trouver du contenu matriciel dans un SVG](https://perfectvector.com/fr/blog/svg-contains-raster-image). Pour toute la figure, examine [la taille des polices et l’épaisseur des lignes au format de publication](https://perfectvector.com/fr/blog/scientific-figure-font-size-line-width) ; un chemin de marqueur correct peut encore être trop complexe pour une petite légende ou un panneau chargé.

Avant d’utiliser ton propre marqueur dans une figure finale, vérifie sa plus petite taille, son apparence en légende, les points qui se chevauchent et les exports SVG/PDF. Garde les données, axes, étiquettes et correspondances d’échelle dans ton processus de graphique. Une icône personnalisée ne remplace pas ces définitions numériques.

## Quand une illustration matricielle a besoin d’un nouveau chemin

Si le symbole ne subsiste que comme dessin matriciel en aplats, le [processus de schéma scientifique vers SVG](https://perfectvector.com/fr/scientific-diagram-to-svg) de PerfectVector peut aider à récupérer une illustration modifiable. Compare le contour récupéré et chaque ouverture à l’original avant de l’adapter à ta chaîne de marqueurs. Conserve un SVG propre existant comme source si tu en as déjà un.

La récupération vectorielle ne reconstruit pas les données sous-jacentes du graphique et ne garantit pas la compatibilité avec ce parseur restreint. Préserve les photographies et les illustrations à tons continus lorsque leur apparence matricielle fait partie de la figure, et choisis une méthode de placement d’image adaptée à ce contenu.

## Questions fréquentes

**Puis-je passer directement un nom de fichier SVG à scatter de Matplotlib ?**
Non. L’API de marqueurs accepte les descriptions prises en charge, dont un Path Matplotlib. Cet exemple convertit un chemin SVG restreint en cet objet avant d’appeler scatter.

**Pourquoi mon marqueur personnalisé est-il à l’envers ?**
Le SVG source et le graphique peuvent utiliser des directions Y différentes. Cet exemple prend l’opposé de Y SVG une fois ; inspecte un détail asymétrique pour éviter une double inversion.

**s est-il égal à la surface peinte de ma forme SVG ?**
Ne l’utilise pas comme valeur mesurée de surface peinte. Scatter définit s en points carrés, tandis que la forme normalisée et le contour de bord déterminent ses dimensions visibles. Cet exemple possède des largeurs de 20, 40 et 60 points pour des valeurs s de 400, 1600 et 3600.

**Ce code préserve-t-il toutes les fonctions SVG ?**
Non. Il prend en charge un seul chemin noir rempli avec nonzero, des commandes absolues explicites M, L et Z et des contours fermés. Les peintures, commandes géométriques, transformations et éléments supplémentaires non pris en charge déclenchent une erreur.

## Sources

1. [API scatter de Matplotlib](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.scatter.html) — définit le paramètre de taille, l’entrée de marqueur et l’interaction avec l’épaisseur de bord.
2. [API de marqueurs Matplotlib 3.10.7](https://matplotlib.org/3.10.7/api/markers_api.html) — documente les marqueurs Path et les coordonnées normalisées de la version testée.
3. [API Path de Matplotlib](https://matplotlib.org/stable/api/path_api.html) — définit les sommets, les codes de dessin et les contours fermés.
4. [Systèmes de coordonnées SVG](https://www.w3.org/TR/SVG2/coords.html) — établit les directions de coordonnées initiales utilisées pour vérifier l’orientation.
5. [Règles de remplissage SVG](https://www.w3.org/TR/SVG2/painting.html#FillRuleProperty) — explique comment le sens des contours affecte l’ouverture remplie avec nonzero.

Pour un symbole uniquement matriciel, [récupère une illustration de schéma modifiable](https://perfectvector.com/fr/scientific-diagram-to-svg), compare son contour et ses trous à la source et teste le chemin adapté dans ta véritable figure avant livraison.

## Explore

- [Read the HTML article](https://perfectvector.com/fr/blog/matplotlib-svg-scatter-marker)
- [SVG dans LaTeX : garder les formes et composer les libellés](https://perfectvector.com/fr/blog/svg-in-latex.md)
- [Ignorer les zones SVG transparentes aux clics dans Konva](https://perfectvector.com/fr/blog/konva-alpha-hit-region.md)
- [Product facts](https://perfectvector.com/product-facts.md)
- [FAQ](https://perfectvector.com/faq.md)
- [Pricing and credits](https://perfectvector.com/pricing.md)
