altair icon indicating copy to clipboard operation
altair copied to clipboard

Make it easy to create arbitrary text box

Open dsandber opened this issue 3 years ago • 10 comments

I'm often making graphs where I would like to list the parameters used to create the graph, for example:

scale_constant = 28.3 median_shift = 12.3 frequency = 93.2

I found this ticket which was talks about it:

https://github.com/altair-viz/altair/issues/984

and this SO question:

https://stackoverflow.com/questions/61277181/adding-r-value-correlation-to-scatter-chart-in-altair

But didn't see a clean solution. It would be great if something like this was possible:

altair.text_box(text=""" scale_constant = 28.3 median_shift = 12.3 frequency = 93.2 """, location="top_right")

or something along those lines. If HTML was supported, that'd be great too.

dsandber avatar Mar 24 '21 08:03 dsandber

To be clear, this stuff isn't related to specific data-points, doesn't belong in the title, and doesn't belong in the legend. It belongs in a box (ideally with a different background color than the graph) that is overlayed onto the graph somewhere where it doesn't occlude the graph data.

dsandber avatar Mar 24 '21 09:03 dsandber

It occurs to me I could create a box with a div in HTML, and overlay it onto the graph with CSS, but this seems hokey. But perhaps if there was an easy way to specify the overlay onto one of the four corners of the graph this would work. Perhaps someone has an example of this already?

dsandber avatar Mar 24 '21 09:03 dsandber

A text box function would have to be added to Vega-Lite before it can be added to Altair. You can check if there is an open issue about it on their tracker or start a new one. Note that it is already possible to do what you want by combining mark_text and mark_rect. With the example you linked to:

import altair as alt
from vega_datasets import data

cars = data.cars()
chart = alt.Chart(cars).mark_circle().encode(
        alt.X('Miles_per_Gallon', scale=alt.Scale(domain=(5,50))),
        y='Weight_in_lbs')
corl = cars[['Miles_per_Gallon','Weight_in_lbs']].corr().iloc[0,1]

text = alt.Chart({'values':[{}]}).mark_text(
    align="left", baseline="top"
).encode(
    x=alt.value(5),  # pixels from left
    y=alt.value(5),  # pixels from top
    text=alt.value([f"r: {corl:.3f}", 'Line 2']))

box = alt.Chart({'values':[{}]}).mark_rect(stroke='black', color='orange').encode(
    x=alt.value(3),
    x2=alt.value(50),
    y=alt.value(3),
    y2=alt.value(30))

chart + box + text + chart.transform_regression('Miles_per_Gallon','Weight_in_lbs').mark_line()

image

joelostblom avatar Apr 01 '21 18:04 joelostblom

Fantastic, thank you! I guess the advantage of a built-in command is that the rectangle size could be auto-calculated, and that it could be done in fewer steps, eh?

I will check that this enhancement request is in Vega-lite, and will link to this answer, thank you!

dsandber avatar Apr 01 '21 19:04 dsandber

Posted here:

https://github.com/vega/vega-lite/issues/7376

dsandber avatar Apr 11 '21 11:04 dsandber

Circling back on this, since Vega-lite already supports this with reactive geometries (see thread: https://github.com/vega/vega-lite/issues/7376) . Can we make so this can easily be done in Altair?

dsandber avatar Jun 02 '21 10:06 dsandber

A text box function would have to be added to Vega-Lite before it can be added to Altair. You can check if there is an open issue about it on their tracker or start a new one. Note that it is already possible to do what you want by combining mark_text and mark_rect. With the example you linked to:

import altair as alt
from vega_datasets import data

cars = data.cars()
chart = alt.Chart(cars).mark_circle().encode(
        alt.X('Miles_per_Gallon', scale=alt.Scale(domain=(5,50))),
        y='Weight_in_lbs')
corl = cars[['Miles_per_Gallon','Weight_in_lbs']].corr().iloc[0,1]

text = alt.Chart({'values':[{}]}).mark_text(
    align="left", baseline="top"
).encode(
    x=alt.value(5),  # pixels from left
    y=alt.value(5),  # pixels from top
    text=alt.value([f"r: {corl:.3f}", 'Line 2']))

box = alt.Chart({'values':[{}]}).mark_rect(stroke='black', color='orange').encode(
    x=alt.value(3),
    x2=alt.value(50),
    y=alt.value(3),
    y2=alt.value(30))

chart + box + text + chart.transform_regression('Miles_per_Gallon','Weight_in_lbs').mark_line()

image

Thanks for providing this example! I noticed that the text from the text box is not selectable, is there a way to change this?

masta-g3 avatar Jul 09 '22 17:07 masta-g3

@masta-g3 I don't think the text is selectable in any Vega-Lite chart, at least not in the Vega Editor or Jupyter Lab. You can try with the examples here https://vega.github.io/vega-lite/examples/. You could always right click and view the source of the page to select it or save an svg

joelostblom avatar Jul 09 '22 18:07 joelostblom

Hello, What if, e.g. my x axis was a text category? How could I give the coordinates of that string? In particular: x=alt.value(5), # pixels from left. y=alt.value(5), # pixels from top

should be replaced, do you guys know how?

ccsuehara avatar Jul 15 '22 05:07 ccsuehara

@ccsuehara alt.value specifies the number of pixels in the charts so it should work regardless of what data type you have on the x-axis. Could you include the code for a complete reproducible example with your data included that shows the issue you are encountering?

joelostblom avatar Jul 15 '22 17:07 joelostblom

@dsandber I am going through a few of the older issues and taking a second look at the reply you got from the Vega-Lite devs indicated that this is not yet supported in Vega-Lite (the reactive geometries are present in Vega, but not Vega-Lite):

To draw the box, you would still need some way to generate a reactive geometry (the box size depends on the size of the text). It could be part of annotations such as https://github.com/vega/vega-lite/pull/3401. Alternatively, Vega could add a text background color (and padding) option to text marks.

I am closing this as there is nothing we can do about this on the altair side of things, following https://github.com/vega/vega-lite/pull/3401 or similar issues in the Vega-Lite repo would be the best way to know when a feature like this might end up in Altair.

joelostblom avatar Feb 06 '23 01:02 joelostblom