I am trying to update the colour of a text on a plot I am creating.
The code looks like this:
plot = figure(
x_axis_location="above", tools="hover,save",
x_range=list(reversed(names)), y_range=names,
tooltips = [('names', '#yname, #xname'), ('count', '#count')]
)
plot.width = 4500
plot.height = 4500
plot.grid.grid_line_color = 'pink'
plot.axis.axis_line_color = 'pink'
plot.axis.major_tick_line_color = 'white'
plot.axis.major_tick_line_color = None
plot.axis.major_label_text_font_size = "22px"
plot.axis.major_label_standoff = 3
plot.xaxis.major_label_orientation = np.pi/2
plot.rect('xname', 'yname', 1.0, 1.0, source=data,
color='colors', alpha='alphas', line_color=None,
hover_line_color='pink', hover_color='colors'
)
save(plot, title='plot.html', filename="plot.html")
According to the documentation it should be pretty simple:
plot.axis.axis_label_text_color = 'white'
However, Bokeh refuses to change the color of any of the axis texts. I'm pretty befuddled on how to get the axis labels to be white or what is going on here?
plot.xaxis.axis_label_text_color = 'white'
plot.yaxis.major_label_text_color = 'white'
It's this. The documentation for this is funky.
Related
I want to keep the labels when you hover, but hide the labels from just appearing over the Sankey as text.
Here is my code:
labels = df_mapping['Name'].to_numpy().tolist() + labels
count_dict = {}
source = []
target = []
value = df_subset['Stuff'].to_numpy().tolist()
index = 0
for x in unique_broad:
count_dict[x] = len(df_mapping.loc[df_mapping['Stuff'] == x])
for key in count_dict:
for i in range(count_dict[key]):
source.append(index)
index += 1
for key in count_dict:
for i in range(count_dict[key]):
target.append(index)
index += 1
number_of_colors = len(source)
color_link = ["#"+''.join([random.choice('0123456789ABCDEF') for j in range(6)])
for i in range(number_of_colors)]
link = dict(source=source, target=target, value=value, color=color_link)
node = dict(label=labels, pad=35, thickness=10)
data = go.Sankey(link=link, node=node)
fig = go.Figure(data)
fig.update_layout(
hovermode = 'x',
title="Sankey for Stuff",
font=dict(size=8, color='white'),
paper_bgcolor='#51504f'
)
return fig
You can make the labels invisible by setting the color of the labels to rgba(0,0,0,0). This ensures that the label will remain in the hovertemplate, but not show up on the nodes.
To do this you can pass textfont=dict(color="rgba(0,0,0,0)", size=1) to go.Sankey such as in the example you used from the Plotly sankey diagram documentation:
import plotly.graph_objects as go
import urllib.request, json
url = 'https://raw.githubusercontent.com/plotly/plotly.js/master/test/image/mocks/sankey_energy.json'
response = urllib.request.urlopen(url)
data = json.loads(response.read())
# override gray link colors with 'source' colors
opacity = 0.4
# change 'magenta' to its 'rgba' value to add opacity
data['data'][0]['node']['color'] = ['rgba(255,0,255, 0.8)' if color == "magenta" else color for color in data['data'][0]['node']['color']]
data['data'][0]['link']['color'] = [data['data'][0]['node']['color'][src].replace("0.8", str(opacity))
for src in data['data'][0]['link']['source']]
fig = go.Figure(data=[go.Sankey(
textfont=dict(color="rgba(0,0,0,0)", size=1),
valueformat = ".0f",
valuesuffix = "TWh",
# Define nodes
node = dict(
pad = 15,
thickness = 15,
line = dict(color = "black", width = 0.5),
label = data['data'][0]['node']['label'],
color = data['data'][0]['node']['color']
),
# Add links
link = dict(
source = data['data'][0]['link']['source'],
target = data['data'][0]['link']['target'],
value = data['data'][0]['link']['value'],
label = data['data'][0]['link']['label'],
color = data['data'][0]['link']['color']
))])
fig.update_layout(title_text="Energy forecast for 2050<br>Source: Department of Energy & Climate Change, Tom Counsell via <a href='https://bost.ocks.org/mike/sankey/'>Mike Bostock</a>",
font_size=10)
fig.show()
You get the following:
Here is my HeatMap plot function :
def plot_heatmap(alphas, k_list, title_prefix="", years=["Y2013", "Y2014"]):
data = [
Heatmap(
name="",
z= alphas,
x=years,
y=k_list,
hoverongaps = False,
zauto=False,
zmin=zmin,
zmax=zmax,
colorscale= color_custom,
colorbar = dict(
title="Alpha Value",
thicknessmode="pixels",
thickness=50,
yanchor="top",
y=1,
len=480,
lenmode="pixels",
ticks="outside",
dtick=zmax / 10
)
)
]
fig = Figure(
data=data,
layout=Layout(
width = 640,
height = round(60 * len(k_list)) if round(60 * len(k_list)) > 640 else 640,
# autosize = True,
title=title_prefix + " | HeatMap : alphas",
)
)
fig.data[0]['hoverinfo'] = 'all'
fig['layout']['yaxis']['scaleanchor']='x'
iplot(fig)
Right now my work around is height = round(60 * len(k_list)) if round(60 * len(k_list)) > 640 else 640, in the *Layout object.
Result is like this : (I don't want to see the grey parts on the plot, how can I do that)
I think what's happening here is that for some reason plotly takes your years input to be numerical, you can make this variable explicitly categorical by adding
fig['layout']['xaxis']['type'] = 'category'
I meet the same problem while setting fixed aspect ratio to figure.
Found the answer here
https://plotly.com/python/axes/#fixed-ratio-axes-with-compressed-domain
fig['layout']['xaxis']['constrain'] = 'domain'
do this :
fig.update_xaxes(tickson='boundaries')
fig.update_yaxes(tickson='boundaries')
Which line of this code:
# Take credit amount values into a list
young = df['Credit_amount'].loc[df['Age_Group'] == 'Young'].values.tolist()
young_adults = df['Credit_amount'].loc[df['Age_Group'] == 'Young Adults'].values.tolist()
senior = df['Credit_amount'].loc[df['Age_Group'] == 'Senior'].values.tolist()
elder_credit = df['Credit_amount'].loc[df['Age_Group'] == 'Elder'].values.tolist()
# Create the box plots by age category
young_credit = go.Box(
y = young,
name = "Young",
jitter = 0.3,
pointpos = -1.8,
boxpoints = 'all',
marker = dict(
color = 'rgb(150, 198, 109)'),
line = dict(
color = 'rgb(111, 200, 37)')
)
young_adults_credit = go.Box(
y = young_adults,
name = "Young Adults",
jitter = 0.3,
pointpos = -1.8,
boxpoints = 'all',
marker = dict(
color = 'rgb(124, 236, 212)'),
line = dict(
color = 'rgb(38, 214, 177)')
)
senior_credit = go.Box(
y = senior,
name = "Seniors",
jitter = 0.3,
pointpos = -1.8,
boxpoints = 'all',
marker = dict(
color = 'rgb(241, 93, 93)'),
line = dict(
color = 'rgb(225, 44, 44)')
)
elder_credit = go.Box(
y = elder_credit,
name = "Elders",
jitter = 0.3,
pointpos = -1.8,
boxpoints = 'all',
marker = dict(
color = 'rgb(180, 121, 72)'),
line = dict(
color = 'rgb(115, 77, 46)')
)
data = [young_credit, young_adults_credit, senior_credit, elder_credit]
layout = dict(
title="Credit Amount by Age Group Segment",
xaxis = dict(title="Age Group"),
yaxis= dict(title="Credit Amount")
)
fig = dict(data=data, layout=layout)
iplot(fig, filename="Box Plot")
concerns the fragments marked in the picture below, I would like to remove those fragments from the chart and which lines of code I have to remove to achieve this goal.
I will be really thankfull for all clear answers because I can not find line of code to remove this fragments of plot.
Thank you so much!
If you Want to totally remove the points, you should remove parameters in each go.Box:
jitter = 0.3,
pointpos = -1.8,
boxpoints = 'all'
From plot.ly/python/box-plots/: With the points argument, display underlying data points with either all points (all), outliers only (outliers, default), or none of them (False).
Plot 1: boxpoints = False
Plot 2: boxpoints = 'all'
I got the same issue and can still not find the fix. The github issue is still open which Ahmed mentioned (https://github.com/plotly/plotly.js/issues/277).
Although, you can use a visible work around. It does not fix the problem! But for the vision it is fixed.
You van marker=dict(opacity=0) which makes the points invisible. When you hover over them, they are still there.
I'm having problems plotting groupings of countries on a world map using Bokeh in combination with the geopandas package. What I want to do is colour each country in a group with a certain colour on the map. The groupings are saved in the dataframe "dataset", which is merged with the geopandas geographical info, converted to json and fed to the mapping functions. (Code below.)
The interesting thing is that no error is generated, Bokeh even reports that "BokehJS 1.4.0 successfully loaded", but no chart is shown.
I am quite convinced that the problem is with my implementation of the CategoricalColorMapper. This is evident since if I change the color mapper to to linear color mapper, the code works perfectly.
This code does not work:
from bokeh.palettes import viridis
from bokeh.models import FactorRange
dataset = gdf.merge(dataset, left_on = 'country', right_on = 'location', how = 'left')
#gdf is geopandas geo info dataframe
#Read data to json
dataset_json = json.loads(dataset.to_json())
#Convert to str like object
dataset_json_data = json.dumps(dataset_json)
#Input GeoJSON source that contains features for plotting.
geosource = GeoJSONDataSource(geojson = dataset_json_data)
catValues=list(dataset["val"].dropna().unique().astype("str"))
palette=viridis(len(catValues))
print("Palette len:", len(palette))
print("Factors:", len(catValues))
print(dataset)
color_mapper = CategoricalColorMapper(palette = palette , factors=catValues)
#Create figure object.
p = figure(title = title_string, plot_height = 600 , plot_width = 950, toolbar_location = None)
p.xgrid.grid_line_color = None
p.ygrid.grid_line_color = None
#Add patch renderer to figure.
p.patches('xs','ys', source = geosource, fill_color = {'field' :'val', 'transform' : color_mapper})
#Display figure inline in Jupyter Notebook.
output_notebook()
#Display figure.
show(p)
Calling the function prints the following, but non map is shown. The number of colors and categories seems fine to me?
Palette len: 118
Factors: 118
BokehJS 1.4.0 successfully loaded.
Replacing only the color mapper works perfectly. This code works:
def plot_map(dataset, title_string = ""):
dataset = gdf.merge(dataset, left_on = 'country', right_on = 'location', how = 'left')
#Read data to json
dataset_json = json.loads(dataset.to_json())
#Convert to str like object
dataset_json_data = json.dumps(dataset_json)
#Input GeoJSON source that contains features for plotting.
geosource = GeoJSONDataSource(geojson = dataset_json_data)
#Define a sequential multi-hue color palette.
palette = brewer['OrRd'][7]
#Reverse color order so that dark blue is highest obesity.
palette = palette[::-1]
#Instantiate LinearColorMapper that linearly maps numbers in a range, into a sequence of colors.
color_mapper = LinearColorMapper(palette = palette, low = dataset.val.min(), high = dataset.val.max())
#Define custom tick labels for color bar.
#tick_labels = {'0': '0', '1': '1', '2':'2', '3':'3', '4':'4', '5':'5', '6':'6','7':'7'}
#Create color bar.
color_bar = ColorBar(color_mapper=color_mapper, label_standoff=7,width = 500, height = 20,
border_line_color=None,location = (0,0), orientation = 'horizontal', major_label_overrides = tick_labels)
#Create figure object.
p = figure(title = title_string, plot_height = 600 , plot_width = 950, toolbar_location = None)
p.xgrid.grid_line_color = None
p.ygrid.grid_line_color = None
#Add patch renderer to figure.
p.patches('xs','ys', source = geosource, fill_color = {'field' :'val', 'transform' : color_mapper},
line_color = 'black', line_width = 0.25, fill_alpha = 1)
#Specify figure layout.
p.add_layout(color_bar, 'below')
#Display figure inline in Jupyter Notebook.
output_notebook()
#Display figure.
show(p)
I'm trying to create slider that as you drag the slider, the portion of the graph that is shown is only what is on the slider. For example, if you look at my graph below, if the slider was set to 1990, you would only see the lines from 1990 to 2016. I found a working example with plotly but I wanted to see if it could be done with Bokeh.
This is my code so far:
p = figure(width = 900, height = 450)
p.xaxis.axis_label = 'Year'
p.yaxis.axis_label = 'Aggregated Number of Degrees in Education'
source = ColumnDataSource(df)
fill_source = ColumnDataSource(data=dict(x=[],y=[]))
# Create objects for each line that will be plotted
stem = p.line('year', 'stem', line_color='#8dd3c7', line_width=3, source=source)
stem = p.circle('year', 'stem', line_color='#8dd3c7', line_width=3, source=source)
sped = p.line('year', 'sped', line_color='#fdb462', line_width=3, source=source)
elem = p.line('year', 'elem', line_color='#bebada', line_width=3, source=source)
elem = p.square('year', 'elem', line_color='#bebada', line_width=3, source=source)
other = p.line('year', 'other', line_color='#fb8072', line_width=4, source=source)
aggtotal = p.line('year', 'aggtotal', line_dash=[4,4,], line_color='#80b1d3', line_width=3, source=source)
yaxis = p.select(dict(type=Axis, layout="left"))[0]
yaxis.formatter.use_scientific = False
legend = Legend(items=[("STEM", [stem])
,("SPED" , [sped])
,("Elementary", [elem])
,("Other", [other])
,("Total Education Graduates", [aggtotal])], location=(0, 0))
p.add_tools(HoverTool(tooltips=[("Date", "#year")]))
p.add_layout(legend, 'right')
callback_test = CustomJS(args=dict(source=source,fill_source=fill_source), code="""
var data = source.data;
var fill_data = fill_source.data;
var s_val = cb_obj.value;
fill_data['x']=[];
fill_data['y']=[];
for (i = 0; i < s_val; i++) {
fill_data['y'][i].push(data['y'][i]);
fill_data['x'][i].push(data['x'][i]);
}
fill_source.trigger('change');
""")
sped_slider = Slider(start=1984, end= 2016, value=1, step=1,title="Year",callback=callback_test)
callback_test.args["sped"] = sped_slider
layout = row(p,widgetbox(sped_slider))
This renders a slider but it doesn't do anything and I'm not sure where to go from here.
There are some issues with your callback code. For example:
you loop i from 0 to s_val (which may be 1990) which is not consistent with the length of your arrays.
your glyphs references columns 'stem', etc... but the fill_source has columns 'x' and 'y'
your glyphs reference source as a source but you change and trigger event on fill_source.
All that could probably be fixed but there's a much easier way, adjust the range in the callback. E.g. replace your callback by this:
x_range = p.x_range
callback_test = CustomJS(args=dict(x_range=x_range), code="""
var start = cb_obj.value;
x_range.start = start;
x_range.change.emit();
""")
Note the change to the event trigger. Your version would work but I think it's going to be deprecated.
Also:
this line callback_test.args["sped"] = sped_slider is not necessary
you could add toolbar_location='above' in figure(...) to avoid rendering conflict with the legend
you're still going to have a layout problem between the slider and the legend which can be fixed in different ways (slider under or put the slider and the legend in a column before adding to the right of the plot, etc...)