Skip to content

added Ishikawa plot in response to issue #25222 add organizational ch… #25248

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 62 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
ed9b7b1
added Ishikawa plot in response to issue #25222 add organizational ch…
Feb 18, 2023
963663d
fixing code style for flake8 first trial
Feb 19, 2023
2ef7f46
second fixing style typo
Feb 19, 2023
2018c75
fix flake8 configuration
Feb 19, 2023
8147bf4
- revised comment style according to flake8 set rules
Feb 21, 2023
c7b9111
fixed file docstring for title overlay sphinx error
Feb 22, 2023
e9ebc66
Fix unmatched offsetText label color
devRD Feb 22, 2023
ddaa7ce
GitHub: inactive label [skip ci]
jklymak Feb 6, 2023
0e55ef1
Add conditional before set labelcolor call
devRD Feb 24, 2023
53691a5
Clean up legend loc parameter documentation
QuLogic Feb 25, 2023
264e7d3
Update test to check offset text color
devRD Feb 25, 2023
d226693
Merge branch 'matplotlib:main' into my-feature
rbt94 Feb 25, 2023
a3c564f
- added wikipedia link to ishikawa desc doc string
Feb 25, 2023
dfaa991
Make draggable legends picklable.
anntzer Feb 23, 2023
f835014
Fix doc issues identified by velin
oscargus Feb 25, 2023
80a6868
Merge pull request #25327 from oscargus/docfixesvelin
timhoffm Feb 26, 2023
07c43e4
Merge pull request #25163 from jklymak/bld-stale-action
rcomer Feb 26, 2023
9bf681e
removing typecasting method to float
xtanion Feb 26, 2023
da46769
adding tests and changing typecasts
xtanion Feb 27, 2023
0d7bd7c
flake8 fix
xtanion Feb 27, 2023
a00680e
removing redundant `to_list` conversion
xtanion Feb 27, 2023
3bdc020
fix typo [skip ci]
rcomer Feb 27, 2023
d787258
Merge pull request #25335 from rcomer/stale-typo
oscargus Feb 27, 2023
66ba515
Merge pull request #25334 from xtanion/contour_nan
oscargus Feb 27, 2023
ea2e9ea
Support pickling of figures with aligned x/y labels.
anntzer Feb 26, 2023
5e8c140
Merge pull request #25311 from anntzer/dlp
QuLogic Feb 27, 2023
a0d25af
Fix RangeSlider.set_val when outside existing value
QuLogic Feb 28, 2023
0489b7a
Merge pull request #25332 from anntzer/alg
greglucas Feb 28, 2023
ff59e46
TST: Increase test_set_line_coll_dash_image tolerance slightly.
QuLogic Feb 28, 2023
52dd5ec
Merge pull request #25341 from QuLogic/dash-test-tol
oscargus Feb 28, 2023
fbe7a44
Merge pull request #25340 from QuLogic/rangeslider-set-clip
oscargus Feb 28, 2023
790bcc2
FIX: use wrapped text in Text._get_layout [skip circle]
rcomer Feb 28, 2023
6462a01
Merge pull request #25346 from rcomer/text-wrap
jklymak Feb 28, 2023
217f8f7
Merge pull request #25325 from QuLogic/legend-loc-doc
rcomer Feb 28, 2023
b85b46a
link to ipympl docs instead of github
ianhi Mar 1, 2023
631d1ab
Merge pull request #25287 from devRD/offset-tickcolor
QuLogic Mar 1, 2023
3742e7e
Merge pull request #25353 from ianhi/patch-4
oscargus Mar 1, 2023
c38c405
MNT: Use WeakKeyDictionary and WeakSet in Grouper
greglucas Feb 27, 2023
7528257
Remove unused menu field from macos NavigationToolbar2.
anntzer Mar 1, 2023
6db06ab
Merge pull request #25352 from greglucas/grouper-pickle
tacaswell Mar 1, 2023
fb86113
Merge pull request #25358 from anntzer/um
QuLogic Mar 2, 2023
f2bcaef
Disable discarded animation warning on save
QuLogic Feb 27, 2023
ad0bac9
BLD: Pre-download Qhull license to put in wheels
QuLogic Mar 2, 2023
f1bed13
Merge pull request #25339 from QuLogic/test-anim-warning
tacaswell Mar 2, 2023
7754112
Pin sphinx themes more strictly
QuLogic Mar 2, 2023
447c0c8
Merge pull request #25369 from QuLogic/pin-sphinx
jklymak Mar 2, 2023
a3f4114
Tk: Fix size of spacers when changing display DPI
QuLogic Mar 3, 2023
bf5bd76
Clean up Curve ArrowStyle docs
QuLogic Mar 3, 2023
981e1d1
Merge pull request #25364 from QuLogic/wheel-license
ksunden Mar 3, 2023
a6acefe
Merge pull request #25371 from QuLogic/tk-spacer
ksunden Mar 3, 2023
ed50a58
Merge pull request #25372 from QuLogic/curve-bracket
jklymak Mar 3, 2023
d9315e9
"Inactive" workflow: bump operations-per-run [skip ci]
rcomer Mar 3, 2023
32b6ebb
Merge pull request #25378 from rcomer/inactive-more-ops
tacaswell Mar 3, 2023
42777af
added Ishikawa plot in response to issue #25222 add organizational ch…
Feb 18, 2023
9cd719c
fixing code style for flake8 first trial
Feb 19, 2023
95fe3b1
second fixing style typo
Feb 19, 2023
9effc90
- revised comment style according to flake8 set rules
Feb 21, 2023
0713507
fixed file docstring for title overlay sphinx error
Feb 22, 2023
965ab5b
- fix PR rebase
Mar 4, 2023
9ed2f7f
Merge branch 'my-feature' of https://github.com/rbt94/matplotlib_ishi…
Mar 4, 2023
709653c
- fix examples folder
Mar 4, 2023
69d76bf
- change plt.figure with plt.subplots
Mar 4, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,4 @@ per-file-ignores =
galleries/examples/user_interfaces/pylab_with_gtk3_sgskip.py: E402
galleries/examples/user_interfaces/pylab_with_gtk4_sgskip.py: E402
galleries/examples/userdemo/pgf_preamble_sgskip.py: E402
force-check = True
force-check = True
8 changes: 8 additions & 0 deletions .github/workflows/cibuildwheel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ jobs:
with:
fetch-depth: 0

# Something changed somewhere that prevents the downloaded-at-build-time
# licenses from being included in built wheels, so pre-download them so
# that they exist before the build and are included.
- name: Pre-download bundled licenses
run: >
curl -Lo LICENSE/LICENSE_QHULL
https://github.com/qhull/qhull/raw/2020.2/COPYING.txt

- name: Build wheels for CPython 3.11
uses: pypa/cibuildwheel@v2.12.0
env:
Expand Down
25 changes: 25 additions & 0 deletions .github/workflows/stale.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: 'Label inactive PRs'
on:
schedule:
- cron: '30 1 * * *'

jobs:
stale:
if: github.repository == 'matplotlib/matplotlib'
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v7
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
operations-per-run: 50
stale-pr-message: 'Since this Pull Request has not been updated in 60 days, it has been marked "inactive." This does not mean that it will be closed, though it may be moved to a "Draft" state. This helps maintainers prioritize their reviewing efforts. You can pick the PR back up anytime - please ping us if you need a review or guidance to move the PR forward! If you do not plan on continuing the work, please let us know so that we can either find someone to take the PR over, or close it.'
stale-pr-label: 'inactive'
days-before-pr-stale: 60
days-before-pr-close: -1
stale-issue-message: 'This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!'
stale-issue-label: 'inactive'
days-before-issue-stale: 365
days-before-issue-close: 30
ascending: true
exempt-issue-labels: "keep"
exempt-pr-labels: "keep,status: orphaned PR"
4 changes: 4 additions & 0 deletions doc/api/next_api_changes/deprecations/25352-GL.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
``Grouper.clean()``
~~~~~~~~~~~~~~~~~~~

with no replacement. The Grouper class now cleans itself up automatically.
2 changes: 1 addition & 1 deletion doc/users/explain/interactive.rst
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ Jupyter Notebooks / JupyterLab
cells.

To get interactive figures in the 'classic' notebook or Jupyter lab,
use the `ipympl <https://github.com/matplotlib/ipympl>`__ backend
use the `ipympl <https://matplotlib.org/ipympl>`__ backend
(must be installed separately) which uses the **ipywidget** framework.
If ``ipympl`` is installed use the magic:

Expand Down
223 changes: 223 additions & 0 deletions galleries/examples/specialty_plots/ishikawa.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
"""
=================
Ishikawa Diagrams
=================

Ishikawa Diagrams, fishbone diagrams, herringbone diagrams, or cause-and-effect
diagrams are useful for visualizing the effect to many cause relationships
Source: https://en.wikipedia.org/wiki/Ishikawa_diagram

"""
from typing import List

import matplotlib.pyplot as plt
import numpy as np

import matplotlib


def drawspines(data: dict, ax: plt.Axes, parentspine: None,
primary_spine_angle: float = 60.0,
secondary_spine_angle: float = 0,
primary_spines_rel_xpos: List[float] = [np.nan],
spine_color: str = "black", reclevel: int = 1,
max_recursion_level: int = 4):
"""
Draw an Ishikawa spine with recursion

Parameters
----------
data : dict
dictionary structure containing problem, causes...
ax : plt.Axes
Matplotlib current axes
parentspine : None
Parent spine object
primary_spine_angle : float, optional
First spine angle using during recursion. The default is 60.0.
secondary_spine_angle : float, optional
Secondary spine angle using during recursion. The default is 0.
primary_spines_rel_xpos : List[float], optional
Relative X-position of primary spines, a list of float is accepted.
The default is [np.nan].
spine_color : str, optional
Spine color. The default is "black".
reclevel : int, optional
Current recursion level. The default is 1.
max_recursion_level : int, optional
Maximum recursion level set. The default is 4.

Raises
------
AttributeError
Maximum recursion level reached or passed a bad parentspine object format.

Returns
-------
None.

"""
# stop recursion if maximum level reached
if reclevel > max_recursion_level:
raise AttributeError('Max Recursion Level Reached')

if isinstance(data, dict):
# switch to correct angle depending on recursion level
if reclevel % 2 != 0:
alpha = primary_spine_angle
else:
alpha = secondary_spine_angle

if isinstance(parentspine, matplotlib.lines.Line2D):
# calculate parent data
([xpb, xpe], [ypb, ype]) = parentspine.get_data()
elif isinstance(parentspine, matplotlib.text.Annotation):
xpb, ypb = parentspine.xy
xpe, ype = parentspine._x, parentspine._y # parentspine._arrow_relpos
else:
raise AttributeError('Wrong Spine Graphical Element')

plen = np.hypot(xpe - xpb, ype - ypb)
palpha = np.round(np.degrees(np.arctan2(ype - ypb, xpe - xpb)))

# calculate spine spacing
# calculate couple pairs, at least 1 pair to start at middle branch
pairs = np.ceil(len(data) / 2) + 1
spacing = plen / pairs

spine_count = 0
s = spacing
# draw spine
for problem, cause in data.items():
# calculate arrow position in the graph
# fix primary spines spacing
if reclevel == 1:
if len(primary_spines_rel_xpos) == len(data):
x_end = primary_spines_rel_xpos[spine_count]
else:
x_end = xpe - (s / 1.5) * np.cos(np.radians(palpha))
y_end = ype - s * np.sin(np.radians(palpha))

x_start = x_end - s * np.cos(np.radians(alpha))
y_start = y_end - s * np.sin(np.radians(alpha))
else:
x_end = xpe - s * np.cos(np.radians(palpha))
y_end = ype - s * np.sin(np.radians(palpha))

x_start = x_end - s * np.cos(np.radians(alpha))
y_start = y_end - s * np.sin(np.radians(alpha))

# draw arrow arc
if reclevel == 1:
props = dict(boxstyle='round', facecolor='lightsteelblue', alpha=1.0)
spine = ax.annotate(problem.upper(), xy=(x_end, y_end),
xytext=(x_start, y_start),
arrowprops=dict(arrowstyle="->",
facecolor=spine_color),
bbox=props, weight='bold')
else:
props = dict(boxstyle='round', facecolor='lavender', alpha=1.0)
spine = ax.annotate(problem, xy=(x_end, y_end),
xytext=(x_start, y_start),
arrowprops=dict(arrowstyle="->",
facecolor=spine_color),
bbox=props)
# Call recursion to draw subspines
drawspines(data=cause, ax=ax, parentspine=spine,
primary_spine_angle=primary_spine_angle,
secondary_spine_angle=secondary_spine_angle,
spine_color=spine_color, reclevel=reclevel + 1,
max_recursion_level=max_recursion_level)
# no primary_spines_rel_xpos is needed to be passed on recursion
# next spine settings - same level
alpha *= -1
spine_count += 1
if spine_count % 2 == 0:
s = s + spacing
return None


def ishikawaplot(data: dict, ax: plt.Axes, left_margin: float = 0.05,
right_margin: float = 0.05,
primary_spine_angle: float = 60.0,
secondary_spine_angle: float = 0.0,
primary_spines_rel_xpos: List[float] = [np.nan],
pd_width: int = 0.1, spine_color: str = "black") -> None:
"""

Parameters
----------
data : dict
Plot data structure.
ax : matplotlib.pyplot.Axes
Axes in which to drow the plot.
left_margin : float, optional
Left spacing from frame border. The default is 0.05.
right_margin : float, optional
Right spacing from frame border. The default is 0.05.
primary_spine_angle : float, optional
First spine angle using during recursion. The default is 60.0.
secondary_spine_angle : float, optional
Secondary spine angle using during recursion. The default is 0.0.
primary_spines_rel_xpos : list, optional
Relative X-position of primary spines, a list of float is accepted.
The default is [np.nan].
pd_width : int, optional
Problem description box relative width. The default is 0.1.
spine_color : str, optional
Spine color. The default is "black".

Returns
-------
None

"""
# format axis
ax.set_xlim(0, 1.0)
ax.set_ylim(0, 1.0)
ax.axis('off')

# draw main spine
main_spine = ax.axhline(y=0.5, xmin=left_margin,
xmax=1 - right_margin - pd_width,
color=spine_color)

# draw fish head
props = dict(boxstyle='round', facecolor='wheat', alpha=1.0)
# add problem tag
ax.text((1 - right_margin - pd_width), 0.5, str.upper(list(data.keys())[0]),
fontsize=12, weight='bold',
verticalalignment='center', horizontalalignment='left', bbox=props)

# draw fish tail
x = (0.0, 0.0, left_margin)
y = (0.5 - left_margin, 0.5 + left_margin, 0.5)
ax.fill(x, y, color=spine_color)

# draw spines with recursion
drawspines(data=data[list(data.keys())[0]], ax=ax, parentspine=main_spine,
primary_spine_angle=primary_spine_angle,
secondary_spine_angle=secondary_spine_angle,
primary_spines_rel_xpos=primary_spines_rel_xpos,
spine_color=spine_color)

return None


# USER DATA
data = {'problem': {'machine': {'cause1': ''},
'process': {'cause2': {'subcause1': '', 'subcause2': ''}
},
'man': {'cause3': {'subcause3': '', 'subcause4': ''}
},
'design': {'cause4': {'subcause5': '', 'subcause6': ''}
}

}
}

# Ishikawa plot generation
fig, ax = plt.subplots(figsize=(12, 6), layout='constrained')

# try also without opt primary_spines rel_xpos
ishikawaplot(data, ax, primary_spines_rel_xpos=[0.8, 0.7, 0.6, 0.4])
Loading