Text#

text mark represents each data point with a text instead of a point.

Text Mark Properties#

Click to show code
import altair as alt
import pandas as pd

angle_slider = alt.binding_range(min=-180, max=180, step=1)
angle_var = alt.param(bind=angle_slider, value=0, name="angle")

dx_slider = alt.binding_range(min=-20, max=20, step=1)
dx_var = alt.param(bind=dx_slider, value=5, name="dx")

dy_slider = alt.binding_range(min=-20, max=20, step=1)
dy_var = alt.param(bind=dy_slider, value=0, name="dy")

xOffset_slider = alt.binding_range(min=-20, max=20, step=1)
xOffset_var = alt.param(bind=xOffset_slider, value=0, name="xOffset")

yOffset_slider = alt.binding_range(min=-20, max=20, step=1)
yOffset_var = alt.param(bind=yOffset_slider, value=0, name="yOffset")

fontSize_slider = alt.binding_range(min=1, max=36, step=1)
fontSize_var = alt.param(bind=fontSize_slider, value=14, name="fontSize")

limit_slider = alt.binding_range(min=0, max=150, step=1)
limit_var = alt.param(bind=limit_slider, value=0, name="limit")

align_select = alt.binding_select(options=["left", "center", "right"])
align_var = alt.param(bind=align_select, value="left", name="align")

baseline_select = alt.binding_select(options=["alphabetic", "top", "middle", "bottom"])
baseline_var = alt.param(bind=baseline_select, value="midle", name="baseline")

font_select = alt.binding_select(options=["sans-serif", "serif", "monospace"])
font_var = alt.param(bind=font_select, value="sans-serif", name="font")

fontWeight_select = alt.binding_select(options=["normal", "bold"])
fontWeight_var = alt.param(bind=fontWeight_select, value="normal", name="fontWeight")

fontStyle_select = alt.binding_select(options=["normal", "italic"])
fontStyle_var = alt.param(bind=fontStyle_select, value="normal", name="fontStyle")

source = pd.DataFrame(
    {
        "a": [30, 25, 70],
        "b": [28, 65, 43],
        "label": ["Andy", "Brian", "Charlie"],
    }
)

base = alt.Chart(source).encode(
    x=alt.X("a:Q").axis(labelAngle=0).scale(domain=[0, 100]),
    y=alt.Y("b:Q").scale(domain=[0, 100]),
)

pts = base.mark_point()

text = base.mark_text(
    dx=dx_var,
    dy=dy_var,
    xOffset=xOffset_var,
    yOffset=yOffset_var,
    angle=angle_var,
    align=align_var,
    baseline=baseline_var,
    font=font_var,
    fontSize=fontSize_var,
    fontStyle=fontStyle_var,
    fontWeight=fontWeight_var,
    limit=limit_var,
).encode(text="label:N")

(pts + text).add_params(
    dx_var,
    dy_var,
    xOffset_var,
    yOffset_var,
    angle_var,
    align_var,
    baseline_var,
    font_var,
    fontSize_var,
    fontStyle_var,
    fontWeight_var,
    limit_var,
)

A text mark definition can contain any standard mark properties and the following special properties:

Click to show table

Property

Type

Description

angle

anyOf(number, ExprRef)

The rotation angle of the text, in degrees.

align

anyOf(Align, ExprRef)

The horizontal alignment of the text or ranged marks (area, bar, image, rect, rule). One of "left", "right", "center".

Note: Expression reference is not supported for range marks.

baseline

anyOf(TextBaseline, ExprRef)

For text marks, the vertical text baseline. One of "alphabetic" (default), "top", "middle", "bottom", "line-top", "line-bottom", or an expression reference that provides one of the valid values. The "line-top" and "line-bottom" values operate similarly to "top" and "bottom", but are calculated relative to the lineHeight rather than fontSize alone.

For range marks, the vertical alignment of the marks. One of "top", "middle", "bottom".

Note: Expression reference is not supported for range marks.

dir

anyOf(TextDirection, ExprRef)

The direction of the text. One of "ltr" (left-to-right) or "rtl" (right-to-left). This property determines on which side is truncated in response to the limit parameter.

Default value: "ltr"

dx

anyOf(number, ExprRef)

The horizontal offset, in pixels, between the text label and its anchor point. The offset is applied after rotation by the angle property.

dy

anyOf(number, ExprRef)

The vertical offset, in pixels, between the text label and its anchor point. The offset is applied after rotation by the angle property.

ellipsis

anyOf(string, ExprRef)

The ellipsis string for text truncated in response to the limit parameter.

Default value: "…"

font

anyOf(string, ExprRef)

The typeface to set the text in (e.g., "Helvetica Neue").

fontSize

anyOf(number, ExprRef)

The font size, in pixels.

Default value: 11

fontStyle

anyOf(FontStyle, ExprRef)

The font style (e.g., "italic").

fontWeight

anyOf(FontWeight, ExprRef)

The font weight. This can be either a string (e.g "bold", "normal") or a number (100, 200, 300, …, 900 where "normal" = 400 and "bold" = 700).

limit

anyOf(number, ExprRef)

The maximum length of the text mark in pixels. The text value will be automatically truncated if the rendered size exceeds the limit.

Default value: 0 – indicating no limit

lineHeight

anyOf(number, ExprRef)

The line height in pixels (the spacing between subsequent lines of text) for multi-line text marks.

radius

anyOf(number, ExprRef)

For arc mark, the primary (outer) radius in pixels.

For text marks, polar coordinate radial offset, in pixels, of the text from the origin determined by the x and y properties.

Default value: min(plot_width, plot_height)/2

text

anyOf(Text, ExprRef)

Placeholder text if the text channel is not specified

theta

anyOf(number, ExprRef)

  • For arc marks, the arc length in radians if theta2 is not specified, otherwise the start arc angle. (A value of 0 indicates up or “north”, increasing values proceed clockwise.)

  • For text marks, polar coordinate angle in radians.

Examples#

Text Table Heatmap#

import altair as alt
from vega_datasets import data

source = data.cars()

base = alt.Chart(source).transform_aggregate(
    num_cars="count()",
    groupby=["Origin", "Cylinders"],
).encode(
    alt.X("Cylinders:O").scale(paddingInner=0),
    alt.Y("Origin:O").scale(paddingInner=0),
)

heatmap = base.mark_rect().encode(
    alt.Color("num_cars:Q")
        .scale(scheme="viridis")
        .legend(direction="horizontal")
)

predicate = alt.datum.num_cars > 100
text = base.mark_text(baseline="middle").encode(
    text="num_cars:Q",
    color=alt.when(predicate).then(alt.value("black")).otherwise(alt.value("white")),
)

heatmap + text

Labels#

You can also use text marks as labels for other marks and set offset (dx or dy), align, and baseline properties of the mark definition.

import altair as alt
import pandas as pd

source = pd.DataFrame({
    "a": ["A", "B", "C"],
    "b": [28, 55, 43]
})

bar = alt.Chart(source).mark_bar().encode(
    y="a:N",
    x=alt.X("b:Q").scale(domain=[0, 60])
)
text = bar.mark_text(
    align="left",
    baseline="middle",
    dx=3
).encode(text="b")

bar + text

Labels Position Based on Condition#

By default, text mark as labels in Altair are positioned above or to the right of the value. However, when dealing with negative values, this default positioning can lead to label overlap with the bar. To address this issue, you can set label positions via Expressions. Here’s an example demonstrating how to do this:

import altair as alt
import pandas as pd

source = pd.DataFrame({
    "a": ["A", "B", "C"],
    "b": [28, -5, 10]
})

bar = alt.Chart(source).mark_bar().encode(
    y="a:N",
    x=alt.X("b:Q").scale(domain=[-10, 35])
)

text_conditioned = bar.mark_text(
    align="left",
    baseline="middle",
    dx=alt.expr(alt.expr.if_(alt.datum.b >= 0, 10, -20))
).encode(text="b")

bar + text_conditioned

Scatter Plot with Text#

Mapping a field to text channel of text mark sets the mark’s text value. For example, we can make a colored scatter plot with text marks showing the initial character of its origin, instead of point marks.

import altair as alt
from vega_datasets import data
from altair import datum

source = data.cars()

alt.Chart(source).mark_text().encode(
    x="Horsepower:Q",
    y="Miles_per_Gallon:Q",
    color="Origin:N",
    text="Origin[0]:N",
)

Geo Text#

By mapping geographic coordinate data to longitude and latitude channels of a corresponding projection, we can show text at accurate locations. The example below shows the name of every US state capital at the location of the capital.

import altair as alt
from vega_datasets import data

states = alt.topo_feature(data.us_10m.url, feature="states")

source = data.us_state_capitals()

background = alt.Chart(states).mark_geoshape(
    fill="lightgray",
    stroke="white",
).properties(
    width=750,
    height=500,
).project("albersUsa")

line = alt.Chart(source).mark_text(dy=-10).encode(
    latitude="lat:Q",
    longitude="lon:Q",
    text="city:N"
)

point = alt.Chart(source).mark_circle().encode(
    latitude="lat:Q",
    longitude="lon:Q",
    color=alt.value("orange"),
)

background + line + point