简体   繁体   中英

How to put fraction labels in normalized bar chart in Altair?

import altair as alt
from vega_datasets import data

# get iris dataset and take 120 observations from it
source = data.iris()[10:130]
# add one more feature to dataset
colors = ['red', 'orange', 'blue', 'red'] * 30
source['colors'] = colors

chart = alt.Chart(source).mark_bar().encode(
    alt.Y('species:N', sort='x'),
    alt.X('count():Q', stack='zero'),
    color=alt.Color('colors', sort=colors), 
    order="colors",
    
)

text = alt.Chart(source).mark_text(dx=-7, dy=3, color='white')\
    .encode(
    alt.Y("species:N", sort='x'),
    alt.X('count():Q', stack="zero"),
    order="colors",
    text=alt.Text('count():Q')
    )

chart + text

结果是带有标签的条形图,显示每个“颜色”特征的计数

Now I want to normalize this chart and labels should represent fraction of a whole.

import altair as alt
from vega_datasets import data

source = data.iris()[10:130]
colors = ['red', 'orange', 'blue', 'red'] * 30
source['colors'] = colors


chart = alt.Chart(source).mark_bar().encode(
    alt.Y('species:N', sort='x'),
    alt.X('count():Q', stack='normalize'),
    color=alt.Color('colors', sort=colors), 
    order="colors",
    
)

text = alt.Chart(source).mark_text(dx=-7, dy=3, color='white')\
    .encode(
    alt.Y("species:N", sort='x'),
    alt.X('count():Q', stack="normalize"),
    order="colors",
    text=alt.Text('count():Q')
    )

chart + text

标准化条形图

So in this example instead of 8, 7, 15 for 1st bar I want to see 0.27, 0.23, 0.5 (rounded to 2 digits after comma). How can I achieve this?

update This is how I managed to progress:

import altair as alt
from vega_datasets import data

source = data.iris()[10:130]
colors = ['red', 'orange', 'blue', 'red'] * 30
sort_order=['blue', 'orange', 'red']
source['colors'] = colors

chart = alt.Chart(source).mark_bar().encode(
    alt.Y('species:N', sort='x'),
    alt.X('count():Q', stack='normalize'),
    color=alt.Color('colors', sort=sort_order), 
    order = 'colors'
)

text = alt.Chart(source).transform_aggregate(count='count()', groupby=['species', 'colors'])\
    .transform_joinaggregate(total='sum(count)', groupby=["species"])\
    .transform_calculate(frac=alt.datum.count / alt.datum.total)\
    .mark_text(align='right', dx=-7, dy=3, color='white')\
    .encode(
    alt.Y("species:N", sort='x'),
    alt.X('count():Q', stack="normalize"),
    text=alt.Text('frac:Q', format='.0%'),
    order = 'colors'
    )

chart + text

在此处输入图片说明

But labels are not aligned correctly (I need them to be just at the end of each color, just how in first two pictures). Any ideas how to fix it?

You can use a series of joinaggregate and calculate transforms to compute the values you wish to display. You can use the format text property to adjust the format using a d3-format string, and the align mark property to adjust the alignment. For example:

import altair as alt
from vega_datasets import data

source = data.iris()[10:130]
colors = ['red', 'orange', 'blue', 'red'] * 30
source['colors'] = colors
sort_order=['blue', 'orange', 'red']


chart = alt.Chart(source).mark_bar().encode(
    alt.Y('species:N', sort='x'),
    alt.X('count():Q', stack='normalize'),
    color=alt.Color('colors', sort=sort_order), 
    order="colors",
    
)

text = chart.transform_joinaggregate(
    count='count()',
    groupby=['species', 'colors']
).transform_joinaggregate(
    total='count()',
    groupby=['species']
).transform_calculate(
    fraction='datum.count / datum.total'   
).mark_text(
    dx=-7, dy=3, align='right'
).encode(
    color=alt.ColorValue('white'),
    text=alt.Text('fraction:Q', format='.2')
)

chart + text

在此处输入图片说明

(Note: I also made the script a bit more concise by sharing the data and encodings between the two charts)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM