I have a bar plot with two static HLine, and would like to add a label to them (or a legend) so that they are defined on the plot. I tried something like:
eq = (
sr2.hvplot(
kind="bar",
groupby ="rl",
dynamic = False,)
* hv.HLine(0.35, name="IA1").opts(color='red')
* hv.HLine(0.2, label="IA2").opts(color='green')
)
but no label comes with the chart.
This answer to a similar question explains that it is not easy, and you need a workaround:
How do I get a full-height vertical line with a legend label in holoviews + bokeh?
You can also use parts of this solution:
https://discourse.holoviz.org/t/horizontal-spikes/117
Maybe the easiest is just to not use hv.HLine() when you would like a legend with your horizontal line, but create a manual line yourself with hv.Curve() instead:
# import libraries
import pandas as pd
import seaborn as sns
import holoviews as hv
import hvplot.pandas
hv.extension('bokeh')
# create sample dataset
df = sns.load_dataset('anscombe')
# create some horizontal lines manually defining start and end point
manual_horizontal_line = hv.Curve([[0, 10], [15, 10]], label='my_own_line')
another_horizontal_line = hv.Curve([[0, 5], [15, 5]], label='another_line')
# create scatterplot
scatter_plot = df.hvplot.scatter(x='x', y='y', groupby='dataset', dynamic=False)
# overlay manual horizontal lines on scatterplot
scatter_plot * manual_horizontal_line * another_horizontal_line
Resulting plot:
If you want text labels instead of a legend, you can use hv.Text:
import holoviews as hv
hv.extension('bokeh')
hv.Scatter(([4,6,8,10],[0.1,0.5,0.01,0.7])) * \
hv.HLine(0.35).opts(color='red') * hv.Text(9, 0.38, "IA1").opts(color='red') * \
hv.HLine(0.20).opts(color='green') * hv.Text(9, 0.23, "IA2").opts(color='green')
Related
When I introduce gaps in between bricks in a plotly annotated heatmap, vertical black lines appear behind the bricks (visible in the gaps). The lines appear to line up with the x-axis labels. Even more oddly, if the x-axis category is numeric, the label "0" will not get a vertical line. I want the vertical lines removed. I've looked at the documentation and can't figure out what these lines are. You'll notice that there are also horizontal vertical and white lines that line up with the x- and y-axis labels. I don't mind those.
import plotly.graph_objs as go
from plotly.figure_factory import create_annotated_heatmap
import numpy as np
fig = go.Figure(create_annotated_heatmap(z = np.arange(12).reshape(3,4),
x = [0,1,2,3],
y = ['A','B','C'],
xgap = 30, ygap = 30
)
)
fig.update_layout(title = 'What are these vertical lines?')
fig.show()
This is not an issue with the standard heatmap:
fig2 = go.Figure(go.Heatmap(z = np.arange(12).reshape(3,4),
x = [0,1,2,3],
y = ['A','B','C'],
xgap = 30, ygap = 30
)
)
fig2.update_layout(title = 'No vertical lines here.')
fig2.show()
Regarding the documentation from help(create_annotated_heatmap), there is a short list of parameters that don't seem to have anything to do with it, and kwargs that go through the standard plotly Heatmap.
The line under the zero is the 'zeroline' while the other lines are the 'gridlines'. They can be removed by setting zeroline=False and showgrid=False in the figure layout.
import plotly.graph_objs as go
from plotly.figure_factory import create_annotated_heatmap
import numpy as np
fig = go.Figure(create_annotated_heatmap(z=np.arange(12).reshape(3,4),
x=[0,1,2,3],
y=['A','B','C'],
xgap=30, ygap=30))
fig.update_layout(xaxis=dict(zeroline=False, showgrid=False),
yaxis=dict(zeroline=False, showgrid=False))
fig.show()
Alternatively, you can change their color to white as in the standard heatmap.
import plotly.graph_objs as go
from plotly.figure_factory import create_annotated_heatmap
import numpy as np
fig = go.Figure(create_annotated_heatmap(z=np.arange(12).reshape(3,4),
x=[0,1,2,3],
y=['A','B','C'],
xgap=30, ygap=30))
fig.update_layout(xaxis=dict(zeroline=False, gridcolor='white'),
yaxis=dict(zeroline=False, gridcolor='white'))
fig.show()
I have several histograms that I succeded to plot using plotly like this:
fig.add_trace(go.Histogram(x=np.array(data[key]), name=self.labels[i]))
I would like to create something like this 3D stacked histogram but with the difference that each 2D histogram inside is a true histogram and not just a hardcoded line (my data is of the form [0.5 0.4 0.5 0.7 0.4] so using Histogram directly is very convenient)
Note that what I am asking is not similar to this and therefore also not the same as this. In the matplotlib example, the data is presented directly in a 2D array so the histogram is the 3rd dimension. In my case, I wanted to feed a function with many already computed histograms.
The snippet below takes care of both binning and formatting of the figure so that it appears as a stacked 3D chart using multiple traces of go.Scatter3D and np.Histogram.
The input is a dataframe with random numbers using np.random.normal(50, 5, size=(300, 4))
We can talk more about the other details if this is something you can use:
Plot 1: Angle 1
Plot 2: Angle 2
Complete code:
# imports
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import plotly.io as pio
pio.renderers.default = 'browser'
# data
np.random.seed(123)
df = pd.DataFrame(np.random.normal(50, 5, size=(300, 4)), columns=list('ABCD'))
# plotly setup
fig=go.Figure()
# data binning and traces
for i, col in enumerate(df.columns):
a0=np.histogram(df[col], bins=10, density=False)[0].tolist()
a0=np.repeat(a0,2).tolist()
a0.insert(0,0)
a0.pop()
a1=np.histogram(df[col], bins=10, density=False)[1].tolist()
a1=np.repeat(a1,2)
fig.add_traces(go.Scatter3d(x=[i]*len(a0), y=a1, z=a0,
mode='lines',
name=col
)
)
fig.show()
Unfortunately you can't use go.Histogram in a 3D space so you should use an alternative way. I used go.Scatter3d and I wanted to use the option to fill line doc but there is an evident bug see
import numpy as np
import plotly.graph_objs as go
# random mat
m = 6
n = 5
mat = np.random.uniform(size=(m,n)).round(1)
# we want to have the number repeated
mat = mat.repeat(2).reshape(m, n*2)
# and finally plot
x = np.arange(2*n)
y = np.ones(2*n)
fig = go.Figure()
for i in range(m):
fig.add_trace(go.Scatter3d(x=x,
y=y*i,
z=mat[i,:],
mode="lines",
# surfaceaxis=1 # bug
)
)
fig.show()
I am trying to represent the data using the bokeh scatter.
Here is my code:
from bokeh.plotting import Scatter, output_file, show import pandas
df=pandas.Dataframe(colume["X","Y"])
df["X"]=[1,2,3,4,5,6,7]
df["Y"]=[23,43,32,12,34,54,33]
p=Scatter(df,x="X",y="Y", title="Day Temperature measurement", xlabel="Tempetature", ylabel="Day")
output_file("File.html")
show(p)
The Output should look like this:
Expected Output
The error is:
ImportError Traceback (most recent call
> last) <ipython-input-14-1730ac6ad003> in <module>
> ----> 1 from bokeh.plotting import Scatter, output_file, show
> 2 import pandas
> 3
> 4 df=pandas.Dataframe(colume["X","Y"])
> 5
ImportError: cannot import name 'Scatter' from 'bokeh.plotting'
(C:\Users\LENOVO\Anaconda3\lib\site-packages\bokeh\plotting__init__.py)
I had also found that the Scatter is no longer maintained now. Is there is any way to use it?
Also which alternative do I have to represent the data same as the Scatter using any another python libraries?
Using older version of Bokeh will resolve this issue?
Scatter (with a capital S) has never been part of bokeh.plotting. It used to be a part of the old bokeh.charts API that was removed several years ago. However, it is not needed at all to create basic scatter plots, since all the glyph methods in bokeh.plotting (e.g circle, square) are all implicitly scatter-type functions to begin with:
from bokeh.plotting import figure, show
import pandas as pd
df = pd.DataFrame({"X" :[1,2,3,4,5,6,7],
"Y": [23,43,32,12,34,54,33]})
p = figure(x_axis_label="Tempetature", y_axis_label="Day",
title="Day Temperature measurement")
p.circle("X", "Y", size=15, source=df)
show(p)
Which yields:
You can also just pass the data directly to circle as in the other answer.
If you want to do fancier things, like map the marker type based on a column there is also a plot.scatter (lower case s) methods on the figure:
from bokeh.plotting import figure, show
from bokeh.sampledata.iris import flowers
from bokeh.transform import factor_cmap, factor_mark
SPECIES = ['setosa', 'versicolor', 'virginica']
MARKERS = ['hex', 'circle_x', 'triangle']
p = figure(title = "Iris Morphology")
p.xaxis.axis_label = 'Petal Length'
p.yaxis.axis_label = 'Sepal Width'
p.scatter("petal_length", "sepal_width", source=flowers, legend_field="species", fill_alpha=0.4, size=12,
marker=factor_mark('species', MARKERS, SPECIES),
color=factor_cmap('species', 'Category10_3', SPECIES))
show(p)
which yields:
If you look up "scatter" in the docs, you'll find
Scatter Markers
To scatter circle markers on a plot, use the circle() method of Figure:
from bokeh.plotting import figure, output_file, show
# output to static HTML file
output_file("line.html")
p = figure(plot_width=400, plot_height=400)
# add a circle renderer with a size, color, and alpha
p.circle([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], size=20, color="navy", alpha=0.5)
# show the results
show(p)
To work with dataframes, just pass in the columns like df.X and df.Y to the x and y args.
from bokeh.plotting import figure, show, output_file
import pandas as pd
df = pd.DataFrame(columns=["X","Y"])
df["X"] = [1,2,3,4,5,6,7]
df["Y"] = [23,43,32,12,34,54,33]
p = figure()
p.scatter(df.X, df.Y, marker="circle")
#from bokeh.io import output_notebook
#output_notebook()
show(p) # or output to a file...
import pandas as pd
from plotnine import *
df=pd.DataFrame({'A':[1,1,1,1],'B':[1,9,5,11],'C':['x','x','y','y'],'D':['a','b','a','b']})
p=ggplot(df,aes('A','B'))
p + geom_bar(stat="identity") + facet_grid('D~C',scales='free_y')
This will draw axes on both the left and right grids. Is there a way to omit the axes on the right grids, such that it is only visible on the left? I know I could use scales="fixed" but I would like them to vary between rows.
It is misleading to have free scales for all panels yet have ticks and labels for only some of them. But if you really want to, you have to drop into Matplotlib for that
import pandas as pd
from plotnine import *
df=pd.DataFrame({'A':[1,1,1,1],'B':[1,9,5,11],'C':['x','x','y','y'],'D':['a','b','a','b']})
p=ggplot(df,aes('A','B'))
p = p + geom_bar(stat="identity") + facet_grid('D~C',scales='free_y')
fig, p = p.draw(return_ggplot=True)
for i, ax in enumerate(p.axs):
if i % 2:
ax.set_yticklabels([])
ax.set_yticks([])
In Bokeh I am able to add a text annotation to each point in my plot programmatically by using LabelSet. Below I give an example for a simple bar plot:
import numpy as np
import pandas as pd
# Make some data
dat = \
(pd.DataFrame({'team':['a','b','c'], 'n_people':[10,5,12]})
.assign(n_people_percent = lambda x: (x['n_people']/np.sum(x['n_people'])*100)
.round(1).astype('string') + '%')
)
dat
# Bar plot with text annotations for every bar
from bkcharts import show, Bar
from bkcharts.attributes import CatAttr
from bokeh.models import (ColumnDataSource, LabelSet)
source_labs = ColumnDataSource(data = dat)
p = Bar(data = dat, label = CatAttr(columns = 'team'), values = 'n_people')
labels = LabelSet(x = 'team', y = 'n_people',
text = 'n_people_percent', source = source_labs)
p.add_layout(labels)
show(p)
However I am not sure how to achieve the same thing with Holoviews. I can make the same bar plot without the annotations very easily:
import holoviews as hv
hv.extension('bokeh')
p = hv.Bars(dat, kdims=['team'], vdims=['n_people'])
p
I can add a single text label adding an Overlay with the hv.Text element
p * hv.Text('a', 11, '37.0%')
But I have no idea how I can label each bar without explicitly calling hv.Text separately for every data point (bar). The problem seems to be that hv.Text does not accept a data argument like other elements e.g. hv.Bars, instead just x and y coordinates. My intuition would be that I should be able to do something like
p * hv.Text(dat, kdims=['team'], vdims=['n_people_percent'])
Any help with this appreciated!
Looks like this commit adds vectorized labels to hv.Labels, so try:
import holoviews as hv
hv.extension('bokeh')
p = hv.Bars(dat, kdims=['team'], vdims=['n_people'])
p * hv.Labels(dat, kdims=['team', 'n_people'], vdims=['n_people_percent'])