vega / altair

Declarative statistical visualization library for Python
https://altair-viz.github.io/
BSD 3-Clause "New" or "Revised" License
9.37k stars 793 forks source link

configure_axisX(title='foo') removes x-axis title #3582

Closed MarcoGorelli closed 2 months ago

MarcoGorelli commented 2 months ago

What happened?

I ran

alt.Chart(pl.DataFrame({'a':[1,2,3], 'b':[4,5,6]})).mark_line().encode(x='a', y='b').configure_axisX(title='foo')

and got

image

What would you like to happen instead?

image

Which version of Altair are you using?

5.4.1

dangotbanned commented 2 months ago

@MarcoGorelli to get the result you're after, the title needs to be set as a property on the X encoding

Solution

# NOTE: Original
# chart = (
#     alt.Chart(pl.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]}))
#     .mark_line()
#     .encode(x="a", y="b")
#     .configure_axisX(title="foo")
# )
# NOTE: Fixed
chart = (
    alt.Chart(pl.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]}))
    .mark_line()
    .encode(x=alt.X("a").title("foo"), y="b")
)
chart

image

You can check where this ends up in the spec with Chart.to_dict():

Original ```py {'config': {'view': {'continuousWidth': 300, 'continuousHeight': 300}, 'axisX': {'title': 'foo'}}, # <----------------------- 'data': {'name': 'data-be85e675f9b8f0270cc9ebf085a7356c'}, 'mark': {'type': 'line'}, 'encoding': {'x': {'field': 'a', 'type': 'quantitative'}, 'y': {'field': 'b', 'type': 'quantitative'}}, '$schema': 'https://vega.github.io/schema/vega-lite/v5.20.1.json', 'datasets': {'data-be85e675f9b8f0270cc9ebf085a7356c': [{'a': 1, 'b': 4}, {'a': 2, 'b': 5}, {'a': 3, 'b': 6}]}} ```
Fixed ```py {'config': {'view': {'continuousWidth': 300, 'continuousHeight': 300}}, 'data': {'name': 'data-be85e675f9b8f0270cc9ebf085a7356c'}, 'mark': {'type': 'line'}, 'encoding': {'x': {'field': 'a', 'title': 'foo', 'type': 'quantitative'}, # <----------------------- 'y': {'field': 'b', 'type': 'quantitative'}}, '$schema': 'https://vega.github.io/schema/vega-lite/v5.20.1.json', 'datasets': {'data-be85e675f9b8f0270cc9ebf085a7356c': [{'a': 1, 'b': 4}, {'a': 2, 'b': 5}, {'a': 3, 'b': 6}]}} ```

More Info

MarcoGorelli commented 2 months ago

ah, thanks @dangotbanned ! 🙏 closing then

mattijn commented 2 months ago

I also got a question, related to this. How would I call this within the polars plot directive?

Given

import polars as pl

df = pl.DataFrame({'x':[1,2,3], 'y':[4,5,6]})
df.plot.point(x='x', y='y')

How can I call the equivalent of doing:

alt.Chart(df).mark_point().encode(
    x=alt.X('x', title='foo'),
    y='y'
)

I see from the docstring there is a ChannelX type: image

But there is not such a thing as pl.ChannelX().

df.plot.point(
    x=pl.ChannelX('x', title='foo'), 
    y='y'
)
AttributeError: module 'polars' has no attribute 'ChannelX'

I realised I can do as such:

import altair as alt

df.plot.point(
    x=alt.X('x').title('foo'), 
    y='y'
)

But I'm not sure if this mixing is the intended approach.

dangotbanned commented 2 months ago

I also got a question, related to this. How would I call this within the polars plot directive?

Given

import polars as pl

df = pl.DataFrame({'x':[1,2,3], 'y':[4,5,6]})
df.plot.point(x='x', y='y')

How can I call the equivalent of doing:

alt.Chart(df).mark_point().encode(
    x=alt.X('x').title('foo'),
    y='y'
)

I see from the docstring there is a ChannelX type: image

But there is not such a thing as pl.ChannelX().

df.plot.point(
    x=pl.ChannelX('x', title='foo'), 
    y='y'
)
AttributeError: module 'polars' has no attribute 'ChannelX'

I realised I can do as such:

import altair as alt

df.plot.point(
    x=alt.X('x').title('foo'), 
    y='y'
)

But I'm not sure if this mixing is the intended approach.

@mattijn I'm not sure what it looks like in your IDE - but ChannelX is a type alias that should show something like this on hover:

https://altair-viz.github.io/user_guide/generated/typing/altair.typing.ChannelX.html#altair.typing.ChannelX

IIRC there are some screenshots in here showing VSCode https://github.com/vega/altair/pull/3515

MarcoGorelli commented 2 months ago

thanks for your inputs - yup alt.X('x', title='foo') is how users could do it, though in https://github.com/pola-rs/polars/pull/18609 I'm suggesting adding it as an argument because of how common the desire to rename axis labels is, i.e. df.plot.line(x='x', y='y', x_axis_title='foo')

mattijn commented 2 months ago

@dangotbanned, I did attach a screenshot of how it looks like in my IDE, which is JupyterLab. As far as I understand, there is not such a thing as on hover in the tooltip / signature-help that I activated by pressing shift+tab upon entering the function.

@MarcoGorelli, my mental model is currently favouring something like, everything related to x do it by using a X object, everything related to y by using a Y object and everything related to color, do it by using a Color object.

Edit: it can be named differently.

dangotbanned commented 2 months ago

@dangotbanned, I did attach a screenshot of how it looks like in my IDE, which is JupyterLab. As far as I understand, there is not such a thing as on hover in the tooltip / signature-help that I activated by pressing shift+tab upon entering the function.

Apologies @mattijn what I meant was that I wasn't sure what would be displayed if you hovered over the ChannelX symbol.

From a quick search, it seems you might need something like this https://github.com/jupyter-lsp/jupyterlab-lsp

Although @MarcoGorelli @binste if you have any experience with Jupyterlab - maybe you'd have a better suggestion?

Edit

This would be the equivalent for VSCode https://github.com/microsoft/pylance-release Adding as it might have more visual examples

MarcoGorelli commented 2 months ago

@MarcoGorelli, my mental model is currently favouring something like, everything related to x do it by using a X object, everything related to y by using a Y object and everything related to color, do it by using a Color object.

Yup I'd also prefer this, I'm just thinking about discoverability for people just using df.plot without necessarily knowing too much about Altair. An alternative to exposing x_axis_title / y_axis_title would be to just have some "plot customisation cheat sheet" in the Polars docs, showing some common tricks like alt.X('x', title=...) and then linking to the Altair docs if people want to take it further

It's hard to say where to draw the line 🤔 will sleep on it some more

joelostblom commented 2 months ago

I'm going to reopen this, since I think we should either support configure_axisX(title='foo') to have the intended effect or remove this option from the code base. Currently our documentation indicates that this should work which is confusing. I rarely use the configure_* methods myself, so I'm ok with removing it

Also asked here on SO recently.

mattijn commented 2 months ago

Yeah,

One more thing to add, colors and color schemes is also something that people like to control often. Which could be done as such:

import polars as pl
import altair as alt

path = "https://raw.githubusercontent.com/pola-rs/polars/main/docs/data/iris.csv"
df = pl.read_csv(path)

df.plot.scatter(
    x='petal_length', 
    y='petal_width',
    color=alt.Color('sepal_length').scale(scheme="lightgreyred")
)

But with some more sleep and showers (🙌) we might come up with a more polars-centric approach.

dangotbanned commented 2 months ago

I'm going to reopen this, since I think we should either support configure_axisX(title='foo') to have the intended effect or remove this option from the code base. Currently our documentation indicates that this should work which is confusing. I rarely use the configure_* methods myself, so I'm ok with removing it

Also asked here on SO recently.

@joelostblom These are the specs for each chart from https://github.com/vega/altair/issues/3582#issuecomment-2336665843

I will admit it is odd that setting a default value for the axis results in its removal; but this is consistent with vega-lite

mattijn commented 2 months ago

Raised in VL https://github.com/vega/vega-lite/issues/9429

joelostblom commented 2 months ago

Thank you @mattijn, closing this one then.