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

# Znaczniki SVG w Matplotlib scatter: sprawdź ścieżkę

> Użyj prostej ścieżki SVG jako znacznika Matplotlib scatter. Odtwórz kontrolę środka, orientacji, otworów i rozmiaru w rzeczywistych eksportach SVG i PDF.

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

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

Aby użyć SVG jako własnego znacznika Matplotlib scatter, zamień obsługiwaną geometrię na `matplotlib.path.Path`, wyśrodkuj wokół `(0, 0)` i przekaż obiekt do `scatter(marker=...)`. Kolor i rozmiar znacznika pochodzą wtedy z wykresu. [API znaczników Matplotlib przyjmuje Path](https://matplotlib.org/3.10.7/api/markers_api.html); nie przyjmuje dowolnego dokumentu SVG jako nazwy pliku znacznika.

Samouczek używa autorskiego asymetrycznego wielokąta z prostokątnym otworem. Kompletny skrypt poniżej obsługuje celowo mały podzbiór SVG i odrzuca funkcje, których nie zachowa. Uruchomiliśmy go z Matplotlib 3.10.7, sprawdziliśmy zapisane ścieżki SVG i PDF oraz oba wyrenderowane wyniki.

Jeśli chcesz jedną ilustrację obok eksportowanego wykresu, użyj oddzielnego [poradnika kompozycji grafiki SVG w Matplotlib](https://perfectvector.com/pl/blog/matplotlib-svg-artwork). Znacznik scatter powtarza się we współrzędnych danych, więc jego środek, orientacja i rozmiar wymagają innej kontroli.

## 1. Zacznij od jednego obsługiwanego kształtu

Potrzebujesz Pythona z Matplotlib i NumPy oraz uprawnienia do zapisu folderu wyjściowego. Przykład zawiera własny SVG, więc nie musisz pobierać ikony. Trzy współrzędne wykresu są ilustracyjne, a nie obserwacjami badawczymi.

Parser przyjmuje dokładnie jeden czarno wypełniony `path` w korzeniu SVG z `viewBox`. Każdy kontur musi zamykać się przez `Z`; każda para współrzędnych wymaga jawnego wielkiego `M` lub `L`. Obsługuje ścieżkę złożoną z konturem zewnętrznym i otworem o przeciwnych kierunkach przy `fill-rule="nonzero"`.

Odrzuca krzywe, polecenia względne, transformacje, obrysy, CSS, wiele elementów ścieżek, tekst, przycinanie, gradienty i inne kolory. Odrzuca też dodatkowe atrybuty korzenia, takie jak `width` i `height`. Ograniczenie pozwala sprawdzić przykład; to nie ogólny importer SVG. Zachowaj nietkniętą kopię grafiki przed przygotowaniem zgodnego wejścia.

Matplotlib Path przechowuje wierzchołki i kody rysowania zamiast pełnego modelu malowania SVG. [API Path](https://matplotlib.org/stable/api/path_api.html) definiuje użyte operacje ruchu, linii i zamknięcia. Jeśli symbol wymaga wielu kolorów lub efektów, wybierz proces zachowujący te cechy zamiast po cichu je odrzucać dla dopasowania do parsera.

## 2. Uruchom kompletny przykład znacznika

Zapisz jako `scatter_marker.py`, a potem uruchom `python scatter_marker.py` w tym samym folderze. Powstaną `marker-output/marker.svg`, `scatter.svg`, `scatter.pdf` i podgląd PNG. Wygenerowane źródło SVG i wszystkie ustawienia wykresu są tutaj.

```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)
```

Otwórz `scatter.svg` i `scatter.pdf`. Powinny pokazać trzy turkusowe wersje tego samego kształtu: prostokątny otwór po lewej i stopień wystający w dół i w prawo. Porównaj najmniejszy znacznik w docelowym rozmiarze oraz na powiększeniu.

![Rzeczywiste eksporty SVG i PDF Matplotlib pokazujące autorski asymetryczny znacznik, jego otwór i trzy sprawdzone rozmiary](https://perfectvector.com/blog/matplotlib-svg-scatter-marker/scatter-proof.webp)

*Autorski zestaw testowy i rzeczywiste zapisane eksporty Matplotlib 3.10.7. Widok PDF jest renderem zapisanego pliku wektorowego; wartości danych są ilustracyjne.*

## 3. Sprawdź środek i odwróć Y jeden raz

Grafika źródłowa zajmuje `x=10…90` i `y=10…70`. Środek prostokąta ograniczającego to `(50, 40)`. Odjęcie środka daje granice `−40…40` poziomo i `−30…30` pionowo. To centrowanie prostokąta ograniczającego, a nie środek ciężkości lub oszacowanie równowagi wizualnej.

Dolny prawy stopień jest przydatny, bo symetryczny okrąg ukryłby odwrócenie pionowe. W początkowym [układzie współrzędnych SVG](https://www.w3.org/TR/SVG2/coords.html) dodatnie Y biegnie w dół. Skrypt odwraca znak Y jeden raz, aby zachować wyświetlaną orientację źródła na zwykłym wykresie z Y w górę.

Oba kontury otrzymują tę samą transformację. Ich względne kierunki pozostają przeciwne, więc prostokątny otwór pozostaje otworem. [Reguła nonzero SVG](https://www.w3.org/TR/SVG2/painting.html#FillRuleProperty) zależy od kierunku konturu; źródło `evenodd` jest tutaj jawnie odrzucane zamiast traktowane jako równoważne.

Następnie Matplotlib normalizuje wyśrodkowany Path. W testowanej wersji wynik rozciąga się od `−0.5…0.5` w X i `−0.375…0.375` w Y. Punkt przekazany do `scatter` trafia w środek tych granic. Możesz świadomie wybrać inny punkt zakotwiczenia dla szczególnego symbolu, ale nie zakładaj, że lewy górny narożnik SVG jest właściwym zakotwiczeniem danych.

## 4. Odczytuj rozmiar scatter w punktach kwadratowych

[API scatter](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.scatter.html) definiuje `s` w punktach kwadratowych, gdzie punkt typograficzny ma 1/72 cala. Niezerowa krawędź znacznika może zwiększyć widoczny rozmiar. Przykład ustawia `linewidths=0` i `edgecolors="none"`, aby kontrola dotyczyła wypełnionej geometrii.

Dla tego znormalizowanego znacznika szerokość wynosi jedną jednostkę znacznika, a wysokość 0.75 jednostki. Zapisana szerokość to więc `sqrt(s)` punktów, a wysokość `0.75 * sqrt(s)` punktów.

| `s` (punkty kwadratowe) | Oczekiwana i zmierzona szerokość | Oczekiwana i zmierzona wysokość |
| ----------------------- | -------------------------------- | ------------------------------- |
| 400                     | 20 pt                            | 15 pt                           |
| 1600                    | 40 pt                            | 30 pt                           |
| 3600                    | 60 pt                            | 45 pt                           |

To wymiary tego konkretnego znormalizowanego kształtu, a nie jego zamalowana powierzchnia. Inne proporcje, zakotwiczenie lub obrys krawędzi zmieniają relację parametru rozmiaru do widocznego symbolu. Czterokrotne zwiększenie `s` podwaja wymiary w tym przykładzie.

Sprawdziliśmy rzeczywiste zapisane współrzędne SVG i operacje wypełnionych ścieżek PDF z tolerancją 0.000001 punktu. Wszystkie trzy znaczniki miały oczekiwane granice, wyśrodkowanie w punkcie danych i przekształcone wierzchołki. Zapisany SVG nie zawierał elementów obrazu, a PDF obiektów obrazu.

Wyrenderowaliśmy też PDF w 144 dpi i sprawdziliśmy trzy zadeklarowane miejsca wewnętrzne: otwór był biały, górne prawe wcięcie białe, a dolne prawe przedłużenie turkusowe. Natywny render przeglądarkowy rzeczywistego SVG przeszedł te same kontrole. Weryfikuje to wybraną orientację i otwór; nie oznacza identycznych wygładzonych krawędzi między rendererami ani obsługi dowolnej grafiki SVG.

## 5. Napraw przyczynę przed regulacją rozmiaru

| Objaw                                 | Kontrola i naprawa                                                                                                                                                          |
| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Parser zgłasza nieobsługiwaną funkcję | Sprawdź wskazaną cechę. Przygotuj oddzielny zgodny wielokąt lub pełniejszą metodę konwersji i ją przetestuj. Usunięcie atrybutu bez zachowania efektu może zmienić grafikę. |
| Kształt jest do góry nogami           | Sprawdź, czy Y odwrócono już przed funkcją. Zastosuj zmianę jeden raz i porównaj asymetryczną cechę.                                                                        |
| Symbol leży poza punktem danych       | Sprawdź granice i wybrane zakotwiczenie. Funkcja centruje granice grafiki niezależnie od pustego miejsca w `viewBox`.                                                       |
| Otwór się wypełnia                    | Sprawdź kierunki konturów i regułę wypełnienia. Zachowaj przeciwne kierunki dla tego przykładu nonzero; parser nie konwertuje geometrii evenodd.                            |
| Zapisany plik zawiera symbol rastrowy | Sprawdź eksportowane elementy lub zasoby. Sama nazwa SVG lub PDF nie dowodzi wektorowości każdego elementu wykresu.                                                         |
| Małe znaczniki tracą ważne detale     | Obejrzyj figurę w końcowym rozmiarze. Powiększ znacznik, uprość symbol lub użyj wbudowanego znacznika, jeśli jaśniej komunikuje kategorię.                                  |

Kontrolę plików poza tym przykładem opisuje [wyszukiwanie rastra wewnątrz SVG](https://perfectvector.com/pl/blog/svg-contains-raster-image). Dla całej figury sprawdź [rozmiar czcionki i grubość linii przy rozmiarze publikacji](https://perfectvector.com/pl/blog/scientific-figure-font-size-line-width); poprawna ścieżka może nadal być zbyt szczegółowa dla małej legendy lub zatłoczonego panelu.

Przed użyciem własnego znacznika w gotowej figurze sprawdź najmniejszy rozmiar, wygląd legendy, nakładające się punkty i eksportowane SVG/PDF. Zachowaj dane, osie, etykiety i mapowanie skali w procesie tworzenia wykresu. Własna ikona nie zastępuje tych definicji liczbowych.

## Gdy raster potrzebuje nowej ścieżki

Jeśli jedynym zachowanym symbolem jest płaski raster, [proces diagramu naukowego do SVG PerfectVector](https://perfectvector.com/pl/scientific-diagram-to-svg) może pomóc odzyskać edytowalną grafikę. Porównaj odzyskany kontur i każdy otwór z oryginałem przed adaptacją do procesu znaczników. Zachowaj istniejący czysty SVG jako źródło, jeśli go masz.

Odzyskiwanie wektorów nie odtwarza danych wykresu ani nie gwarantuje zgodności z tym ograniczonym parserem. Zachowaj fotografie i ilustracje tonalne, gdy ich rastrowy wygląd jest częścią figury, i wybierz odpowiednią metodę umieszczania obrazu.

## FAQ

**Czy mogę przekazać nazwę pliku SVG bezpośrednio do Matplotlib scatter?**
Nie. API znaczników przyjmuje obsługiwane opisy, w tym Matplotlib Path. Przykład konwertuje ograniczoną ścieżkę SVG do tego obiektu przed wywołaniem scatter.

**Dlaczego mój znacznik jest odwrócony?**
Źródłowy SVG i wykres mogą mieć różne kierunki Y. Przykład odwraca SVG Y raz; sprawdź asymetryczną cechę, aby nie odwrócić dwukrotnie.

**Czy s równa się zamalowanej powierzchni mojego SVG?**
Nie używaj go jako zmierzonej zamalowanej powierzchni. Scatter definiuje s w punktach kwadratowych, a znormalizowany kształt i obrys określają widoczne wymiary. Ten przykład ma szerokości 20, 40 i 60 punktów przy s równym 400, 1600 i 3600.

**Czy kod zachowuje każdą funkcję SVG?**
Nie. Obsługuje jedną czarną ścieżkę nonzero z jawnymi bezwzględnymi M, L i Z oraz zamkniętymi konturami. Nieobsługiwane malowanie, komendy geometrii, transformacje i dodatkowe elementy powodują błąd.

## Źródła

1. [API Matplotlib scatter](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.scatter.html) — definiuje parametr rozmiaru, wejście znacznika i wpływ szerokości krawędzi.
2. [API znaczników Matplotlib 3.10.7](https://matplotlib.org/3.10.7/api/markers_api.html) — opisuje znaczniki Path i znormalizowane współrzędne testowanej wersji.
3. [API Matplotlib Path](https://matplotlib.org/stable/api/path_api.html) — definiuje wierzchołki, kody rysowania i zamknięte kontury.
4. [Układy współrzędnych SVG](https://www.w3.org/TR/SVG2/coords.html) — określa początkowe kierunki współrzędnych kontroli orientacji.
5. [Reguły wypełnienia SVG](https://www.w3.org/TR/SVG2/painting.html#FillRuleProperty) — wyjaśnia wpływ kierunku konturu na otwór nonzero.

Dla symbolu wyłącznie rastrowego [odzyskaj edytowalną grafikę diagramu](https://perfectvector.com/pl/scientific-diagram-to-svg), porównaj kontur i otwory ze źródłem oraz przetestuj dostosowaną ścieżkę we własnej figurze przed przekazaniem.

## Explore

- [Read the HTML article](https://perfectvector.com/pl/blog/matplotlib-svg-scatter-marker)
- [SVG w LaTeX: zachowaj kształty i składaj tekst etykiet ilustracji](https://perfectvector.com/pl/blog/svg-in-latex.md)
- [Pomijaj przezroczyste obszary SVG przy trafieniach Konva](https://perfectvector.com/pl/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)
