Implementation von Texturen

Sowohl in Bokeh als auch in Altair können benutzerdefinierte Muster verwendet werden.

Implementation Bokeh

In Bokeh werden ganze Dateien geladen, deswegen muss bei Tilings dafür gesorgt werden, dass die SVG ein Rechteck enthält, dessen Maße passend zu einer Tile sind und als fill das Pattern enthält.

Wir benötigen folgende Importe, um Bokeh zu verwenden und im Notebook anzuzeigen, um lokale Dateien verwenden zu können, müssen wir base64-codieren.

[1]:
from bokeh.io import show, output_notebook
from bokeh.plotting import figure
from bokeh.models import ImageURLTexture
from bokeh.resources import INLINE
import base64

output_notebook(resources=INLINE)
Loading BokehJS ...

Dann definieren wir eine Liste mit den Dateinamen der SVG-Dateien:

[2]:
svg_files = [
    "dotspattern.svg",
    "caretpattern.svg",
    "trianglepattern.svg",
    "spiralpattern.svg",
    "crosspattern.svg",
    "diamondpattern.svg",
    "squarepattern.svg",
    "tristarpattern.svg"
]

Wir codieren die lokalen SVG-Dateien zu Base64-Data-URLs, dies ist für online verfügbare Dateien nicht nötig, schadet dort aber auch nicht.

[3]:
# Funktion zum Umwandeln der SVGs in Base64-URLs
def svg_to_base64(svg_path):
    with open(svg_path, "rb") as svg_file:
        encoded_svg = base64.b64encode(svg_file.read()).decode("utf-8")
    return f"data:image/svg+xml;base64,{encoded_svg}"

# Dictionary aus Hatchpattern und SVGs erstellen
hatch_patterns = {f"pattern_{i}":
    ImageURLTexture(
        url=svg_to_base64("../../_static/pattern/"+svg_files[i])
                    ) for i in range(len(svg_files))}

Farbnamen als Bezeichner der x-Achse und Beispielwerte für den Plot:

[4]:
categories = ["Purple", "Teal", "Orange", "Blue", "Red", "Cyan", "Magenta", "Green"]
values = [90, 80, 50, 55, 50, 60, 40, 80]

Nun erstellen wir den Bokeh-Plot mit einem Säulendiagramm (vbar)

[5]:
p = figure(x_range=categories, height=400,
           title="cusy Pattern Example", tools="")

p.vbar(x=categories, top=values, width=0.9,
       fill_color="#75757533",      # Hintergrundfarbe der Säulen
       hatch_extra=hatch_patterns,  # Pattern anwenden
       hatch_pattern=[f"pattern_{i}" for i in range(len(categories))],
       line_color=None)
[5]:
GlyphRenderer(
id = 'p1038', …)

Um die SVG-Muster anzeigen zu lassen, müssen wir das SVG-Backend auswählen.

[6]:
p.output_backend="svg"
p.y_range.start=0 # Säulen auf der x-Achse beginnen lassen
p.background_fill_color = None # Hintergrundfarbe entfernen
p.border_fill_color = None     # Umrandung entfernen

show(p)

Implementation Vega-Altair

In Altair wird direkt die pattern-Funktionalität der SVG benutzt. Hier wird über die ID ein Pattern aus den SVG-defs ausgewählt.

Wir benötigen die Funktion display_html von IPython, um die SVGs inline ins HTML zu integrieren, damit wir sie später als Pattern in Altair verwenden können.

[7]:
from IPython.display import display_html

Wir definieren eine Funktion zum Einlesen und decodieren der SVG. Anschließend rufen wir die Funktion für unsere SVG auf und integrieren die Pattern in das HTML.

[8]:
def read_svg(svg_path):
    with open(svg_path, "rb") as svg_file:
        svg = svg_file.read().decode("utf-8")
    return svg

display_html(
    read_svg("../../_static/pattern/cusypattern.svg"),
    raw=True
)

Wir benutzen einen pandas-Dataframe, um die Daten für Altair bereitzustellen.

[9]:
import altair as alt
import pandas as pd

# Daten definieren
data = pd.DataFrame({
    "category": ["Purple", "Teal", "Orange", "Blue", "Red", "Cyan", "Magenta", "Green"],
    "value": [90, 80, 50, 55, 50, 60, 40, 80],
    "pattern": [
        "pattern_0",
        "pattern_1",
        "pattern_2",
        "pattern_3",
        "pattern_4",
        "pattern_5",
        "pattern_6",
        "pattern_7"
    ]
})
Wir sagen Pandas, dass die x-Achse aus den Kategorien bestehen und die y-Achse die zugehörigen Werte darstellen soll.
Im alt.Fill-Channel benutzen wir die ID des jeweiligen SVG-Pattern, um die Füllung festzulegen.
[10]:
chart = alt.Chart(data, title="cusy Pattern Example", width=600, height=400).mark_bar(stroke=None).encode(
    x=alt.X("category:N",
            sort=["Purple", "Teal", "Orange", "Blue", "Red", "Cyan", "Magenta", "Green"],
            axis=alt.Axis(title=None, labelAngle=0)),
    y=alt.Y("value:Q", axis=alt.Axis(title=None)),
    fill=alt.Fill("pattern:N",
                  scale=alt.Scale(
                      range=[
                          "url(#dotspattern)",
                          "url(#caretpattern)",
                          "url(#trianglepattern)",
                          "url(#spiralpattern)",
                          "url(#crosspattern)",
                          "url(#diamondpattern)",
                          "url(#squarepattern)",
                          "url(#tristarpattern)",
                      ]
                  ),
                  legend=None)
)
Auch in Altair müssen wir auf das SVG-Backend wechseln.
Anschließend geben wir den Namen des Plots an, um ihn anzuzeigen.
[11]:
alt.renderers.set_embed_options(renderer='svg')

chart
[11]: