Bokeh - multiple figures on same page - django

I have a need to have 2 bokeh figures on a page. I need them to separate from each other. Currently I can have only one figure (with multiple plots using grid/rows/columns) but not with multiple figures.

See the documentation on how to append figures in rows or columns.
For an example on how to plot figures in the same row see
from bokeh.io import output_file, show
from bokeh.layouts import row
from bokeh.plotting import figure
output_file("layout.html")
x = list(range(11))
y0 = x
y1 = [10 - i for i in x]
y2 = [abs(i - 5) for i in x]
# create a new plot
s1 = figure(plot_width=250, plot_height=250, title=None)
s1.circle(x, y0, size=10, color="navy", alpha=0.5)
# create another one
s2 = figure(plot_width=250, plot_height=250, title=None)
s2.triangle(x, y1, size=10, color="firebrick", alpha=0.5)
# create and another
s3 = figure(plot_width=250, plot_height=250, title=None)
s3.square(x, y2, size=10, color="olive", alpha=0.5)
# put the results in a row
show(row(s1, s2, s3))
Likewise, you could put the results in a column using
show(column(s1, s2, s3))
Of course, you can combine the two to create a grid, so if you have a list of figures, say graphs, you could do something like
cols = []
row_num = 3
for i in range(0, len(graphs), row_num):
r = row(graphs[i: i + row_num])
cols.append(r)
show(column(cols))

Try having different script and div tag for the plots.
Example using Django:
from django.shortcuts import render_to_response
from bokeh.plotting import figure,output_file,show
from bokeh.embed import components
import random
**Define your View function this way**
def plot(request,*args,**kwargs):
PLOT_OPTIONS = dict(plot_width=800, plot_height=300)
SCATTER_OPTIONS = dict(size=12, alpha=0.5)
data = lambda: [random.choice([i for i in range(100)]) for r in range(10)]
red = figure(sizing_mode='scale_width', tools='pan', **PLOT_OPTIONS)
red.scatter(data(), data(), color="red", **SCATTER_OPTIONS)
blue = figure(sizing_mode='fixed', tools='pan', **PLOT_OPTIONS)
blue.scatter(data(), data(), color="blue", **SCATTER_OPTIONS)
green = figure(sizing_mode='scale_width', tools='pan', **PLOT_OPTIONS)
green.scatter(data(), data(), color="green", **SCATTER_OPTIONS)
script1, div1 = components(red)
script2, div2 = components(blue)
script3, div3 = components(green)
context = {'script1':script1,'div1':div1,'script2':script2,'div2':div2,'script3':script3,'div3':div3}
return render_to_response('graph.html',context=context)
**Then your Template should look like this:**
first load the dependencies inside the HEAD tag
<link href="http://cdn.bokeh.org/bokeh/release/bokeh-1.3.4.min.css" rel="stylesheet" type="text/css">
<link href="http://cdn.bokeh.org/bokeh/release/bokeh-widgets-1.3.4.min.css" rel="stylesheet" type="text/css">
<script src="http://cdn.bokeh.org/bokeh/release/bokeh-1.3.4.min.js"></script>
<script src="http://cdn.bokeh.org/bokeh/release/bokeh-widgets-1.3.4.min.js"></script>
{{ script1 | safe }}
{{ script2 | safe }}
{{ script3 | safe }}
Inside the body or wherever you want to display the plots
<div> {{ div1 | safe }} </div>
<div> {{ div2 | safe }} </div>
<div> {{ div3 | safe }} </div>

from bokeh.plotting import figure
from bokeh.embed import file_html
from bokeh.resources import CDN
x = [1,4,6]
y = [4,6,9]
plot = figure(width=300, height=300)
plot.line(x, y)
html1 = file_html(plot, CDN, 'my plot')
This way you can create multiple plots and insert them using standard jinja2 syntax
Like:
<h1> First plot </h1>
{{ html1 }}
<h1> Second plot </h1>
{{ html2 }}
More information you can find here
Also you can try using Tab Panels

Related

Django Html and plots to PDF

I am using the Django application to generate some graphs using Plotly so
I tried to generate a pdf file of the following plots of
my HTML page is:
<center> <p class="lead"><strong><em> All Plots </em></strong></p></center>
<div class="row">
<div class="col-md-6" >
{{ sunburst|safe}}
</div>
<div class="col-md-6" >
{{ sunburst2|safe}}
</div>
</div>
and my url.py :
path('export_pdf/', views.export_pdf, name='export_pdf'),
and the views.py;
def export_pdf(request):
df = px.data.gapminder().query("country=='Canada'")
fig = px.line(df, x="year", y="lifeExp", title='Life expectancy in Canada')
df2 = px.data.gapminder().query("continent=='Oceania'")
fig2 = px.line(df2, x="year", y="lifeExp", color='country')
chart = fig.to_html()
chart2 = fig2.to_html()
context= {
'sunburst':chart,
'sunburst2':chart2,
}
return render(request, 'Home/Reporting/part_pdf.html',context)
I tried some code from this page but I can't generate the file any help?
https://plotly.com/python/v3/pdf-reports/
are there any other methods for doing that (html page with plots and tables)?
All my best
I searched high and low and could not find anything. Hacked together a solution that worked very well for me:
import plotly.graph_objs as go
import plotly.io as pio
labels = ['Alice','Bob','Carl']
vals = [2,5,4]
data = [go.Bar(x=vals, y=labels, orientation='h')]
layout = go.Layout(margin_pad=10)
fig = go.Figure(data=data,layout=layout)
svg = pio.to_image(fig, format="svg")
context["svg"] = svg.decode("utf-8")
and then in the template something like this
<div>
{{ svg|safe }}
</div>
The first solution was the one you referenced as well. The quality was very bad though. This solution is a lot more crisp.
I used it with weasyprint, but I'm sure it will work with other solutions as well.

Trying to pass Bokeh Wordcloud2 to Django Templete

I am working on passing data visualization using Bokeh library to Django project. I am able to pass standard Bokeh visualization but when I am trying to used an external wordcloud2 library my project crashes. I receive message "A server error occurred. Please contact the administrator." Any ideas would be much appreciated?!
Imports used:
from bokeh.plotting import figure, output_file, show
from bokeh.embed import components
# Bokeh WordCloud
from bokeh.io import show
from bokeh.models import ColumnDataSource
from bokeh_wordcloud2 import WordCloud2
views.py
Code below is working:
x = [1,2,3,4,5]
y = [1,2,3,4,5]
plot = figure(title='Line Graph', x_axis_label='x_stuff', y_axis_label='y_stuff', plot_width=400, plot_height=400)
plot.line(x, y, line_width=2)
#auto_div - dynamically generates div elemetent
script, auto_div = components(plot)
Based on example above, I am trying to pass values for wordcloud in very similar way but it is not working:
titles = ['lorem ipsum dolor sit amet',
'consectetur adipiscing elit',
'cras iaculis semper odio',
'eu posuere urna vulputate sed']
test1 = ColumnDataSource({'titles':titles})
wordcloud = WordCloud2(source=test1,wordCol="titles",color=['pink','blue','green'])
script, auto_div = components(wordcloud)
context = {
'script':script,
'auto_div':auto_div,
}
return render(request, 'two_boxes/home.html', context)
Based on my testing it seems like the problem occurs at script, auto_div = components(wordcloud) statement
home.html
<head>
{% block bokeh_dependencies %}
<link href="http://cdn.bokeh.org/bokeh/release/bokeh-1.3.4.min.css" rel=”stylesheet” type=”text/css”>
<link href="http://cdn.bokeh.org/bokeh/release/bokeh-widgets-1.3.4.min.css" rel=”stylesheet” type=”text/css”>
<script src="http://cdn.bokeh.org/bokeh/release/bokeh-1.3.4.min.js"></script>
<script src="http://cdn.bokeh.org/bokeh/release/bokeh-widgets-1.3.4.min.js"></script>
{{ script | safe }}
{% endblock %}
</head>
<body>
<h1>Bokeh Stuff</h1>
{{ auto_div | safe }}
</body>
Resources:
https://hackernoon.com/integrating-bokeh-visualisations-into-django-projects-a1c01a16b67a
https://github.com/joranbeasley/bokeh_wordcloud2

How do I consider an element's ancestor when parsing with BeautifulSoup?

I'm using Python 3.7, Django, and BeautifulSoup. I am currnently looking for "span" elements in my document that contain the text "Review". I do so like this
html = urllib2.urlopen(req, timeout=settings.SOCKET_TIMEOUT_IN_SECONDS).read()
my_soup = BeautifulSoup(html, features="html.parser")
rev_elts = my_soup.findAll("span", text=re.compile("Review"))
for rev_elt in rev_elts:
... processing
but I'd like to add a wrinkle to where I don't want to consider those elements if they have a DIV ancestor with the class "child". So for example, I don't want to consider something like this
<div class="child">
<p>
<span class="s">Reviews</span>
...
</p>
</div>
How can I adjust my search to take this into account?
If you are using BeautifulSoup 4.7+, it has some improved CSS selector support. It handles many selectors up through CSS level 4 and a couple of custom ones like :contains(). In addition to all of that, it handles complex selectors in pseudo-classes like :not() which level 4 was supposed to handle, but they've recently pushed that support out to CSS level 5 selector support.
So in this example we will use the custom :contains selector to search for spans which contain the text Review. In addition, we will say we don't want it to match div.class span.
from bs4 import BeautifulSoup
html = """
<div>
<p><span>Review: Let's find this</span></p>
</div>
<div class="child">
<p><span>Review: Do not want this</span></p>
</div>
"""
soup = BeautifulSoup(html, features="html.parser")
spans = soup.select('span:contains(Review):not(div.child span)')
print(spans)
Output
[<span>Review: Let's find this</span>]
Depending on your case, maybe :contains isn't robust enough. In that case, you can still do something similar. Soup Sieve is the underlying library included with Beautiful Soup 4.7+, and you can import it directly to filter your regular expression returns:
from bs4 import BeautifulSoup
import soupsieve as sv
import re
html = """
<div>
<p><span>Review: Let's find this</span></p>
</div>
<div class="child">
<p><span>Review: Do not want this</span></p>
</div>
"""
soup = BeautifulSoup(html, features="html.parser")
spans = soup.find_all("span", text=re.compile("Review"))
spans = sv.filter(':not(div.child span)', spans)
print(spans)
Output
[<span>Review: Let's find this</span>]
CSS selector is the way to go in this case as #facelessuser has answered. But just in case you are wondering this can be done without using css selector as well.
You can iterate over all of an element’s parents with .parents. You could define a custom filter function which checks if any of the parents has a class of "child" and return True otherwise (in addition to all your other conditions).
from bs4 import BeautifulSoup, Tag
html="""
<div class="child">
<p><span id="1">Review</span></p>
</div>
<div>
<p><span id="2">Review</span></p>
</div>
"""
soup=BeautifulSoup(html,'html.parser')
def my_func(item):
if isinstance(item,Tag) and item.name=='span' and 'Review' in item.text:
for parent in item.parents:
if parent.has_attr('class'):
if 'child' in parent.get('class'):
return False
return True
my_spans=soup.find_all(my_func)
print(my_spans)
Outputs:
[<span id="2">Review</span>]

How to remove strings which does not belong to HTML tag in an HTML file

I have an HTML file which contains;
<html>
<head></head>
<body><p>thanks god its Friday</p></body>
</html>a& ca-79069608498"
<div class="cont" id="aka"></div>
<footer>
<div class="tent"><div class="cont"></div>
<h2><img alt="dscdsc" height="18" src="dsc.png" srcset="" width="116"/></h2>
</div>
</footer>
ipt> (window.NORLQ=window.NORLQ||[]).push(function(){var
ns,i,p,img;ns=document.getElementsByTagName('noscript');for(i=0;i<ns.len)>-1){img=document.createEleight'));img.setAttribute('alt',p.getAttribute('data-alt'));p.parentNode.replaceChild(img,p);}}});/*]]>*/</script><script>(window.RLQ=window.RLQ||[]).push(function(
Name of the file is a.html
I want to remove everything after </html> in the HTML file using Python 2.7 but all the strings after HTML tag do not belong to a tag and some of them just noisy so I could not do it using Beautifulsoup and I don't know if it's smart to use regex for HTML file.
How can I remove strings after </html> and write to HTML file?
with regex
import re
...
newhtml = re.sub('</html>[\s\S.]+', '</html>', oldhtml)
a = open(path, "r").read()
b = a.split('</html>', 1)[0]
open(path, 'w').write(b)
Python has a module called HTMLParser for dealing this sort of problem.
While the proposed regexpr seem to handle your problem well for now, it can be problematic to debug when something went wrong when it cant handle edge cases HTML.
Therefore I am proposing a HTMLParser solution which gives you more control on its parsing behaviour.
Example:
from HTMLParser import HTMLParser
class MyHTMLParser(HTMLParser):
buffer = ""
end_of_html = False
def get_html(self):
return self.buffer
def handle_starttag(self, tag, attrs):
if not self.end_of_html:
value = "<" + tag
for attr in attrs:
value += attr[0] + "=" + attr[1]
self.buffer += value + ">"
def handle_data(self, data):
if not self.end_of_html:
self.buffer += data
def handle_endtag(self, tag):
if not self.end_of_html:
self.buffer += "</" + tag + ">"
if tag == "html":
self.end_of_html = True
parser = MyHTMLParser();
parser.feed("""<html>
</div>
<head></head>
<body><p>thanks god its Friday</p></body>
</html>a& ca-79069608498"
<div class="cont" id="aka"></div>
<footer>
<div class="tent"><div class="cont"></div>
<h2><img alt="dscdsc" height="18" src="dsc.png" srcset="" width="116"/></h2>
</div>
</footer>
ipt> (window.NORLQ=window.NORLQ||[]).push(function(){var
ns,i,p,img;ns=document.getElementsByTagName('noscript');for(i=0;i<ns.len)>-1){img=document.createEleight'));img.setAttribute('alt',p.getAttribute('data-alt'));p.parentNode.replaceChild(img,p);}}});/*]]>*/</script><script>(window.RLQ=window.RLQ||[]).push(function(
""")
print parser.get_html()
Output:
<html>
</div>
<head></head>
<body><p>thanks god its Friday</p></body>
</html>

Python, BeautifulSoup code seems to work, but no data in the CSV?

I have about 500 html files in a directory, and I want to extract data from them and save the results in a CSV.
The code I'm using doesn't get any error messages, and seems to be scanning all the files, but the resulting CSV is empty except for the top row.
I'm fairly new to python and I'm clearly doing something wrong. I hope someone out there can help!
from bs4 import BeautifulSoup
import csv
import urllib2
import os
def processData( pageFile ):
f = open(pageFile, "r")
page = f.read()
f.close()
soup = BeautifulSoup(page)
metaData = soup.find_all('div class="item_details"')
priceData = soup.find_all('div class="price_big"')
# define where we will store info
vendors = []
shipsfroms = []
shipstos = []
prices = []
for html in metaData:
text = BeautifulSoup(str(html).strip()).get_text().encode("utf-8").replace("\n", "")
vendors.append(text.split("vendor:")[1].split("ships from:")[0].strip())
shipsfroms.append(text.split("ships from:")[1].split("ships to:")[0].strip())
shipstos.append(text.split("ships to:")[1].strip())
for price in priceData:
prices.append(BeautifulSoup(str(price)).get_text().encode("utf-8").strip())
csvfile = open('drugs.csv', 'ab')
writer = csv.writer(csvfile)
for shipsto, shipsfrom, vendor, price in zip(shipstos, shipsfroms, vendors, prices):
writer.writerow([shipsto, shipsfrom, vendor, price])
csvfile.close()
dir = "drugs"
csvFile = "drugs.csv"
csvfile = open(csvFile, 'wb')
writer = csv.writer(csvfile)
writer.writerow(["Vendors", "ShipsTo", "ShipsFrom", "Prices"])
csvfile.close()
fileList = os.listdir(dir)
totalLen = len(fileList)
count = 1
for htmlFile in fileList:
path = os.path.join(dir, htmlFile)
processData(path)
print "Processed '" + path + "'(" + str(count) + "/" + str(totalLen) + ")..."
count = count + 1
I suspect that I'm telling BS to look in the wrong part of the html code? But I can't see what it should be instead. Here's an excerpt of the html code with the info I need:
</div>
<div class="item" style="overflow: hidden;">
<div class="item_image" style="width: 180px; height: 125px;" id="image_255"></div>
<div class="item_body">
<div class="item_title">200mg High Quality DMT</div>
<div class="item_details">
vendor: ringo deathstarr<br>
ships from: United States<br>
ships to: Worldwide
</div>
</div>
<div class="item_price">
<div class="price_big">฿0.031052</div>
add to cart
</div>
</div>
Disclaimer: the information is for a research project about online drug trade.
The way you are doing is wrong. Here is a working example:
metaData = soup.find_all("div", {"class":"item_details"})
priceData = soup.find_all("div", {"class":"price_big"})
You can find more about it's usage from here.