altair icon indicating copy to clipboard operation
altair copied to clipboard

Arc based charts (i.e. Pie or Radial charts) layered text not aligned

Open robml opened this issue 1 year ago • 1 comments

I have attempting to make a few simple pie charts with an added text that indicates percentages. An issue I have run into is the alignment of the text, where the text is not aligned with the appropriate parts of the pie chart. Here is the code below:

color_scale = alt.Scale(
      domain=["Android","iOS"],
      range=["#9F7F9F","#31AFD4"]
  )
base = alt.Chart(df).encode(
        theta=alt.Theta("count():Q",stack=True),
        color=alt.Color("device_type:N",
                        legend=alt.Legend(title=None,
                                          orient="none",
                                          direction="horizontal",legendX=-10,legendY=-30),
                        scale=color_scale,
        ),tooltip="count():Q",
        
    ).properties(
        title=alt.Title(text="Apple or Android?",offset=10),
        height=200,
    )

pie = base.mark_arc(outerRadius=80)
text = base.mark_text(
            radius=100, 
            size=10,
        ).transform_aggregate(
            count="count():Q",
            groupby=['device_type']
        ).transform_joinaggregate(
            total_devices='sum(count)',
        ).transform_calculate(
            perc="datum.count / datum.total_devices",
        ).encode(
            text=alt.Text("perc:Q",format=".2%",),
            color=alt.value("#605E5C"),
            tooltip=alt.Text("count:Q"),
        )
device_chart = alt.layer(pie, text)

and the resulting chart looks like this (notice the inconsistency of the text):

altair troubleshooting

I have played around with the theta and offset parameters to no luck, is this resolvable?

Please follow these steps to make it more efficient to solve your issue:

  • [x] Since Altair is a Python wrapper around the Vega-Lite visualization grammar, most bugs should be reported directly to Vega-Lite. You can click the Action Button of your Altair chart and "Open in Vega Editor" to create a reproducible Vega-Lite example and see if you get the same error in the Vega Editor.
  • [x] Search for duplicate issues.
  • [x] Use the latest version of Altair.
  • [ x Describe how to reproduce the bug and include the full code and data to reproduce it, ideally using a sample data set from vega_datasets.

robml avatar May 26 '23 22:05 robml

Hi @robml , I reproduce this issue using Altair version 5.1.2:

df = pd.DataFrame({"device_type": ["Android", "iOS", "iOS", "iOS", "iOS", "iOS", "iOS", "iOS"]})
color_scale = alt.Scale(
      domain=["Android","iOS"],
      range=["#9F7F9F","#31AFD4"]
  )
base = alt.Chart(df).encode(
        theta=alt.Theta("count():Q",stack=True),
        color=alt.Color("device_type:N",
                        legend=alt.Legend(title=None,
                                          orient="none",
                                          direction="horizontal",legendX=-10,legendY=-30),
                        scale=color_scale,
        ),tooltip="count():Q",
        
    ).properties(
        title=alt.Title(text="Apple or Android?",offset=10),
        height=200,
    )

pie = base.mark_arc(outerRadius=80)
text = base.mark_text(
            radius=100, 
            size=10,
            align="center",
        ).transform_aggregate(
            count="count():Q",
            groupby=['device_type']
        ).transform_joinaggregate(
            total_devices='sum(count)',
        ).transform_calculate(
            perc="datum.count / datum.total_devices",
        ).encode(
            text=alt.Text("perc:Q",format=".2%",),
            color=alt.value("#605E5C"),
            tooltip=alt.Text("count:Q"),
        )
pie+text

results in image that the text does not overlap with the pie chart.

Another approach is place the text at the center of the arc

# Adjust the data frame format
df['number'] = df.groupby(['device_type'])['device_type'].transform('count')
df = df.drop_duplicates()

color_scale = alt.Scale(
      domain=["Android","iOS"],
      range=["#9F7F9F","#31AFD4"]
  )
base = alt.Chart(df).encode(
        theta=alt.Theta("number",stack=True),
        color=alt.Color("device_type:N",
                        legend=alt.Legend(title=None,
                                          orient="none",
                                          direction="horizontal",legendX=-10,legendY=-30),
                        scale=color_scale,
        ),tooltip="number:Q",
        
    ).properties(
        title=alt.Title(text="Apple or Android?",offset=10),
        height=200,
    )

pie = base.mark_arc(outerRadius=80)
text = base.mark_text(
            radius=100, 
            size=10,
            align="center",
        ).transform_joinaggregate(
            total_devices='sum(number)',
        ).transform_calculate(
            perc="datum.number / datum.total_devices",
        ).encode(
            text=alt.Text("perc:Q",format=".2%",),
            color=alt.value("#605E5C"),
            tooltip=alt.Text("number:Q"),
        )
pie+text

image

But I'm not sure why using transform_aggregate() does not center the text automatically.

ChiaLingWeng avatar Oct 13 '23 04:10 ChiaLingWeng