Setting Colors in Altair Layered Chart - python

Similar to Setting colors in Altair charts I am using a layered chart and I like to set excplicit colors. Somehow, when I use the solution from the answer, my colors are always changing to some arbitrary colors. I wonder, if anyone knows, why this happens and how to force altair to use the colors given in the colors array.
Here a test example:
import altair as alt
from vega_datasets import data
source = data.movies()
colors = ['#170c3b', '#fa8d0b']
plot = alt.Chart(source).mark_line().encode(
x=alt.X("IMDB_Rating", bin=True),
y=alt.Y(
alt.repeat("layer"), aggregate="mean", title="Mean of US and Worldwide Gross"
),
color=alt.datum(alt.repeat("layer")),
).repeat(
layer=["US_Gross", "Worldwide_Gross"]
).configure_range(
category=alt.RangeScheme(scheme=colors)
)
plot.show()
Here the colors from the array:
#170c3b
#fa8d0b
Here the plot with changed colors:

The category=alt.RangeScheme(scheme=colors) should be category=alt.RangeScheme(colors) instead.
import altair as alt
from vega_datasets import data
source = data.movies()
colors = ['#170c3b', '#fa8d0b']
plot = alt.Chart(source).mark_line().encode(
x=alt.X("IMDB_Rating", bin=True),
y=alt.Y(
alt.repeat("layer"), aggregate="mean", title="Mean of US and Worldwide Gross"
),
color=alt.datum(alt.repeat("layer")),
).repeat(
layer=["US_Gross", "Worldwide_Gross"]
).configure_range(
category=alt.RangeScheme(colors)
)
plot
yields:

Related

Align rotated labels of Altair boxplot

I'm plotting a boxplot graph using Altair to divide the data into groups (similar to what was done here: Altair boxplot with nested grouping by two categorical variables) . The code I have is below:
import seaborn as sns
import altair as alt
tips = sns.load_dataset("tips")
tips = tips.replace('Sun', 'Sunday morning')
chart = alt.Chart(tips).mark_boxplot(ticks=True).encode(
x=alt.X("smoker:O", title=None, axis=alt.Axis(labels=False, ticks=False), scale=alt.Scale(padding=1)),
y=alt.Y("total_bill:Q"),
color="smoker:N",
column=alt.Column('day:N', sort=['Thur','Fri','Sat','Sunday morning'],header=alt.Header(labelPadding=-530,labelAngle=-90))
).properties(
width=80,
height = 350
).configure_view(
stroke=None
)
However, I can not manage to properly position the column labels after rotating them 90 degrees. In the image below, you can see the graph I'm getting in 'a', and the graph I would like to get in 'b'.
I looked for configurations on https://altair-viz.github.io/user_guide/configuration.html, but could not find a solution. Does anyone have an idea? (I'm using Altair 4.2.2 - the latest version).
I have tried various parameters, referring to this answer. I got the intended result by setting the label orient to bottom and modifying the padding. I don't have as much experience with Altair as you describe.
import seaborn as sns
import altair as alt
tips = sns.load_dataset("tips")
tips = tips.replace('Sun', 'Sunday morning')
chart = alt.Chart(tips).mark_boxplot(ticks=True).encode(
x=alt.X("smoker:O", title=None, axis=alt.Axis(labels=False, ticks=False), scale=alt.Scale(padding=1)),
y=alt.Y("total_bill:Q"),
color="smoker:N",
column=alt.Column('day:N',
sort=['Thur','Fri','Sat','Sunday morning'],
header=alt.Header(
labelPadding=360,
labelAngle=-90,
orient='bottom',
labelOrient='bottom',
labelAlign='right'
))
).properties(
width=80,
height = 350
).configure_view(
stroke=None
)
chart

Is there a way to format tooltip values in Altair boxplot

Is is possible to format the values within a tooltip for a boxplot? From this Vega documentation, it appears so, but I can't quite figure out how to do it with Altair for python
from vega_datasets import data
import altair as alt
source = data.population.url
alt.Chart(source).mark_boxplot().encode(
alt.X("age:O"),
alt.Y("people:Q"),
tooltip=[
alt.Tooltip("people:Q", format=",.2f"),
],
)
I believe you need to provide an aggregation for composite marks like mark_boxplot. This works:
from vega_datasets import data
import altair as alt
source = data.population.url
alt.Chart(source).mark_boxplot().encode(
alt.X("age:O"),
alt.Y("people:Q"),
tooltip=alt.Tooltip("mean(people):Q", format=",.2f"),)
Update: As it is currently impossible to add multiple aggregated tooltips to a boxplot, I combined my answer with How to change Altair boxplot infobox to display mean rather than median? to put a transparent box with a custom tooltip on top of the boxplot. I still kept the boxplot underneath in order to have the outliers and whiskers plotted as a Tukey boxplot instead of min-max. I also added a point for the mean, since this is what I wanted to see in the tooltip:
alt.Chart(source).mark_boxplot(median={'color': '#353535'}).encode(
alt.X("age:O"),
alt.Y("people:Q"),
tooltip=[
alt.Tooltip("people:Q", format=",.2f"),
],
) + alt.Chart(source).mark_circle(color='#353535', size=15).encode(
x='age:O',
y='mean(people):Q'
) + alt.Chart(source).transform_aggregate(
min="min(people)",
max="max(people)",
mean="mean(people)",
median="median(people)",
q1="q1(people)",
q3="q3(people)",
count="count()",
groupby=['age']
).mark_bar(opacity=0).encode(
x='age:O',
y='q1:Q',
y2='q3:Q',
tooltip=alt.Tooltip(['min:Q', 'q1:Q', 'mean:Q', 'median:Q', 'q3:Q', 'max:Q', 'count:Q'], format='.1f')
)
There is a way to add multiple columns to the tooltip. You can pass in multiple columns in square brackets as a list.
import altair as alt
from vega_datasets import data
stocks = data.stocks()
alt.Chart(stocks).mark_point().transform_window(
previous_price = 'lag(price)'
).transform_calculate(
pct_change = '(datum.price - datum.previous_price) / datum.previous_price'
).encode(
x='date',
y='price',
color='symbol',
tooltip=[ 'price', 'symbol', alt.Tooltip('pct_change:Q', format='.1%')]
)

Generate two legends via Altair

I would like to have two legends via Altair just like the picture below.
I have created the legend of "Count of actors", but I don't know how to generate the other one. My code is below:
plot = base.mark_circle(
opacity=0.8,
stroke='black',
strokeWidth=1
).encode(
alt.X('TYPE:O'),
alt.Y('index:N',
sort= movies_order
),
alt.Size('count(index):Q',
scale=alt.Scale(range=[0,4500]),
legend=alt.Legend(title='Count of actors', symbolFillColor='white')),
alt.Color('GENDER', legend=None)
#complete this
).properties(
width=350,
height=880
And the chart I created is like this:
This is the default behavior in Altair, but you have disabled the color legend. Change alt.Color('GENDER', legend=None) to alt.Color('GENDER').
Here is a modifed example for the Altair gallery with two legends:
import altair as alt
from vega_datasets import data
source = data.cars()
alt.Chart(source).mark_circle().encode(
x='Horsepower',
y='Miles_per_Gallon',
color='Origin',
size='Cylinders')

How to plot y-axis bands in Altair charts?

Can Altair plot bands on the y axis, similar to this Highcharts example?
The docs have an example showing how to draw a line on the y axis, but adapting the example to use plot_rect to draw a band instead doesn't quite work:
import altair as alt
from vega_datasets import data
weather = data.seattle_weather.url
chart = alt.Chart(weather).encode(
alt.X("date:T")
)
bars = chart.mark_bar().encode(
y='precipitation:Q'
)
band = chart.mark_rect().encode(
y=alt.value(20),
y2=alt.value(50),
color=alt.value('firebrick')
)
alt.layer(bars, band)
I think the problem when you give a value with alt.value is that you specify the value in pixels starting from the top of the graph : it is not mapped to the data.
In the initial answer, with mark_rule, it would'nt create a clean band but a lot of vertical stripes, so here is a way to correctly plot a band.
First solution is to create a brand new data frame for the band, and layer that on top of the bars:
import altair as alt
import pandas as pd
from vega_datasets import data
weather = data('seattle_weather')
band_df = pd.DataFrame([{'x_min': weather.date.min(),
'x_max': weather.date.max(),
'y_min': 20,
'y_max': 50}])
bars = alt.Chart(weather).mark_bar().encode(
x=alt.X('date:T'),
y=alt.Y('precipitation:Q', title="Precipitation")
)
band_2 = alt.Chart(band_df).mark_rect(color='firebrick', opacity=0.3).encode(
x='x_min:T',
x2='x_max:T',
y='y_min:Q',
y2='y_max:Q'
)
alt.layer(bars, band_2)
Second option, if you do not want/cannot create a dataframe, is to use transform_calculate, and manually specify x and x2 in the band chart:
bars = alt.Chart().mark_bar().encode(
x=alt.X('date:T', title='Date'),
y=alt.Y('precipitation:Q', title="Precipitation")
)
band_3 = alt.Chart().mark_rect(color='firebrick', opacity=0.3).encode(
x='min(date):T',
x2='max(date):T',
y='y_min:Q',
y2='y_max:Q'
).transform_calculate(y_min='20', y_max='50')
alt.layer(bars, band_3, data=data.seattle_weather.url)
Initial answer
I would do 2 things to mimic the highchart example you gave. First, use a transform_calculate to set y_min and y_max values. And second, I'll use mark_rule so that the band span on the X axis where there are values. (I also added some opacity and changed the order of the layers so that the band is behind the bars.)
import altair as alt
from vega_datasets import data
weather = data.seattle_weather.url
chart = alt.Chart().encode(
alt.X("date:T")
)
bars = chart.mark_bar().encode(
y='precipitation:Q'
)
band = chart.mark_rule(color='firebrick',
opacity=0.3).encode(
y=alt.Y('y_min:Q'),
y2=alt.Y('y_max:Q')
).transform_calculate(y_min="20",
y_max="50")
alt.layer(band, bars, data=weather)

How to do annotations with Altair?

I am trying to write some text inside the figure to highlight something in my plot (equivalent to 'annotate' in matplotlib). Any idea? Thanks
You can get annotations into your Altair plots in two steps:
Use mark_text() to specify the annotation's position, fontsize etc.
Use transform_filter() from datum to select the points (data subset) that needs the annotation. Note the line from altair import datum.
Code:
import altair as alt
from vega_datasets import data
alt.renderers.enable('notebook')
from altair import datum #Needed for subsetting (transforming data)
iris = data.iris()
points = alt.Chart(iris).mark_point().encode(
x='petalLength',
y='petalWidth',
color='species')
annotation = alt.Chart(iris).mark_text(
align='left',
baseline='middle',
fontSize = 20,
dx = 7
).encode(
x='petalLength',
y='petalWidth',
text='petalLength'
).transform_filter(
(datum.petalLength >= 5.1) & (datum.petalWidth < 1.6)
)
points + annotation
which produces:
These are static annotations. You can also get interactive annotations by binding selections to the plots.

Categories

Resources