Using Bokeh for a dropdown menu which would create different charts - python

I am trying to create a dropdown interface for my work. My dataset looks like this, it is a random dataset
Now I would like 2 dropdowns say CNN and BBC here. After selecting a channel from dropdown, I would like to select a Topic which would produce a bar chart according to it's value.
I am trying to access just one value initially, but it gives me a blank graph.
from bokeh.plotting import figure
from bokeh.io import output_notebook,show,output_file
p=figure()
import csv
data = [row for row in csv.reader(open('C:/Users/Aishwarya/Documents/books/books_q4/crowd_computing/Bokeh-Python-Visualization-master/interactive/data/data.csv', 'r',encoding="utf8"))]
p.vbar(x=data[1][2], width=0.5, bottom=0,
top=data[1][1], color="firebrick")
#output_notebook()
output_file('1.html')
show(p)

There are probably two issues going on:
The first is that if you are using categorical coordinates on an axis, e.g. "CNN" which it appears you are expecting to use, then you need to etll Bokeh what the categorical range is:
p.figure(x_range=["CNN", ...]) # list all the factors for x_range
If you need to update the axis later you can update the range directly:
p.x_range.factors = [...]
Additionally, as of Bokeh 0.13.0 there is a current open issue that prevents "single" factors from working as coordinates: #6660 Coordinates should accept single categorical values. The upshot is that you will have to put the data in a Bokeh ColumnDataSource explicityl (always an option), or in this case a workaround is also just to pass a single-item list instead:
p.vbar(x=["cnn"], ...)
Here is a complete update of your code, with some fake data put in:
from bokeh.plotting import figure
from bokeh.io import show
p = figure(x_range=["cnn"])
p.vbar(x=["cnn"], width=0.5, bottom=0, top=10, color="firebrick")
show(p)
I would also recommend studying the User's guide section Handling Categorical Data.

Related

Why am I unable to make a plot containing subplots in plotly using a px.scatter plot?

I have been trying to make a figure using plotly that combines multiple figures together. In order to do this, I have been trying to use the make_subplots function, but I have found it very difficult to have the plots added in such a way that they are properly formatted. I can currently make singular plots (as seen directly below):
However, whenever I try to combine these singular plots using make_subplots, I end up with this:
This figure has the subplots set up completely wrong, since I need each of the four subplots to contain data pertaining to the four methods (A, B, C, and D). In other words, I would like to have four subplots that look like my singular plot example above.
I have set up the code in the following way:
for sequence in sequences:
#process for making sequence profile is done here
sequence_df = pd.DataFrame(sequence_profile)
row_number=1
grand_figure = make_subplots(rows=4, cols=1)
#there are four groups per sequence, so the grand figure should have four subplots in total
for group in sequence_df["group"].unique():
figure_df_group = sequence_df[(sequence_df["group"]==group)]
figure_df_group.sort_values("sample", ascending=True, inplace=True)
figure = px.line(figure_df_group, x = figure_df_group["sample"], y = figure_df_group["intensity"], color= figure_df_group["method"])
figure.update_xaxes(title= "sample")
figure.update_traces(mode='markers+lines')
#note: the next line fails, since data must be extracted from the figure, hence why it is commented out
#grand_figure.append_trace(figure, row = row_number, col=1)
figure.update_layout(title_text="{} Profile Plot".format(sequence))
grand_figure.append_trace(figure.data[0], row = row_number, col=1)
row_number+=1
figure.write_image(os.path.join(output_directory+"{}_profile_plot_subplots_in_{}.jpg".format(sequence, group)))
grand_figure.write_image(os.path.join(output_directory+"grand_figure_{}_profile_plot_subplots.jpg".format(sequence)))
I have tried following directions (like for example, here: ValueError: Invalid element(s) received for the 'data' property) but I was unable to get my figures added as is as subplots. At first it seemed like I needed to use the graph object (go) module in plotly (https://plotly.com/python/subplots/), but I would really like to keep the formatting/design of my current singular plot. I just want the plots to be conglomerated in groups of four. However, when I try to add the subplots like I currently do, I need to use the data property of the figure, which causes the design of my scatter plot to be completely messed up. Any help for how I can ameliorate this problem would be great.
Ok, so I found a solution here. Rather than using the make_subplots function, I just instead exported all the figures onto an .html file (Plotly saving multiple plots into a single html) and then converted it into an image (HTML to IMAGE using Python). This isn't exactly the approach I would have preferred to have, but it does work.
UPDATE
I have found that plotly express offers another solution, as the px.line object has the parameter of facet that allows one to set up multiple subplots within their plot. My code is set up like this, and is different from the code above in that the dataframe does not need to be iterated in a for loop based on its groups:
sequence_df = pd.DataFrame(sequence_profile)
figure = px.line(sequence_df, x = sequence_df["sample"], y = sequence_df["intensity"], color= sequence_df["method"], facet_col= sequence_df["group"])
Although it still needs more formatting, my plot now looks like this, which is works much better for my purposes:

Access data in matplotlib histogram axes

Where is the data plotted stored in a matplotlib ax object drawing a histogram?
My scenario:
I've written a function which draws a custom histogram using matplotlib. I am writing a unit test and would like to test whether the plotted data
Ideal behaviour:
import matplotlib.pyplot as plt
f, ax = plt.subplots()
ax.hist(some_data)
data_i_want = ax.plotted_data
I'm not sure what exactly you want to achieve, but the plt.hist(...) function returns the data for the histogram:
histinfo = plt.hist(data)
histinfo[0] #This is the information about the # of instances
histinfo[1] #This is the information about the position of the bins
If you want to get the information from the plot itself at all cost (assuming you have a barplot):
container = ax.containers[0] #https://matplotlib.org/3.1.1/api/container_api.html
for rect in container: #https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.patches.Rectangle.html#matplotlib.patches.Rectangle
print(rect.xy)
You can get the containers, and those containers will contain the information about the plotted bars (rectangles) at the commented URL, you can find every information about that.
Ps.: You probably have to adapt the code for the specific instance, but this is a way to get some information from the plot. (It is possible that there is a better way to do it, this is the best i know)

Dynamic indexes while zooming in Python Bokeh

I am fairly new to Bokeh and try to achieve the following:
I have a dataset with rows containing dates in the format dd-mm-yyyy.
The dates are counted and then plotted.
When zoomed in I want Bokeh to show the indiviudal dates (that works already).
When zoomed out I want Bokeh only to show the months (or years when zoomed out even further). Right know the index gets pretty messy due to individual dates getting closer and closer the more you zoom out.
Is there a way to tell Bokeh to change what is shown in the index depending on how far you zoomed in or out?
Here is my code:
import pandas as pd
from bokeh.charts import TimeSeries
from bokeh.io import output_file, show, gridplot
transactionssent = dict(pd.melt(df,value_vars=['datesent']).groupby('value').size())
transactionssent2 = pd.DataFrame.from_dict(transactionssent, orient= 'index')
transactionssent2.columns = ['Amount']
transactionssent2.index.rename('Date sent', inplace= True)
ts = TimeSeries(transactionssent2, x='index', y='Amount')
ts.xaxis.axis_label = 'Date sent'
If someone knows please point me in the right direction.
Thanks and best regards,
Stefan
What you've described as what you want already sounds like the standard behavior of the built in datetime axis. So, my guess is that TimeSeries is treating your dates as string/categorical values, which would explain why you are not seeing standard datetime axis scaling.
I should add that bokeh.charts (including TimeSeries) has recently been removed to a separate project and also is known to have problems. I would actually discourage it's use at this point. Fortunately, it's also easy to plot timeseries with the bokeh.plotting API, which is stable, well-tested and documented, and in widespread use.
Here is an example to demonstrate:
import datetime
import numpy as np
from bokeh.io import show, output_file
from bokeh.plotting import figure
# some fake data just for this example, Pandas columns work fine too
start = datetime.datetime(2017, 1, 1)
x = np.array([start + datetime.timedelta(hours=i) for i in range(800)])
y = np.sin(np.linspace(0, 2, len(x))) + 0.05 * np.random.random(len(x))
p = figure(x_axis_type="datetime")
p.line(x, y)
output_file("stocks.html")
show(p)
Whose axis looks like this when first displayed:
But like this when zoomed in:
You can also further customize how the dates are formatter by setting various properties on the p.xaxis[0].formatter. For details about available properties, see the reference guide:
http://docs.bokeh.org/en/latest/docs/reference/models/formatters.html#bokeh.models.formatters.DatetimeTickFormatter

Bokeh time series plotting

I have a bunch of time series objects I'm charting with bokeh.charts.TimeSeries data that I want to make into a beautiful plot with a description and title, etc. How can I add a chart to a bokeh.plotting.figure object? I'm using bokeh.layouts.row to organise them, but I want to make it look more professional than a webpage with nothing but a chart.
Is this possible? I was looking at the plotting interface, but I don't see a time series API. Would I just use my pandas.Series objects as the data for the line API?
The old bokeh.charts API, including TimeSeries was deprecated and subsequently removed. You can and should plot time series using the stable bokeh.plotting API. Here is a complete example created with Bokeh 0.13.0:
from bokeh.plotting import figure, show
from bokeh.sampledata.glucose import data
p = figure(x_axis_type="datetime", title="Glocose Range", plot_height=350, plot_width=800)
p.xgrid.grid_line_color=None
p.ygrid.grid_line_alpha=0.5
p.xaxis.axis_label = 'Time'
p.yaxis.axis_label = 'Value'
p.line(week.index, week.glucose)
show(p)

Python Bokeh: Plotting same chart multiple times in gridplot

I'm currently trying to get an overview of plots of data of different dates. To get a good feeling of the data I would like to plot relevant plots next to each other. This means I want to use the same plot multiple times in the gridplot command. However what I noticed is that when i use the same chart multiple times it will only show it once in the final .html file. My first attempt at solving this was to use a copy.deepcopy for the charts, but this gave the following error:
RuntimeError: Cannot get a property value 'label' from a LineGlyph instance before HasProps.__init__
My approach has been as follows:
from bokeh.charts import Line, output_file, show, gridplot
import pandas as pd
output_file('test.html')
plots = []
df = pd.DataFrame([[1,2], [3,1], [2,2]])
print(df)
df.columns = ['x', 'y']
for i in range(10):
plots.append(Line(df, x='x', y='y', title='Forecast: ' + str(i),
plot_width=250, plot_height=250))
plot_matrix = []
for i in range(len(plots)-1, 2, -1):
plot_matrix.append([plots[i-3], plots[i-2], plots[i]])
p = gridplot(plot_matrix)
show(p)
The results of which is a an html page with a grid plot with a lot of missing graphs. Each graph is exactly shown once (instead of the 3 times required), which leads me to think that the gridplot does not like me using the same object multiple times. An obvious solve is to simply create every graph 3 times as a different object, which I will do for now, but not only is this inefficient, it also hurts my eyes when looking at my code. I'm hoping somebody has a more elegant solution for my problem.
EDIT: made code runable
This is not possible. Bokeh plots (or Bokeh objects in general) may not be re-used in layouts.

Categories