Willie Hallock
Willie Hallock

Reputation: 35

Altair: how to include a place in a bar chart for a value that has no data

I have data that looks like:

data = {'ColA': {('A', 'A-1'): 'w',
                 ('A', 'A-2'): 'w',
                 ('A', 'A-3'): 'w',
                 ('B', 'B-1'): 'q',
                 ('B', 'B-2'): 'q',
                 ('B', 'B-3'): 'r',
                 ('C', 'C-1'): 'w',
                 ('C', 'C-2'): 'q',
                 ('C', 'C-3'): 'q',
                 ('C', 'C-4'): 'r'},
        'ColB': {('A', 'A-1'): 'r',
                 ('A', 'A-2'): 'w',
                 ('A', 'A-3'): 'w',
                 ('B', 'B-1'): 'q',
                 ('B', 'B-2'): 'q',
                 ('B', 'B-3'): 'e',
                 ('C', 'C-1'): 'e',
                 ('C', 'C-2'): 'q',
                 ('C', 'C-3'): 'r',
                 ('C', 'C-4'): 'w'}}

I then do the following to generate two bar charts:

df               = pd.DataFrame(data)
df[ df.columns ] = df[ df.columns ].astype('category')
df.index         = pd.Index( [ ': '.join( x ) for x in list( df.index.to_flat_index() ) ] )

X = alt.X( alt.repeat(), type = 'nominal' )

Y_scale = alt.Scale( domain = [0, len( df.index ) ] )
Y       = alt.Y( aggregate = 'count', type = 'quantitative', scale = Y_scale )

alt.Chart( df ).mark_bar().encode( X, Y ).repeat( list( df.columns ) )

which results in two bar charts that look like:

bar charts

Note the data for ColA contains no 'e' values, so the bar chart does not contain any tick mark for those values. For the ColA chart, I would like a tick mark for 'e' and for there to be no bar there so both the ColA and ColB charts look the same and can be easily compared.

How can I do that?

Upvotes: 2

Views: 382

Answers (1)

jakevdp
jakevdp

Reputation: 86443

You can use chart.resolve_scale(x='shared') to specify that you want the x scales to be shared between the charts. For example:

df = pd.DataFrame(data).reset_index(drop=True)

alt.Chart(df).mark_bar().encode(
    x=alt.X(alt.repeat(), type='nominal'),
    y=alt.Y('count():Q', scale=alt.Scale(domain=[0, len(df.index)]))
).repeat(
    list(df.columns)
).resolve_scale(
    x='shared'
)

enter image description here

Note that I removed several extraneous lines of your code, such as constructing the index (Altair drops indices from dataframes) and converting dtypes to category (Altair converts category dtypes back to strings).

Upvotes: 2

Related Questions