I'm trying to horizontally concatenate two charts in altair, but I can't get them to look just like I want them to.
Here is what they look like:
And here is the code I'm using:
pick_ausbildung = alt.selection_single(fields = ["Ausbildungsstand"], on = "mouseover")
ausbildung_chart = alt.Chart(umfrage,
title = "Ausbildungsstand").mark_bar().encode(
y=alt.Y("Ausbildungsstand", axis = alt.Axis(title = None)),
x="count()",
color = alt.condition(pick_ausbildung,
alt.Color("Ausbildungsstand:N",
legend = None), alt.value("lightgrey")),
tooltip = ["Ausbildungsstand","count()"]).properties(height=200).add_selection(pick_ausbildung)
g_ausbildung_chart = alt.Chart(umfrage).mark_bar().encode(
x="Geschlecht",
y="count()",
color = "Geschlecht",
tooltip = ["Geschlecht","count()"]).properties(width=300).transform_filter(pick_ausbildung)
ausbildung_chart|g_ausbildung_chart
And basically, I would like to place the chart "Ausbildungsstand" in the middle of the chart area. I mean, I'd like to separate it from the top edge of the canvas.
I can sort of get the result I want by adjusting the height of the charts (if they have the same height, they're aligned), but I'd like to know how to move the chart inside the "canvas".
Thanks in advance for any help.
You can use the alt.hconcat() function and pass center=True. For example:
import altair as alt
import pandas as pd
df = pd.DataFrame({
'label': ['A', 'B', 'C', 'D', 'E'],
'value': [3, 5, 4, 6, 2],
})
chart1 = alt.Chart(df).mark_bar().encode(y='label', x='value')
chart2 = alt.Chart(df).mark_bar().encode(x='label', y='value')
alt.hconcat(chart1, chart2, center=True)
Related
I have my plot clipped so it only shows certain ranges on the y axis. I added text to it using this code:
text2 = plot2.mark_text(align='left', dx=5, dy= -8, size = 15).encode(text = alt.Text('Accuracy', format = ',.2f'))
But this added annotation appears outside of the plot. So I need to get rid of it.
In the plot, I'm using sth like this:clip = True in mark_line().
You need to set clip=True for the text mark explicitly:
df = pd.DataFrame({'x': [1, 3], 'y': [1, 4], 'text': ['a', 'b']})
chart = alt.Chart(df).mark_line(clip=True).encode(
x=alt.X('x', scale=alt.Scale(domain=[0, 2])),
y='y'
)
chart + chart.mark_text().encode(text='text')
chart + chart.mark_text(clip=True).encode(text='text')
I have a dataframe like this
df = pd.DataFrame({'name':['a', 'b', 'c', 'd', 'e'], 'value':[54.2, 53.239, 43.352, 36.442, -12.487]})
df
I'd like to plot a simple stacked bar chart like the one below whit plotly.express
How can a I do that?
I've seen on documentation several examples but none of them solved my problem
Thank you
It's a little wordy, but you can set a single value for the x axis, in this case zero. Then you just need to tweak your dimension, lables, and ranges.
import pandas as pd
import plotly.express as px
df = pd.DataFrame({'name':['a', 'b', 'c', 'd', 'e'], 'value':[54.2, 53.239, 43.352, 36.442, -12.487]})
df['x'] = 0
fig = px.bar(df, x='x', y='value',color='name', width=500, height=1000)
fig.update_xaxes(showticklabels=False, title=None)
fig.update_yaxes(range=[-50,200])
fig.update_traces(width=.3)
fig.show()
The bar chart's only ever going to have one column? That seems like an odd use-case for a bar chart, but...
What I would do is create one trace per "name", filtering df as trace_df=df[df['name']==name], and then make a Bar for each of those, something like this:
import plotly.graph_objects as go
trace_dfs = [df[df['name']==name] for name in df['name']]
bars = [
go.Bar(
name=name,
x=['constant' for _ in trace_frame['value']],
y=trace_frame['value'],
)
for trace_frame in trace_dfs
]
fig = go.Figure(
data=bars,
barmode='stack'
)
Granted, that's not plotly_express, it's core plotly, which allows a lot more control. If you want multiple stacked bars for different values, you'll need separate labels and separate values for x and y, not the two-column DF you described. There are several more examples here and a full description of the available bar chart options here.
I create a network graph with Bokeh networkx from a df:
df = pd.DataFrame('source': [1,2,3], 'target': ['a', 'b', 'c], 'name': ['a1', 'b2', 'c3'])
with source nodes from column source and target nodes from column target
Is there any way to show up hovers of
HoverTool(tooltips = [("SOURCE", "$source"), ("NAME", "$name")])
for 'source' nodes and
HoverTool(tooltips = [("TARGET", "$target")])
for target nodes?
My code is as following:
import pandas as pd
from bokeh.io import show
import networkx as nx
from bokeh.models import Plot, MultiLine, Circle
from bokeh.models.graphs import from_networkx
net_graph = networkx.from_pandas_edgelist(df, 'source', 'target', 'name')
for index, row in df.iterrows():
net_graph.nodes[row['source']]['source_hover'] = row['source']
net_graph.nodes[row['source']]['name hover'] = row['name']
net_graph.nodes[row['target']]['target hover'] = row['target']
graph_plot = Plot(plot_width = 800, plot_height = 600, x_range = Range1d(-1.1, 1.1), y_range = Range1d(-1.1, 1.1))
graph_setup = from_networkx(net_graph, nx.spring_layout, scale = 1, center = (0, 0))
graph_plot.add_tools(HoverTool(renderers=[graph_setup], tooltips=[("SOURCE", "#source_hover"), ("NAME", "#name_hover")]))
graph_plot.add_tools(HoverTool(renderers=[graph_setup], tooltips=[("TARGET", "#target_hover")]))
graph_setup.node_renderer.glyph = Circle(size = 20, fill_color = 'red')
graph_setup.edge_renderer.glyph = MultiLine(line_color = "grey", line_alpha = 0.8, line_width = 1)
graph_plot.renderers.append(graph_setup)
show(graph_plot)
you could change renderers below by your graph or if you have different glyphs you could add p, p2, etc. I cannot help very well because your intention is unclear and we don't have your codes.
p.add_tools(HoverTool(renderers=[], tooltips=[("SOURCE", "$source"), ("NAME", "$name")])
p.add_tools(HoverTool(renderers=[], tooltips=[("TARGET", "$target")])
renderers=[] part will help you to do that. renderers are basically your lines/bars etc. for above you've got Multiline (which have list of renderers) and Circle. so basically for circle graph just use renderers[]
for example for circle first give a name:
circlename = Circle(size = 20, fill_color = 'red')
and in hover tool add renderers=[circlename]
MultiLine part is little bit tricky. You've got multiple lines. so you have to give names to all of them. You basically could use dictinary and for loop to give names. and could detetermine in renderers part like above. You could check it out from here
I'm trying to use Altair in Python to make a bar chart where the bars have varying width depending on the data in a column of the source dataframe. The ultimate goal is to get a chart like this one:
The height of the bars corresponds to a marginal-cost of each energy-technology (given as a column in the source dataframe). The bar width corresponds to the capacity of each energy-technology (also given as a columns in the source dataframe). Colors are ordinal data also from the source dataframe. The bars are sorted in increasing order of marginal cost. (A plot like this is called a "generation stack" in the energy industry). This is easy to achieve in matplotlib like shown in the code below:
import matplotlib.pyplot as plt
# Make fake dataset
height = [3, 12, 5, 18, 45]
bars = ('A', 'B', 'C', 'D', 'E')
# Choose the width of each bar and their positions
width = [0.1,0.2,3,1.5,0.3]
y_pos = [0,0.3,2,4.5,5.5]
# Make the plot
plt.bar(y_pos, height, width=width)
plt.xticks(y_pos, bars)
plt.show()
(code from https://python-graph-gallery.com/5-control-width-and-space-in-barplots/)
But is there a way to do this with Altair? I would want to do this with Altair so I can still get the other great features of Altair like a tooltip, selectors/bindings as I have lots of other data I want to show alongside the bar-chart.
First 20 rows of my source data looks like this:
(does not match exactly the chart shown above).
In Altair, the way to do this would be to use the rect mark and construct your bars explicitly. Here is an example that mimics your data:
import altair as alt
import pandas as pd
import numpy as np
np.random.seed(0)
df = pd.DataFrame({
'MarginalCost': 100 * np.random.rand(30),
'Capacity': 10 * np.random.rand(30),
'Technology': np.random.choice(['SOLAR', 'THERMAL', 'WIND', 'GAS'], 30)
})
df = df.sort_values('MarginalCost')
df['x1'] = df['Capacity'].cumsum()
df['x0'] = df['x1'].shift(fill_value=0)
alt.Chart(df).mark_rect().encode(
x=alt.X('x0:Q', title='Capacity'),
x2='x1',
y=alt.Y('MarginalCost:Q', title='Marginal Cost'),
color='Technology:N',
tooltip=["Technology", "Capacity", "MarginalCost"]
)
To get the same result without preprocessing of the data, you can use Altair's transform syntax:
df = pd.DataFrame({
'MarginalCost': 100 * np.random.rand(30),
'Capacity': 10 * np.random.rand(30),
'Technology': np.random.choice(['SOLAR', 'THERMAL', 'WIND', 'GAS'], 30)
})
alt.Chart(df).transform_window(
x1='sum(Capacity)',
sort=[alt.SortField('MarginalCost')]
).transform_calculate(
x0='datum.x1 - datum.Capacity'
).mark_rect().encode(
x=alt.X('x0:Q', title='Capacity'),
x2='x1',
y=alt.Y('MarginalCost:Q', title='Marginal Cost'),
color='Technology:N',
tooltip=["Technology", "Capacity", "MarginalCost"]
)
I'm trying to create a heatmap using the Altair lib, but I'm looking to filter my data with a slider for different views. The slider works fine with the standard color only heatmap, but when I try to add text to the boxes to describe the values in each cell I get the javascript error below. (Adding the text to the heatmap works fine without any filter slider.)
import altair as alt
import pandas as pd
source = pd.DataFrame({'year':[2017, 2017, 2018, 2018],
'age':[1, 2, 1, 2],
'y':['a', 'a', 'a', 'a'],
'n':[1, 2, 3, 4]})
slider = alt.binding_range(min=2017, max=2018, step=1)
select_year = alt.selection_single(name="my_year_slider", fields=['year'], bind=slider)
base = alt.Chart(source).add_selection(select_year).transform_filter(select_year)
heatmap = base.mark_rect().encode(
x='age:O',
y='y:O',
color='n:Q')
text = base.mark_text(baseline='middle').encode(
x='age:O',
y='y:O',
text='n:Q')
heatmap + text
This returns Javascript Error: Duplicate signal name: "my_year_slider_tuple"
Because you added the selection to the base chart and then layered two copies of it, the selection is defined twice. The solution is to only define the selection once; something like this:
import altair as alt
import pandas as pd
source = pd.DataFrame({'year':[2017, 2017, 2018, 2018],
'age':[1, 2, 1, 2],
'y':['a', 'a', 'a', 'a'],
'n':[1, 2, 3, 4]})
slider = alt.binding_range(min=2017, max=2018, step=1)
select_year = alt.selection_single(name="my_year_slider", fields=['year'], bind=slider)
base = alt.Chart(source).encode(
x='age:O',
y='y:O',
).transform_filter(select_year)
heatmap = base.mark_rect().encode(color='n:Q').add_selection(select_year)
text = base.mark_text(baseline='middle').encode(text='n:Q')
heatmap + text