Is it possibile to optimize Plotly carpet rendering? - python

I am working on a plotter for Finite Element Method solutions. I decided to use the Plotly library because of the carpet plots. I have my data to plot and this is my result:
Flow over NACA0012
Each element is represented as a Carpet, and for each carpet the solution is shown as a Countourcarpet. Everything is in place, but the rendering is too slow and the interactive interface is therefore nearly useless. Is there a way to enhance the performance of the rendering? I have read about different renderers in Plotly, but the plot just does not open. Is there a a way to speed up the rendering? Surely I will have to manage larger dataset. In this example I am using 740 carpets.
These are the Contourcarpet settings:
fig.add_trace(go.Contourcarpet(
a = a,
b = b,
z = u, # Sution correspondent at (a,b) parametric location
showlegend = showLegendFlag,
name = "Density",
legendgroup = "Density",
autocolorscale = False,
colorscale = "Inferno",
autocontour = False,
carpet = str(e), # The carpet on which to plot the solution is
# referenced as a string number
contours = dict(
start = start1, # Min value
end = end1, # Max value
size = abs(end1-start1) / countour_number, # Plot colour discretization
showlines = False
),
line = dict(
smoothing = 0
),
colorbar = dict(
len = 0.4,
y = 0.25
)
))
And these are the layout settings:
fig.update_layout(
plot_bgcolor="#FFF",
yaxis = dict(
zeroline = False,
range = [-1.800,1.800],
showgrid = False
),
dragmode = "pan",
height = 700,
xaxis = dict(
zeroline = False,
scaleratio = 1,
scaleanchor = 'y',
range = [-3.800,3.800],
showgrid = False
),
title = "Flow over NACA 0012",
hovermode = "closest",
margin = dict(
r = 80,
b = 40,
l = 40,
t = 80
),
width = 900
)
fig.show()

Related

Plotly slider function only updates one argument

I am trying to build a heatmap with annotations and a title. This title and the annotations should update when the slider is moved. I get this to work, but only for one of the two arguments at the same time. The argument that is at index [1] is being updated, but the other one isn't
Below is a snippet of my code and the error happens in the step for loop:
from plotly.offline import init_notebook_mode, iplot
import plotly.graph_objs as go
import numpy as np
# initialize notebook for offline plotting
init_notebook_mode()
# Set initial slider/title index
start_index = 0
# Build all traces with visible=False
timestep = 5
#df2 = np.random.rand(18,365)*70
data = [go.Heatmap(
visible = False,
x = ['P', 'C', 'S'],
y = [11,10,9,8,7,6],
z = df.iloc[:18,[step]].to_numpy().reshape(6,3),
# z = df2[:,step].reshape(6,3),
zmin = 0,
zmax = 70)
for step in np.arange(0, len(df2.transpose())-1, timestep)
]
# Make initial trace visible
data[start_index]['visible'] = True
# Build slider steps
steps = []
for i in range(len(data)):
step = dict(
# Update method allows us to update both trace and layout properties
method = 'update',
args = [
# Make the ith trace visible
{'visible': [t == i for t in range(len(data))]},
{'annotations' : [dict(
x = x,
y = y,
text = str(round(df.iloc[:18,[i]].to_numpy().reshape(6,3)[-y+11,x],1)),
# text = str(df2[:,i].reshape(6,3)[-y+11,x]),
showarrow = False)
for x in range(3) for y in range(6,12)]},
{'title.text': str(df.columns[i*timestep])},]
)
steps.append(step)
# Build sliders
sliders = [go.layout.Slider(
active = start_index,
currentvalue = {"prefix": "Timestep: "},
pad = {"t": 72},
steps = steps
)]
layout = go.Layout(
sliders=sliders,
title={'text': str(df.columns[start_index])},
yaxis = dict(
tickmode = 'array',
tickvals = [11,10,9,8,7,6],
ticktext = ['06','07','08','09','10','11']
),
annotations = steps[start_index]['args'][1]['annotations']
)
fig = go.Figure(
data=data,
layout=layout)
iplot(fig)
I found the problem. Apparently you need to specify 'annotations' and 'title.text in the same dictionary, instead of seperate ones. The code should thus be changed to:
{'annotations' : [dict(
x = x,
y = y,
text = str(round(df.iloc[:18,[i]].to_numpy().reshape(6,3)[-y+11,x],1)),
# text = str(df2[:,i].reshape(6,3)[-y+11,x]),
showarrow = False)
for x in range(3) for y in range(6,12)],
'title.text': str(df.columns[i*timestep])}

Choropleth map in Plotly: colours not showing correctly

Trying to make a choropleth map in plotly using some data I have in a csv file. Have created the following map:
my choromap
This isn't a correct display of the data however. Here is an excerpt of my csv file:
China,2447
...
Trinidad And Tobago,2
Turkey,26
Ukraine,8
United Arab Emirates,97
United States of America,2008
Based on this I'd expected China to appear in a similar colour to that which the US has loaded in, however it looks the same as countries with values of less than 200. Does anyone know what the reason for this is?
Here's my full code for reference:
import pandas as pd
import plotly as py
df = pd.read_csv('app_country_data_minus_uk.csv')
data = [dict(type='choropleth',
locations = df['Country'],
locationmode = 'country names',
z = df['Applications'],
text = df['Country'],
colorbar = {'title':'Apps per country'},
colorscale = 'Jet',
reversescale = False
)]
layout = dict(title='Application Jan-June 2018',
geo = dict(showframe=False,projection={'type':'mercator'}))
choromap = dict(data = data,layout = layout)
red = py.offline.plot(choromap,filename='world.html')
per your comment I would make sure that china is indeed 2447 and not something like 244. I would also follow the plotly documentation although you example code works.
import plotly.plotly as py
import pandas as pd
df = pd.read_csv('app_country_data_minus_uk.csv')
data = [ dict(
type = 'choropleth',
locations = df['Country'],
locationmode = 'country names',
z = df['Applications'],
colorscale = 'Jet',
reversescale = False,
marker = dict(
line = dict (
color = 'rgb(180,180,180)',
width = 0.5
) ),
colorbar = dict(
autotick = False,
tickprefix = '',
title = 'Apps per country'),
) ]
layout = dict(
title = 'app_country_data_minus_uk',
geo = dict(
showframe = True,
showcoastlines = True,
projection = dict(
type = 'Mercator'
)
)
)
fig = dict( data=data, layout=layout )
py.iplot( fig, validate=False, filename='d3-world-map' )
or if you want to plot it offline:
import plotly.plotly as py
import pandas as pd
import plotly
df = pd.read_csv('app_country_data_minus_uk.csv')
data = [ dict(
type = 'choropleth',
locations = df['Country'],
locationmode = 'country names',
z = df['Applications'],
colorscale = 'Jet',
reversescale = False,
marker = dict(
line = dict (
color = 'rgb(180,180,180)',
width = 0.5
) ),
colorbar = dict(
title = 'Apps per country'),
) ]
layout = dict(
title = 'app_country_data_minus_uk',
geo = dict(
showframe = True,
showcoastlines = True,
projection = dict(
type = 'Mercator'
)
)
)
fig = dict( data=data, layout=layout )
plotly.offline.plot(fig,filename='world.html')
If you use iplot you will be able to edit the chart and see the data in plotly to make sure your data looks correct

Python Plotly: Using dropdown menu to switch between choropleth map and scatterplot

I am new to using plotly and I am attempting to build a dynamic visualisation using python and plotly. I hope to be able to switch between a world choropleth map and a scatter plot using a drop-down menu.
So far I have been able to successfully get a dropdown menu to appear and show the required labels and even show a single plot by removing either the choropleth map or scatter plot trace from the data variable. The problem is that I when I try to have both plots implemented the choropleth map is drawn over the top of the scatterplot regardless of the menu option I choose.
A screenshot of the output.
Areas I Have Looked For A Solution
The plotly reference and looked through the updatemenus and layout sections among many others.
Reviewed the ploty python tutorial page for dropdowns and implementing parts of the suggestion in my code with a focus on the update method.
I have found a StackOverflow page that seemed to be very close to the answer I needed however not quite.
Finally, I also searched the plotly community forum.
The Code
Note I have removed a portion of the code such as imports and data at the beginning.
scatterplot = go.Scatter(
y = df2['Renewable energy consumption (% of total final energy consumption) 2015'],
x = df2['GDP per capita, PPP (constant 2011 international $) 2015'],
mode='markers',
ids=df2['Country Name'],
showlegend = False,
marker = dict(
size = 8,
color = np.random.randn(500),
),
textfont = dict(
size = 14,
color = 'black')
)
choropleth_map = dict(
type = 'choropleth',
locations = df['ISO3166_alpha3'],
z = df['renewables_mtoe'],
text = df['Country'],
colorscale = [[0,"rgb(106, 240, 255)"],[0.10,"rgb(106, 199, 255)"],[0.70,"rgb(50, 100, 255)"],[0.93,"rgb(0, 43, 198)"],\
[0.99999,"rgb(0, 24, 109)"],[1,"rgb(220, 220, 220)"]],
autocolorscale = False,
reversescale = True,
marker = dict(
line = dict (
color = 'rgb(180,180,180)',
width = 0.5
) ),
colorbar = dict(
title = 'mtoe<br>',
tickfont = dict(
size = 16),
titlefont = dict(
size = 16)),
)
data = [choropleth_map, scatterplot]
updatemenus = list([
dict(active=0,
buttons=list([
dict(label = 'choropleth_map',
method = 'update',
args = [{'visible': [True,False]},
{'title': 'The Map'}]),
dict(label = 'scatterplot',
method = 'update',
args = [{'visible': [False,True]},
{'title': 'Scatterplot'}]),
]),
)
])
layout = dict(title='default', showlegend=False,
updatemenus=updatemenus,
geo = dict(showframe = True,
showcoastlines = False,
showland = True,
landcolor = '#dcdcdc',
projection = dict(type = 'natural earth'))
)
fig = dict( data=data, layout=layout )
plotly.offline.iplot(fig, validate=True)
A big thank you in advance to anyone who can help. I have spent days trying to solve this problem, it has even driven me to make my first post on StackOverflow.

Customizing a Networkx graph (or Scatter) with Python Plotly

I'm using Plotly's Python interface to generate a network. I've managed to create a network with my desired nodes and edges, and to control the size of the nodes.
I am desperately looking for help on how to do the following:
add node labels
add edge labels according to a list of weights
control the edge line width according to a list of weights
All this without using the "hovering" option, as it has to go in a non-interactive paper. I'd greatly appreciate any help! Plotly's output |
In case this fails, the figure itself |
matrix.csv
This is my code (most is copy-pasted from the Plotly tutorial for Networkx):
import pandas as pd
import plotly.plotly as py
from plotly.graph_objs import *
import networkx as nx
matrix = pd.read_csv("matrix.csv", sep = "\t", index_col = 0, header = 0)
G = nx.DiGraph()
# add nodes:
G.add_nodes_from(matrix.columns)
# add edges:
edge_lst = [(i,j, matrix.loc[i,j])
for i in matrix.index
for j in matrix.columns
if matrix.loc[i,j] != 0]
G.add_weighted_edges_from(edge_lst)
# create node trace:
node_trace = Scatter(x = [], y = [], text = [], mode = 'markers',
marker = Marker(
showscale = True,
colorscale = 'YIGnBu',
reversescale = True,
color = [],
size = [],
colorbar = dict(
thickness = 15,
title = 'Node Connections',
xanchor = 'left',
titleside = 'right'),
line = dict(width = 2)))
# set node positions
pos = nx.spring_layout(G)
for node in G.nodes():
G.node[node]['pos']= pos[node]
for node in G.nodes():
x, y = G.node[node]['pos']
node_trace['x'].append(x)
node_trace['y'].append(y)
# create edge trace:
edge_trace = Scatter(x = [], y = [], text = [],
line = Line(width = [], color = '#888'),
mode = 'lines')
for edge in G.edges():
x0, y0 = G.node[edge[0]]['pos']
x1, y1 = G.node[edge[1]]['pos']
edge_trace['x'] += [x0, x1, None]
edge_trace['y'] += [y0, y1, None]
edge_trace['text'] += str(matrix.loc[edge[0], edge[1]])[:5]
# size nodes by degree
deg_dict = {deg[0]:int(deg[1]) for deg in list(G.degree())}
for node, degree in enumerate(deg_dict):
node_trace['marker']['size'].append(deg_dict[degree] + 20)
fig = Figure(data = Data([edge_trace, node_trace]),
layout = Layout(
title = '<br>AA Substitution Rates',
titlefont = dict(size = 16),
showlegend = True,
margin = dict(b = 20, l = 5, r = 5, t = 40),
annotations = [dict(
text = "sub title text",
showarrow = False,
xref = "paper", yref = "paper",
x = 0.005, y = -0.002)],
xaxis = XAxis(showgrid = False,
zeroline = False,
showticklabels = False),
yaxis = YAxis(showgrid = False,
zeroline = False,
showticklabels = False)))
py.plot(fig, filename = 'networkx')
So
1. The solution to this is relative easy, you create a list with the node ids and you set it in the text attribute of the scatter plot. Then you set the mode as "markers+text" and you're done.
2. This is a little bit more tricky. You have to calculate the middle of each line and create a list of dicts including the line's middle position and weight. Then you add set as the layout's annotation.
3. This is too compicated to be done using plotly IMO. As for now I am calculating the position of each node using networkx spring_layout function. If you'd want to set the width of each line based on its weight you would have to modify the position using a function that takes into account all the markers that each line is attached to.
Bonus I give you the option to color each of the graph's components differently.
Here's a (slightly modified) function I made a while ago that does 1 and 2:
import pandas as pd
import plotly.plotly as py
import plotly.graph_objs as go
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
import networkx as nx
def scatter_plot_2d(G, folderPath, name, savePng = False):
print("Creating scatter plot (2D)...")
Nodes = [comp for comp in nx.connected_components(G)] # Looks for the graph's communities
Edges = G.edges()
edge_weights = nx.get_edge_attributes(G,'weight')
labels = [] # names of the nodes to plot
group = [] # id of the communities
group_cnt = 0
print("Communities | Number of Nodes")
for subgroup in Nodes:
group_cnt += 1
print(" %d | %d" % (group_cnt, len(subgroup)))
for node in subgroup:
labels.append(int(node))
group.append(group_cnt)
labels, group = (list(t) for t in zip(*sorted(zip(labels, group))))
layt = nx.spring_layout(G, dim=2) # Generates the layout of the graph
Xn = [layt[k][0] for k in list(layt.keys())] # x-coordinates of nodes
Yn = [layt[k][1] for k in list(layt.keys())] # y-coordinates
Xe = []
Ye = []
plot_weights = []
for e in Edges:
Xe += [layt[e[0]][0], layt[e[1]][0], None]
Ye += [layt[e[0]][1], layt[e[1]][1], None]
ax = (layt[e[0]][0]+layt[e[1]][0])/2
ay = (layt[e[0]][1]+layt[e[1]][1])/2
plot_weights.append((edge_weights[(e[0], e[1])], ax, ay))
annotations_list =[
dict(
x=plot_weight[1],
y=plot_weight[2],
xref='x',
yref='y',
text=plot_weight[0],
showarrow=True,
arrowhead=7,
ax=plot_weight[1],
ay=plot_weight[2]
)
for plot_weight in plot_weights
]
trace1 = go.Scatter( x=Xe,
y=Ye,
mode='lines',
line=dict(color='rgb(90, 90, 90)', width=1),
hoverinfo='none'
)
trace2 = go.Scatter( x=Xn,
y=Yn,
mode='markers+text',
name='Nodes',
marker=dict(symbol='circle',
size=8,
color=group,
colorscale='Viridis',
line=dict(color='rgb(255,255,255)', width=1)
),
text=labels,
textposition='top center',
hoverinfo='none'
)
xaxis = dict(
backgroundcolor="rgb(200, 200, 230)",
gridcolor="rgb(255, 255, 255)",
showbackground=True,
zerolinecolor="rgb(255, 255, 255)"
)
yaxis = dict(
backgroundcolor="rgb(230, 200,230)",
gridcolor="rgb(255, 255, 255)",
showbackground=True,
zerolinecolor="rgb(255, 255, 255)"
)
layout = go.Layout(
title=name,
width=700,
height=700,
showlegend=False,
plot_bgcolor="rgb(230, 230, 200)",
scene=dict(
xaxis=dict(xaxis),
yaxis=dict(yaxis)
),
margin=dict(
t=100
),
hovermode='closest',
annotations=annotations_list
, )
data = [trace1, trace2]
fig = go.Figure(data=data, layout=layout)
plotDir = folderPath + "/"
print("Plotting..")
if savePng:
plot(fig, filename=plotDir + name + ".html", auto_open=True, image = 'png', image_filename=plotDir + name,
output_type='file', image_width=700, image_height=700, validate=False)
else:
plot(fig, filename=plotDir + name + ".html")
The d3graph library provides the functionalities you want.
pip install d3graph
I downloaded your data and imported it for demonstration:
# Import data
df = pd.read_csv('data.csv', index_col=0)
# Import library
from d3graph import d3graph
# Convert your Pvalues. Note that any edge is set when a value in the matrix is >0. The edge width is however based on this value. A conversion is therefore useful when you work with Pvalues.
df[df.values==0]=1
df = -np.log10(df)
# Increase some distance between edges. Maybe something like this.
df = (np.exp(df)-1)/10
# Make the graph with default settings
d3 = d3graph()
# Make the graph by setting some parameters
d3.graph(df)
# Set edge properties
d3.set_edge_properties(directed=True)
# Set node properties
d3.set_node_properties(color=df.columns.values, size=size, edge_size=10, edge_color='#000000', cmap='Set2')
This will result in an interactive network graph. Two screenshots: one with the default settings and the one with tweaked settings. More examples can be found here.

Python Plotly: Combine a line graph with a map

Trying to use plotly to combine this line graph (that's already stacked):
import plotly
import plotly.graph_objs as plgo
#... Some Code
max = plgo.Scatter(x = day_times_str, y = max_val , name = "Max")
min = plgo.Scatter(x = day_times_str, y = min_val, name = "Min")
layout_opts = plgo.Layout(
xaxis = dict(title = 'xaxis'),
yaxis = dict(title = 'yaxis', rangemode = "tozero"),
)
figure1 = plgo.Figure(
data = [max, min],
layout = layout_opts,
)
and a map that shows location above this line graph...
#Assume geo_coord is a dataframe of coordinates, with columns 'lat', 'long' and 'text'
geo_data = [
plgo.Scattermapbox(
lat = geo_coord['lat'],
lon = geo_coord['lon'],
text = geo_coord['text'],
marker = dict(
color = geo_coord['text'],
size = 12,
),
mode = 'markers'
)
]
geo_layout = plgo.Layout(
autosize=True,
hovermode='closest',
mapbox=dict(
accesstoken= GMapsAPIHelper.MAPBOX_TOKEN, #Constant stored in global object
bearing=0,
pitch=0,
center=dict(
lat=49.04,
lon=-122.7
), #Modify by project details
zoom= 13
),
)
figure2 = dict(data = geo_data, layout = geo_layout)
plotly.offline.plot takes only 1 figure or set of data and I cannot pass in a list for graphing. I have tried using append_trace but because I've defined x and y axes in the line graph layout, this causes an error for the map, as follows:
File "C:\Anaconda2\lib\site-packages\plotly\graph_objs\graph_objs.py", line 934, in append_trace
trace['xaxis'] = ref[0]
TypeError: list indices must be integers, not str
Any help in solving this issue is appreciated.

Categories

Resources