I'm using plotly to plot a large csv file (with a lot of data) and to subplot in line (which works actually) some charts.
The problem is that for each chart I get all of the date+time for each point on the x-axis resulting in a bad quality chart. How can I hide for the 5 first chart the values of the x-axis while keeping it for the last one to have a better vieaw of the charts ?
Here is my code : the problematic part start at line 125
import os
import pandas as pd
import plotly
import plotly.graph_objs as go
from tkinter import *
window = Tk()
window.title("Interface utilisateur")
consigne=Label(window, text="Merci de remplir les champs ci-dessous avant de cliquer sur start")
consigne.grid(column = 1,row=0)
consDateDEB=Label(window, text="Date de début de sélection (format JJMMAAAA)")
consDateDEB.grid(column=0,row=1)
DateDEB = Entry(window,width=25)
DateDEB.grid(column = 2,row=1)
consDateFIN=Label(window, text="Date de fin de sélection (format JJMMAAAA)")
consDateFIN.grid(column=0,row=2)
DateFIN = Entry(window,width=25)
DateFIN.grid(column = 2,row=2)
consPathIN=Label(window, text="Chemin d'accès aux fichiers (format C://user/dossier1/dossier2/)")
consPathIN.grid(column=0,row=3)
PathIN = Entry(window,width=50)
PathIN.grid(column = 2,row=3)
consPathOUT=Label(window, text="Chemin d'écriture des fichiers (format C://user/dossier1/dossier2/)")
consPathOUT.grid(column=0,row=4)
PathOUT = Entry(window,width=50)
PathOUT.grid(column = 2,row=4)
def click():
pathIN = PathIN.get()
pathOUT = PathOUT.get()
DateDebut = DateDEB.get()
DateFin = DateFIN.get()
tracer(DateDebut,DateFin,pathIN,pathOUT)
btn = Button(window, text='Start',command=click,width=30,height=2,activebackground='red')
btn.grid(column = 1,row=5)
window.mainloop()
def tracer(DateDebut,DateFin,pathIN,pathOUT):
# DateDebut=10052019
# DateFin=12052019
# pathIN='D://Clef64go/PJT/Logfiles/'
# pathOUT='D://Clef64go/PJT/OUT/'
Logfiles = os.listdir(pathIN)
def conversion(Logfile_JJMMAAAA):
nomFichierINT = int(Logfile_JJMMAAAA[12:16] + Logfile_JJMMAAAA[10:12] + Logfile_JJMMAAAA[8:10])
return nomFichierINT
def conversionInverse(AAAAMMJJ):
AAAAMMJJ = str(AAAAMMJJ)
nomFichierSTR = "Logfile_" + AAAAMMJJ[6:8] + AAAAMMJJ[4:6] + AAAAMMJJ[0:4]+".csv"
return nomFichierSTR
DateDebut = str(DateDebut)
DateFin = str(DateFin)
DebTempo = DateDebut[4:8]+DateDebut[2:4]+DateDebut[0:2]
FinTempo = DateFin[4:8]+DateFin[2:4]+DateFin[0:2]
DateDebut=int(DebTempo)
DateFin=int(FinTempo)
L_Selection=[]
for fichier in Logfiles:
Tempo=conversion(fichier)
if Tempo >= DateDebut and Tempo <= DateFin :
L_Selection.append(Tempo)
L_Selection = sorted(L_Selection)
L_Clean=[]
for fichier in L_Selection :
Tempo = conversionInverse(fichier)
L_Clean.append(Tempo)
#L_Log = os.listdir("D://Clef64go/PJT/TEST2/")
dfList=[]
colnames=['No.','Date','Time','Temp1','Unit','Temp2','Unit','Lux2','Unit','BP1','Humidité Relat','Unit','CO2','Unit','Présence','Temp1_EnO','Unit','Temp2_EnO','Unit','Temp3_EnO','Unit','RH3_EnO','Unit','Chauffage','test']
for filename in L_Clean:
filename = pathIN + filename
typefile=type(filename)
df=pd.read_csv(filename, sep = ';', error_bad_lines=False, encoding="ANSI")
dfList.append(df)
concatDf=pd.concat(dfList,axis=0)
concatDf.columns=colnames
pathOUT = pathOUT + "/" + str(DateDebut) +" a "+ str(DateFin) + ".csv"
concatDf.to_csv(pathOUT, sep = ';',index=False)
df = pd.read_csv(pathOUT,decimal=",",sep = ';', error_bad_lines=False, encoding="ANSI",names=colnames)
df['Temp1'] = [x.replace(',', '.') for x in df['Temp1']]
df['Temp2'] = [x.replace(',', '.') for x in df['Temp2']]
df['Temp1_EnO'] = [x.replace(',', '.') for x in df['Temp1_EnO']]
df['Temp2_EnO'] = [x.replace(',', '.') for x in df['Temp2_EnO']]
df['Temp3_EnO'] = [x.replace(',', '.') for x in df['Temp3_EnO']]
date = df['Date']+df['Time']
y1 = df['Temp1']
y2 = df['Temp2']
y3 = df['Temp3_EnO']
y4 = df['Humidité Relat']
y5 = df['CO2']
y6 = df['Présence']
#plotly.offline.plot({
# "data": [go.Scatter(x=x, y=y)],
# "layout": go.Layout(title="Température 1 en fonction du temps")
# }, auto_open=True)
temp1 = go.Scatter(
x=date,
y=y1,
name="Température 1 (°C)"
)
temp2 = go.Scatter(
x=date,
y=y2,
name="Température 2 (°C)"
)
temp3 = go.Scatter(
x=date,
y=y3,
name="Température 3 (°C)"
)
Humidite = go.Scatter(
x=date,
y=y4,
name="Humidité relative (%)"
)
dioxyde_de_carbone = go.Scatter(
x=date,
y=y5,
name="Taux C02 (ppm)"
)
presence = go.Scatter(
x=date,
y=y6,
name="Présence"
)
fig = plotly.tools.make_subplots(rows=6, cols=1)
fig.append_trace(temp1, 1, 1)
fig.append_trace(temp2, 2, 1)
fig.append_trace(temp3, 3, 1)
fig.append_trace(Humidite, 4, 1)
fig.append_trace(dioxyde_de_carbone, 5, 1)
fig.append_trace(presence, 6, 1)
fig['layout'].update(title='Représentation graphique des données')
plotly.offline.plot(fig, filename=str(DateDebut) +" a "+ str(DateFin) + ".csv", auto_open=True)
Here is what I get :
You can fix it with these two lines:
fig.update_xaxes(showticklabels=False) # hide all the xticks
fig.update_xaxes(showticklabels=True, row=6, col=1)
The other answers here are workarounds. Plotly offers the functionality you're looking for with the shared_xaxes parameter in the make_subplots method:
fig = plotly.subplots.make_subplots(rows=6, cols=1, shared_xaxes=True)
You can also choose to share the y axes as well with shared_yaxes=True. However if sharing both axes is your intent, you may find it easier to have a single plot and just add multiple traces to it (vs using subplots).
I started from Andrews comment. But the relevant source is this one:
trace0 = go.Scatter(
x = [0,1,1,0,0,1,1,2,2,3,3,2,2,3],
y = [0,0,1,1,3,3,2,2,3,3,1,1,0,0]
)
trace1 = go.Scatter(
x = [0,1,2,3],
y = [1,2,4,8],
yaxis = "y2"
)
layout = go.Layout(
width = 800,
height = 500,
title = "fixed-ratio axes",
xaxis = dict(
nticks = 10,
domain = [0, 0.45],
title = "shared X axis"
),
yaxis = dict(
scaleanchor = "x",
domain = [0, 0.45],
title = "1:1"
),
yaxis2 = dict(
scaleanchor = "x",
scaleratio = 0.2,
domain = [0.55,1],
title = "1:5"
))
Explanation
You need to define the xaxis once. For every other subplot you set scaleanchor = "x", so that every subplot shares the same xaxis. All the plots that share one xasis are automatically plotted as subplots, so you coul get rid of your present structure.
Required Edit
data = [temp1,temp2,temp3,Humidite, dioxyde_de_carbone, presence]
layout = go.Layout(
width = 800,
height = 500,
title = "fixed-ratio axes using 10 ticks",
xaxis = dict(
nticks = 10,
title = "shared X axis showing DATE"
),
yaxis = dict(
scaleanchor = "x",
title = "Temp"
),
yaxis2 = dict(
scaleanchor = "x",
title = "Humidity"
),
yaxis3 = dict(
scaleanchor = "x",
title = "ppm"
),
yaxis4 = dict(
scaleanchor = "x",
title = "presence"
),
showlegend= False
)
fig = go.Figure(data=data, layout=layout)
plotly.offline.plot(fig, filename=str(DateDebut) +" a "+ str(DateFin) + ".csv", auto_open=True)
fig = plotly.tools.make_subplots(rows=6, cols=1)
Related
The following plots two separate scatterplots using Plotly. I want to combine the points from each subplot into a single legend. However, if I plot the figure as is, there are some duplicate entries. On the other hand, if I hide a legend from a certain subplot, not all entries are displayed.
df = pd.DataFrame({'Type' : ['1','1','1','1','1','2','2','2','2','2'],
'Category' : ['A','D','D','D','F','B','D','A','D','E']
})
df['Color'] = df['Category'].map(dict(zip(df['Category'].unique(),
px.colors.qualitative.Dark24[:len(df['Category'].unique())])))
df = pd.concat([df]*10, ignore_index = True)
df['Lat'] = np.random.randint(0, 20, 100)
df['Lon'] = np.random.randint(0, 20, 100)
Color = df['Color'].unique()
Category = df['Category'].unique()
cats = dict(zip(Color, Category))
df_type_1 = df[df['Type'] == '1'].copy()
df_type_2 = df[df['Type'] == '2'].copy()
fig = make_subplots(
rows = 1,
cols = 2,
specs = [[{"type": "scattermapbox"}, {"type": "scattermapbox"}]],
vertical_spacing = 0.05,
horizontal_spacing = 0.05
)
for c in df_type_1['Color'].unique():
df_color = df_type_1[df_type_1['Color'] == c]
fig.add_trace(go.Scattermapbox(
lat = df_color['Lat'],
lon = df_color['Lon'],
mode = 'markers',
name = cats[c],
marker = dict(color = c),
opacity = 0.8,
#legendgroup = 'group2',
#showlegend = True,
),
row = 1,
col = 1
)
for c in df_type_2['Color'].unique():
df_color = df_type_2[df_type_2['Color'] == c]
fig.add_trace(go.Scattermapbox(
lat = df_color['Lat'],
lon = df_color['Lon'],
mode = 'markers',
name = cats[c],
marker = dict(color = c),
opacity = 0.8,
#legendgroup = 'group2',
#showlegend = False,
),
row = 1,
col = 2
)
fig.update_layout(height = 600, width = 800, margin = dict(l = 10, r = 10, t = 30, b = 10));
fig.update_layout(mapbox1 = dict(zoom = 2, style = 'carto-positron'),
mapbox2 = dict(zoom = 2, style = 'carto-positron'),
)
fig.show()
output: duplicate entries
if I use showlegend = False on either subplot, then the legend will not show all applicable entries.
output: (subplot 2 showlegend = False)
The best way to remove duplicate legends at this time is to use set() to remove duplicates from the created legend and update it with that content. I am saving this as a snippet. I am getting the snippet from this answer. I have also changed the method to use the color information set in the columns. I have also redesigned it so that it can be created in a single loop process without creating an extra data frame.
fig = make_subplots(
rows = 1,
cols = 2,
specs = [[{"type": "scattermapbox"}, {"type": "scattermapbox"}]],
vertical_spacing = 0.05,
horizontal_spacing = 0.05
)
for t in df['Type'].unique():
dff = df.query('Type ==#t')
for c in dff['Category'].unique():
dffc = dff.query('Category == #c')
fig.add_trace(go.Scattermapbox(
lat = dffc['Lat'],
lon = dffc['Lon'],
mode = 'markers',
name = c,
marker = dict(color = dffc['Color']),
opacity = 0.8,
),
row = 1,
col = int(t)
)
fig.update_layout(height = 600, width = 800, margin = dict(l = 10, r = 10, t = 30, b = 10));
fig.update_layout(mapbox1 = dict(zoom = 2, style = 'carto-positron'),
mapbox2 = dict(zoom = 2, style = 'carto-positron'),
)
names = set()
fig.for_each_trace(
lambda trace:
trace.update(showlegend=False)
if (trace.name in names) else names.add(trace.name))
fig.show()
I have a bokeh line plot and I'm adding a series of labels to the left of the y-axis. I have also tried LabelSet but with LabelSet nothing is coming up. so I used a for loop to add all of the labels to the layout but the graph is getting squeezed to the right. any ideas?
p = figure(plot_width=1000, plot_height=800, x_axis_type="datetime",
title='String Line - Lakeshore West', sizing_mode='stretch_both')
for (key, grp), color in zip (Lakeshore_West.groupby('CourseID'), Viridis256):
source = ColumnDataSource(dict(courseID = grp.CourseID, time=grp.Time, chainage = grp.Chainage
, station=grp.Station, serviceType=grp.ServiceType))
line = p.line('time', 'chainage', color= color, source=source)
ttips = HoverTool(renderers = [line], tooltips = [
('CourseID ', ' #courseID'),
('Time ', ' #time{%H:%M:%S}'),
('Chainage ', ' #chainage'),
('Service Type ', ' #serviceType')],formatters={'#time': 'datetime'})
p.add_tools(ttips)
format = '%H:%M:%S'
x_min = str(Lakeshore_West['Time'].min())
x_max = str(Lakeshore_West['Time'].max())
minDateTime = datetime.strptime(x_min, format)
maxDateTime = datetime.strptime(x_max, format)
diff = maxDateTime - minDateTime
x_centre = (diff/2 + minDateTime).time()
for ind in trackNo.index:
if trackNo['Corridor'][ind] == 1:
y_centre = (trackNo['Chainage'][ind+1] - trackNo['Chainage'][ind])/2 + trackNo['Chainage'][ind]
colors = trackNo['Color'][ind]
tracks = trackNo['TrackNo'][ind]
if (trackNo['Chainage'][ind+1] - trackNo['Chainage'][ind]) > 0:
height = trackNo['Chainage'][ind+1] - trackNo['Chainage'][ind]
rectangle = p.rect(x=x_centre, y=y_centre, width=diff, height= height,
angle=0, fill_color= colors, line_color=colors, width_units ='data',
height_units='data', fill_alpha=0.2, line_alpha=0.0,
legend_field = 'TrackNo')
LSW_sta = Lakeshore_West[['Station', 'Chainage']].drop_duplicates()
for ind in LSW_sta.index:
p.add_layout(Label(x=Lakeshore_West['Time'].min(), y=LSW_sta['Chainage'][ind], x_offset=-150, y_offset=0,
text=LSW_sta['Station'][ind], text_font_size= "10px", render_mode='canvas'), 'left')
p.x_range = Range1d(Lakeshore_West['Time'].min(), Lakeshore_West['Time'].max())
p.y_range = Range1d(Lakeshore_West['Chainage'].min(), Lakeshore_West['Chainage'].max())
p.legend.visible = False
show(p)
For this example, I modified the dataset and code from https://plotly.com/python/map-subplots-and-small-multiples/ to add a column to plot it in colors.
What I want to do here and in my dataset is to add a legend with a color scale.
Here the range is the same (0-9) among maps, so, a general legend or a legend for each subplot would work. Here the general legend is wrong.
Related: Plotly contour subplots each having their own colorbar
import plotly.graph_objects as go
import pandas as pd
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/1962_2006_walmart_store_openings.csv')
df.head()
# new column
import numpy as np
df["counts"] = np.random.choice(range(0,10),df.shape[0])
data = []
layout = dict(
title = 'New Walmart Stores per year 1962-2006<br>\
Source: <a href="http://www.econ.umn.edu/~holmes/data/WalMart/index.html">\
University of Minnesota</a>',
# showlegend = False,
autosize = False,
width = 1000,
height = 900,
hovermode = False,
legend = dict(
x=0.7,
y=-0.1,
bgcolor="rgba(255, 255, 255, 0)",
font = dict( size=11 ),
)
)
years = df['YEAR'].unique()
for i in range(len(years)):
geo_key = 'geo'+str(i+1) if i != 0 else 'geo'
lons = list(df[ df['YEAR'] == years[i] ]['LON'])
lats = list(df[ df['YEAR'] == years[i] ]['LAT'])
mycolor = list(df[ df['YEAR'] == years[i] ]['counts']) # new
# Walmart store data
data.append(
dict(
type = 'scattergeo',
showlegend=False,
lon = lons,
lat = lats,
geo = geo_key,
name = int(years[i]),
marker = dict(
color = mycolor, # new
#color = "rgb(0, 0, 255)",
opacity = 0.5
,showscale=True # new
)
)
)
# Year markers
data.append(
dict(
type = 'scattergeo',
showlegend = False,
lon = [-78],
lat = [47],
geo = geo_key,
text = [years[i]],
mode = 'text',
)
)
layout[geo_key] = dict(
scope = 'usa',
showland = True,
landcolor = 'rgb(229, 229, 229)',
showcountries = False,
domain = dict( x = [], y = [] ),
subunitcolor = "rgb(255, 255, 255)",
)
z = 0
COLS = 5
ROWS = 9
for y in reversed(range(ROWS)):
for x in range(COLS):
geo_key = 'geo'+str(z+1) if z != 0 else 'geo'
layout[geo_key]['domain']['x'] = [float(x)/float(COLS), float(x+1)/float(COLS)]
layout[geo_key]['domain']['y'] = [float(y)/float(ROWS), float(y+1)/float(ROWS)]
z=z+1
if z > 42:
break
fig = go.Figure(data=data, layout=layout)
fig.update_layout(width=800)
config = {'staticPlot': True}
fig.show(config=config)
I had to build the coordinates for each colorbar:
import plotly.graph_objects as go
import pandas as pd
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/1962_2006_walmart_store_openings.csv')
df.head()
z1 = z = 0
COLS = 5
ROWS = 9
mylist = [[],[]]
for y in reversed(range(ROWS)):
for x in range(COLS):
mylist[0].append((float(x+1)/float(COLS))-.02)
mylist[1].append((float(y+1)/float(ROWS))-.05)
z1=z1+1
if z1 > 42:
break
# new column
import numpy as np
df["counts"] = np.random.choice(range(0,10),df.shape[0])
data = []
layout = dict(
title = 'New Walmart Stores per year 1962-2006<br>\
Source: <a href="http://www.econ.umn.edu/~holmes/data/WalMart/index.html">\
University of Minnesota</a>',
# showlegend = False,
autosize = False,
width = 1000,
height = 900,
hovermode = False,
legend = dict(
x=0.7,
y=-0.1,
bgcolor="rgba(255, 255, 255, 0)",
font = dict( size=11 ),
)
)
years = df['YEAR'].unique()
for i in range(len(years)):
geo_key = 'geo'+str(i+1) if i != 0 else 'geo'
lons = list(df[ df['YEAR'] == years[i] ]['LON'])
lats = list(df[ df['YEAR'] == years[i] ]['LAT'])
mycolor = list(df[ df['YEAR'] == years[i] ]['counts']) # new
# Walmart store data
data.append(
dict(
type = 'scattergeo',
showlegend=False,
lon = lons,
lat = lats,
geo = geo_key,
name = int(years[i]),
marker = dict(
color = mycolor, # new
#color = "rgb(0, 0, 255)",
opacity = 0.5,
showscale=True
,colorbar=dict(len=0.1
, x=mylist[0][i]
, y=mylist[1][i]
,thickness=5
)
)
)
)
# Year markers
data.append(
dict(
type = 'scattergeo',
showlegend = False,
lon = [-78],
lat = [47],
geo = geo_key,
text = [years[i]],
mode = 'text',
)
)
layout[geo_key] = dict(
scope = 'usa',
showland = True,
landcolor = 'rgb(229, 229, 229)',
showcountries = False,
domain = dict( x = [], y = [] ),
subunitcolor = "rgb(255, 255, 255)",
)
for y in reversed(range(ROWS)):
for x in range(COLS):
geo_key = 'geo'+str(z+1) if z != 0 else 'geo'
layout[geo_key]['domain']['x'] = [float(x)/float(COLS), float(x+1)/float(COLS)]
layout[geo_key]['domain']['y'] = [float(y)/float(ROWS), float(y+1)/float(ROWS)]
z=z+1
if z > 42:
break
fig = go.Figure(data=data, layout=layout)
fig.update_layout(width=800)
config = {'staticPlot': True}
fig.show(config=config)
I have this error:
TypeError: list indices must be integers or slices, not DataFrame
divisasIndica is a list of DataFrame objects, and I have this code:
datachart=[]
def dchart ():
for i in divisasIndica[:]:
df=divisasIndica[i]
data = [ dict(
type = 'candlestick',
open = df.Open,
high = df.High,
low = df.Low,
close = df.Close,
x = df.index,
rsi=df.RSI,
SMA50=df.SMA50,
SMA200=df.SMA200,
SAR=df.SAR,
yaxis = 'y2',
name = tit,
increasing = dict( line = dict( color = INCREASING_COLOR ) ),
decreasing = dict( line = dict( color = DECREASING_COLOR ) ),
) ]
layout=dict()
fig = dict( data=data, layout=layout )
datachart.append(fig)
The question is can't I read a list of DataFrames with for?
The values of your list are already dataframes, no need to try to get them using an index. for i in divisasIndica gives you all the elements of divisasIndica directly, not their indexes. And there is no need to do divisasIndica[:].
Change your code to this:
datachart=[]
def dchart ():
for df in divisasIndica:
data = [ dict(
type = 'candlestick',
open = df.Open,
high = df.High,
low = df.Low,
close = df.Close,
x = df.index,
rsi=df.RSI,
SMA50=df.SMA50,
SMA200=df.SMA200,
SAR=df.SAR,
yaxis = 'y2',
name = tit,
increasing = dict( line = dict( color = INCREASING_COLOR ) ),
decreasing = dict( line = dict( color = DECREASING_COLOR ) ),
) ]
layout=dict()
fig = dict( data=data, layout=layout )
datachart.append(fig)
datachart=[]
def dchart (divisasIndica):
for df in divisasIndica:
data = [ dict(
type = 'candlestick',
open = df.Open,
high = df.High,
low = df.Low,
close = df.Close,
x = df.index,
rsi=df.RSI,
SMA50=df.SMA50,
SMA200=df.SMA200,
SAR=df.SAR,
yaxis = 'y2',
name = titu,
increasing = dict( line = dict( color = INCREASING_COLOR ) ),
decreasing = dict( line = dict( color = DECREASING_COLOR ) ),
) ]
layout=dict()
fig = dict( data=data, layout=layout )
datachart.append(fig)
The goal is to plot some data using plotly where the text param contains multiple columns.
Here is my DataFrame:
import pandas as pd
import numpy as np
import plotly as py
import plotly.graph_objs as go
np.random.seed(1)
df = pd.DataFrame({'Mean Age': np.random.randint(40,60,10),
'Percent': np.random.randint(20,80,10),
'Number Column': np.random.randint(100,500,10)},
index=list('ABCDEFGHIJ'))
df.index.name = 'Text Column'
df = df.sort_values('Mean Age')
Here is an example of how I plotted the data with text from one column to show on hover:
# trace for Percent
trace0 = go.Scatter(
x = df.index,
y = df['Percent'],
name = 'Percent',
text = df['Mean Age'], # text to show on hover from df column
mode = 'lines+markers',
line = dict(
color = ('rgb(0,0,255)'), # blue
width = 4)
)
layout = dict(title = 'Test Plot',
xaxis = dict(title = 'Text Column'),
yaxis = dict(title = 'Percent'),
)
data = [trace0]
fig = dict(data=data, layout=layout)
py.offline.plot(fig, filename = 'Test_Plot.html')
I am looking to add another column's data to the text param. I can accomplish this by doing some list comprehensions but is there an easier/more efficient way to do this?
I am looking for an output similar to what is below but in a more efficient way than using list comprehension:
# column values to list
num = list(df['Number Column'])
age = list(df['Mean Age'])
# trace for Percent
trace0 = go.Scatter(
x = df.index,
y = df['Percent'],
name = 'Percent',
# list comprehension to get the data to show
text = [f'Number Column: {x}; Mean Age: {y}' for x,y in list(zip(num, age))],
mode = 'lines+markers',
line = dict(
color = ('rgb(0,0,255)'), # blue
width = 4)
)
layout = dict(title = 'Test Plot',
xaxis = dict(title = 'Text Column'),
yaxis = dict(title = 'Percent'),
)
data = [trace0]
fig = dict(data=data, layout=layout)
py.offline.plot(fig, filename = 'Test_Plot_Output.html')
You could also do something along the lines of the following:
trace0 = go.Scatter(
x = df.index,
y = df['Percent'],
name = 'Percent',
# string concatenation in pandas
# also the <br> puts the data on a new line in the hover text
text = "Number Column: " + df["Number Column"].astype(str) + "<br>Mean Age: " + df["Mean Age"].astype(str),
mode = 'lines+markers',
line = dict(
color = ('rgb(0,0,255)'), # blue
width = 4)
)