8000 add ishikawa diagram to examples · matplotlib/matplotlib@27dd4a3 · GitHub
[go: up one dir, main page]

Skip to content

Commit 27dd4a3

Browse files
committed
add ishikawa diagram to examples
1 parent 01cfd30 commit 27dd4a3

File tree

1 file changed

+201
-0
lines changed

1 file changed

+201
-0
lines changed
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
"""
2+
================
3+
Ishikawa Diagram
4+
================
5+
6+
Ishikawa Diagrams, fishbone diagrams, herringbone diagrams, or cause-and-effect
7+
diagrams are used to identify problems in a system by showing how causes and
8+
effects are linked.
9+
Source: https://en.wikipedia.org/wiki/Ishikawa_diagram
10+
11+
"""
12+
import matplotlib.pyplot as plt
13+
14+
from matplotlib.patches import Polygon, Wedge
15+
16+
# Create the fishbone diagram
17+
fig, ax = plt.subplots(figsize=(10, 6), layout='constrained')
18+
ax.set_xlim(-5, 5)
19+
ax.set_ylim(-5, 5)
20+
ax.axis('off')
21+
COLOR = 'C0'
22+
23+
24+
def problems(data: list,
25+
problem_x: float, problem_y: float,
26+
prob_angle_x: float, prob_angle_y: float):
27+
"""
28+
Draw each problem section of the Ishikawa plot.
29+
30+
Parameters
31+
----------
32+
data : indexable object
33+
The input data (can be list or tuple).
34+
problem_x, problem_y : float, optional
35+
The `X` and `Y` positions of the problem arrows (`Y` defaults to zero).
36+
prob_angle_x, prob_angle_y : float, optional
37+
The angle of the problem annotations. They are angled towards
38+
the tail of the plot.
39+
40+
Returns
41+
-------
42+
None.
43+
44+
"""
45+
ax.annotate(str.upper(data[0]), xy=(problem_x, problem_y),
46+
xytext=(prob_angle_x, prob_angle_y),
47+
fontsize='10',
48+
color='white',
49+
weight='bold',
50+
xycoords='data',
51+
verticalalignment="center",
52+
horizontalalignment="center",
53+
textcoords='offset fontsize',
54+
arrowprops=dict(arrowstyle="->", facecolor='black'),
55+
bbox=dict(boxstyle='square',
56+
facecolor=COLOR,
57+
pad=0.8))
58+
59+
60+
def causes(data: list, cause_x: float, cause_y: float,
61+
cause_xytext=(-9, -0.3), top: bool = True):
62+
"""
63+
Place each cause to a position relative to the problems
64+
annotations.
65+
66+
Parameters
67+
----------
68+
data : indexible object
69+
The input data (can be list or tuple). IndexError is
70+
raised if more than six arguments are passed.
71+
cause_x, cause_y : float
72+
The `X` and `Y` position of the cause annotations.
73+
cause_xytext : tuple, optional
74+
Adjust to set the distance of the cause text from the problem
75+
arrow in fontsize units.
76+
top : bool
77+
78+
Returns
79+
-------
80+
None.
81+
82+
"""
83+
for index, cause in enumerate(data[1]):
84+
# First cause annotation is placed in the middle of the problems arrow
85+
# and each subsequent cause is plotted above or below it in succession.
86+
87+
# [<x pos>, [<y pos top>, <y pos bottom>]]
88+
coords = [[0, [0, 0]],
89+
[0.23, [0.5, -0.5]],
90+
[-0.46, [-1, 1]],
91+
[0.69, [1.5, -1.5]],
92+
[-0.92, [-2, 2]],
93+
[1.15, [2.5, -2.5]]]
94+
if top:
95+
cause_x -= coords[index][0]
96+
cause_y += coords[index][1][0]
97+
else:
98+
cause_x -= coords[index][0]
99+
cause_y += coords[index][1][1]
100+
101+
ax.annotate(cause, xy=(cause_x, cause_y),
102+
horizontalalignment='center',
103+
xytext=cause_xytext,
104+
fontsize='9',
105+
xycoords='data',
106+
textcoords='offset fontsize',
107+
arrowprops=dict(arrowstyle="->",
108+
facecolor='black'))
109+
110+
111+
def draw_body(*args):
112+
"""
113+
Place each section in its correct place by changing
114+
the coordinates on each loop.
115+
116+
Parameters
117+
----------
118+
*args : indexable object
119+
The input data (can be list or tuple). ValueError is
120+
raised if more than six arguments are passed.
121+
122+
Returns
123+
-------
124+
None.
125+
126+
"""
127+
second_sections = []
128+
third_sections = []
129+
# Resize diagram to automatically scale in response to the number
130+
# of problems in the input data.
131+
if len(args) == 1 or len(args) == 2:
132+
spine_length = (-2.1, 2)
133+
head_pos = (2, 0)
134+
tail_pos = ((-2.8, 0.8), (-2.8, -0.8), (-2.0, -0.01))
135+
first_section = [1.6, 0.8]
136+
elif len(args) == 3 or len(args) == 4:
137+
spine_length = (-3.1, 3)
138+
head_pos = (3, 0)
139+
tail_pos = ((-3.8, 0.8), (-3.8, -0.8), (-3.0, -0.01))
140+
first_section = [2.6, 1.8]
141+
second_sections = [-0.4, -1.2]
142+
else: # num == 5 or 6
143+
spine_length = (-4.1, 4)
144+
head_pos = (4, 0)
145+
tail_pos = ((-4.8, 0.8), (-4.8, -0.8), (-4.0, -0.01))
146+
first_section = [3.5, 2.7]
147+
second_sections = [1, 0.2]
148+
third_sections = [-1.5, -2.3]
149+
150+
# Change the coordinates of the annotations on each loop.
151+
for index, problem in enumerate(args):
152+
top_row = True
153+
cause_arrow_y = 1.7
154+
if index % 2 != 0: # Plot problems below the spine.
155+
top_row = False
156+
y_prob_angle = -16
157+
cause_arrow_y = -1.7
158+
else: # Plot problems above the spine.
159+
y_prob_angle = 16
160+
# Plot the 3 sections in pairs along the main spine.
161+
if index in (0, 1):
162+
prob_arrow_x = first_section[0]
163+
cause_arrow_x = first_section[1]
164+
elif index in (2, 3):
165+
prob_arrow_x = second_sections[0]
166+
cause_arrow_x = second_sections[1]
167+
else:
168+
prob_arrow_x = third_sections[0]
169+
cause_arrow_x = third_sections[1]
170+
if index > 5:
171+
raise ValueError(f'Maximum number of problems is 6, you have entered '
172+
f'{len(args)}')
173+
174+
# draw main spine
175+
ax.plot(spine_length, [0, 0], color=COLOR, linewidth=2)
176+
# draw fish head
177+
ax.text(head_pos[0] + 0.1, head_pos[1] - 0.05, 'PROBLEM', fontsize=10,
178+
color='white')
179+
semicircle = Wedge(head_pos, 1, 270, 90, fc=COLOR)
180+
ax.add_patch(semicircle)
181+
# draw fishtail
182+
triangle = Polygon(tail_pos, fc=COLOR)
183+
ax.add_patch(triangle)
184+
185+
problems(problem, prob_arrow_x, 0, -12, y_prob_angle)
186+
causes(problem, cause_arrow_x, cause_arrow_y, top=top_row)
187+
188+
189+
# Input data
190+
method = ['Method', ['Time consumption', 'Cost', 'Procedures',
191+
'Inefficient process', 'Sampling']]
192+
machine = ['Machine', ['Faulty equipment', 'Compatibility']]
193+
material = ['Material', ['Poor-quality input', 'Raw materials', 'Supplier',
194+
'Shortage']]
195+
measure = ['Measurement', ['Calibration', 'Performance', 'Wrong measurements']]
196+
env = ['Environment', ['Bad conditions']]
197+
people = ['People', ['Lack of training', 'Managers', 'Labor shortage',
198+
'Procedures', 'Sales strategy']]
199+
200+
draw_body(method, machine, material, measure, env, people)
201+
plt.show()

0 commit comments

Comments
 (0)
0