Search code examples
pythonaltair

How to increase bar spacing in altair


I am trying to create an interactive chart such as when selecting a category, it displays all items in that category, but I haven't found a way to account for the variable height of the lower chart, which depends on the number of items in each category. This is how the chart ideally would show, when selecting category 1:

enter image description here

And when I select category 2, I would like it to be like this:

enter image description here

But, if I specify a fixed chart height that accommodates all the items in category 1 with proper spacing, this is what I get when I select category 2:

enter image description here

If I don't specify a chart height, the bars get too close together and some triangles don't show:

enter image description here

Here is the code:

df=pd.read_excel('df_test.xlsx')
    
    
domain=[df.Lowest.min(),df.Highest.max()]
    
    
selection=alt.selection_multi(fields=['category'],init=[{"category":"category1"}])
    
   
l=alt.Chart(df,height=80).mark_bar(color='red',size=15).encode(alt.X('min(Lowest)',scale=alt.Scale(domain=domain))
            ,alt.Y('category'),x2='max(middle)')
h=alt.Chart(df).mark_bar(color='green',size=15).encode(alt.X('max(middle)',scale=alt.Scale(domain=domain))
            ,alt.Y('category'),x2='max(Highest)')
avg_triangle=alt.Chart(df).mark_point(size=300,shape='triangle',filled=True,yOffset=-16,angle=180).encode(
    alt.X('mean(Current)',scale=alt.Scale(domain=domain)),alt.Y('category'))

averages=alt.layer(l,h,avg_triangle).add_selection(selection)


low=alt.Chart(df,height=300).mark_bar(color='red',size=15).encode(alt.X('Lowest',scale=alt.Scale(domain=domain))
                ,alt.Y('items'),x2='middle')

high=alt.Chart(df).mark_bar(color='green',size=15).encode(alt.X('middle',scale=alt.Scale(domain=domain))
                ,alt.Y('items'),x2='Highest')

triangle=alt.Chart(df).mark_point(size=300,shape='triangle',filled=True,yOffset=-16,angle=180).encode(
    alt.X('Current',scale=alt.Scale(domain=domain)),alt.Y('items'))
    
items=alt.layer(low,high,triangle).transform_filter(selection)
    

alt.vconcat(averages,items).configure_axis(title=' ')

And this is the dataset:

enter image description here

And the data for easy grab:

df=pd.DataFrame([{'category': 'category1',
  'items': 'c1-item1',
  'Highest': 77.9674166,
  'Lowest': 64.76344086,
  'middle': 71.36542873,
  'Current': 70.11278195},
 {'category': 'category1',
  'items': 'c1-item2',
  'Highest': 85.71428571,
  'Lowest': 64.38053097,
  'middle': 75.04740834,
  'Current': 84.1},
 {'category': 'category1',
  'items': 'c1-item3',
  'Highest': 82.51879699,
  'Lowest': 62.63736264,
  'middle': 72.578079815,
  'Current': 81.2},
 {'category': 'category1',
  'items': 'c1-item4',
  'Highest': 80.47182176,
  'Lowest': 51.37362637,
  'middle': 65.922724065,
  'Current': 76.69172932},
 {'category': 'category1',
  'items': 'c1-item5',
  'Highest': 85.19003932,
  'Lowest': 55.43010753,
  'middle': 70.310073425,
  'Current': 83.64661654},
 {'category': 'category1',
  'items': 'c1-item6',
  'Highest': 79.45609436,
  'Lowest': 40.56776557,
  'middle': 60.01192996499999,
  'Current': 74.81203008},
 {'category': 'category2',
  'items': 'c2-item1',
  'Highest': 76.14678899,
  'Lowest': 50.0,
  'middle': 63.073394495,
  'Current': 69.17293233},
 {'category': 'category2',
  'items': 'c2-item2',
  'Highest': 81.06159895,
  'Lowest': 58.77956989,
  'middle': 69.92058442,
  'Current': 75.0}])

Solution

  • You can increase the number of pixels used for each discrete step (i.e. each bar) by using {"step": <number>}. You can also create more space between bars by narrowing the bars themselves with bandPaddingInner:

    import altair as alt
    import pandas as pd
    
    source = pd.DataFrame({
        'a': ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'],
        'b': [28, 55, 43, 91, 81, 53, 19, 87, 52]
    })
    
    alt.Chart(source, width=alt.Step(100)).mark_bar().encode(
        x='a',
        y='b'
    ).configure_scale(
        bandPaddingInner=0.5
    )
    

    enter image description here

    You will also need to add .configure(autosize=alt.AutoSizeParams(resize=True)) to the end of your concatenated chart in order for the overall chart size (not just the axes) to update on a selection.

    In the next version of Altair you will be able to dynamically change the height of a chart using parameters.