Plotly: Annotate marker at the last value in line chart - python

I am trying to mark the last value of the line chart with a big red dot in plotly express python, could someone please help me?
I am successful in building the line chart but not able to annotate the dot.
Below is my dataframe and I want the last value in the dataframe to be annotated.
Below is the line chart created and I want my chart to be similar to the second image in the screenshot
Code I am working with:
fig = px.line(gapdf, x='gap', y='clusterCount', text="clusterCount")
fig.show()

The suggestion from gflavia works perfectly well.
But you can also set up an extra trace and associated text by addressing the elements in the figure directly instead of the data source like this:
fig.add_scatter(x = [fig.data[0].x[-1]], y = [fig.data[0].y[-1]])
Plot 1
Complete code:
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
gapdf = pd.DataFrame({
'clusterCount': [1, 2, 3, 4, 5, 6, 7, 8],
'gap': [-15.789, -14.489, -13.735, -13.212, -12.805, -12.475, -12.202, -11.965]
})
fig = px.line(gapdf, x='gap', y='clusterCount')
fig.add_scatter(x = [fig.data[0].x[-1]], y = [fig.data[0].y[-1]],
mode = 'markers + text',
marker = {'color':'red', 'size':14},
showlegend = False,
text = [fig.data[0].y[-1]],
textposition='middle right')
fig.show()

You could overlay an additional trace for the last data point with plotly.graph_objects:
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
gapdf = pd.DataFrame({
'clusterCount': [1, 2, 3, 4, 5, 6, 7, 8],
'gap': [-15.789, -14.489, -13.735, -13.212, -12.805, -12.475, -12.202, -11.965]
})
fig = px.line(gapdf, x='gap', y='clusterCount')
fig.add_trace(go.Scatter(x=[gapdf['gap'].iloc[-1]],
y=[gapdf['clusterCount'].iloc[-1]],
text=[gapdf['clusterCount'].iloc[-1]],
mode='markers+text',
marker=dict(color='red', size=10),
textfont=dict(color='green', size=20),
textposition='top right',
showlegend=False))
fig.update_layout(plot_bgcolor='white',
xaxis=dict(linecolor='gray', mirror=True),
yaxis=dict(linecolor='gray', mirror=True))
fig.show()

Related

Fixed heatmap table with customised colours

I've been breaking my head with this problem. I want to make in plotly something like this:
This is very common in excel plots, so I want to see if it is possible to make this in Plotly for python.
The idea is to customise the plot, I mean, show exactly what the image above shows, I need this to use it as a background in another plot that I made. So, I need to know if its possible to make something like this:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import colors
Index= ['1', '2', '3', '4', '5']
Cols = ['A', 'B', 'C', 'D','E']
data= [[ 0, 0,1, 1,2],[ 0, 1,2, 2,3],[ 1, 2,2, 3,4],[1, 2,3, 4,4],[ 2, 3,4, 4,4]]
df = pd.DataFrame(data, index=Index, columns=Cols)
cmap = colors.ListedColormap(['darkgreen','lightgreen','yellow','orange','red'])
bounds=[0, 1, 2, 3, 4,5]
norm = colors.BoundaryNorm(bounds, cmap.N)
heatmap = plt.pcolor(np.array(data), cmap=cmap, norm=norm)
plt.colorbar(heatmap, ticks=[0, 1, 2, 3,4,5])
plt.show()
And that code give us this plot:
Sorry to bother but by this point I'm completely hopeless haha, I've searched a lot and found nothing.
Thanks so much for reading, any help is appreciated.
The MPL heatmap you presented has some remaining issues but was created plotly. I used this example from the official reference as a basis.
import plotly.express as px
data= [[ 0, 0, 1, 1, 2],[ 0, 1, 2, 2, 3],[ 1, 2, 2, 3, 4],[1, 2, 3, 4, 4],[2, 3, 4, 4, 4]]
fig = px.imshow(data, color_continuous_scale=["darkgreen","lightgreen","yellow","orange","red"])
fig.update_yaxes(autorange=True)
fig.update_layout(
xaxis = dict(
tickmode = 'linear',
tick0 = 0,
dtick = 1
),
autosize=False,
width=500
)
# fig.layout['coloraxis']['colorbar']['x'] = 1.0
fig.update_layout(coloraxis_colorbar=dict(
tickvals=[0,1,2,3,4],
ticktext=[0,1,2,3,4],
x=1.0
))
fig.show()
I would recommend using Seaborn colour pattern:
https://seaborn.pydata.org/tutorial/color_palettes.html
And playing around with the cmap, max, vmin and central which allow you to change the tone of colors base on the scale of data. (It may take a while to get what you want :D)
g = sns.heatmap(data, vmax = 6, vmin = 0, cmap = 'Spectral', center = 3, yticklabels = True)

Dumbbell plots in python with plotly [duplicate]

I want to create a lollipop plot with several horizontal line segments like this - https://python-graph-gallery.com/184-lollipop-plot-with-2-group. I'd like to use plotly since I prefer the graphics (and easy interactivity) but can't find a succint way.
There's both line graphs (https://plot.ly/python/line-charts/) and you can add lines in the layout (https://plot.ly/python/shapes/#vertical-and-horizontal-lines-positioned-relative-to-the-axes), but both of these solutions require each line segment to be added separately, with about 4-8 lines of code each. While I could just for-loop this, would appreciate if anyone can point me to anything with inbuilt vectorization, like the matplotlib solution (first link)!
Edit: Also tried the following code, to first make the plot ala matplotlib, then convert to plotly. The line segments disappear in the process. Starting to think it's just impossible.
mpl_fig = plt.figure()
# make matplotlib plot - WITH HLINES
plt.rcParams['figure.figsize'] = [5,5]
ax = mpl_fig.add_subplot(111)
ax.hlines(y=my_range, xmin=ordered_df['value1'], xmax=ordered_df['value2'],
color='grey', alpha=0.4)
ax.scatter(ordered_df['value1'], my_range, color='skyblue', alpha=1,
label='value1')
ax.scatter(ordered_df['value2'], my_range, color='green', alpha=0.4 ,
label='value2')
ax.legend()
# convert to plotly
plotly_fig = tls.mpl_to_plotly(mpl_fig)
plotly_fig['layout']['xaxis1']['showgrid'] = True
plotly_fig['layout']['xaxis1']['autorange'] = True
plotly_fig['layout']['yaxis1']['showgrid'] = True
plotly_fig['layout']['yaxis1']['autorange'] = True
# plot: hlines disappear :/
iplot(plotly_fig)
You can use None in the data like this:
import plotly.offline as pyo
import plotly.graph_objs as go
fig = go.Figure()
x = [1, 4, None, 2, 3, None, 3, 4]
y = [0, 0, None, 1, 1, None, 2, 2]
fig.add_trace(
go.Scatter(x=x, y=y))
pyo.plot(fig)
Plotly doesn't provide a built in vectorization for such chart, because it can be done easily by yourself, see my example based on your provided links:
import pandas as pd
import numpy as np
import plotly.offline as pyo
import plotly.graph_objs as go
# Create a dataframe
value1 = np.random.uniform(size = 20)
value2 = value1 + np.random.uniform(size = 20) / 4
df = pd.DataFrame({'group':list(map(chr, range(65, 85))), 'value1':value1 , 'value2':value2 })
my_range=range(1,len(df.index)+1)
# Add title and axis names
data1 = go.Scatter(
x=df['value1'],
y=np.array(my_range),
mode='markers',
marker=dict(color='blue')
)
data2 = go.Scatter(
x=df['value2'],
y=np.array(my_range),
mode='markers',
marker=dict(color='green')
)
# Horizontal line shape
shapes=[dict(
type='line',
x0 = df['value1'].loc[i],
y0 = i + 1,
x1 = df['value2'].loc[i],
y1 = i + 1,
line = dict(
color = 'grey',
width = 2
)
) for i in range(len(df['value1']))]
layout = go.Layout(
shapes = shapes,
title='Lollipop Chart'
)
# Plot the chart
fig = go.Figure([data1, data2], layout)
pyo.plot(fig)
With the result I got:

Altair mark_line plots noisier than matplotlib?

I am learning altair to add interactivity to my plots. I am trying to recreate a plot I do in matplotlib, however altair is adding noise to my curves.
this is my dataset
df1
linked here from github: https://raw.githubusercontent.com/leoUninova/Transistor-altair-plots/master/df1.csv
This is the code:
fig, ax = plt.subplots(figsize=(8, 6))
for key, grp in df1.groupby(['Name']):
y=grp.logabsID
x=grp.VG
ax.plot(x, y, label=key)
plt.legend(loc='best')
plt.show()
#doing it directly from link
df1='https://raw.githubusercontent.com/leoUninova/Transistor-altair-plots/master/df1.csv'
import altair as alt
alt.Chart(df1).mark_line(size=1).encode(
x='VG:Q',
y='logabsID:Q',
color='Name:N'
)
Here is the image of the plots I am generating:
matplotlib vs altair plot
How do I remove the noise from altair?
Altair sorts the x axis before drawing lines, so if you have multiple lines in one group it will often lead to "noise", as you call it. This is not noise, but rather an accurate representation of all the points in your dataset shown in the default sort order. Here is a simple example:
import numpy as np
import pandas as pd
import altair as alt
df = pd.DataFrame({
'x': [1, 2, 3, 4, 5, 5, 4, 3, 2, 1],
'y': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
'group': [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]
})
alt.Chart(df).mark_line().encode(
x='x:Q',
y='y:Q'
)
The best way to fix this is to set the detail encoding to a column that distinguishes between the different lines that you would like to be drawn individually:
alt.Chart(df).mark_line().encode(
x='x:Q',
y='y:Q',
detail='group:N'
)
If it is not the grouping that is important, but rather the order of the points, you can specify that by instead providing an order channel:
alt.Chart(df.reset_index()).mark_line().encode(
x='x:Q',
y='y:Q',
order='index:Q'
)
Notice that the two lines are connected on the right end. This is effectively what matplotlib does by default: it maintains the index order even if there is repeated data. Using the order channel for your data produces the result you're looking for:
df1 = pd.read_csv('https://raw.githubusercontent.com/leoUninova/Transistor-altair-plots/master/df1.csv')
alt.Chart(df1.reset_index()).mark_line(size=1).encode(
x='VG:Q',
y='logabsID:Q',
color='Name:N',
order='index:Q'
)
The multiple lines in each group are drawn in order connected at the ends, just as they are in matplotlib.

Show all lines in matplotlib line plot

How do I bring the other line to the front or show both the graphs together?
plot_yield_df.plot(figsize=(20,20))
If plot data overlaps, then one way to view both the data is increase the linewidth along with handling transparency, as shown:
plt.plot(np.arange(5), [5, 8, 6, 9, 4], label='Original', linewidth=5, alpha=0.5)
plt.plot(np.arange(5), [5, 8, 6, 9, 4], label='Predicted')
plt.legend()
Subplotting is other good way.
Problem
The lines are plotted in the order their columns appear in the dataframe. So for example
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
a = np.random.rand(400)*0.9
b = np.random.rand(400)+1
a = np.c_[a,-a].flatten()
b = np.c_[b,-b].flatten()
df = pd.DataFrame({"A" : a, "B" : b})
df.plot()
plt.show()
Here the values of "B" hide those from "A".
Solution 1: Reverse column order
A solution is to reverse their order
df[df.columns[::-1]].plot()
That has also changed the order in the legend and the color coding.
Solution 2: Reverse z-order
So if that is not desired, you can instead play with the zorder.
ax = df.plot()
lines = ax.get_lines()
for line, j in zip(lines, list(range(len(lines)))[::-1]):
line.set_zorder(j)

Centering line scatterplot in plotly

I am creating a line scatter plot using plotly python API. There are old and new data plotted in y-axis with the date in the x-axis.I would like to center the graph such that one part of the graph is old data and another half of the graph is new data. How can this be done in plotly?In the attached image, the blue one represents old and orange represents new data. How old and new can be centered such that one part of the graph is old data and another half of the graph is new data?
Plotly_Line_Scatterplot
Building on the manual axes example from the Plotly documentation:
import plotly.plotly as py
import plotly.graph_objs as go
xold=[5, 6, 7, 8]
yold=[3, 2, 1, 0]
xnew=[10, 11, 12, 13, 14, 15, 16, 17, 18]
ynew=[0, 1, 2, 3, 4, 5, 6, 7, 8]
oldData = go.Scatter(xold, yold)
newData = go.Scatter(xnew, ynew)
data = [oldData, newData]
lenOld = max(xold) - min(xold)
lenNew = max(xnew) - min(xnew)
lenX = 2*max(lenOld,lenNew)
layout = go.Layout(
xaxis=dict(
range=[min(xnew)-lenX/2, min(xnew)+lenX/2]
)
)
fig = go.Figure(data=data, layout=layout)
plot_url = py.plot(fig, filename='axes-range-manual')
For simplicity I have used integers as x-values rather than using dates but the same principle will apply.
Please note, I don't have plotly installed at present so I haven't tested this code.

Categories

Resources