I'm adding an automatic tooltip to an Altair chart successfully, but I'm unable to format the text as a currency.
Here's my code:
contracts = {'date': ['2015-11-12','2020-11-11','2018-02-28','2021-02-27','2019-04-01','2021-03-31','2016-07-02','2019-11-01','2019-04-23','2019-10-14'],
'type': ['Start date','End date','Start date','End date','Start date','End date','Start date','End date','Start date','End date',],
'value': [2200000,2200000,250000,250000,127000,127000,35000,35000,148723,148723],
'supplier': ['Capita','Capita','Deloitte','Deloitte','KPMG','KPMG','Herbert Smith','Herbert Smith','Spend Network','Spend Network'],
'buyer': ['DWP','DWP','MOD','MOD','HMRC','HMRC','MOJ','MOJ','DFID','DFID']
}
df = pd.DataFrame(contracts, columns=['date','type','value','supplier','buyer'])
# 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')
chart = alt.Chart(df, title="Pipeline").mark_point().encode(
alt.X(
'date:T',
scale=alt.Scale(zero=False),
axis=alt.Axis(grid=False)
),
alt.Y(
'supplier:N',
title="",
sort=alt.EncodingSortField(field='yield', op='sum', order='descending'),
scale=alt.Scale(rangeStep=20),
axis=alt.Axis(grid=True)
),
alt.Size('value:Q',
scale=alt.Scale(range=[40, 400]),
legend=alt.Legend(title='Contract size')
),
color=alt.Color('buyer:N', legend=alt.Legend(title="Buyer")),
)
line = alt.Chart(df).mark_line().encode(
alt.X('date:T',
## xAxis title must be "Source: https://openopps.com"
axis=alt.Axis(title="Source: https://openopps.com")
),
alt.Y('supplier:N',
## This is our units section, only describe the units of measurement here.
axis=alt.Axis(title="Units: contract duration, contract value")
),
color=alt.Color('buyer:N'),
)
# Transparent selectors across the chart. This is what tells us
# the x-value of the cursor
selectors = alt.Chart(df).mark_point().encode(
x='date:T',
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 = line.mark_text(align='left', dx=5, dy=-5).encode(
text=(
alt.condition(nearest, 'value:Q', alt.value(' '))
)
)
# Draw a rule at the location of the selection
rules = alt.Chart(df).mark_rule(color='gray').encode(
x='date:T',
).transform_filter(
nearest
)
# Put the five layers into a chart and bind the data
alt.layer(
chart, line, selectors, points, rules, text
)
This gives me this output
You can see from the highlighted line that the tooltip (148723) is not formatted (e.g. no $
for the currency).
I've tried doing a couple of things, without success...
This approach just trashes the chart formatting as whole, with the lines and legends and x-axis screwing up.
# Draw text labels near the points, and highlight based on selection
text = line.mark_text(align='left', dx=5, dy=-5).encode(
text=(
alt.condition(nearest, 'value:Q', alt.value(' '), format='$.,3s')
)
)
And this approach just fails on validation.
# Draw text labels near the points, and highlight based on selection
text = line.mark_text(align='left', dx=5, dy=-5).encode(
text=(
alt.Text('value:Q', format='$.,3s'),
alt.condition(nearest, 'value:Q', alt.value(' '))
)
)
error message:
SchemaValidationError: Invalid specification
altair.vegalite.v3.api.Chart, validating 'required'
'data' is a required property
I'm surprised that the first approach doesn't work, since the alt.conditional
allows for **kwargs
, but I guess I'm misunderstanding what I can pass to the conditional.
Your format string "$.,3s"
is not valid. Altair uses format strings from d3 format, e.g. "$.2f"
. You can specify it like this:
# ...
text=alt.condition(nearest, 'value:Q', alt.value(' '), format='$.2f')
# ...