├── .DS_Store ├── .gitignore ├── LICENSE ├── README.md ├── demos ├── .DS_Store ├── daterange.png ├── default.png ├── gallery.png ├── gnuplot.png ├── jupyter.png └── lavender.png ├── dotplotlib ├── .DS_Store ├── __init__.py └── plots.py └── setup.py /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jl33-ai/dotplotlib/6b2cff6cd10f8fb51fe89dad10a26d50d222ab1a/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ 161 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Justin L 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dotplotlib 2 | 3 | [![Contributors][contributors-shield]][contributors-url] 4 | [![Forks][forks-shield]][forks-url] 5 | [![Stargazers][stars-shield]][stars-url] 6 | [![Issues][issues-shield]][issues-url] 7 | [![Unlicense License][license-shield]][license-url] 8 | 9 | ![](https://github.com/jl33-ai/dotplotlib/blob/main/demos/daterange.png?raw=true) 10 | 11 | A `matplotlib` extension library for making tree dot plots, strip plots or dot charts in Python (`seaborn` compatible) 12 | 13 | ### Installation 14 | 15 | ```text 16 | pip install dotplotlib 17 | ``` 18 | 19 | ### Usage 20 | 21 | ###### Example 1: Simple Dot Chart 22 | 23 | `.dotchart` returns `x` and `y` lists that can be inputted straight into `matplotlib` or `seaborn` [scatterplots](https://www.w3schools.com/python/matplotlib_scatter.asp). 24 | 25 | ```python 26 | from dotplotlib import dotchart 27 | import matplotlib.pyplot as plt 28 | 29 | data = {'size': [1, 2, 2, 3, 3, 3, 4]} 30 | 31 | # Generate dot chart data 32 | x, y = dotchart(data['size']) 33 | 34 | # Plot 35 | plt.scatter(x, y) 36 | plt.show() 37 | ``` 38 | 39 | ###### Example 2: Dot Chart with Color Mapping 40 | 41 | Pass the data you would like to color by to the `color_by=` argument. 42 | 43 | Returns an extra list `c` that should be passed into the `c=` parameter if using `matplotlib` or `hue=` if using `seaborn`. 44 | 45 | ```python 46 | from dotplotlib import dotchart 47 | import matplotlib.pyplot as plt 48 | 49 | data = {'size': [1, 2, 2, 3, 3, 3, 4], 'rating': [3, 2, 5, 4, 3, 6, 4]} 50 | 51 | # Generate dot chart data with color mapping 52 | x, y, c = dotchart(data['size'], color_by=data['rating']) 53 | 54 | # Plot with color mapping 55 | plt.scatter(x, y, c=c, cmap='viridis') 56 | plt.colorbar() 57 | plt.xlabel('Size') 58 | plt.ylabel('Number') 59 | plt.title('Mushroom Size Count Colored by Rating') 60 | plt.show() 61 | ``` 62 | 63 | ###### Example 3: Using `make_dotchart` to plot in one step 64 | 65 | Instead of just giving you `x, y` lists to make the plot yourself, `make_dotplot()` actually generates the plot. 66 | 67 | ```python 68 | from dotplotlib import make_dotchart 69 | 70 | df = {'size': [1, 2, 2, 3, 3, 3, 4], 'rating': [3, 2, 5, 4, 3, 6, 7]} 71 | 72 | # Create a dot chart with optional arguments (only the first one is mandatory) 73 | make_dotchart(df['size'], 74 | color_by=df['rating'], # list to color by 75 | reverse=False, # inverts the color mapping 76 | theme='gnuplot2', # scroll down to see all themes 77 | colorbar=True, 78 | xlabel='Sizes', 79 | ylabel='Size Count', 80 | title='Mushroom Sizes Colored by Rating', 81 | dot_size=40): 82 | ``` 83 | 84 | ###### Example 4: Plotting in a Jupyter Notebook 85 | 86 | If plotting inline, use the default `.dotchart()` to obtain `x` and `y` lists, and then adjust as necessary with one of the following: 87 | 88 | ```python 89 | plt.figure(figsize=(12,6)) # or 90 | plt.figure().set_figwidth(12) # or 91 | plt.figure().set_figheight(12) 92 | ``` 93 | 94 | ![](https://github.com/jl33-ai/dotplotlib/blob/main/demos/jupyter.png?raw=true) 95 | 96 |
97 | 98 | --- 99 | 100 | ### Themes 101 | 102 | ![](https://github.com/jl33-ai/dotplotlib/blob/main/demos/gallery.png?raw=true) 103 | 104 | 105 | ### Feature set 106 | 107 | - Generate strip plots/dot charts by exploiting `matplotlib/seaborn` scatterplots 108 | - Supports any cmap color profile 109 | - The data can be automatically sorted for better visualization, especially when using color mapping. 110 | - Accepts both list and pandas.Series as input data. 111 | - Set custom labels, titles, and dot sizes for your charts. 112 | - Works with Jupyter Notebook 113 | 114 | 115 | ### Contributing 116 | 117 | Anyone is welcome to raise a PR! 118 | 119 | [contributors-shield]: https://img.shields.io/github/contributors/jl33-ai/dotplotlib.svg?style=for-the-badge 120 | [contributors-url]: https://github.com/jl33-ai/dotplotlib/graphs/contributors 121 | [forks-shield]: https://img.shields.io/github/forks/jl33-ai/dotplotlib.svg?style=for-the-badge 122 | [forks-url]: https://github.com/jl33-ai/dotplotlib/network/members 123 | [stars-shield]: https://img.shields.io/github/stars/jl33-ai/dotplotlib.svg?style=for-the-badge 124 | [stars-url]: https://github.com/jl33-ai/dotplotlib/stargazers 125 | [issues-shield]: https://img.shields.io/github/issues/jl33-ai/dotplotlib.svg?style=for-the-badge 126 | [issues-url]: https://github.com/jl33-ai/dotplotlib/issues 127 | [license-shield]: https://img.shields.io/github/license/jl33-ai/dotplotlib.svg?style=for-the-badge 128 | [license-url]: https://github.com/jl33-ai/dotplotlib/blob/master/LICENSE.txt 129 | -------------------------------------------------------------------------------- /demos/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jl33-ai/dotplotlib/6b2cff6cd10f8fb51fe89dad10a26d50d222ab1a/demos/.DS_Store -------------------------------------------------------------------------------- /demos/daterange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jl33-ai/dotplotlib/6b2cff6cd10f8fb51fe89dad10a26d50d222ab1a/demos/daterange.png -------------------------------------------------------------------------------- /demos/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jl33-ai/dotplotlib/6b2cff6cd10f8fb51fe89dad10a26d50d222ab1a/demos/default.png -------------------------------------------------------------------------------- /demos/gallery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jl33-ai/dotplotlib/6b2cff6cd10f8fb51fe89dad10a26d50d222ab1a/demos/gallery.png -------------------------------------------------------------------------------- /demos/gnuplot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jl33-ai/dotplotlib/6b2cff6cd10f8fb51fe89dad10a26d50d222ab1a/demos/gnuplot.png -------------------------------------------------------------------------------- /demos/jupyter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jl33-ai/dotplotlib/6b2cff6cd10f8fb51fe89dad10a26d50d222ab1a/demos/jupyter.png -------------------------------------------------------------------------------- /demos/lavender.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jl33-ai/dotplotlib/6b2cff6cd10f8fb51fe89dad10a26d50d222ab1a/demos/lavender.png -------------------------------------------------------------------------------- /dotplotlib/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jl33-ai/dotplotlib/6b2cff6cd10f8fb51fe89dad10a26d50d222ab1a/dotplotlib/.DS_Store -------------------------------------------------------------------------------- /dotplotlib/__init__.py: -------------------------------------------------------------------------------- 1 | from .plots import dotchart, make_dotchart -------------------------------------------------------------------------------- /dotplotlib/plots.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import warnings 3 | import matplotlib.pyplot as plt 4 | import seaborn as sns 5 | 6 | Y_SCALE=1.4 7 | 8 | def dotchart(x, color_by=None, reverse=False): 9 | """ 10 | Generates x, y coordinates for a dot chart from an array-like input and sorts the data to facilitate color mapping. 11 | OPTIONAL: color_by specifies the data column which controls the colors of the datapoints 12 | 13 | Parameters: 14 | - x: List or pandas.Series containing data points for the x-axis. 15 | - color_by (optional): List or pandas.Series to color the datapoints by. Pass as argument to c= (matplotlib) or hue= (seaborn). 16 | - reverse (optional): Boolean to indicate if the color map should be reversed. 17 | 18 | Returns: 19 | - Tuple of two or three lists (x_out, y, [color_out]): 20 | 'x_out' contains elements of 'x' in the original or sorted order, 21 | 'y' contains the occurrence count for each corresponding x value, 22 | 'color_out' (optional) is returned if color_by is provided, containing 23 | the color_by column to pass to matplotlib or seaborn. 24 | """ 25 | if not isinstance(x, (list, pd.Series)): 26 | raise ValueError("Input x must be a list or a pandas Series") 27 | 28 | if color_by is not None: 29 | if not isinstance(color_by, (list, pd.Series)): 30 | raise ValueError("Input color_by must be a list or a pandas Series (or None)") 31 | if len(x) != len(color_by): 32 | raise ValueError("Inputs x and color_by must be data of the same length") 33 | 34 | df = pd.DataFrame({'x': x, 'color_by': color_by}) 35 | df.sort_values(by='color_by', ascending=not reverse, inplace=True) 36 | x = df['x'] 37 | color_out = df['color_by'] 38 | 39 | seenCount = {} 40 | x_out, y = [], [] 41 | 42 | for value in x: 43 | seenCount[value] = seenCount.get(value, 0) + 1 44 | x_out.append(value) 45 | y.append(seenCount[value]) 46 | 47 | recommended_ymax = int(max(seenCount.values())*Y_SCALE) 48 | warning_msg = f"\n If your plot is oddly shaped, try: \n 1. Adjust the window size with your cursor \n 2. Add the following line: plt.ylim = [{0}, {recommended_ymax}], and fine tune appropriately. \n 3. Create your plot in a separate script instead of inline with Jupyter and use plt.figure(figsize=(width,height))" 49 | warnings.simplefilter('always', UserWarning) 50 | warnings.warn(warning_msg) 51 | 52 | try: 53 | if color_by is not None: 54 | return x_out, y, color_out 55 | else: 56 | return x_out, y 57 | except ValueError as e: 58 | raise ValueError("Custom Error: The dotchart function is expected to return 2 values when 'color_by' is not used, and 3 values when 'color_by' is used.") from e 59 | 60 | def make_dotchart(x, 61 | color_by=None, 62 | reverse=False, 63 | theme=None, 64 | colorbar=True, 65 | xlabel='x', 66 | ylabel='y', 67 | title='Dot Chart', 68 | dot_size=50): 69 | """ 70 | Creates a dot chart with customizable themes, labels, and color mapping. 71 | 72 | This function generates a scatter plot based on the provided 'x' values. 73 | If 'color_by' is specified, points will be colored based on these values. 74 | The function offers several themes and customization options including labels, 75 | title, and the presence of a color bar. 76 | 77 | Parameters: 78 | - x (list or pandas.Series): Data points for the x-axis. 79 | - color_by (list or pandas.Series, optional): Data points used for color mapping. Default is None. 80 | - reverse (bool, optional): If True, reverses the color mapping order. Default is False. 81 | - theme (str, optional): The theme of the plot. Options are 'custom:lavender' or 'custom:ocean', otherwise you may specify any cmap supported by matplotlib. 82 | - colorbar (bool, optional): If True, includes a color bar in the plot. Default is True. 83 | - xlabel (str, optional): Label for the x-axis. Default is 'x'. 84 | - ylabel (str, optional): Label for the y-axis. Default is 'y'. 85 | - title (str, optional): Title of the plot. Default is 'Dot Chart'. 86 | - dot_size (int, optional): Size of the dots in the scatter plot. Default is 50. 87 | 88 | Raises: 89 | - ValueError: If 'x' or 'color_by' are not lists or pandas.Series, or if they are of different lengths. 90 | 91 | Examples: 92 | - Basic usage: 93 | >>> make_dotchart([1, 2, 3, 4], color_by=[10, 20, 30, 40], theme='default') 94 | 95 | - Customized usage: 96 | >>> make_dotchart([1, 2, 3, 4], color_by=[10, 20, 30, 40], xlabel='Custom X', ylabel='Custom Y', title='Custom Title', dot_size=100) 97 | """ 98 | if color_by is None: 99 | if theme is not None: 100 | warnings.warn("Colour theme was specified but cannot be applied, since no 'color_by' data was given.") 101 | x, y = dotchart(x) 102 | recommended_ymax = int(max(y)*Y_SCALE) 103 | plt.scatter(x, y, s=dot_size) 104 | plt.xlabel(xlabel) 105 | plt.ylabel(ylabel) 106 | plt.title(title) 107 | plt.ylim([0, recommended_ymax]) 108 | plt.show() 109 | 110 | else: 111 | x, y, c = dotchart(x, color_by=color_by, reverse=reverse) 112 | recommended_ymax = int(max(y)*Y_SCALE) 113 | match theme: 114 | case 'custom:lavender': 115 | sns.set(rc={'axes.facecolor': 'lavender'}) 116 | ax = sns.scatterplot(x=x, y=y, hue=c, s=dot_size, legend="full", palette="RdYlGn") 117 | ax.grid(False) 118 | ax.get_legend().remove() 119 | ax.set_xlabel(xlabel) 120 | ax.set_ylabel(ylabel) 121 | ax.set_title(title) 122 | plt.ylim([0, recommended_ymax]) 123 | 124 | if colorbar: 125 | scale_legend = plt.Normalize(c.min(), c.max()) 126 | color_map = plt.cm.ScalarMappable(cmap="RdYlGn", norm=scale_legend) 127 | color_map.set_array([]) 128 | plt.colorbar(color_map, ax=ax) 129 | 130 | plt.show() 131 | case 'custom:ocean': 132 | sns.set(rc={'axes.facecolor': 'lightblue'}) # Set a light blue background 133 | ax = sns.scatterplot(x=x, y=y, hue=c, s=dot_size, legend="full", palette="Blues") 134 | ax.grid(False) 135 | ax.get_legend().remove() 136 | ax.set_xlabel(xlabel) 137 | ax.set_ylabel(ylabel) 138 | ax.set_title(title) 139 | plt.ylim([0, recommended_ymax]) 140 | 141 | if colorbar: 142 | scale_legend = plt.Normalize(c.min(), c.max()) 143 | color_map = plt.cm.ScalarMappable(cmap="Blues", norm=scale_legend) 144 | color_map.set_array([]) 145 | plt.colorbar(color_map, ax=ax) 146 | 147 | plt.show() 148 | case _: 149 | try: 150 | plt.scatter(x, y, c=c, cmap=theme, s=dot_size) 151 | except ValueError as e: 152 | print(f"The theme '{theme}' is unsupported by dotplotlib, and is not a valid cmap.") 153 | print(e) 154 | return 155 | plt.xlabel(xlabel) 156 | plt.ylabel(ylabel) 157 | plt.title(title) 158 | plt.ylim([0, recommended_ymax]) 159 | 160 | if colorbar: 161 | plt.colorbar() 162 | 163 | plt.show() -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | # Read the contents of your README file 4 | with open('README.md', 'r', encoding='utf-8') as fh: 5 | long_description = fh.read() 6 | 7 | setup( 8 | name='dotplotlib', 9 | version='1.1.8', 10 | author='Justin Lee', 11 | author_email='justinkhlee27@gmail.com', 12 | description='An extension library for creating strip plots or dot charts w/ matplotlib + seaboard', 13 | long_description=long_description, 14 | long_description_content_type='text/markdown', 15 | url='https://github.com/jl33-ai/dotplotlib', 16 | packages=find_packages(), 17 | install_requires=[ 18 | 'pandas', 19 | 'matplotlib', 20 | 'seaborn' 21 | ], 22 | classifiers=[ 23 | 'Development Status :: 3 - Alpha', 24 | 'Intended Audience :: Developers', 25 | 'Programming Language :: Python :: 3', 26 | 'Programming Language :: Python :: 3.7', 27 | 'Programming Language :: Python :: 3.8', 28 | 'License :: OSI Approved :: MIT License', 29 | 'Operating System :: OS Independent', 30 | ], 31 | python_requires='>=3.6', 32 | ) 33 | --------------------------------------------------------------------------------