how to add image to plot mplfinance python - python

Trying to add image and price label and add more space on time and it seems like ylim= takes care of that but when i add it my whole graph disappears.
market_colors = mpf.make_marketcolors(
base_mpf_style="charles"
)
rc = {
"axes.labelcolor": "none",
"axes.spines.bottom": True,
"axes.spines.left": False,
"axes.spines.right": False,
"axes.spines.top": False,
"font.size": 10,
}
styles = mpf.make_mpf_style(
base_mpf_style="nightclouds",
marketcolors=market_colors,
gridstyle="",
rc=rc
)
filledShape = {
"y1": df['Close'].values,
"facecolor": "#2279e4"
}
(mpf.plot(df, type='line',
title='Test',
linecolor='white',
style=styles,
volume=True,
figsize=(8, 6),
figscale=0.5,
fill_between=filledShape, tight_layout=True,
scale_padding={'left': 1, 'top': 5, 'right': 1, 'bottom': 2}
))

There are three techniques that I know of to display an image on a matplotlib plot:
Axes.imshow()
Figure.figimage()
Putting the image in an AnnotationBbox
In terms of working with mplfinance, I would say that technique one, calling Axes.imshow() is probably simplest:
Step 1:
For all three of the above techniques, when you call mpf.plot() set kwarg returnfig=True:
fig axlist = mpf.plot(df,...,returnfig=True)
This will give you access to the mplfinance Figure and Axes objects.
Step 2:
Now create a new Axes object where you want the image/logo:
# Note: [left,bottom,width,height] are in terms of fraction of the Figure.
# For example [0.05,0.08,0.10,0.06] means:
# the lower/left corner of the Axes will be located:
# 5% of the way in from the left
# 8% down from the top,
# and the Axes will be
# 10% of the Figure wide and
# 6% of the Figure high.
logo_axes = fig.add_axes([left,bottom,width,height])
Step 3:
Read in the image:
import Image
im = Image.open('image_file_name.png')
Step 4:
Call imshow() on the newly created Axes, and turn of the axis lines:
logo_axes.imshow(im)
logo_axes.axis('off')
Step 5:
Since returnfig=True causes mplfinance to not show the Figure, call mpf.show()
mpf.show()

I'm not sure if this answer will help you or not since I'm not sure what kind of images you want to add. I assume you want to add a corporate logo or something like that, so I did some research and found an answer to whether you can add a watermark to an mpf. I used this answer as a guide and added the icons used on stackoveflow.com to the graph. However, it was not possible to add them to the axes, so I had to add them to the fig. I have changed the style to add the image.
img = plt.imread('./data/se-icon.png')
market_colors = mpf.make_marketcolors(
base_mpf_style="charles"
)
rc = {
"axes.labelcolor": "none",
"axes.spines.bottom": True,
"axes.spines.left": False,
"axes.spines.right": False,
"axes.spines.top": False,
"font.size": 10,
}
styles = mpf.make_mpf_style(
base_mpf_style="yahoo",# nightclouds
marketcolors=market_colors,
gridstyle="",
rc=rc
)
filledShape = {
"y1": df['Close'].values,
"facecolor": "#2279e4"
}
fig, axes = mpf.plot(df, type='line',
title='Test',
linecolor='white',
style=styles,
volume=True,
figsize=(8, 6),
figscale=0.5,
fill_between=filledShape,
tight_layout=True,
scale_padding={'left': 1, 'top': 5, 'right': 1, 'bottom': 2},
returnfig=True
)
#axes[0].imshow(img)
#height = img.shape[1]
fig.figimage(img, 0, fig.bbox.ymax - height*1.5)
plt.show()

Related

interactive PCA with dropdown menu for the both axis with Plotly python

I'm trying to create an interactive PCA plot using plotly-express and graph objects in python (go.Scatter).
The plot should have 2 dropdowns menus (for x-axis and y-axis) to change between the first 5 PCA in the data.
Each data point also belongs to a treatment group either Before, After, or QC.
I was able to plot the PCA1 and PCA2 with plotly-express package but when trying to add the 2 dropdown menus that will update the plot between the 5 PCA it become a mess.
The example data is in my GitHub link,the first 5 columns are the first 5 PCAs.
The code the generate PC1 vs PC2 is:
labels={'0': 'PC 1 (22.0%)',
'1': 'PC 2 (19.6%)',
'2': 'PC 3 (11.1%)',
'3': 'PC 4 (8.2%)',
'4': 'PC 5 (3.9%)',
'color': 'Group'}
fig1 = px.scatter(components_df, x=0 , y=2 ,
color = 'Class',
width=1000, height=700,
template='presentation',
labels=labels,
title="PCA Score Plot (PC{} vs. PC{})".format(1, 2) ,
hover_data=['idx', 'SampleID']
)
fig1.show()
and it looks like this :
I'm trying to add 2 dropdown menus like I draw above to update the x-axis and the y-axis with the different PC's.
So first step was to add_trace on the figure to add other PCs to the figure but dont know how to add graph object to plotly-express to that what i did:
fig = go.Figure()
for Class, group in components_df.groupby("Class"):
# print(group[0])
fig.add_trace(go.Scatter(x=group[0], y=group[1], name=Class, mode='markers',
hovertemplate="Class=%s<br>PC1=%%{x}<br>PC2=%%{y}<extra></extra>"% Class))
for Class, group in components_df.groupby("Class"):
# print(group[0])
fig.add_trace(go.Scatter(x=group[0], y=group[2], name=Class, mode='markers',
hovertemplate="Class=%s<br>PC1=%%{x}<br>PC3=%%{y}<extra></extra>"% Class))
fig.update_layout(
updatemenus=[go.layout.Updatemenu(
active=0,
buttons=list(
[dict(label = 'All',
method = 'update',
args = [{'visible': [True, True, True, True,True]},
{'title': 'All',
'showlegend':True}]),
dict(label = 'PC1 PC1',
method = 'update',
args = [{'visible': [True, False, False, False, False]}, # the index of True aligns with the indices of plot traces
{'title': 'PC1 PC1',
'showlegend':True}]),
dict(label = 'PC1 PC2',
method = 'update',
args = [{'visible': [False, True, False, False, False]},
{'title': 'AAPL',
'showlegend':True}]),
dict(label = 'PC1 PC3',
method = 'update',
args = [{'visible': [False, False, True, False, False]},
{'title': 'AMZN',
'showlegend':True}]),
])
)
])
and that is the result:
There are many problems with that:
when changing the different options in the dropdown menu also the legends change (they suppose the stay fixed)
when changing the different options in the dropdown menu it does not lools like the data should be
it does not look nice like in the plotly-express.
there is only one dropdown
The code is base on many explanations in the documentation and blogs:
How to change plot data using dropdowns
Dropdown Menus in Python
Adding interactive filters
Setting the Font, Title, Legend Entries, and Axis Titles in Python
Any hint will be appreciated on how to add correct add_trac or correct dropdown menu
Thank you!!!
it's all about being highly structured and systematic. Plotly Express does generate a decent base chart. Use fig1.to_dict() to view graph object structures it has built
challenge I found with adding updatemenus to Plotly Express figure - it's a multi-trace figure with trace defining marker color. This can be simplified to a single trace figure with an array defining marker color
then it's a case of building updatemenus. This I have done as nested list comprehensions. Outer loop axis (each menu), inner loop principle component (each menu item)
Updates
magic colors - fair critique. I had used a hard coded dict for color mapping. Now programmatically build cmap Reverted back to static definition of cmap as dict comprehension is not wanted. Changed to a pandas approach to building cmap with lambda function
"y": 1 if ax == "x" else 0.9 We are building two drop downs, one for xaxis and one for yaxis. Hopefully it's obvious that the positions of these menus needs to be different. See docs: https://plotly.com/python/reference/layout/updatemenus/ For similar reason active property s being set. Make sure drop downs show what is actually plotted in the figure
legend refer back to point I made about multi-trace figures. Increases complexity! Have to use synthetic traces and this technique Plotly: How to update one specific trace using updatemenus?
import pandas as pd
import plotly.graph_objects as go
import plotly.express as px
components_df = pd.read_csv(
"https://raw.githubusercontent.com/TalWac/stakoverflow-Qustion/main/components_df.csv"
)
labels = {
"0": "PC 1 (22.0%)",
"1": "PC 2 (19.6%)",
"2": "PC 3 (11.1%)",
"3": "PC 4 (8.2%)",
"4": "PC 5 (3.9%)",
"color": "Group",
}
# cmap = {
# cl: px.colors.qualitative.Plotly[i]
# for i, cl in enumerate(
# components_df.groupby("Class", as_index=False).first()["Class"]
# )
# }
# revert back to static dictionary as dynamic building is not wanted
# cmap = {'After': '#636EFA', 'Before': '#EF553B', 'QC': '#00CC96'}
# use lambda functions instead of dict comprehension
df_c = components_df.groupby("Class", as_index=False).first()
df_c["color"] = df_c.apply(lambda r: px.colors.qualitative.Plotly[r.name], axis=1)
cmap = df_c.set_index("Class").loc[:,"color"].to_dict()
fig1 = go.Figure(
go.Scatter(
x=components_df["0"],
y=components_df["1"],
customdata=components_df.loc[:, ["idx", "SampleID", "Class"]],
marker_color=components_df["Class"].map(cmap),
mode="markers",
hovertemplate="Class=%{customdata[2]}<br>x=%{x}<br>y=%{y}<br>idx=%{customdata[0]}<br>SampleID=%{customdata[1]}<extra></extra>",
)
).update_layout(
template="presentation",
xaxis_title_text=labels["0"],
yaxis_title_text=labels["1"],
height=700,
)
fig1.update_layout(
updatemenus=[
{
"active": 0 if ax == "x" else 1,
"buttons": [
{
"label": f"{ax}-PCA{pca+1}",
"method": "update",
"args": [
{ax: [components_df[str(pca)]]},
{f"{ax}axis": {"title": {"text": labels[str(pca)]}}},
[0],
],
}
for pca in range(5)
],
"y": 1 if ax == "x" else 0.9,
}
for ax in ["x", "y"]
]
).update_traces(showlegend=False)
# add a legend by using synthetic traces. NB, this will leave markers at 0,0
fig1.add_traces(
px.scatter(
components_df.groupby("Class", as_index=False).first(),
x="0",
y="1",
color="Class",
color_discrete_map=cmap,
)
.update_traces(x=[0], y=[0])
.data
)

Is there a way to always show all markers in a plotly scattermapbox, regardless of manual zooming?

I am trying to generate several maps with different content based on a dataframe.
So far, I have managed to display the information I needed on the interactive maps.
However, as I need to include the generated maps as figures in a report, I need to find a way to show all the markers in the figures. Problem is: some markers only are shown when I manually zoom in the area.
Is there a way to always make the markers visible?
Here is the code:
import plotly.graph_objects as go
token = open("token.mapbox_token").read() # you need your own token
df_select = df_map.loc[df_map['Budget'] == 0.9]
fig= go.Figure(go.Scattermapbox(lat=df_select.Latitude, lon=df_select.Longitude,
mode='markers', marker=go.scattermapbox.Marker(
size=df_select.Warehouse_Size*5, color = df_select.Warehouse_Size,
colorscale = ['white','red','orange','green','blue','purple'],
showscale = False)))
fig = fig.add_trace(go.Choroplethmapbox(geojson=br_geo, locations=df_select.State,
featureidkey="properties.UF_05",
z=df_select.Top10,
colorscale=["white","pink"], showscale=False,
zmin = 0,
zmax=1,
marker_opacity=0.5, marker_line_width=1
))
df_prio = df_select.loc[df_select['Prioritisated'] == 1]
fig= fig.add_trace(go.Scattermapbox(lat=df_prio.Latitude, lon=df_prio.Longitude+1,
mode='markers',
marker=go.scattermapbox.Marker(symbol = "campsite", size = 10)))
fig.update_layout(height=850,width = 870,
mapbox_style = "mapbox://styles/rafaelaveloli/ckollp2dg21dd19pmgm3vyebu",
mapbox_zoom=3.4, mapbox_center = {"lat": -14.5 ,"lon": -52},
mapbox_accesstoken = token, showlegend= False)
fig.show()
This is the result I get:
And this is one of the hidden markers that are only visible when zooming in:
How can I make it visible in the first figure, without changing the figure zoom and dimensions?
Passing allowoverlap=True to go.scattermapbox.Marker() seems to resolve the issue (link to relevant docs).

Plotly: Define colours for a pie graph - (using low-level API)

So I got this code to make a pie chart, but I wanted to changes the colors of each class to the colors listed in the colors variable. The documentation about formatting plots in json is really hard to find so I can't figure it out. Does anyone know how to add colors to the plot? The code can be found below.
def plot_donut(df):
colors = ['#ca0020','#f4a582','#D9DDDC','#92c5de','#0571b0']
trace1 = {
"hole": 0.8,
"type": "pie",
"labels": ['-2','-1','0','1','2'],
"values": df['Time Spent (seconds)'],
"showlegend": False
}
fig = go.Figure(data=data, layout=layout)
fig.show()
plot_donut(df)
Further to my earlier comment, please see the code below for specifying named colours for a Plotly donut (pie) graph.
Like you, I much prefer to use the low-level Plotly API, rather than relying on the convenience wrappers. The code below shows how this is done at a low level.
Example code:
import plotly.io as pio
values = [2, 3, 5, 7, 11]
colours = ['#440154', '#3e4989', '#26828e', '#35b779', '#fde725']
trace1 = {'values': values,
'marker': {'colors': colours}, # <--- This is the key.
'type': 'pie',
'hole': 0.8,
'showlegend': False}
pio.show({'data': [trace1]})
Output:

Plotly clustered heatmap (with dendrogram)/Python

I am trying to create a clustered heatmap (with a dendrogram) using plotly in Python. The one they have made in their website does not scale well, I have come to various solutions, but most of them are in R or JavaScript. I am trying to create a heatmap with a dendrogram from the left side of the heatmap only, showing clusters across the y axis (from the hierarchical clustering). A really good looking example is this one: https://chart-studio.plotly.com/~jackp/6748. My purpose is to create something like this, but only with the left-side dendrogram. If someone can implement something like this in Python, I will be really grateful!
Let the data be X = np.random.randint(0, 10, size=(120, 10))
The following suggestion draws on elements from both Dendrograms in Python and chart-studio.plotly.com/~jackp. This particular plot uses your data X = np.random.randint(0, 10, size=(120, 10)). One thing that the linked approaches had in common, was, in my opinion, that the datasets and data munging procedures were a bit messy. So I decided to build the following figure on a pandas dataframe with df = pd.DataFrame(X) to hopefully make everything a bit clearer
Plot
Complete code
import plotly.graph_objects as go
import plotly.figure_factory as ff
import numpy as np
import pandas as pd
from scipy.spatial.distance import pdist, squareform
import random
import string
X = np.random.randint(0, 10, size=(120, 10))
df = pd.DataFrame(X)
# Initialize figure by creating upper dendrogram
fig = ff.create_dendrogram(df.values, orientation='bottom')
fig.for_each_trace(lambda trace: trace.update(visible=False))
for i in range(len(fig['data'])):
fig['data'][i]['yaxis'] = 'y2'
# Create Side Dendrogram
# dendro_side = ff.create_dendrogram(X, orientation='right', labels = labels)
dendro_side = ff.create_dendrogram(X, orientation='right')
for i in range(len(dendro_side['data'])):
dendro_side['data'][i]['xaxis'] = 'x2'
# Add Side Dendrogram Data to Figure
for data in dendro_side['data']:
fig.add_trace(data)
# Create Heatmap
dendro_leaves = dendro_side['layout']['yaxis']['ticktext']
dendro_leaves = list(map(int, dendro_leaves))
data_dist = pdist(df.values)
heat_data = squareform(data_dist)
heat_data = heat_data[dendro_leaves,:]
heat_data = heat_data[:,dendro_leaves]
heatmap = [
go.Heatmap(
x = dendro_leaves,
y = dendro_leaves,
z = heat_data,
colorscale = 'Blues'
)
]
heatmap[0]['x'] = fig['layout']['xaxis']['tickvals']
heatmap[0]['y'] = dendro_side['layout']['yaxis']['tickvals']
# Add Heatmap Data to Figure
for data in heatmap:
fig.add_trace(data)
# Edit Layout
fig.update_layout({'width':800, 'height':800,
'showlegend':False, 'hovermode': 'closest',
})
# Edit xaxis
fig.update_layout(xaxis={'domain': [.15, 1],
'mirror': False,
'showgrid': False,
'showline': False,
'zeroline': False,
'ticks':""})
# Edit xaxis2
fig.update_layout(xaxis2={'domain': [0, .15],
'mirror': False,
'showgrid': False,
'showline': False,
'zeroline': False,
'showticklabels': False,
'ticks':""})
# Edit yaxis
fig.update_layout(yaxis={'domain': [0, 1],
'mirror': False,
'showgrid': False,
'showline': False,
'zeroline': False,
'showticklabels': False,
'ticks': ""
})
# # Edit yaxis2
fig.update_layout(yaxis2={'domain':[.825, .975],
'mirror': False,
'showgrid': False,
'showline': False,
'zeroline': False,
'showticklabels': False,
'ticks':""})
fig.update_layout(paper_bgcolor="rgba(0,0,0,0)",
plot_bgcolor="rgba(0,0,0,0)",
xaxis_tickfont = dict(color = 'rgba(0,0,0,0)'))
fig.show()
The simplest solution to this problem is to use dash_bio.Clustergram function in dash_bio package.
import pandas as pd
import dash_bio as dashbio
X = np.random.randint(0, 10, size=(120, 10))
dashbio.Clustergram(
data=X,
# row_labels=rows,
# column_labels=columns,
cluster='row',
color_threshold={
'row': 250,
'col': 700
},
height=800,
width=700,
color_map= [
[0.0, '#636EFA'],
[0.25, '#AB63FA'],
[0.5, '#FFFFFF'],
[0.75, '#E763FA'],
[1.0, '#EF553B']
]
)
An more laborious solution is to use the plot function plotly.figure_factory.create_dendrogram combined with plotly.graph_objects.Heatmap as in plotly document
the example is not a dendrogram heat map but rather a pair wised distance heat map, you can use the two function to create dendrogram heat map though.
can also use seabornes clustermap
https://seaborn.pydata.org/generated/seaborn.clustermap.html

How to resize subfigures when using ImageGrid from Matplotlib

Using Python and Matplotlib, I'm trying to produce a figure consisting of two subfigures, each containing a group of plots with a common colour bar. I have almost everything working. The only part I cannot figure out is how to make the top and bottom subplots have the same width - i.e. the 4x2 grid + colour bar should have the same width as the 2x1 grid + colour bar. It is deliberate that the bottom left image is not the same shape as the other plots.
Here's the code I have:
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import AxesGrid, ImageGrid
from numpy.random import rand
fig = plt.figure(1)
grid1 = ImageGrid(fig, 211,
nrows_ncols = (2, 4),
axes_pad = 0.07,
share_all=True,
label_mode = "L",
cbar_location = "right",
cbar_mode="single",
cbar_size="7%",
cbar_pad="7%",
aspect = True
)
for n in range(8):
im = grid1[n].imshow(rand(10,10),interpolation="nearest")
grid1.axes_all
cb1 = grid1.cbar_axes[0].colorbar(im)
cb1.set_label_text('subfig 1')
grid2 = ImageGrid(fig, 212,
nrows_ncols = (1, 2),
axes_pad = 0.1,
label_mode = "L",
share_all = False,
cbar_location="right",
cbar_mode="single",
cbar_size="7%",
cbar_pad="7%",
aspect = True
)
im = grid2[0].imshow(rand(10,15),interpolation="nearest")
im = grid2[1].imshow(rand(10,10),interpolation="nearest")
cb2 = grid2.cbar_axes[0].colorbar(im)
cb2.set_label_text('subfig 2')
plt.figtext(0.05,0.85,'(a)',size=20)
plt.figtext(0.05,0.45,'(b)',size=20)
plt.show()
Here's the result:
The actual plots are images, so it is important that I maintain the correct aspect ratio for each one.
I think the missing step is to gain access to the axes of each subplot (not of the sub-subplots), but I have no idea how to do that.
I read through the documentation and looked at examples at matplotlib.org. There are examples showing how to resize individual grid[n] but I can't find any examples showing how to resize grid. Does anyone have any pointers?
For me, specifying a figure size helped:
fig = plt.figure(1, (6., 6.))
I also had to change the figtext location:
plt.figtext(0.0,0.85,'(a)',size=20)
plt.figtext(0.0,0.45,'(b)',size=20)
Result:
Seems like it maintained the aspect ratio

Categories

Resources