Is there anyway for plotly to make a gantt chart? - gantt-chart

Help. I'm trying to make a Gantt chart in plotly, and I'm just not seeing
a way to do it.
A Gantt chart is similar to a horizontal bar chart, with a 'starting' point
coordinate given for each bar.
So it looks like:
XXXXX
XXXXXX
XXXXXXXXXXXXXXXXXXXXX
XXXX
I can't find a way to make the 'bars' in a bar chart start at a specific
X coordinate.
Any tips/tricks/hints?

Here are my results... not a perfect gantt chart,
(you can not change the size of the bar, no dependencies
between entries) but good enough for my purpose:
A code snippet to show how I did the transparent trace(s):
// loop through all obj's to draw, for each one
// make a transparent offset to mimic gantt chart.
traces.push( {
x: [ obj.totalrunTime ],
y: [ key ],
name: key,
text: [ obj.totalrunTime+' '+key ],
orientation: 'h',
marker: { color: obj.color },
type: 'bar'
});
traces.push( {
x: [ offset ],
y: [ key ],
showlegend: false,
orientation: 'h',
marker: { color: 'rgba(255,255,255,0)' },
hoverinfo: "none",
name: key,
type: 'bar'
});
offset = offset + jobs[key].totalrunTime;

Riddhiman has made a great solution for this in R.
http://moderndata.plot.ly/gantt-charts-in-r-using-plotly/ .
I was reluctant in the beginning because of the loop but it gives a tremendous amount of freedom.
I added a bit of extra layout for my needs:
p <- plot_ly()
for(i in 1:(nrow(df) - 1)){
p <- add_trace(p,
x = c(df$Start[i], df$Start[i] + df$Duration[i]), # x0, x1
y = c(i, i), # y0, y1
mode = "lines+markers+text",
marker = list(color = df$color[i]
,symbol = "line-ns-open"
,size = 13), #markers ensures visability
text = c(df$text[i],"") # adds a text string
textposition = "middle left" #to the left of the bar
line = list(color = df$color[i]
, width = 20),
showlegend = F,
hoverinfo = "text",
# Create custom hover text
text = paste0("<b>Task:</b> ", df$Task[i], "<br>",
"<b>Duration:</b> ", df$Duration[i], " days<br>",
"<b>Resource:</b> ", df$Resource[i]),
evaluate = T # needed to avoid lazy loading
)}

Yes!
import plotly.figure_factory as ff
ff.create_gantt(df)
Plotly has built in gantt charts. You do not need to create them from a bar chart. You can feed it a list of dictionaries or you can feed it a dataframe. If you do the latter make sure to tell the figure which column is the task, and your start and end dates. I found it much easier to use datatime and label the columns Start and Finish. this way the gantt chart will read them automatically the documentation is in the following link.
https://plot.ly/python/gantt/

If you use plotly, use group_tasks=True if you want to want to have similar keys in one line.

R Package vistime uses Plotly to create a Gantt chart. There is also a kind of intelligent vertical distribution of the events in the chart, such that the chart is not larger than needed.
install.packages("vistime")
library(vistime)
pres <- data.frame(Position = rep(c("President", "Vice"), each = 3),
Name = c("Washington", rep(c("Adams", "Jefferson"), 2), "Burr"),
start = c("1789-03-29", "1797-02-03", "1801-02-03"),
end = c("1797-02-03", "1801-02-03", "1809-02-03"),
color = c('#cbb69d', '#603913', '#c69c6e'),
fontcolor = c("black", "white", "black"))
vistime(pres, events="Position", groups="Name", title="Presidents of the USA")

This is a Gantt chart with plotly
The content of "test.data":
The format is: Task\tStart\tFinish\tResource
A 2017-04-26 10:12:04 2017-04-26 10:34:18 Done
B 2017-04-26 10:54:18 2017-04-26 11:07:41 Done
C 2017-04-26 11:47:42 2017-04-26 12:25:12 Done
A 2017-04-26 12:35:12 2017-04-26 13:28:29 Done
B 2017-04-26 13:48:29 2017-04-26 14:07:50 Done
A 2017-04-26 14:37:50 2017-04-26 15:12:08 Done
B 2017-04-26 15:32:09 2017-04-26 15:44:43 Done
C 2017-04-27 07:14:46 2017-04-27 07:29:48 Done
A 2017-04-27 08:49:48 2017-04-27 09:06:07 Done
A 2017-04-27 09:38:03 2017-04-27 09:59:03 Done
C 2017-04-27 10:09:03 2017-04-27 10:27:40 Done
B 2017-04-27 11:07:40 B2017-04-27 11:23:48 Done
Here is the code:
import plotly.offline as offline
import plotly.plotly as py
import plotly.figure_factory as ff
import plotly.graph_objs as go
import plotly.io as pio
import pandas as pd
import numpy as np
filePath="test.data"
df = pd.read_table(filePath,
header=None,
usecols=[0,1,2,3],
sep='\t',
converters={1:np.datetime64, 2:np.datetime64},
)
df.columns = ['Task', 'Start', 'Finish', 'Resource']
colors = {'Done': 'rgb(0, 240, 0)',}
fig = ff.create_gantt(df,
title='My Tasks',
bar_width=0.1,
showgrid_x=False,
showgrid_y=False,
colors=colors,
#colors='Viridis',
index_col='Resource',
show_colorbar = True,
group_tasks=True,
)
fig['layout'].update(plot_bgcolor = 'rgba(0,0,0,250)',
paper_bgcolor = 'rgba(0,0,0,0)',
showlegend = True,
violinmode='overlay',
colorway = ['rgb(0, 150, 0)'],
)
pio.write_image(fig, 'testdata.pdf', format='pdf', width=1000, height=1000, scale=1)
The output:

Related

Plotly : How to enable text label in line graph for the last value?

I am trying to build a graph where the line graph should show the value of only the last element in some beautiful formating.
line graph with no text at end
Now the current method of the text shows for all elements and is a straight text that creates a lot of collisions with different lines in the same graph and looks clumsy.
Will be very nice to achieve something as mentioned in the below image.
desired line graph with text
This is now handled through:
legendgroup = d.name
Plot 1: All
Plot 2: Deselect GOOG in the legend and see that the marker disappears as well:
Complet code:
# imports
import pandas as pd
import plotly.express as px
# data
df = px.data.stocks()
df = df.drop('AMZN', axis = 1)
colors = px.colors.qualitative.T10
# plotly
fig = px.line(df,
x = 'date',
y = [c for c in df.columns if c != 'date'],
template = 'plotly_dark',
color_discrete_sequence = colors,
title = 'Stocks',
)
# move legend
fig.layout.legend.x = -0.3
# add traces for annotations and text for end of lines
for i, d in enumerate(fig.data):
fig.add_scatter(x=[d.x[-1]], y = [d.y[-1]],
mode = 'markers+text',
text = d.y[-1],
textfont = dict(color=d.line.color),
textposition='middle right',
marker = dict(color = d.line.color, size = 12),
legendgroup = d.name,
showlegend=False)
fig.show()

How could I edit my code to plot 4D contour something similar to this example in python?

Similar to many other researchers on stackoverflow who are trying to plot a contour graph out of 4D data (i.e., X,Y,Z and their corresponding value C), I am attempting to plot a 4D contour map out of my data. I have tried many of the suggested solutions in stackover flow. From all of the plots suggested this, and this were the closest to what I want but sill not quite what I need in terms of data interpretation. Here is the ideal plot example: (source)
Here is a subset of the data. I put it on the dropbox. Once this data is downloaded to the directory of the python file, the following code will work. I have modified this script from this post.
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.tri as mtri
#####Importing the data
df = pd.read_csv('Data_4D_plot.csv')
do_random_pt_example = False;
index_x = 0; index_y = 1; index_z = 2; index_c = 3;
list_name_variables = ['x', 'y', 'z', 'c'];
name_color_map = 'seismic';
if do_random_pt_example:
number_of_points = 200;
x = np.random.rand(number_of_points);
y = np.random.rand(number_of_points);
z = np.random.rand(number_of_points);
c = np.random.rand(number_of_points);
else:
x = df['X'].to_numpy();
y = df['Y'].to_numpy();
z = df['Z'].to_numpy();
c = df['C'].to_numpy();
#end
#-----
# We create triangles that join 3 pt at a time and where their colors will be
# determined by the values of their 4th dimension. Each triangle contains 3
# indexes corresponding to the line number of the points to be grouped.
# Therefore, different methods can be used to define the value that
# will represent the 3 grouped points and I put some examples.
triangles = mtri.Triangulation(x, y).triangles;
choice_calcuation_colors = 2;
if choice_calcuation_colors == 1: # Mean of the "c" values of the 3 pt of the triangle
colors = np.mean( [c[triangles[:,0]], c[triangles[:,1]], c[triangles[:,2]]], axis = 0);
elif choice_calcuation_colors == 2: # Mediane of the "c" values of the 3 pt of the triangle
colors = np.median( [c[triangles[:,0]], c[triangles[:,1]], c[triangles[:,2]]], axis = 0);
elif choice_calcuation_colors == 3: # Max of the "c" values of the 3 pt of the triangle
colors = np.max( [c[triangles[:,0]], c[triangles[:,1]], c[triangles[:,2]]], axis = 0);
#end
#----------
###=====adjust this part for the labeling of the graph
list_name_variables[index_x] = 'X (m)'
list_name_variables[index_y] = 'Y (m)'
list_name_variables[index_z] = 'Z (m)'
list_name_variables[index_c] = 'C values'
# Displays the 4D graphic.
fig = plt.figure(figsize = (15,15));
ax = fig.gca(projection='3d');
triang = mtri.Triangulation(x, y, triangles);
surf = ax.plot_trisurf(triang, z, cmap = name_color_map, shade=False, linewidth=0.2);
surf.set_array(colors); surf.autoscale();
#Add a color bar with a title to explain which variable is represented by the color.
cbar = fig.colorbar(surf, shrink=0.5, aspect=5);
cbar.ax.get_yaxis().labelpad = 15; cbar.ax.set_ylabel(list_name_variables[index_c], rotation = 270);
# Add titles to the axes and a title in the figure.
ax.set_xlabel(list_name_variables[index_x]); ax.set_ylabel(list_name_variables[index_y]);
ax.set_zlabel(list_name_variables[index_z]);
ax.view_init(elev=15., azim=45)
plt.show()
Here would be the output:
Although it looks brilliant, it is not quite what I am looking for (the above contour map example). I have modified the following script from this post in the hope to reach the required graph, however, the chart looks nothing similar to what I was expecting (something similar to the previous output graph). Warning: the following code may take some time to run.
import matplotlib
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
df = pd.read_csv('Data_4D_plot.csv')
x = df['X'].to_numpy();
y = df['Y'].to_numpy();
z = df['Z'].to_numpy();
cc = df['C'].to_numpy();
# convert to 2d matrices
Z = np.outer(z.T, z)
X, Y = np.meshgrid(x, y)
C = np.outer(cc.T,cc)
# fourth dimention - colormap
# create colormap according to cc-value
color_dimension = C # change to desired fourth dimension
minn, maxx = color_dimension.min(), color_dimension.max()
norm = matplotlib.colors.Normalize(minn, maxx)
m = plt.cm.ScalarMappable(norm=norm, cmap='jet')
m.set_array([])
fcolors = m.to_rgba(color_dimension)
# plot
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_surface(X,Y,Z, rstride=1, cstride=1, facecolors=fcolors, vmin=minn, vmax=maxx, shade=False)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
plt.show()
Now I was wondering from our kind community and experts if you can help me to plot a contour figure similar to the example graph (image one in this post), where the contours are based on the values within the range of C?

HoverTool on Bokeh: Time format issue (date do not appear correctly) x axis from datetime DF column in pandas

Here the code I wrote.
I took the data from pandas DF (not pasted here).
The x values are from DF index columns that is a DateTime column.
The issue that I want to resolve is in line:
TOOLTIPS = [("index", "$index"),("(Time,Temperature)", "($x, $y)"),]
when I have to change the $x format to a correct format in order to see the the time format in the hover window on the bokeh plot.
see the python code
import datetime as dt
from bokeh.plotting import figure, output_file, show
from bokeh.layouts import gridplot
from bokeh.models import ColumnDataSource, CDSView, BooleanFilter
from bokeh.models import DatetimeTickFormatter
x=df_bases.index
y0=df_bases["base_1"]
y1=df_bases["base_5"]
y2=df_bases["base_12"]
# output to static HTML file
output_file("temperatures from thermocouples.html")
# add some renderers
output_file("Thermocouples temperature.html", title="Thermocouples temperature")
TOOLTIPS = [("index", "$index"),("(Time,Temperature)", "($x, $y)"),]
# create a new plot with a datetime axis type
p = figure( tooltips=TOOLTIPS , plot_width=1250, plot_height=580, x_axis_type="datetime", x_axis_label='Time',
y_axis_label='Temperature [°C]', title="Thermocouples temperature")
p.line(x, y0, legend="thermocouple 1", line_width=1 , color='navy', alpha=1)
p.line(x, y1, legend="thermocouple 5", color="green")
p.line(x, y2, legend="thermocouple 12", line_width=1 , color='orange', alpha=1)#, line_dash="4 4")
p.border_fill_color = "whitesmoke"
p.xaxis.formatter=DatetimeTickFormatter(
microseconds = ['%Y-%m-%d %H:%M:%S.%f'],
milliseconds = ['%Y-%m-%d %H:%M:%S.%3N'],
seconds = ["%Y-%m-%d %H:%M:%S"],
minsec = ["%Y-%m-%d %H:%M:%S"],
minutes = ["%Y-%m-%d %H:%M:%S"],
hourmin = ["%Y-%m-%d %H:%M:%S"],
hours=["%Y-%m-%d %H:%M:%S"],
days=["%Y-%m-%d %H:%M:%S"],
months=["%Y-%m-%d %H:%M:%S"],
years=["%Y-%m-%d %H:%M:%S"],
)
p.title.align = 'center'
# create a column data source for the plots to share
source = ColumnDataSource(data=dict(x=x, y0=y0, y1=y1, y2=y2))
# create a view of the source for one plot to use
view = CDSView(source=source)
# show the results
show(p)
Currently (as of Bokeh 1.2) the hover tool does not have any "always on" mode It only hovers in response to hit-testing glyphs that are added to the plot. Additionally there is no way to apply formatting to "special vars" like $x (that will be possible starting in Bokeh 2.0). Custom formatters can only be applied to hover tooltips for data columns. Given that, my best suggestion is to switch to using #xinstead (which interrogates the "x" data column, not the x mouse position". If you do that, you can use all the techniques in the Formatting Tooltip Fields section of the docs.
Since you did not provide a complete example (no data to run), I can only provide partial untested suggestions:
# use #x{%F} to specify the %F datetime format (or choose another) for the x column
TOOLTIPS = [("index", "$index"),("(Time,Temperature)", "(#x{%F}, $y)")]
# tell bokeh to use the "datetime" formatter for the x column
p.hover.formatters = {'x': 'datetime'}
# just a suggestion, often useful for timeseries plots
p.hover.mode = 'vline'

Data and X axis labels not align

Trying to plot X axis (Event) values on their respective x Axis. Y axis is relative to Time (of the day) when and how long the event lasted. The first label and data plotted are correct. However, the second set of data appears to skip over the major x axis tick and is placed afterwards but before the next major x axis tick. This is repeated for each additional x Axis value plotted. The data does not show a problem with which X axis it should appear on.
Defined the data (source) and can plot the issue with about 50 lines of code.
from bokeh.io import output_file
from bokeh.models import ColumnDataSource, LabelSet
from bokeh.plotting import figure, show
from bokeh.models.formatters import NumeralTickFormatter
import pandas as pd
import math
output_file("events.html", mode="inline")
x1 = []
y1 = []
x2 = []
y2 = []
colorList = []
shortNames = []
nameAndId = ["Event1", 0]
x1.append(nameAndId)
y1.append(33470)
x2.append(nameAndId)
y2.append(33492)
colorList.append("red")
shortNames.append("Evt1")
nameAndId = ["Event2", 1]
x1.append(nameAndId)
y1.append(34116)
x2.append(nameAndId)
y2.append(34151)
colorList.append("green")
shortNames.append("Evt2")
xAxisLabels = ["Event1", "Event2"]
data = {"x1": x1, "y1": y1, "x2": x2, "y2": y2, "color": colorList,\
"shortName": shortNames}
eventDF = pd.DataFrame(data=data,
columns=("x1", "y1", "x2", "y2", "color",\
"shortName"))
source = ColumnDataSource(eventDF)
yRange = [34151, 33470]
p = figure(plot_width=700, plot_height=750, x_range=xAxisLabels,\
y_range=yRange, output_backend="webgl")
p.xaxis.major_label_orientation = math.pi / -2
p.segment(x0="x1",y0="y1",x1="x2",y1="y2", source=source, color="color"\
line_width=12)
p.yaxis[0].formatter = NumeralTickFormatter(format="00:00:00")
p.xaxis.axis_label = "Events"
labels = LabelSet(x="x2",y="y2", text="shortName", text_font_size="8pt"\
text_color="black", level="glyph", x_offset=-6,\
y_offset=-5, render_mode="canvas", angle=270,\
angle_units="deg", source=source)
p.add_layout(labels)
show(p)
I'm thinking this is something simple I've over-looked like a xAxis formatter. I've tried to define one but none seem to work for my use case. The data doesn't seem to be associated to the xAxisLabel. I Expect Event 1 to show on the first X axis tick with Event 2 on the second X axis tick. Event 1 is correct but for each event afterwards, every major X axis tick is skipped with the data residing between tick marks.
The issue in your code is that the actual value for the x-coordinate you are supplying is:
nameAndId = ["Event2", 1]
This kind of list with a category name and a number in a list is understood by Bokeh as a categorical offset. You are explicitly telling Bokeh to position the glyph a distance of 1 (in "synthetic" coordinates) away from the location of "Event2". The reason things "work" for the Event1 case is that the offset in that case is 0:
nameAndId = ["Event1", 0]
I'm not sure what you are trying to accomplish by passing these lists with the second numerical value, so I can't really offer any additional suggestion except to say that it should probably not be passed on to Bokeh.

Matplotlib Multi-colored Title (Text) - in practice

There is an example here for how to create a multi-colored text title.
However, I want to apply this to a plot that already has a figure in it.
For example, if I apply it to this (same code as with the example minus a few extras and with another figure)...:
plt.rcdefaults()
import matplotlib.pyplot as plt
%matplotlib inline
from matplotlib import transforms
fig = plt.figure(figsize=(4,3), dpi=300)
def rainbow_text(x,y,ls,lc,**kw):
t = plt.gca().transData
fig = plt.gcf()
plt.show()
#horizontal version
for s,c in zip(ls,lc):
text = plt.text(x,y," "+s+" ",color=c, transform=t, **kw)
text.draw(fig.canvas.get_renderer())
ex = text.get_window_extent()
t = transforms.offset_copy(text._transform, x=ex.width, units='dots')
plt.figure()
rainbow_text(0.5,0.5,"all unicorns poop rainbows ! ! !".split(),
['red', 'orange', 'brown', 'green', 'blue', 'purple', 'black'],
size=40)
...the result is 2 plots with the title enlarged.
This sort of makes sense to me because I'm using plt. two times.
But how do I integrate it so that it only refers to the first instance of plt. in creating the title?
Also, about this line:
t = transforms.offset_copy(text._transform, x=ex.width, units='dots')
I notice it can alter the spacing between words, but when I play with the values of x, results are not predictable (spacing is inconsistent between words).
How can I meaningfully adjust that value?
And finally, where it says "units='dots'", what are the other options? Are 'dots' 1/72nd of an inch (and is that the default for Matplotlib?)?
How can I convert units from dots to inches?
Thanks in advance!
In fact the bounding box of the text comes in units unlike the ones used, for example, in scatterplot. Text is a different kind of object that gets somehow redraw if you resize the window or change the ratio. By having a stabilized window you can ask the coordinates of the bounding box in plot units and build your colored text that way:
a = "all unicorns poop rainbows ! ! !".split()
c = ['red', 'orange', 'brown', 'green', 'blue', 'purple', 'black']
f = plt.figure(figsize=(4,3), dpi=120)
ax = f.add_subplot(111)
r = f.canvas.get_renderer()
space = 0.1
w = 0.5
counter = 0
for i in a:
t = ax.text(w, 1.2, a[counter],color=c[counter],fontsize=12,ha='left')
transf = ax.transData.inverted()
bb = t.get_window_extent(renderer=f.canvas.renderer)
bb = bb.transformed(transf)
w = w + bb.xmax-bb.xmin + space
counter = counter + 1
plt.ylim(0.5,2.5)
plt.xlim(0.6,1.6)
plt.show()
, which results in:
This, however, is still not ideal since you need to keep controlling the size of your plot axis to obtain the correct spaces between words. This is somewhat arbitrary but if you manage to do your program with such a control it's feasible to use plot units to achieve your intended purpose.
ORIGINAL POST:
plt. is just the call to the library. In truth you are creating an instance of plt.figure in the global scope (so it can be seen in locally in the function). Due to this you are overwriting the figure because you use the same name for the variable (so it's just one single instance in the end). To solve this try controlling the names of your figure instances. For example:
import matplotlib.pyplot as plt
#%matplotlib inline
from matplotlib import transforms
fig = plt.figure(figsize=(4,3), dpi=300)
#plt.show(fig)
def rainbow_text(x,y,ls,lc,**kw):
t = plt.gca().transData
figlocal = plt.gcf()
#horizontal version
for s,c in zip(ls,lc):
text = plt.text(x,y," "+s+" ",color=c, transform=t, **kw)
text.draw(figlocal.canvas.get_renderer())
ex = text.get_window_extent()
t = transforms.offset_copy(text._transform, x=ex.width, units='dots')
plt.show(figlocal) #plt.show((figlocal,fig))
#plt.figure()
rainbow_text(0.5,0.5,"all unicorns poop rainbows ! ! !".split(),
['red', 'orange', 'brown', 'green', 'blue', 'purple', 'black'],
size=40,)
I've commented several instructions but notice I give a different name for the figure local to the function (figlocal). Also notice that in my examples of show I control directly which figure should be shown.
As for your other questions notice you can use other units as can be seen in the function documentation:
Return a new transform with an added offset.
args:
trans is any transform
kwargs:
fig is the current figure; it can be None if units are 'dots'
x, y give the offset
units is 'inches', 'points' or 'dots'
EDIT: Apparently there's some kind of problem with the extents of the bounding box for text that does not give the correct width of the word and thus the space between words is not stable. My advise is to use the latex functionality of Matplotlib to write the colors in the same string (so only one call of plt.text). You can do it like this:
import matplotlib
import matplotlib.pyplot as plt
matplotlib.use('pgf')
from matplotlib import rc
rc('text',usetex=True)
rc('text.latex', preamble=r'\usepackage{color}')
a = "all unicorns poop rainbows ! ! !".split()
c = ['red', 'orange', 'brown', 'green', 'blue', 'purple', 'black']
st = ''
for i in range(len(a)):
st = st + r'\textcolor{'+c[i]+'}{'+a[i]+'}'
plt.text(0.5,0.5,st)
plt.show()
This however is not an ideal solution. The reason is that you need to have Latex installed, including the necessary packages (notice I'm using the color package). Take a look at Yann answer in this question: Partial coloring of text in matplotlib
#armatita: I think your answer actually does what I need. I thought I needed display coordinates instead, but it looks like I can just use axis 1 coordinates, if that's what this is (I'm planning on using multiple axes via subplot2grid). Here's an example:
import matplotlib.pyplot as plt
%matplotlib inline
dpi=300
f_width=4
f_height=3
f = plt.figure(figsize=(f_width,f_height), dpi=dpi)
ax1 = plt.subplot2grid((100,115), (0,0), rowspan=95, colspan=25)
ax2 = plt.subplot2grid((100,115), (0,30), rowspan=95, colspan=20)
ax3 = plt.subplot2grid((100,115), (0,55), rowspan=95, colspan=35)
ax4 = plt.subplot2grid((100,115), (0,95), rowspan=95, colspan=20)
r = f.canvas.get_renderer()
t = ax1.text(.5, 1.1, 'a lot of text here',fontsize=12,ha='left')
space=0.1
w=.5
transf = ax1.transData.inverted()
bb = t.get_window_extent(renderer=f.canvas.renderer)
bb = bb.transformed(transf)
e = ax1.text(.5+bb.width+space, 1.1, 'text',fontsize=12,ha='left')
print(bb)
plt.show()
I'm not sure what you mean about controlling the axis size, though. Are you referring to using the code in different environments or exporting the image in different sizes? I plan on having the image used in the same environment and in the same size (per instance of using this approach), so I think it will be okay. Does my logic make sense? I have a weak grasp on what's really going on, so I hope so. I would use it with a function (via splitting the text) like you did, but there are cases where I need to split on other characters (i.e. when a word in parentheses should be colored, but not the parentheses). Maybe I can just put a delimiter in there like ','? I think I need a different form of .split() because it didn't work when I tried it.
At any rate, if I can implement this across all of my charts, it will save me countless hours. Thank you so much!
Here is an example where there are 2 plots and 2 instances of using the function for posterity:
import matplotlib.pyplot as plt
%matplotlib inline
dpi=300
f_width=4
f_height=3
f = plt.figure(figsize=(f_width,f_height), dpi=dpi)
ax1 = plt.subplot2grid((100,60), (0,0), rowspan=95, colspan=30)
ax2 = plt.subplot2grid((100,60), (0,30), rowspan=95, colspan=30)
f=f #Name for figure
string = str("Group 1 ,vs. ,Group 2 (,sub1,) and (,sub2,)").split(',')
color = ['black','red','black','green','black','blue','black']
xpos = .5
ypos = 1.2
axis=ax1
#No need to include space if incuded between delimiters above
#space = 0.1
def colortext(f,string,color,xpos,ypos,axis):
#f=figure object name (i.e. fig, f, figure)
r = f.canvas.get_renderer()
counter = 0
for i in string:
t = axis.text(xpos, ypos, string[counter],color=color[counter],fontsize=12,ha='left')
transf = axis.transData.inverted()
bb = t.get_window_extent(renderer=f.canvas.renderer)
bb = bb.transformed(transf)
xpos = xpos + bb.xmax-bb.xmin
counter = counter + 1
colortext(f,string,color,xpos,ypos,axis)
string2 = str("Group 1 part 2 ,vs. ,Group 2 (,sub1,) and (,sub2,)").split(',')
ypos2=1.1
colortext(f,string2,color,xpos,ypos2,axis)
plt.show()

Resources