Giter Site home page Giter Site logo

gyli / pywaffle Goto Github PK

View Code? Open in Web Editor NEW
575.0 10.0 104.0 3.32 MB

๐Ÿง‡ Make Waffle Charts in Python.

License: MIT License

Python 87.43% Shell 0.44% Jupyter Notebook 12.13%
python waffle charts matplotlib visualization data-visualization waffle-charts

pywaffle's Introduction

PyWaffle

PyPI version ReadTheDocs Binder

PyWaffle is an open source, MIT-licensed Python package for plotting waffle charts.

It provides a Figure constructor class Waffle, which could be passed to matplotlib.pyplot.figure and generates a matplotlib Figure object.

PyPI Page: https://pypi.org/project/pywaffle/

Documentation: http://pywaffle.readthedocs.io/

Installation

pip install pywaffle

Requirements

  • Python 3.5+
  • Matplotlib

Examples

1. Value Scaling

import matplotlib.pyplot as plt
from pywaffle import Waffle
fig = plt.figure(
    FigureClass=Waffle, 
    rows=5, 
    columns=10, 
    values=[48, 46, 6],
    figsize=(5, 3)
)
plt.show()

basic

The values are automatically scaled to 24, 23 and 3 to fit 5 * 10 chart size.

FigureClass and figsize are parameters of matplotlib.pyplot.figure, you may find the full parameter list on matplotlib.pyplot.figure function reference.

Other parameters, including rows, columns, and values in this example, are from Waffle, and see PyWaffle's API Reference for details.

2. Values in dict & Auto-sizing

data = {'Cat1': 10, 'Cat2': 7, 'Cat3': 9}
fig = plt.figure(
    FigureClass=Waffle,
    rows=5,
    values=data,
    legend={'loc': 'upper left', 'bbox_to_anchor': (1.05, 1)},
)
plt.show()

Use values in dictionary; use absolute value as block number, without defining columns

In this example, only rows is specified and columns is empty, absolute values in values are used as block numbers. Similarly, rows could also be optional if columns is specified.

If values is a dict, the keys will be used as labels in the legend.

3. More style settings including Legend, Title, Colors, Direction, Arranging Style, etc.

data = {'Car': 58, 'Pickup': 21, 'Truck': 11, 'Motorcycle': 7}
fig = plt.figure(
    FigureClass=Waffle,
    rows=5,
    values=data,
    colors=["#C1D82F", "#00A4E4", "#FBB034", '#6A737B'],
    title={'label': 'Vehicle Sales by Vehicle Type', 'loc': 'left'},
    labels=[f"{k} ({v}%)" for k, v in data.items()],
    legend={'loc': 'lower left', 'bbox_to_anchor': (0, -0.4), 'ncol': len(data), 'framealpha': 0},
    starting_location='NW',
    vertical=True,
    block_arranging_style='snake'
)
fig.set_facecolor('#EEEEEE')
plt.show()

Add title, legend and background color; customize the block color

Parameter colors allows you to change the block color, and it accepts a list of colors that matplotlib can recognize, including hex, RGB in tuple, single character notation, etc. See Matplotlib Colors for details.

Parameter title and legend accept the same parameters as in Matplotlib, matplotlib.pyplot.title and matplotlib.pyplot.legend.

Parameter starting_location, vertical, and block_arranging_style controls Where to Start First Block, Plotting Direction, and Where to Start Each Category.

You may find more details under Examples section in PyWaffle Documentation.

4. Plot with Icons - Pictogram Chart

data = {'Car': 58, 'Pickup': 21, 'Truck': 11, 'Motorcycle': 7}
fig = plt.figure(
    FigureClass=Waffle,
    rows=5,
    values=data,
    colors=["#c1d82f", "#00a4e4", "#fbb034", '#6a737b'],
    legend={'loc': 'upper left', 'bbox_to_anchor': (1, 1)},
    icons=['car-side', 'truck-pickup', 'truck', 'motorcycle'],
    font_size=12,
    icon_legend=True
)
plt.show()

Use Font Awesome icons

PyWaffle supports Font Awesome icons in the chart. See Plot with Characters or Icons for details.

5. Plotting on Existed Figure and Axis

fig = plt.figure()
ax = fig.add_subplot(111)

# Modify existed axis
ax.set_title("Axis Title")
ax.set_aspect(aspect="equal")

Waffle.make_waffle(
    ax=ax,  # pass axis to make_waffle
    rows=5, 
    columns=10, 
    values=[30, 16, 4], 
    title={"label": "Waffle Title", "loc": "left"}
)

Plotting on Existed Figure and Axis

6. Multiple Plots in One Chart

import pandas as pd
data = pd.DataFrame(
    {
        'labels': ['Car', 'Truck', 'Motorcycle'],
        'Factory A': [32384, 13354, 5245],
        'Factory B': [22147, 6678, 2156],
        'Factory C': [8932, 3879, 896],
    },
).set_index('labels')

# A glance of the data:
#             Factory A  Factory B  Factory C
# labels
# Car             27384      22147       8932
# Truck            7354       6678       3879
# Motorcycle       3245       2156       1196

fig = plt.figure(
    FigureClass=Waffle,
    plots={
        311: {
            'values': data['Factory A'] / 1000,  # Convert actual number to a reasonable block number
            'labels': [f"{k} ({v})" for k, v in data['Factory A'].items()],
            'legend': {'loc': 'upper left', 'bbox_to_anchor': (1.05, 1), 'fontsize': 8},
            'title': {'label': 'Vehicle Production of Factory A', 'loc': 'left', 'fontsize': 12}
        },
        312: {
            'values': data['Factory B'] / 1000,
            'labels': [f"{k} ({v})" for k, v in data['Factory B'].items()],
            'legend': {'loc': 'upper left', 'bbox_to_anchor': (1.2, 1), 'fontsize': 8},
            'title': {'label': 'Vehicle Production of Factory B', 'loc': 'left', 'fontsize': 12}
        },
        313: {
            'values': data['Factory C'] / 1000,
            'labels': [f"{k} ({v})" for k, v in data['Factory C'].items()],
            'legend': {'loc': 'upper left', 'bbox_to_anchor': (1.3, 1), 'fontsize': 8},
            'title': {'label': 'Vehicle Production of Factory C', 'loc': 'left', 'fontsize': 12}
        },
    },
    rows=5,  # Outside parameter applied to all subplots, same as below
    cmap_name="Accent",  # Change color with cmap
    rounding_rule='ceil',  # Change rounding rule, so value less than 1000 will still have at least 1 block
    figsize=(6, 5)
)

fig.suptitle('Vehicle Production by Vehicle Type', fontsize=14, fontweight='bold')
fig.supxlabel('1 block = 1000 vehicles', fontsize=8, x=0.14)
fig.set_facecolor('#EEEDE7')

plt.show()

Multiple plots

Demo

Wanna try it yourself? There is Online Demo!

What's New

See CHANGELOG

License

  • PyWaffle is under MIT license, see LICENSE file for the details.
  • The Font Awesome font is licensed under the SIL OFL 1.1: http://scripts.sil.org/OFL

pywaffle's People

Contributors

asongtoruin avatar gyli avatar lianjiayi1 avatar toddrme2178 avatar tweakimp avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pywaffle's Issues

width problems with a thousand blocks

When plotting a larger number of blocks, the width of the white space between them become unstable:

plt.figure(
    FigureClass=Waffle,
    rows=20,
    columns=80,
    values=[300, 700],
    figsize=(18, 10)
);
plt.savefig('example.png')

image

This is probably outside the original scope of the package and maybe should even be discouraged, but sometimes is useful to give the reader the impression of dealing with a large population. Feel free to close this issue.

ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

When using a pandas DataFrame, I keep getting this error, no matter what DataFrame I use:

Traceback (most recent call last):
  File "clean_input_file.py", line 177, in <module>
    main()
  File "clean_input_file.py", line 172, in main
    plot_res(data_FSW)
  File "clean_input_file.py", line 149, in plot_res
    title={'label': 'Vote Percentage in 2016 US Presidential Election', 'loc': 'left'}
  File "C:\Users\Pez Amaury\AppData\Local\Programs\Python\Python36-32\lib\site-packages\matplotlib\pyplot.py", line 548, in figure
    **kwargs)
  File "C:\Users\Pez Amaury\AppData\Local\Programs\Python\Python36-32\lib\site-packages\matplotlib\backend_bases.py", line 160, in new_figure_manager
    fig = fig_cls(*args, **kwargs)
  File "C:\Users\Pez Amaury\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pywaffle\waffle.py", line 158, in __init__
    if (not self.fig_args['values'] or not self.fig_args['rows']) and not self.plots:
  File "C:\Users\Pez Amaury\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pandas\core\generic.py", line 1573, in __nonzero__
    .format(self.__class__.__name__))
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

I understand this is due to using operators or/and in waffle.py, so changing them to np.logical_and and np.logical_or would probably solve the problem.

Here's the dataFrame I'm using:

Variables
ART last 12 months 0.0
Access to condoms last 12 months 0.0
Any STI treatment last 12 months 0.0
Clean needles/syringes at last injection 0.0
Condom use at last time they had sex 0.0
Condom use when had anal sex last time with a male partner 0.0
Condom use when had sex last time with a commercial client 0.0
HBV Prevalence 0.0
HCV Prevalence 0.0
HIV Prevalence 0.0
HIV incidence 0.0
HIV test history 0.0
HIV test in last 12 months 0.0
HIV-1 Prevalence 0.0
HIV-2 Prevalence 0.0
OST/MMT last 6 months 0.0
Other STI Prevalence 0.0
Received HIV test results 0.0
STI test last 12 months 0.0
Syphilis Prevalence 0.0
Name: 2001, dtype: float64

Add Font Awesome through Python package

Font Awesome v6 now provides Python package on Pypi https://pypi.org/project/fontawesomefree/#description

Add Font Awesome through Python package, so no need to add a copy of otf files in PyWaffle.

This solution might not reduce the content that need to be downloaded when installing, and is not able to use system's font first (#25), while it does avoid some unnecessary downloads when there are other Python packages also using Font Awesome.

Font awesome license

I notice that files from the Font Awesome project are being used. The project's license requires attribution. IANAL, and I am not affiliated with that project, but I think there probably needs to be a license file for these files, and this needs to be in the MANIFEST.in file (see #18) so it is included in sdists.

Problem with Font awesome on VsCode

I have a strange problem with the PyWaffle library. I am running the examples and everything is working fine except the examples with fontawesome.

I am running the following code and the result shows distorted picture.

dict_users = {'Regular': 62, 'New': 20, 'Churned': 16, 'Suspended': 2}
df = pd.Series(dict_users)
colors_list = ['slateblue', 'limegreen', 'red', 'grey']
colors = {df.index[i]:colors_list[i] for i in range(len(df))}
fig = plt.figure(FigureClass=Waffle,
                 figsize=(10,5),
                 values=dict_users,
                 rows=10,
                 colors=list(colors.values()),
                 icons=['user','user-plus', 'user-minus', 'user-clock'],
                 font_size=15,
                 icon_legend=True,
                 legend={'bbox_to_anchor': (1.55, 1), 'fontsize': 15, 'frameon': False})              
plt.title('User dynamics in July 2021', fontsize=20)
plt.show()

I am also having the the following warning message : IPython\core\pylabtools.py:151: UserWarning: Tight layout not applied. The bottom and top margins cannot be made large enough to accommodate all axes decorations.
fig.canvas.print_figure(bytes_io, **kw)

I also tried to use the command fig.set_tight_layout(False)
but the warning message did not go away.

output

cant specify columns

Each chart I create (python 3.7.4, PyWaffle 0.2.3) is coming out square regardless of what is passed in the columns parameter. Its as if the columns parameter is being overwritten by what is passed to rows

    plt.figure(
        FigureClass=Waffle,
        rows=5,
        columns=10,
        values=[150, 75, 25],
        figsize=(16, 9)
    );
    plt.savefig('example.png')

example

Feature request

Great package! Are we able to get an option to change the square or dot to a custom graphic? For instance if it's a viz about humans, can we put a human svg icon from say font awesome and put it in there?

Ed

characters argument and plt.savefig

There seem to be a problem when saving figures as pdf with matplotlib.pyplot.savefig and using the characters argument in Waffle: some characters are replaced by others.

list_character = ["๐Ÿ–ณ", "๐Ÿ"]

from pywaffle import Waffle
from matplotlib import pyplot as plt

n_row = 4
n_column = 5

import numpy as np
list_value = np.ones(len(list_character))

fig = plt.figure(
    FigureClass=Waffle,
    rows=1,
    columns=len(list_character),
    values=list_value,
    characters=list_character,
    font_file="~/.fonts/Symbola.ttf",
    font_size=50
    )

plt.savefig("replaced_character.pdf")
plt.close("all")

yields the attached file (two snakes instead of a computer and a snake).
The computer alone is correctly printed. Inverting the order yields two snakes.

replaced_character.pdf

missing corner squares with automatic scaling

I'm making 50ร—20 grids, so each block is 0.1%. I've found that with some data, the top right corner ends up empty. Here's a real-world example:

#!/usr/bin/python3

import matplotlib as m
m.use("Agg")
import matplotlib.pyplot as plt
import matplotlib.dates as dates
from pywaffle import Waffle

m.style.use('seaborn-whitegrid')
m.rcParams['font.size'] = 12

fig = plt.figure( 
FigureClass=Waffle, 
 rows=20,
 columns=50,
 values={'32': 192,'33': 76,'34':59},
 figsize=[16, 9],
)              

fig.savefig('test.png',dpi=300)

            
plt.close()   

This results in:

test

... with an empty square in the top right -- a total of 999 squares instead of 1000.

I assume this is because all values are getting rounded down.

Request: ability to use system-packaged Fontawesome if found?

In Fedora, we have FontAwesome as an included package, and our packaging guidelines require bundled fonts to be ... debundled.

I've patched this in to my package build scripts (see https://mattdm.org/misc/fedora/pywaffle/python-pywaffle.spec), but it would be ideal if the library just noticed and used those. I guess this could be a "build time" thing, but I'm thinking maybe if

/usr/share/fonts/fontawesome5-free-fonts/Font Awesome 5 Free-Regular-400.otf
/usr/share/fonts/fontawesome5-free-fonts/Font Awesome 5 Free-Solid-900.otf
/usr/share/fonts/fontawesome5-brands-fonts/Font Awesome 5 Brands-Regular-400.otf

are installed, those could be automatically used -- with the mappings created at runtime from /usr/share/fontawesome5/metadata/icons.json.

That'd also benefit people who install via pip or whatever.

What do you think?

Update to FontAwesome v5

Hello,

Thanks for creating this module, it's really useful and pretty straightforward to use.

One thing I've noticed is that the module currently uses FontAwesome v4.7.0, with the latest version of FA being v5.5.0. It'd be nice to be able to use the latest set of icons and updating the equivalence should be relatively straightforward through the cheatsheet they provide, but the download is now (I believe since 5.0) split into three .otf files - "solid", "regular" and "brands". I see there being two ways to deal with this:

  1. Pick one of the new three to have as the main reference. The "regular" is I think meant to be the most similar to the old icons, but the "solid" is the most complete.
  2. Keep all three and either:
    1. Add an extra parameter to choose which of the three icon sets to use (e.g. icon_set as a string).
    2. Rename the icon lookups to include which set they come from, and ensure the user only uses icons from one set.

I am happy to work on a PR for this based on your preference. I think 2i would work best (and might start looking at implementing it), but will defer to your judgement. There may also be some compatibility issues with name changes, but I think it's worth it for the wider set of icons available.

Thanks,

Adam

Vertical orientation?

It would be interesting to be able to generate plots that were vertically orientated - i.e. rather than drawing the blocks column by column, they could be drawn row by row. I've got a basic working attempt at asongtoruin/pywaffle@e4d73b7 which adds the extra boolean kwarg vertical to do this, but I wasn't sure if this was functionality you think would be useful for the module.

For example, if we take the basic example from the examples folder:
fig = plt.figure(FigureClass=Waffle, rows=5, columns=10, values=[48, 46, 3])
this produces the following horizontal plot:
basic_horiz

If we change this to
fig = plt.figure(FigureClass=Waffle, rows=10, columns=5, values=[48, 46, 3], vertical=True)
we instead get a vertically oriented plot:

basic

Thoughts?

Font file not found when using FontAwesome icons

When creating a chart with icons a FileNotFoundError is thrown, whilst looking for the file font/FontAwesome.otf. This seems to be because it's trying to find the file relative to the location of the calling script, rather than in the font package created during installation.

A simple fix for this would be to modify waffle.py to import the font package and use its __path__ value to construct the path to the FontAwesome file:

import font
import os
FONTAWESOME_FILE = os.path.join(font.__path__[0], 'FontAwesome.otf')

If you're happy with this fix I can make a pull request.

Fractional blocks

Is there a way to enable fractional blocks, so that there is no rounding? I'm especially running into issues where the rounding causes the total number of blocks to fluctuate, as in the example below. Both datasets total to 87.46 but the top plot has an extra block. It'd be great if the last block could have fractional size, and if the intermediate blocks could have split colors.

output

Code to reproduce:

plot1 = {'Net income': 18.46, 'Income tax': 1.64, 'MG&A': 7.52, 'R&D': 15.3, 'Cost of sales': 44.54}
plot2 = {'Misc income': 1.2, 'Revenue': 86.26}
fig = plt.figure(
    FigureClass=Waffle,
    plots={
        311: {'values':plot1},
        312: {'values':plot2}
    },
    rows=5,
    figsize=(9, 7),
    legend={'loc': 'upper left', 'bbox_to_anchor': (1.1, 1)},
    block_arranging_style='snake',
)

How to arrange the filling order as from top to bottom when using new-line?

Hi. I want to use the new-line arrangement for my plot.

This is what's shown in the documentation.

fig = plt.figure(
FigureClass=Waffle,
columns=10,
values=[30, 16, 4],
block_arranging_style='new-line',
vertical=True
)

image

However, as you can see, for the orange ones, if they have to span multiple rows and cannot span multiple rows, the plot will try to fill out the last row first, leaving the top row of the category incomplete. Is there a way to reverse it so that within each category, the top rows get filled out first then the bottom rows and if multiple rows cannot be filled the last row will be incomplete instead of the first row.

block present when value is 0

Hi! Thanks for creating PyWaffle and the useful examples.

I started using it today and everything is great, except that sometimes I have a value of 0 for some items. But when the waffle chart is generated, a block is still present for the category with a value of 0.
Screen Shot 2019-08-30 at 4 53 12 PM

Could you look into making it so that there is no block for a label if the value is 0?

Feature request

Great package! Are we able to get an option to change the square or dot to a custom graphic? For instance if it's a viz about humans, can we put a human svg icon from say font awesome and put it in there?

Ed

UserWarning: This figure includes Axes that are not compatible with tight_layout

First of all, incredibly awesome and fun package. Thanks a lot!

I'm getting this warning in all the plots:

UserWarning: This figure includes Axes that are not compatible with tight_layout, so results might be incorrect. warnings.warn("This figure includes Axes that are not compatible ").

It seems a false positive in this context, right?

KeyError: 'rows' in waffle.py

Hi,
I tried your last example Multiple plot and had the following error

figsize=(9, 5) # figsize is a parameter of plt.figure
File "/usr/local/lib/python3.6/dist-packages/matplotlib/pyplot.py", line 533, in figure
**kwargs)
File "/usr/local/lib/python3.6/dist-packages/matplotlib/backend_bases.py", line 160, in new_figure_manager
fig = fig_cls(*args, **kwargs)
File "/usr/local/lib/python3.6/dist-packages/pywaffle/waffle.py", line 165, in init
self._waffle(loc, **copy.deepcopy(setting))
File "/usr/local/lib/python3.6/dist-packages/pywaffle/waffle.py", line 174, in _waffle
if len(self._pa['values']) == 0 or not self._pa['rows']:
KeyError: 'rows'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.