Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit e253aa2

Browse filesBrowse files
authored
Merge pull request #28144 from saranti/refactor_fishbone
DOC: Refactor code in the fishbone diagram example
2 parents 2ba8545 + f393a8e commit e253aa2
Copy full SHA for e253aa2

File tree

1 file changed

+75
-82
lines changed
Filter options

1 file changed

+75
-82
lines changed

‎galleries/examples/specialty_plots/ishikawa_diagram.py

Copy file name to clipboardExpand all lines: galleries/examples/specialty_plots/ishikawa_diagram.py
+75-82Lines changed: 75 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@
99
Source: https://en.wikipedia.org/wiki/Ishikawa_diagram
1010
1111
"""
12+
import math
13+
1214
import matplotlib.pyplot as plt
1315

1416
from matplotlib.patches import Polygon, Wedge
1517

16-
# Create the fishbone diagram
1718
fig, ax = plt.subplots(figsize=(10, 6), layout='constrained')
1819
ax.set_xlim(-5, 5)
1920
ax.set_ylim(-5, 5)
@@ -22,18 +23,18 @@
2223

2324
def problems(data: str,
2425
problem_x: float, problem_y: float,
25-
prob_angle_x: float, prob_angle_y: float):
26+
angle_x: float, angle_y: float):
2627
"""
2728
Draw each problem section of the Ishikawa plot.
2829
2930
Parameters
3031
----------
3132
data : str
32-
The category name.
33+
The name of the problem category.
3334
problem_x, problem_y : float, optional
3435
The `X` and `Y` positions of the problem arrows (`Y` defaults to zero).
35-
prob_angle_x, prob_angle_y : float, optional
36-
The angle of the problem annotations. They are angled towards
36+
angle_x, angle_y : float, optional
37+
The angle of the problem annotations. They are always angled towards
3738
the tail of the plot.
3839
3940
Returns
@@ -42,8 +43,8 @@ def problems(data: str,
4243
4344
"""
4445
ax.annotate(str.upper(data), xy=(problem_x, problem_y),
45-
xytext=(prob_angle_x, prob_angle_y),
46-
fontsize='10',
46+
xytext=(angle_x, angle_y),
47+
fontsize=10,
4748
color='white',
4849
weight='bold',
4950
xycoords='data',
@@ -56,7 +57,8 @@ def problems(data: str,
5657
pad=0.8))
5758

5859

59-
def causes(data: list, cause_x: float, cause_y: float,
60+
def causes(data: list,
61+
cause_x: float, cause_y: float,
6062
cause_xytext=(-9, -0.3), top: bool = True):
6163
"""
6264
Place each cause to a position relative to the problems
@@ -72,34 +74,33 @@ def causes(data: list, cause_x: float, cause_y: float,
7274
cause_xytext : tuple, optional
7375
Adjust to set the distance of the cause text from the problem
7476
arrow in fontsize units.
75-
top : bool
77+
top : bool, default: True
78+
Determines whether the next cause annotation will be
79+
plotted above or below the previous one.
7680
7781
Returns
7882
-------
7983
None.
8084
8185
"""
8286
for index, cause in enumerate(data):
83-
# First cause annotation is placed in the middle of the problems arrow
87+
# [<x pos>, <y pos>]
88+
coords = [[0.02, 0],
89+
[0.23, 0.5],
90+
[-0.46, -1],
91+
[0.69, 1.5],
92+
[-0.92, -2],
93+
[1.15, 2.5]]
94+
95+
# First 'cause' annotation is placed in the middle of the 'problems' arrow
8496
# and each subsequent cause is plotted above or below it in succession.
85-
86-
# [<x pos>, [<y pos top>, <y pos bottom>]]
87-
coords = [[0, [0, 0]],
88-
[0.23, [0.5, -0.5]],
89-
[-0.46, [-1, 1]],
90-
[0.69, [1.5, -1.5]],
91-
[-0.92, [-2, 2]],
92-
[1.15, [2.5, -2.5]]]
93-
if top:
94-
cause_y += coords[index][1][0]
95-
else:
96-
cause_y += coords[index][1][1]
9797
cause_x -= coords[index][0]
98+
cause_y += coords[index][1] if top else -coords[index][1]
9899

99100
ax.annotate(cause, xy=(cause_x, cause_y),
100101
horizontalalignment='center',
101102
xytext=cause_xytext,
102-
fontsize='9',
103+
fontsize=9,
103104
xycoords='data',
104105
textcoords='offset fontsize',
105106
arrowprops=dict(arrowstyle="->",
@@ -108,82 +109,74 @@ def causes(data: list, cause_x: float, cause_y: float,
108109

109110
def draw_body(data: dict):
110111
"""
111-
Place each section in its correct place by changing
112+
Place each problem section in its correct place by changing
112113
the coordinates on each loop.
113114
114115
Parameters
115116
----------
116117
data : dict
117-
The input data (can be list or tuple). ValueError is
118-
raised if more than six arguments are passed.
118+
The input data (can be a dict of lists or tuples). ValueError
119+
is raised if more than six arguments are passed.
119120
120121
Returns
121122
-------
122123
None.
123124
124125
"""
125-
second_sections = []
126-
third_sections = []
127-
# Resize diagram to automatically scale in response to the number
128-
# of problems in the input data.
129-
if len(data) == 1 or len(data) == 2:
130-
spine_length = (-2.1, 2)
131-
head_pos = (2, 0)
132-
tail_pos = ((-2.8, 0.8), (-2.8, -0.8), (-2.0, -0.01))
133-
first_section = [1.6, 0.8]
134-
elif len(data) == 3 or len(data) == 4:
135-
spine_length = (-3.1, 3)
136-
head_pos = (3, 0)
137-
tail_pos = ((-3.8, 0.8), (-3.8, -0.8), (-3.0, -0.01))
138-
first_section = [2.6, 1.8]
139-
second_sections = [-0.4, -1.2]
140-
else: # len(data) == 5 or 6
141-
spine_length = (-4.1, 4)
142-
head_pos = (4, 0)
143-
tail_pos = ((-4.8, 0.8), (-4.8, -0.8), (-4.0, -0.01))
144-
first_section = [3.5, 2.7]
145-
second_sections = [1, 0.2]
146-
third_sections = [-1.5, -2.3]
147-
148-
# Change the coordinates of the annotations on each loop.
126+
# Set the length of the spine according to the number of 'problem' categories.
127+
length = (math.ceil(len(data) / 2)) - 1
128+
draw_spine(-2 - length, 2 + length)
129+
130+
# Change the coordinates of the 'problem' annotations after each one is rendered.
131+
offset = 0
132+
prob_section = [1.55, 0.8]
149133
for index, problem in enumerate(data.values()):
150-
top_row = True
151-
cause_arrow_y = 1.7
152-
if index % 2 != 0: # Plot problems below the spine.
153-
top_row = False
154-
y_prob_angle = -16
155-
cause_arrow_y = -1.7
156-
else: # Plot problems above the spine.
157-
y_prob_angle = 16
158-
# Plot the 3 sections in pairs along the main spine.
159-
if index in (0, 1):
160-
prob_arrow_x = first_section[0]
161-
cause_arrow_x = first_section[1]
162-
elif index in (2, 3):
163-
prob_arrow_x = second_sections[0]
164-
cause_arrow_x = second_sections[1]
165-
else:
166-
prob_arrow_x = third_sections[0]
167-
cause_arrow_x = third_sections[1]
134+
plot_above = index % 2 == 0
135+
cause_arrow_y = 1.7 if plot_above else -1.7
136+
y_prob_angle = 16 if plot_above else -16
137+
138+
# Plot each section in pairs along the main spine.
139+
prob_arrow_x = prob_section[0] + length + offset
140+
cause_arrow_x = prob_section[1] + length + offset
141+
if not plot_above:
142+
offset -= 2.5
168143
if index > 5:
169144
raise ValueError(f'Maximum number of problems is 6, you have entered '
170145
f'{len(data)}')
171146

172-
# draw main spine
173-
ax.plot(spine_length, [0, 0], color='tab:blue', linewidth=2)
174-
# draw fish head
175-
ax.text(head_pos[0] + 0.1, head_pos[1] - 0.05, 'PROBLEM', fontsize=10,
176-
weight='bold', color='white')
177-
semicircle = Wedge(head_pos, 1, 270, 90, fc='tab:blue')
178-
ax.add_patch(semicircle)
179-
# draw fishtail
180-
triangle = Polygon(tail_pos, fc='tab:blue')
181-
ax.add_patch(triangle)
182-
# Pass each category name to the problems function as a string on each loop.
183147
problems(list(data.keys())[index], prob_arrow_x, 0, -12, y_prob_angle)
184-
# Start the cause function with the first annotation being plotted at
185-
# the cause_arrow_x, cause_arrow_y coordinates.
186-
causes(problem, cause_arrow_x, cause_arrow_y, top=top_row)
148+
causes(problem, cause_arrow_x, cause_arrow_y, top=plot_above)
149+
150+
151+
def draw_spine(xmin: int, xmax: int):
152+
"""
153+
Draw main spine, head and tail.
154+
155+
Parameters
156+
----------
157+
xmin : int
158+
The default position of the head of the spine's
159+
x-coordinate.
160+
xmax : int
161+
The default position of the tail of the spine's
162+
x-coordinate.
163+
164+
Returns
165+
-------
166+
None.
167+
168+
"""
169+
# draw main spine
170+
ax.plot([xmin - 0.1, xmax], [0, 0], color='tab:blue', linewidth=2)
171+
# draw fish head
172+
ax.text(xmax + 0.1, - 0.05, 'PROBLEM', fontsize=10,
173+
weight='bold', color='white')
174+
semicircle = Wedge((xmax, 0), 1, 270, 90, fc='tab:blue')
175+
ax.add_patch(semicircle)
176+
# draw fish tail
177+
tail_pos = [[xmin - 0.8, 0.8], [xmin - 0.8, -0.8], [xmin, -0.01]]
178+
triangle = Polygon(tail_pos, fc='tab:blue')
179+
ax.add_patch(triangle)
187180

188181

189182
# Input data

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.