mirror of
https://github.com/aykhans/AzSuicideDataVisualization.git
synced 2025-04-22 10:28:02 +00:00
1037 lines
34 KiB
Python
1037 lines
34 KiB
Python
from __future__ import absolute_import
|
|
|
|
from numbers import Number
|
|
|
|
import copy
|
|
|
|
from plotly import exceptions, optional_imports
|
|
import plotly.colors as clrs
|
|
from plotly.figure_factory import utils
|
|
import plotly.graph_objects as go
|
|
|
|
pd = optional_imports.get_module("pandas")
|
|
|
|
REQUIRED_GANTT_KEYS = ["Task", "Start", "Finish"]
|
|
|
|
|
|
def _get_corner_points(x0, y0, x1, y1):
|
|
"""
|
|
Returns the corner points of a scatter rectangle
|
|
|
|
:param x0: x-start
|
|
:param y0: y-lower
|
|
:param x1: x-end
|
|
:param y1: y-upper
|
|
:return: ([x], [y]), tuple of lists containing the x and y values
|
|
"""
|
|
|
|
return ([x0, x1, x1, x0], [y0, y0, y1, y1])
|
|
|
|
|
|
def validate_gantt(df):
|
|
"""
|
|
Validates the inputted dataframe or list
|
|
"""
|
|
if pd and isinstance(df, pd.core.frame.DataFrame):
|
|
# validate that df has all the required keys
|
|
for key in REQUIRED_GANTT_KEYS:
|
|
if key not in df:
|
|
raise exceptions.PlotlyError(
|
|
"The columns in your dataframe must include the "
|
|
"following keys: {0}".format(", ".join(REQUIRED_GANTT_KEYS))
|
|
)
|
|
|
|
num_of_rows = len(df.index)
|
|
chart = []
|
|
for index in range(num_of_rows):
|
|
task_dict = {}
|
|
for key in df:
|
|
task_dict[key] = df.iloc[index][key]
|
|
chart.append(task_dict)
|
|
|
|
return chart
|
|
|
|
# validate if df is a list
|
|
if not isinstance(df, list):
|
|
raise exceptions.PlotlyError(
|
|
"You must input either a dataframe " "or a list of dictionaries."
|
|
)
|
|
|
|
# validate if df is empty
|
|
if len(df) <= 0:
|
|
raise exceptions.PlotlyError(
|
|
"Your list is empty. It must contain " "at least one dictionary."
|
|
)
|
|
if not isinstance(df[0], dict):
|
|
raise exceptions.PlotlyError("Your list must only " "include dictionaries.")
|
|
return df
|
|
|
|
|
|
def gantt(
|
|
chart,
|
|
colors,
|
|
title,
|
|
bar_width,
|
|
showgrid_x,
|
|
showgrid_y,
|
|
height,
|
|
width,
|
|
tasks=None,
|
|
task_names=None,
|
|
data=None,
|
|
group_tasks=False,
|
|
show_hover_fill=True,
|
|
show_colorbar=True,
|
|
):
|
|
"""
|
|
Refer to create_gantt() for docstring
|
|
"""
|
|
if tasks is None:
|
|
tasks = []
|
|
if task_names is None:
|
|
task_names = []
|
|
if data is None:
|
|
data = []
|
|
|
|
for index in range(len(chart)):
|
|
task = dict(
|
|
x0=chart[index]["Start"],
|
|
x1=chart[index]["Finish"],
|
|
name=chart[index]["Task"],
|
|
)
|
|
if "Description" in chart[index]:
|
|
task["description"] = chart[index]["Description"]
|
|
tasks.append(task)
|
|
|
|
# create a scatter trace for every task group
|
|
scatter_data_dict = dict()
|
|
marker_data_dict = dict()
|
|
|
|
if show_hover_fill:
|
|
hoverinfo = "name"
|
|
else:
|
|
hoverinfo = "skip"
|
|
|
|
scatter_data_template = {
|
|
"x": [],
|
|
"y": [],
|
|
"mode": "none",
|
|
"fill": "toself",
|
|
"hoverinfo": hoverinfo,
|
|
}
|
|
|
|
marker_data_template = {
|
|
"x": [],
|
|
"y": [],
|
|
"mode": "markers",
|
|
"text": [],
|
|
"marker": dict(color="", size=1, opacity=0),
|
|
"name": "",
|
|
"showlegend": False,
|
|
}
|
|
|
|
# create the list of task names
|
|
for index in range(len(tasks)):
|
|
tn = tasks[index]["name"]
|
|
# Is added to task_names if group_tasks is set to False,
|
|
# or if the option is used (True) it only adds them if the
|
|
# name is not already in the list
|
|
if not group_tasks or tn not in task_names:
|
|
task_names.append(tn)
|
|
# Guarantees that for grouped tasks the tasks that are inserted first
|
|
# are shown at the top
|
|
if group_tasks:
|
|
task_names.reverse()
|
|
|
|
color_index = 0
|
|
for index in range(len(tasks)):
|
|
tn = tasks[index]["name"]
|
|
del tasks[index]["name"]
|
|
|
|
# If group_tasks is True, all tasks with the same name belong
|
|
# to the same row.
|
|
groupID = index
|
|
if group_tasks:
|
|
groupID = task_names.index(tn)
|
|
tasks[index]["y0"] = groupID - bar_width
|
|
tasks[index]["y1"] = groupID + bar_width
|
|
|
|
# check if colors need to be looped
|
|
if color_index >= len(colors):
|
|
color_index = 0
|
|
tasks[index]["fillcolor"] = colors[color_index]
|
|
color_id = tasks[index]["fillcolor"]
|
|
|
|
if color_id not in scatter_data_dict:
|
|
scatter_data_dict[color_id] = copy.deepcopy(scatter_data_template)
|
|
|
|
scatter_data_dict[color_id]["fillcolor"] = color_id
|
|
scatter_data_dict[color_id]["name"] = str(tn)
|
|
scatter_data_dict[color_id]["legendgroup"] = color_id
|
|
|
|
# if there are already values append the gap
|
|
if len(scatter_data_dict[color_id]["x"]) > 0:
|
|
# a gap on the scatterplot separates the rectangles from each other
|
|
scatter_data_dict[color_id]["x"].append(
|
|
scatter_data_dict[color_id]["x"][-1]
|
|
)
|
|
scatter_data_dict[color_id]["y"].append(None)
|
|
|
|
xs, ys = _get_corner_points(
|
|
tasks[index]["x0"],
|
|
tasks[index]["y0"],
|
|
tasks[index]["x1"],
|
|
tasks[index]["y1"],
|
|
)
|
|
|
|
scatter_data_dict[color_id]["x"] += xs
|
|
scatter_data_dict[color_id]["y"] += ys
|
|
|
|
# append dummy markers for showing start and end of interval
|
|
if color_id not in marker_data_dict:
|
|
marker_data_dict[color_id] = copy.deepcopy(marker_data_template)
|
|
marker_data_dict[color_id]["marker"]["color"] = color_id
|
|
marker_data_dict[color_id]["legendgroup"] = color_id
|
|
|
|
marker_data_dict[color_id]["x"].append(tasks[index]["x0"])
|
|
marker_data_dict[color_id]["x"].append(tasks[index]["x1"])
|
|
marker_data_dict[color_id]["y"].append(groupID)
|
|
marker_data_dict[color_id]["y"].append(groupID)
|
|
|
|
if "description" in tasks[index]:
|
|
marker_data_dict[color_id]["text"].append(tasks[index]["description"])
|
|
marker_data_dict[color_id]["text"].append(tasks[index]["description"])
|
|
del tasks[index]["description"]
|
|
else:
|
|
marker_data_dict[color_id]["text"].append(None)
|
|
marker_data_dict[color_id]["text"].append(None)
|
|
|
|
color_index += 1
|
|
|
|
showlegend = show_colorbar
|
|
|
|
layout = dict(
|
|
title=title,
|
|
showlegend=showlegend,
|
|
height=height,
|
|
width=width,
|
|
shapes=[],
|
|
hovermode="closest",
|
|
yaxis=dict(
|
|
showgrid=showgrid_y,
|
|
ticktext=task_names,
|
|
tickvals=list(range(len(task_names))),
|
|
range=[-1, len(task_names) + 1],
|
|
autorange=False,
|
|
zeroline=False,
|
|
),
|
|
xaxis=dict(
|
|
showgrid=showgrid_x,
|
|
zeroline=False,
|
|
rangeselector=dict(
|
|
buttons=list(
|
|
[
|
|
dict(count=7, label="1w", step="day", stepmode="backward"),
|
|
dict(count=1, label="1m", step="month", stepmode="backward"),
|
|
dict(count=6, label="6m", step="month", stepmode="backward"),
|
|
dict(count=1, label="YTD", step="year", stepmode="todate"),
|
|
dict(count=1, label="1y", step="year", stepmode="backward"),
|
|
dict(step="all"),
|
|
]
|
|
)
|
|
),
|
|
type="date",
|
|
),
|
|
)
|
|
|
|
data = [scatter_data_dict[k] for k in sorted(scatter_data_dict)]
|
|
data += [marker_data_dict[k] for k in sorted(marker_data_dict)]
|
|
|
|
# fig = dict(
|
|
# data=data, layout=layout
|
|
# )
|
|
fig = go.Figure(data=data, layout=layout)
|
|
return fig
|
|
|
|
|
|
def gantt_colorscale(
|
|
chart,
|
|
colors,
|
|
title,
|
|
index_col,
|
|
show_colorbar,
|
|
bar_width,
|
|
showgrid_x,
|
|
showgrid_y,
|
|
height,
|
|
width,
|
|
tasks=None,
|
|
task_names=None,
|
|
data=None,
|
|
group_tasks=False,
|
|
show_hover_fill=True,
|
|
):
|
|
"""
|
|
Refer to FigureFactory.create_gantt() for docstring
|
|
"""
|
|
if tasks is None:
|
|
tasks = []
|
|
if task_names is None:
|
|
task_names = []
|
|
if data is None:
|
|
data = []
|
|
showlegend = False
|
|
|
|
for index in range(len(chart)):
|
|
task = dict(
|
|
x0=chart[index]["Start"],
|
|
x1=chart[index]["Finish"],
|
|
name=chart[index]["Task"],
|
|
)
|
|
if "Description" in chart[index]:
|
|
task["description"] = chart[index]["Description"]
|
|
tasks.append(task)
|
|
|
|
# create a scatter trace for every task group
|
|
scatter_data_dict = dict()
|
|
# create scatter traces for the start- and endpoints
|
|
marker_data_dict = dict()
|
|
|
|
if show_hover_fill:
|
|
hoverinfo = "name"
|
|
else:
|
|
hoverinfo = "skip"
|
|
|
|
scatter_data_template = {
|
|
"x": [],
|
|
"y": [],
|
|
"mode": "none",
|
|
"fill": "toself",
|
|
"showlegend": False,
|
|
"hoverinfo": hoverinfo,
|
|
"legendgroup": "",
|
|
}
|
|
|
|
marker_data_template = {
|
|
"x": [],
|
|
"y": [],
|
|
"mode": "markers",
|
|
"text": [],
|
|
"marker": dict(color="", size=1, opacity=0),
|
|
"name": "",
|
|
"showlegend": False,
|
|
"legendgroup": "",
|
|
}
|
|
|
|
index_vals = []
|
|
for row in range(len(tasks)):
|
|
if chart[row][index_col] not in index_vals:
|
|
index_vals.append(chart[row][index_col])
|
|
|
|
index_vals.sort()
|
|
|
|
# compute the color for task based on indexing column
|
|
if isinstance(chart[0][index_col], Number):
|
|
# check that colors has at least 2 colors
|
|
if len(colors) < 2:
|
|
raise exceptions.PlotlyError(
|
|
"You must use at least 2 colors in 'colors' if you "
|
|
"are using a colorscale. However only the first two "
|
|
"colors given will be used for the lower and upper "
|
|
"bounds on the colormap."
|
|
)
|
|
|
|
# create the list of task names
|
|
for index in range(len(tasks)):
|
|
tn = tasks[index]["name"]
|
|
# Is added to task_names if group_tasks is set to False,
|
|
# or if the option is used (True) it only adds them if the
|
|
# name is not already in the list
|
|
if not group_tasks or tn not in task_names:
|
|
task_names.append(tn)
|
|
# Guarantees that for grouped tasks the tasks that are inserted
|
|
# first are shown at the top
|
|
if group_tasks:
|
|
task_names.reverse()
|
|
|
|
for index in range(len(tasks)):
|
|
tn = tasks[index]["name"]
|
|
del tasks[index]["name"]
|
|
|
|
# If group_tasks is True, all tasks with the same name belong
|
|
# to the same row.
|
|
groupID = index
|
|
if group_tasks:
|
|
groupID = task_names.index(tn)
|
|
tasks[index]["y0"] = groupID - bar_width
|
|
tasks[index]["y1"] = groupID + bar_width
|
|
|
|
# unlabel color
|
|
colors = clrs.color_parser(colors, clrs.unlabel_rgb)
|
|
lowcolor = colors[0]
|
|
highcolor = colors[1]
|
|
|
|
intermed = (chart[index][index_col]) / 100.0
|
|
intermed_color = clrs.find_intermediate_color(lowcolor, highcolor, intermed)
|
|
intermed_color = clrs.color_parser(intermed_color, clrs.label_rgb)
|
|
tasks[index]["fillcolor"] = intermed_color
|
|
color_id = tasks[index]["fillcolor"]
|
|
|
|
if color_id not in scatter_data_dict:
|
|
scatter_data_dict[color_id] = copy.deepcopy(scatter_data_template)
|
|
|
|
scatter_data_dict[color_id]["fillcolor"] = color_id
|
|
scatter_data_dict[color_id]["name"] = str(chart[index][index_col])
|
|
scatter_data_dict[color_id]["legendgroup"] = color_id
|
|
|
|
# relabel colors with 'rgb'
|
|
colors = clrs.color_parser(colors, clrs.label_rgb)
|
|
|
|
# if there are already values append the gap
|
|
if len(scatter_data_dict[color_id]["x"]) > 0:
|
|
# a gap on the scatterplot separates the rectangles from each other
|
|
scatter_data_dict[color_id]["x"].append(
|
|
scatter_data_dict[color_id]["x"][-1]
|
|
)
|
|
scatter_data_dict[color_id]["y"].append(None)
|
|
|
|
xs, ys = _get_corner_points(
|
|
tasks[index]["x0"],
|
|
tasks[index]["y0"],
|
|
tasks[index]["x1"],
|
|
tasks[index]["y1"],
|
|
)
|
|
|
|
scatter_data_dict[color_id]["x"] += xs
|
|
scatter_data_dict[color_id]["y"] += ys
|
|
|
|
# append dummy markers for showing start and end of interval
|
|
if color_id not in marker_data_dict:
|
|
marker_data_dict[color_id] = copy.deepcopy(marker_data_template)
|
|
marker_data_dict[color_id]["marker"]["color"] = color_id
|
|
marker_data_dict[color_id]["legendgroup"] = color_id
|
|
|
|
marker_data_dict[color_id]["x"].append(tasks[index]["x0"])
|
|
marker_data_dict[color_id]["x"].append(tasks[index]["x1"])
|
|
marker_data_dict[color_id]["y"].append(groupID)
|
|
marker_data_dict[color_id]["y"].append(groupID)
|
|
|
|
if "description" in tasks[index]:
|
|
marker_data_dict[color_id]["text"].append(tasks[index]["description"])
|
|
marker_data_dict[color_id]["text"].append(tasks[index]["description"])
|
|
del tasks[index]["description"]
|
|
else:
|
|
marker_data_dict[color_id]["text"].append(None)
|
|
marker_data_dict[color_id]["text"].append(None)
|
|
|
|
# add colorbar to one of the traces randomly just for display
|
|
if show_colorbar is True:
|
|
k = list(marker_data_dict.keys())[0]
|
|
marker_data_dict[k]["marker"].update(
|
|
dict(
|
|
colorscale=[[0, colors[0]], [1, colors[1]]],
|
|
showscale=True,
|
|
cmax=100,
|
|
cmin=0,
|
|
)
|
|
)
|
|
|
|
if isinstance(chart[0][index_col], str):
|
|
index_vals = []
|
|
for row in range(len(tasks)):
|
|
if chart[row][index_col] not in index_vals:
|
|
index_vals.append(chart[row][index_col])
|
|
|
|
index_vals.sort()
|
|
|
|
if len(colors) < len(index_vals):
|
|
raise exceptions.PlotlyError(
|
|
"Error. The number of colors in 'colors' must be no less "
|
|
"than the number of unique index values in your group "
|
|
"column."
|
|
)
|
|
|
|
# make a dictionary assignment to each index value
|
|
index_vals_dict = {}
|
|
# define color index
|
|
c_index = 0
|
|
for key in index_vals:
|
|
if c_index > len(colors) - 1:
|
|
c_index = 0
|
|
index_vals_dict[key] = colors[c_index]
|
|
c_index += 1
|
|
|
|
# create the list of task names
|
|
for index in range(len(tasks)):
|
|
tn = tasks[index]["name"]
|
|
# Is added to task_names if group_tasks is set to False,
|
|
# or if the option is used (True) it only adds them if the
|
|
# name is not already in the list
|
|
if not group_tasks or tn not in task_names:
|
|
task_names.append(tn)
|
|
# Guarantees that for grouped tasks the tasks that are inserted
|
|
# first are shown at the top
|
|
if group_tasks:
|
|
task_names.reverse()
|
|
|
|
for index in range(len(tasks)):
|
|
tn = tasks[index]["name"]
|
|
del tasks[index]["name"]
|
|
|
|
# If group_tasks is True, all tasks with the same name belong
|
|
# to the same row.
|
|
groupID = index
|
|
if group_tasks:
|
|
groupID = task_names.index(tn)
|
|
tasks[index]["y0"] = groupID - bar_width
|
|
tasks[index]["y1"] = groupID + bar_width
|
|
|
|
tasks[index]["fillcolor"] = index_vals_dict[chart[index][index_col]]
|
|
color_id = tasks[index]["fillcolor"]
|
|
|
|
if color_id not in scatter_data_dict:
|
|
scatter_data_dict[color_id] = copy.deepcopy(scatter_data_template)
|
|
|
|
scatter_data_dict[color_id]["fillcolor"] = color_id
|
|
scatter_data_dict[color_id]["legendgroup"] = color_id
|
|
scatter_data_dict[color_id]["name"] = str(chart[index][index_col])
|
|
|
|
# relabel colors with 'rgb'
|
|
colors = clrs.color_parser(colors, clrs.label_rgb)
|
|
|
|
# if there are already values append the gap
|
|
if len(scatter_data_dict[color_id]["x"]) > 0:
|
|
# a gap on the scatterplot separates the rectangles from each other
|
|
scatter_data_dict[color_id]["x"].append(
|
|
scatter_data_dict[color_id]["x"][-1]
|
|
)
|
|
scatter_data_dict[color_id]["y"].append(None)
|
|
|
|
xs, ys = _get_corner_points(
|
|
tasks[index]["x0"],
|
|
tasks[index]["y0"],
|
|
tasks[index]["x1"],
|
|
tasks[index]["y1"],
|
|
)
|
|
|
|
scatter_data_dict[color_id]["x"] += xs
|
|
scatter_data_dict[color_id]["y"] += ys
|
|
|
|
# append dummy markers for showing start and end of interval
|
|
if color_id not in marker_data_dict:
|
|
marker_data_dict[color_id] = copy.deepcopy(marker_data_template)
|
|
marker_data_dict[color_id]["marker"]["color"] = color_id
|
|
marker_data_dict[color_id]["legendgroup"] = color_id
|
|
|
|
marker_data_dict[color_id]["x"].append(tasks[index]["x0"])
|
|
marker_data_dict[color_id]["x"].append(tasks[index]["x1"])
|
|
marker_data_dict[color_id]["y"].append(groupID)
|
|
marker_data_dict[color_id]["y"].append(groupID)
|
|
|
|
if "description" in tasks[index]:
|
|
marker_data_dict[color_id]["text"].append(tasks[index]["description"])
|
|
marker_data_dict[color_id]["text"].append(tasks[index]["description"])
|
|
del tasks[index]["description"]
|
|
else:
|
|
marker_data_dict[color_id]["text"].append(None)
|
|
marker_data_dict[color_id]["text"].append(None)
|
|
|
|
if show_colorbar is True:
|
|
showlegend = True
|
|
for k in scatter_data_dict:
|
|
scatter_data_dict[k]["showlegend"] = showlegend
|
|
# add colorbar to one of the traces randomly just for display
|
|
# if show_colorbar is True:
|
|
# k = list(marker_data_dict.keys())[0]
|
|
# marker_data_dict[k]["marker"].update(
|
|
# dict(
|
|
# colorscale=[[0, colors[0]], [1, colors[1]]],
|
|
# showscale=True,
|
|
# cmax=100,
|
|
# cmin=0,
|
|
# )
|
|
# )
|
|
|
|
layout = dict(
|
|
title=title,
|
|
showlegend=showlegend,
|
|
height=height,
|
|
width=width,
|
|
shapes=[],
|
|
hovermode="closest",
|
|
yaxis=dict(
|
|
showgrid=showgrid_y,
|
|
ticktext=task_names,
|
|
tickvals=list(range(len(task_names))),
|
|
range=[-1, len(task_names) + 1],
|
|
autorange=False,
|
|
zeroline=False,
|
|
),
|
|
xaxis=dict(
|
|
showgrid=showgrid_x,
|
|
zeroline=False,
|
|
rangeselector=dict(
|
|
buttons=list(
|
|
[
|
|
dict(count=7, label="1w", step="day", stepmode="backward"),
|
|
dict(count=1, label="1m", step="month", stepmode="backward"),
|
|
dict(count=6, label="6m", step="month", stepmode="backward"),
|
|
dict(count=1, label="YTD", step="year", stepmode="todate"),
|
|
dict(count=1, label="1y", step="year", stepmode="backward"),
|
|
dict(step="all"),
|
|
]
|
|
)
|
|
),
|
|
type="date",
|
|
),
|
|
)
|
|
|
|
data = [scatter_data_dict[k] for k in sorted(scatter_data_dict)]
|
|
data += [marker_data_dict[k] for k in sorted(marker_data_dict)]
|
|
|
|
# fig = dict(
|
|
# data=data, layout=layout
|
|
# )
|
|
fig = go.Figure(data=data, layout=layout)
|
|
return fig
|
|
|
|
|
|
def gantt_dict(
|
|
chart,
|
|
colors,
|
|
title,
|
|
index_col,
|
|
show_colorbar,
|
|
bar_width,
|
|
showgrid_x,
|
|
showgrid_y,
|
|
height,
|
|
width,
|
|
tasks=None,
|
|
task_names=None,
|
|
data=None,
|
|
group_tasks=False,
|
|
show_hover_fill=True,
|
|
):
|
|
"""
|
|
Refer to FigureFactory.create_gantt() for docstring
|
|
"""
|
|
|
|
if tasks is None:
|
|
tasks = []
|
|
if task_names is None:
|
|
task_names = []
|
|
if data is None:
|
|
data = []
|
|
showlegend = False
|
|
|
|
for index in range(len(chart)):
|
|
task = dict(
|
|
x0=chart[index]["Start"],
|
|
x1=chart[index]["Finish"],
|
|
name=chart[index]["Task"],
|
|
)
|
|
if "Description" in chart[index]:
|
|
task["description"] = chart[index]["Description"]
|
|
tasks.append(task)
|
|
|
|
# create a scatter trace for every task group
|
|
scatter_data_dict = dict()
|
|
# create scatter traces for the start- and endpoints
|
|
marker_data_dict = dict()
|
|
|
|
if show_hover_fill:
|
|
hoverinfo = "name"
|
|
else:
|
|
hoverinfo = "skip"
|
|
|
|
scatter_data_template = {
|
|
"x": [],
|
|
"y": [],
|
|
"mode": "none",
|
|
"fill": "toself",
|
|
"hoverinfo": hoverinfo,
|
|
"legendgroup": "",
|
|
}
|
|
|
|
marker_data_template = {
|
|
"x": [],
|
|
"y": [],
|
|
"mode": "markers",
|
|
"text": [],
|
|
"marker": dict(color="", size=1, opacity=0),
|
|
"name": "",
|
|
"showlegend": False,
|
|
}
|
|
|
|
index_vals = []
|
|
for row in range(len(tasks)):
|
|
if chart[row][index_col] not in index_vals:
|
|
index_vals.append(chart[row][index_col])
|
|
|
|
index_vals.sort()
|
|
|
|
# verify each value in index column appears in colors dictionary
|
|
for key in index_vals:
|
|
if key not in colors:
|
|
raise exceptions.PlotlyError(
|
|
"If you are using colors as a dictionary, all of its "
|
|
"keys must be all the values in the index column."
|
|
)
|
|
|
|
# create the list of task names
|
|
for index in range(len(tasks)):
|
|
tn = tasks[index]["name"]
|
|
# Is added to task_names if group_tasks is set to False,
|
|
# or if the option is used (True) it only adds them if the
|
|
# name is not already in the list
|
|
if not group_tasks or tn not in task_names:
|
|
task_names.append(tn)
|
|
# Guarantees that for grouped tasks the tasks that are inserted first
|
|
# are shown at the top
|
|
if group_tasks:
|
|
task_names.reverse()
|
|
|
|
for index in range(len(tasks)):
|
|
tn = tasks[index]["name"]
|
|
del tasks[index]["name"]
|
|
|
|
# If group_tasks is True, all tasks with the same name belong
|
|
# to the same row.
|
|
groupID = index
|
|
if group_tasks:
|
|
groupID = task_names.index(tn)
|
|
tasks[index]["y0"] = groupID - bar_width
|
|
tasks[index]["y1"] = groupID + bar_width
|
|
|
|
tasks[index]["fillcolor"] = colors[chart[index][index_col]]
|
|
color_id = tasks[index]["fillcolor"]
|
|
|
|
if color_id not in scatter_data_dict:
|
|
scatter_data_dict[color_id] = copy.deepcopy(scatter_data_template)
|
|
|
|
scatter_data_dict[color_id]["legendgroup"] = color_id
|
|
scatter_data_dict[color_id]["fillcolor"] = color_id
|
|
|
|
# if there are already values append the gap
|
|
if len(scatter_data_dict[color_id]["x"]) > 0:
|
|
# a gap on the scatterplot separates the rectangles from each other
|
|
scatter_data_dict[color_id]["x"].append(
|
|
scatter_data_dict[color_id]["x"][-1]
|
|
)
|
|
scatter_data_dict[color_id]["y"].append(None)
|
|
|
|
xs, ys = _get_corner_points(
|
|
tasks[index]["x0"],
|
|
tasks[index]["y0"],
|
|
tasks[index]["x1"],
|
|
tasks[index]["y1"],
|
|
)
|
|
|
|
scatter_data_dict[color_id]["x"] += xs
|
|
scatter_data_dict[color_id]["y"] += ys
|
|
|
|
# append dummy markers for showing start and end of interval
|
|
if color_id not in marker_data_dict:
|
|
marker_data_dict[color_id] = copy.deepcopy(marker_data_template)
|
|
marker_data_dict[color_id]["marker"]["color"] = color_id
|
|
marker_data_dict[color_id]["legendgroup"] = color_id
|
|
|
|
marker_data_dict[color_id]["x"].append(tasks[index]["x0"])
|
|
marker_data_dict[color_id]["x"].append(tasks[index]["x1"])
|
|
marker_data_dict[color_id]["y"].append(groupID)
|
|
marker_data_dict[color_id]["y"].append(groupID)
|
|
|
|
if "description" in tasks[index]:
|
|
marker_data_dict[color_id]["text"].append(tasks[index]["description"])
|
|
marker_data_dict[color_id]["text"].append(tasks[index]["description"])
|
|
del tasks[index]["description"]
|
|
else:
|
|
marker_data_dict[color_id]["text"].append(None)
|
|
marker_data_dict[color_id]["text"].append(None)
|
|
|
|
if show_colorbar is True:
|
|
showlegend = True
|
|
|
|
for index_value in index_vals:
|
|
scatter_data_dict[colors[index_value]]["name"] = str(index_value)
|
|
|
|
layout = dict(
|
|
title=title,
|
|
showlegend=showlegend,
|
|
height=height,
|
|
width=width,
|
|
shapes=[],
|
|
hovermode="closest",
|
|
yaxis=dict(
|
|
showgrid=showgrid_y,
|
|
ticktext=task_names,
|
|
tickvals=list(range(len(task_names))),
|
|
range=[-1, len(task_names) + 1],
|
|
autorange=False,
|
|
zeroline=False,
|
|
),
|
|
xaxis=dict(
|
|
showgrid=showgrid_x,
|
|
zeroline=False,
|
|
rangeselector=dict(
|
|
buttons=list(
|
|
[
|
|
dict(count=7, label="1w", step="day", stepmode="backward"),
|
|
dict(count=1, label="1m", step="month", stepmode="backward"),
|
|
dict(count=6, label="6m", step="month", stepmode="backward"),
|
|
dict(count=1, label="YTD", step="year", stepmode="todate"),
|
|
dict(count=1, label="1y", step="year", stepmode="backward"),
|
|
dict(step="all"),
|
|
]
|
|
)
|
|
),
|
|
type="date",
|
|
),
|
|
)
|
|
|
|
data = [scatter_data_dict[k] for k in sorted(scatter_data_dict)]
|
|
data += [marker_data_dict[k] for k in sorted(marker_data_dict)]
|
|
|
|
# fig = dict(
|
|
# data=data, layout=layout
|
|
# )
|
|
fig = go.Figure(data=data, layout=layout)
|
|
return fig
|
|
|
|
|
|
def create_gantt(
|
|
df,
|
|
colors=None,
|
|
index_col=None,
|
|
show_colorbar=False,
|
|
reverse_colors=False,
|
|
title="Gantt Chart",
|
|
bar_width=0.2,
|
|
showgrid_x=False,
|
|
showgrid_y=False,
|
|
height=600,
|
|
width=None,
|
|
tasks=None,
|
|
task_names=None,
|
|
data=None,
|
|
group_tasks=False,
|
|
show_hover_fill=True,
|
|
):
|
|
"""
|
|
**deprecated**, use instead
|
|
:func:`plotly.express.timeline`.
|
|
|
|
Returns figure for a gantt chart
|
|
|
|
:param (array|list) df: input data for gantt chart. Must be either a
|
|
a dataframe or a list. If dataframe, the columns must include
|
|
'Task', 'Start' and 'Finish'. Other columns can be included and
|
|
used for indexing. If a list, its elements must be dictionaries
|
|
with the same required column headers: 'Task', 'Start' and
|
|
'Finish'.
|
|
:param (str|list|dict|tuple) colors: either a plotly scale name, an
|
|
rgb or hex color, a color tuple or a list of colors. An rgb color
|
|
is of the form 'rgb(x, y, z)' where x, y, z belong to the interval
|
|
[0, 255] and a color tuple is a tuple of the form (a, b, c) where
|
|
a, b and c belong to [0, 1]. If colors is a list, it must
|
|
contain the valid color types aforementioned as its members.
|
|
If a dictionary, all values of the indexing column must be keys in
|
|
colors.
|
|
:param (str|float) index_col: the column header (if df is a data
|
|
frame) that will function as the indexing column. If df is a list,
|
|
index_col must be one of the keys in all the items of df.
|
|
:param (bool) show_colorbar: determines if colorbar will be visible.
|
|
Only applies if values in the index column are numeric.
|
|
:param (bool) show_hover_fill: enables/disables the hovertext for the
|
|
filled area of the chart.
|
|
:param (bool) reverse_colors: reverses the order of selected colors
|
|
:param (str) title: the title of the chart
|
|
:param (float) bar_width: the width of the horizontal bars in the plot
|
|
:param (bool) showgrid_x: show/hide the x-axis grid
|
|
:param (bool) showgrid_y: show/hide the y-axis grid
|
|
:param (float) height: the height of the chart
|
|
:param (float) width: the width of the chart
|
|
|
|
Example 1: Simple Gantt Chart
|
|
|
|
>>> from plotly.figure_factory import create_gantt
|
|
|
|
>>> # Make data for chart
|
|
>>> df = [dict(Task="Job A", Start='2009-01-01', Finish='2009-02-30'),
|
|
... dict(Task="Job B", Start='2009-03-05', Finish='2009-04-15'),
|
|
... dict(Task="Job C", Start='2009-02-20', Finish='2009-05-30')]
|
|
|
|
>>> # Create a figure
|
|
>>> fig = create_gantt(df)
|
|
>>> fig.show()
|
|
|
|
|
|
Example 2: Index by Column with Numerical Entries
|
|
|
|
>>> from plotly.figure_factory import create_gantt
|
|
|
|
>>> # Make data for chart
|
|
>>> df = [dict(Task="Job A", Start='2009-01-01',
|
|
... Finish='2009-02-30', Complete=10),
|
|
... dict(Task="Job B", Start='2009-03-05',
|
|
... Finish='2009-04-15', Complete=60),
|
|
... dict(Task="Job C", Start='2009-02-20',
|
|
... Finish='2009-05-30', Complete=95)]
|
|
|
|
>>> # Create a figure with Plotly colorscale
|
|
>>> fig = create_gantt(df, colors='Blues', index_col='Complete',
|
|
... show_colorbar=True, bar_width=0.5,
|
|
... showgrid_x=True, showgrid_y=True)
|
|
>>> fig.show()
|
|
|
|
|
|
Example 3: Index by Column with String Entries
|
|
|
|
>>> from plotly.figure_factory import create_gantt
|
|
|
|
>>> # Make data for chart
|
|
>>> df = [dict(Task="Job A", Start='2009-01-01',
|
|
... Finish='2009-02-30', Resource='Apple'),
|
|
... dict(Task="Job B", Start='2009-03-05',
|
|
... Finish='2009-04-15', Resource='Grape'),
|
|
... dict(Task="Job C", Start='2009-02-20',
|
|
... Finish='2009-05-30', Resource='Banana')]
|
|
|
|
>>> # Create a figure with Plotly colorscale
|
|
>>> fig = create_gantt(df, colors=['rgb(200, 50, 25)', (1, 0, 1), '#6c4774'],
|
|
... index_col='Resource', reverse_colors=True,
|
|
... show_colorbar=True)
|
|
>>> fig.show()
|
|
|
|
|
|
Example 4: Use a dictionary for colors
|
|
|
|
>>> from plotly.figure_factory import create_gantt
|
|
>>> # Make data for chart
|
|
>>> df = [dict(Task="Job A", Start='2009-01-01',
|
|
... Finish='2009-02-30', Resource='Apple'),
|
|
... dict(Task="Job B", Start='2009-03-05',
|
|
... Finish='2009-04-15', Resource='Grape'),
|
|
... dict(Task="Job C", Start='2009-02-20',
|
|
... Finish='2009-05-30', Resource='Banana')]
|
|
|
|
>>> # Make a dictionary of colors
|
|
>>> colors = {'Apple': 'rgb(255, 0, 0)',
|
|
... 'Grape': 'rgb(170, 14, 200)',
|
|
... 'Banana': (1, 1, 0.2)}
|
|
|
|
>>> # Create a figure with Plotly colorscale
|
|
>>> fig = create_gantt(df, colors=colors, index_col='Resource',
|
|
... show_colorbar=True)
|
|
|
|
>>> fig.show()
|
|
|
|
Example 5: Use a pandas dataframe
|
|
|
|
>>> from plotly.figure_factory import create_gantt
|
|
>>> import pandas as pd
|
|
|
|
>>> # Make data as a dataframe
|
|
>>> df = pd.DataFrame([['Run', '2010-01-01', '2011-02-02', 10],
|
|
... ['Fast', '2011-01-01', '2012-06-05', 55],
|
|
... ['Eat', '2012-01-05', '2013-07-05', 94]],
|
|
... columns=['Task', 'Start', 'Finish', 'Complete'])
|
|
|
|
>>> # Create a figure with Plotly colorscale
|
|
>>> fig = create_gantt(df, colors='Blues', index_col='Complete',
|
|
... show_colorbar=True, bar_width=0.5,
|
|
... showgrid_x=True, showgrid_y=True)
|
|
>>> fig.show()
|
|
"""
|
|
# validate gantt input data
|
|
chart = validate_gantt(df)
|
|
|
|
if index_col:
|
|
if index_col not in chart[0]:
|
|
raise exceptions.PlotlyError(
|
|
"In order to use an indexing column and assign colors to "
|
|
"the values of the index, you must choose an actual "
|
|
"column name in the dataframe or key if a list of "
|
|
"dictionaries is being used."
|
|
)
|
|
|
|
# validate gantt index column
|
|
index_list = []
|
|
for dictionary in chart:
|
|
index_list.append(dictionary[index_col])
|
|
utils.validate_index(index_list)
|
|
|
|
# Validate colors
|
|
if isinstance(colors, dict):
|
|
colors = clrs.validate_colors_dict(colors, "rgb")
|
|
else:
|
|
colors = clrs.validate_colors(colors, "rgb")
|
|
|
|
if reverse_colors is True:
|
|
colors.reverse()
|
|
|
|
if not index_col:
|
|
if isinstance(colors, dict):
|
|
raise exceptions.PlotlyError(
|
|
"Error. You have set colors to a dictionary but have not "
|
|
"picked an index. An index is required if you are "
|
|
"assigning colors to particular values in a dictionary."
|
|
)
|
|
fig = gantt(
|
|
chart,
|
|
colors,
|
|
title,
|
|
bar_width,
|
|
showgrid_x,
|
|
showgrid_y,
|
|
height,
|
|
width,
|
|
tasks=None,
|
|
task_names=None,
|
|
data=None,
|
|
group_tasks=group_tasks,
|
|
show_hover_fill=show_hover_fill,
|
|
show_colorbar=show_colorbar,
|
|
)
|
|
return fig
|
|
else:
|
|
if not isinstance(colors, dict):
|
|
fig = gantt_colorscale(
|
|
chart,
|
|
colors,
|
|
title,
|
|
index_col,
|
|
show_colorbar,
|
|
bar_width,
|
|
showgrid_x,
|
|
showgrid_y,
|
|
height,
|
|
width,
|
|
tasks=None,
|
|
task_names=None,
|
|
data=None,
|
|
group_tasks=group_tasks,
|
|
show_hover_fill=show_hover_fill,
|
|
)
|
|
return fig
|
|
else:
|
|
fig = gantt_dict(
|
|
chart,
|
|
colors,
|
|
title,
|
|
index_col,
|
|
show_colorbar,
|
|
bar_width,
|
|
showgrid_x,
|
|
showgrid_y,
|
|
height,
|
|
width,
|
|
tasks=None,
|
|
task_names=None,
|
|
data=None,
|
|
group_tasks=group_tasks,
|
|
show_hover_fill=show_hover_fill,
|
|
)
|
|
return fig
|