I am new to Altair, and am attempting to plot a monthly time-series variable, and have a vertical line tooltip display the date and corresponding y-value.
The code I have (warning, probably a bit ugly) gets me most of the way there:
import altair as alt
import datetime as dt
import numpy as np
import pandas as pd
# create DataFrame
monthly_dates = pd.date_range('1997-09-01', '2022-08-01', freq = 'M')
monthly_data = pd.DataFrame(
index=['Date', 'y_var'],
data=[monthly_dates, np.random.normal(size = len(monthly_dates))]
).T
# Create a selection that chooses the nearest point & selects based on x-value
nearest = alt.selection(type='single', nearest=True, on='mouseover',
fields=['Date'], empty='none')
# The basic line
line = alt.Chart(monthly_data).mark_line().encode(
x='Date:T',
y=alt.Y('y_var', title='Y variable')
)
# Transparent selectors across the chart. This is what tells us
# the x-value of the cursor
selectors = alt.Chart(monthly_data).mark_point().encode(
x='Date',
opacity=alt.value(0),
).add_selection(
nearest
)
# Draw points on the line, and highlight based on selection
points = line.mark_point().encode(
opacity=alt.condition(nearest, alt.value(1), alt.value(0))
)
# Draw text labels near the points, and highlight based on selection
text_x = line.mark_text(align='left', dx=5, dy=-10).encode(
text=alt.condition(nearest, 'Date', alt.value(' '))
)
# Draw text labels near the points, and highlight based on selection
text_y = line.mark_text(align='left', dx=5, dy=5).encode(
text=alt.condition(nearest, 'y_var', alt.value(' '))
).transform_calculate(label='datum.y_var + "%"')
# Draw a rule at the location of the selection
rules = alt.Chart(monthly_data).mark_rule(color='gray').encode(
x='Date',
).transform_filter(
nearest
)
# Put the seven layers into a chart and bind the data
chart = alt.layer(
line, selectors, points, rules, text_x, text_y
).properties(
width=600, height=300
).interactive()
chart.show()
yields the following interactive chart:
There are two things I need to do, though:
text_y
) but to no avail.Any and all help would be greatly appreciated. Apologies in advance for any dumb mistakes or poor coding practices; again, I am still learning the basics of Altair.
Update: I figured both out.
The solutions to both 1 and 2 are in the code below.
For 1: instead of trying to add a box around the text manually, I instead added tooltips to the selectors
object and dropped the text_x
and text_y
entirely.
For 2: I used transform_calculate
to create new fields for x_label
and y_label
that are exactly what I want to display, then feed these into the tooltip objects. This page has tons of ways to transform data.
selectors = alt.Chart(monthly_data).mark_point().transform_calculate(
x_label='timeFormat(datum.Date, "%b %Y")',
y_label='format(datum.y_var, ".1f") + "%"'
).encode(
x='Date',
opacity=alt.value(0),
tooltip=[
alt.Tooltip('x_label:N', title='Date'),
alt.Tooltip('y_label:N', title='Pct. Change')
]
).add_selection(
nearest
)
The finished product: