Bokeh categorical x-axis alignment on scatter - python

I have a scatter with a categorical x-axis, but my circles don't align with the axis. This code example replicates the issue:
import pandas as pd
from bokeh.plotting import figure
from bokeh.io import output_file, show
from bokeh.models import ColumnDataSource, HoverTool
data = [[1,12],[2,8]]
x_axis_rng = ['VAL 1','VAL 2']
df = pd.DataFrame(data)
df.columns = ['x','y']
chart_data = ColumnDataSource(df)
print(chart_data)
plot = figure(title='Example',
x_axis_label='x',
y_axis_label='y',
x_range=x_axis_rng)
plot.circle('x',
'y',
size=10,
source=chart_data)
hover = HoverTool(tooltips=[('x', '#x'),('y', '#y')])
plot.add_tools(hover)
output_file('test.html')
show(plot)
I read about offsetting the x-axis, but I could not get that to work, and it didn't seem like ti should be necessary in this case?
Any help appreciated!
Rob

You created an axis/range with categorical factors, but are giving circle coordinates as numbers. If you want to position glyphs according to categorical values, the coordinates have to reflect that:
data = [['VAL 1', 12],['VAL 2', 8]]

Related

Colour horizontal bars on a seaborn plot based on the y-axis with seaborn/matplotlib

So before anyone says, I'm not trying to create a horizontal bar plot. I'm trying to make a scatter graph that categorises the different plots based on the y values.
So this is my current code:
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import datetime
import random
f = []
for i in range(10):
f.append(random.randint(60,80))
df = pd.DataFrame({
"Weight": f, "Dates": ["01/12/20", "05/11/20", "12/02/20", "18/09/20", "22/04/20", "19/01/20", "18/02/20", "02/01/20", "28/11/20", "26/03/20"]
}, columns=["Weight", "Dates"])
df["Dates"] = pd.to_datetime(df["Dates"])
df.sort_values(by="Dates", inplace=True, ascending=True)
sns.set_theme(style="dark")
dates = [datetime.datetime.date(x) for x in df["Dates"]]
graph = sns.stripplot(data=df, x=dates, y="Weight")
graph.set_xticklabels(graph.get_xticklabels(), rotation=45)
plt.show()
So this is the current output:
But I want to be able to add some bars so I can categorise the data like (sorry for my poor drawing):
I still want to see the points afterwards, but I don't care about what colour they are.
I don't know if this is possible, but thanks!
EDIT: Answered by tmdavidson in comments.
I would recommend axhspan that was made for this very purpose
bands = [77.5,72.5,67.5,60]
colors = plt.cm.get_cmap('tab10')(range(len(limits)))
for y1,y2,c in zip(bands[0:], bands[1:], colors):
graph.axhspan(ymin=y1, ymax=y2, color=c, zorder=0, alpha=0.5)

Strange Labels on Bokeh Pie/Donut Chart

I'm following the response in this question: Adding labels in pie chart wedge in bokeh
I'm trying to add labels to my Bokeh chart so users can see data values. For some reason, the chart is rendering like this:
I'm not sure why this is happening. I tried commenting out the z variable to see if this made a difference.. it does not.
Here is my code:
import os
import pandas as pd
import pyodbc
from bokeh.plotting import figure, show
from bokeh.io import export_png
from bokeh.models import LabelSet, ColumnDataSource
from bokeh.palettes import Category20
import matplotlib as plt
from math import pi
from bokeh.transform import cumsum
lst = ['On_Time', 'All']
lst2 = [8, 85]
df = pd.DataFrame(list(zip(lst, lst2)),
columns =['Column', 'Value'])
df
df['angle'] = df['value']/df['value'].sum() * 2*pi
df['angle']
df['color'] = ['#084594', '#2171b5']
#z=110*(df['value']/df['value'].sum())
#df['value']=z
#df
p = figure(plot_height=350, title="", toolbar_location=None,
tools="", x_range=(-.5, .5))
p.annular_wedge(x=0, y=1, inner_radius=0.15, outer_radius=0.25, direction="anticlock",
start_angle=cumsum('angle', include_zero=True), end_angle=cumsum('angle'),
line_color="white", fill_color='color', legend='column', source=df)
df["value"] = df['value'].astype(str)
df["value"] = df["value"].str.pad(35, side = "left")
source = ColumnDataSource(df)
labels = LabelSet(x=0, y=1, text='value', level='glyph',
angle=cumsum('angle', include_zero=True), source=source, render_mode='canvas')
p.add_layout(labels)
p.axis.axis_label=None
p.axis.visible=False
p.grid.grid_line_color = None
show(p)
The labels are "underneath" because you have set
level='glyph'
in the call to LabelSet. There is generally not very many reasons to disturb the default render levels. If you remove this, the label will show up "on top" the way annotations such as LabelSet are intended to.
Also note the "str padding" that the other answer used to position the label alignment. That's slightly hacky but serviceable. Font differences between viewers' browser might make small differences. The alternative is to compute actual, exact x, y positions around the circle where you want the labels to go.

Bokeh skip tick labels for categorical data

I'm using Bokeh version 0.12.13.
I have a mixed numerical and categorical data. I only have one categorical data on the x-axis and the rest is numerical. I converted everything to categorical data to do the plotting (might not be the easiest way to achieve my goal). Now my x-axis tick labels are way denser than I need. I would like to space them out every 10th value so the labels are 10,20,...,90,rest
This is what I tried so far:
import pandas as pd
from bokeh.io import show
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure
from bokeh.models.tickers import FixedTicker
# create mock data
n = [str(i) for i in np.arange(1,100)]
n.append('rest')
t = pd.DataFrame([n,list(np.random.randint(25,size=100))]).T
t.columns = ['Number','Value']
t.loc[t['Number']==100,'Number'] = 'Rest'
source = ColumnDataSource(t)
p = figure(plot_width=800, plot_height=400, title="",
x_range=t['Number'].tolist(),toolbar_location=None, tools="")
p.vbar(x='Number', top='Value', width=1, source=source,
line_color="white")
#p.xaxis.ticker = FixedTicker(ticks=[i for i in range(0,100,10)])
show(p)
Ideally, I would like the grid and the x-axis labels to appear every 10th value. Any help on how to get there would be greatly appreciated.
An easier way to do it is to keep the numerical data and use the xaxis.major_label_overrides. Here is the code:
import pandas as pd
from bokeh.io import show
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure
from bokeh.models.tickers import FixedTicker
# create mock data
n = np.arange(1,101)
t = pd.DataFrame([n,list(np.random.randint(25,size=100))]).T
t.columns = ['Number','Value']
source = ColumnDataSource(t)
p = figure(plot_width=800, plot_height=400, title="",
toolbar_location=None, tools="")
p.vbar(x='Number', top='Value', width=1, source=source,
line_color="white")
p.xaxis.major_label_overrides = {100: 'Rest'}
show(p)
You can do this now (in Bokeh 2.2.3) using FuncTickFormatter:
# This prints out only every 10th tick label
p.axis.formatter = FuncTickFormatter(code="""
if (index % 10 == 0)
{
return tick;
}
else
{
return "";
}
""")
Sometimes you might want to do this instead of using numerical axis and major_label_overrides e.g. in a heatmap to get positioning of the content rects in the right place, or if you don't have numerical data at all but still want gaps in the axis labels.

Plotly: How to set a unique color for each series in a scatter plot?

I have a dataframe in pandas with stock tickers as the index and 2 columns 'Active Weights' and 'Weights'. I can make a scatter plot using the code below, but I want to use a unique color for each ticker. How can I do this?
scatter = [go.Scatter(x = df['Active Weight'], y = df['Weight'],
mode='markers', text=df.index)]
plotly.offline.iplot(scatter)
I may be missing something here, but by the sound of it, you're really just looking for a scatter plot such as this:
The code below is set up to follow the default color cycle of plotly. But you can change that to any other colorscheme from plotly express like px.colors.qualitative.Plotly. Or just make your own like ['black', 'yellow'].
Complete code:
# imports
import plotly.express as px
import plotly.graph_objs as go
import pandas as pd
import numpy as np
# data 1
np.random.seed(123)
frame_rows = 40
frame_columns = ['Active Weight', 'Weight']
df= pd.DataFrame(np.random.uniform(-10,10,size=(frame_rows, len(frame_columns))),
index=pd.date_range('1/1/2020', periods=frame_rows),
columns=frame_columns)
df=df.cumsum()+100
df.iloc[0]=100
# color settings
#colors=px.colors.qualitative.plotly
#colors=px.colors.qualitative.Dark24_r
colors = ['black', 'yellow']
# plotly figure
fig = go.Figure()
for i, col in enumerate(df):
fig.add_traces(go.Scatter(x=df.index, y = df[col].values,
mode = 'markers',
name = col,
marker=dict(color=colors[i])))
fig.show()

python bokeh plot how to format axis display

the y axis ticks seem to be formatting numbers like 500000000 to 5.000e+8. Is there a way to control the display so that it displays as 500000000?
using python 2.7, bokeh 0.5.2
i m trying out the timeseries example at bokeh tutorials page
The tutorial plots 'Adj Close' against 'Date' but i'm plotting with 'Volume' against 'Date'
You can also use NumeralTickFormatter as used in the toy plot below. The other possible values in place of '00' are listed here.
import pandas as pd
import numpy as np
from bokeh.plotting import figure, output_file, show
from bokeh.models import NumeralTickFormatter
df = pd.DataFrame(np.random.randint(0, 90000000000, (10,1)), columns=['my_int'])
p = figure(plot_width=700, plot_height=280, y_range=[0,100000000000])
output_file("toy_plot_with_commas")
for index, record in df.iterrows():
p.rect([index], [record['my_int']/2], 0.8, [record['my_int']], fill_color="red", line_color="black")
p.yaxis.formatter=NumeralTickFormatter(format="00")
show(p)
You have to add the option p.left[0].formatter.use_scientific = False to your code. In the timeseries tutorial, it'd be:
p1 = figure(title="Stocks")
p1.line(
AAPL['Date'],
AAPL['Adj Close'],
color='#A6CEE3',
legend='AAPL',
)
p1.left[0].formatter.use_scientific = False # <---- This forces showing 500000000 instead of 5.000e+8 as you want
show(VBox(p1, p2))

Categories