Skip to content
This repository has been archived by the owner on Nov 30, 2022. It is now read-only.

Commit

Permalink
Merge pull request #41 from nschloe/bar-plots
Browse files Browse the repository at this point in the history
bar plots
  • Loading branch information
nschloe authored Jul 19, 2021
2 parents 9da3d10 + 5b60bbb commit 67a051b
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 22 deletions.
32 changes: 27 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
This package creates clean and beautiful plots that work on light and dark backgrounds.
Inspired by the work of [Edward Tufte](https://en.wikipedia.org/wiki/Edward_Tufte).

<img src="https://nschloe.github.io/dufte/ex1-light.svg"> | <img src="https://nschloe.github.io/dufte/ex1-dark.svg">
<img src="https://nschloe.github.io/dufte/ex1.svg"> | <img src="https://nschloe.github.io/dufte/bar.svg">
:----:|:----:|

To use, simply select the `dufte` style. Check out `dufte.legend()` and
Expand All @@ -29,30 +29,52 @@ import numpy as np

plt.style.use(dufte.style)

np.random.seed(0)
rng = np.random.default_rng(0)

x0 = np.linspace(0.0, 3.0, 100)
y0 = x0 / (x0 + 1)
y0 += 0.1 * np.random.rand(len(y0))
y0 += 0.1 * rng.random(len(y0))
plt.plot(x0, y0, label="no balacing")

x1 = np.linspace(0.0, 3.0, 100)
y1 = 1.5 * x1 / (x1 + 1)
y1 += 0.1 * np.random.rand(len(y1))
y1 += 0.1 * rng.random(len(y1))
plt.plot(x1, y1, label="CRV-27")

x2 = np.linspace(0.0, 3.0, 100)
y2 = 1.6 * x2 / (x2 + 1)
y2 += 0.1 * np.random.rand(len(y2))
y2 += 0.1 * rng.random(len(y2))
plt.plot(x2, y2, label="CRV-27*")

dufte.ylabel("ylabel")
dufte.legend()

plt.show()
```
The bar plot is created with `dufte.style_bar` here and `dufte.show_bar_values()`.
Note the use of `context` instead of `style.use()`; both are appropriate.
```python
import matplotlib.pyplot as plt
import dufte


with plt.style.context(dufte.style_bar):
labels = ["Australia", "Brazil", "China", "Germany", "Mexico", "United\nStates"]
vals = [21.65, 24.5, 6.95, 8.40, 21.00, 8.55]
xpos = range(len(vals))
plt.bar(xpos, vals)
plt.xticks(xpos, labels)
dufte.show_bar_values("{:.2f}")
plt.title("average temperature [°C]")
plt.show()
```

Further reading:

* [Remove to improve: data-ink ratio](https://www.darkhorseanalytics.com/blog/data-looks-better-naked)
<img src="https://nschloe.github.io/dufte/data-ink.webp">
* [Remove to improve: Line Graph Edition](https://youtu.be/bDbJBWvonVI)
* [Show the Data - Maximize the Data Ink Ratio](https://youtu.be/pCp0a5_YIWE)
* [Randal S. Olson's blog entry](http://www.randalolson.com/2014/06/28/how-to-make-beautiful-data-visualizations-in-python-with-matplotlib/)
* [prettyplotlib](https://github.com/olgabot/prettyplotlib)
* [Wikipedia: Chartjunk](https://en.wikipedia.org/wiki/Chartjunk)
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = dufte
version = 0.2.23
version = 0.2.24
author = Nico Schlömer
author_email = [email protected]
description = Clean matplotlib plots
Expand Down
4 changes: 2 additions & 2 deletions src/dufte/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from .main import legend, style, ylabel
from .main import legend, show_bar_values, style, style_bar, ylabel

__all__ = ["legend", "style", "ylabel"]
__all__ = ["legend", "style", "style_bar", "ylabel", "show_bar_values"]
40 changes: 38 additions & 2 deletions src/dufte/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
# decides to turn them on.
"axes.edgecolor": _gray,
"axes.linewidth": _stroke_width,
# default is "line", i.e., below lines but above patches (bars)
"axes.axisbelow": True,
#
"ytick.right": False,
"ytick.color": _gray,
Expand Down Expand Up @@ -74,10 +76,18 @@
"9edae5",
],
),
"axes.titlepad": 30.0,
"axes.titlesize": 14,
"axes.titlepad": 40,
"axes.titlesize": 18,
"axes.titlelocation": "left",
}

style_bar = style.copy()
# hide xticks for bars; the label is enough
style_bar["xtick.major.width"] = 0
# unhide the bar labels
style_bar["xtick.major.pad"] = 13
style_bar["font.size"] = 16


def _move_min_distance(targets, min_distance):
"""Move the targets such that they are close to their original positions, but keep
Expand Down Expand Up @@ -225,3 +235,29 @@ def ylabel(string):
# place the label 10% above the top tick
ax.yaxis.set_label_coords(pos_x, pos_y)
ylabel.set_rotation(0)


def show_bar_values(fmt="{}"):
ax = plt.gca()

# turn off y-ticks and y-grid
plt.tick_params(axis="y", which="both", left=False, right=False, labelleft=False)
plt.grid(False)

data_to_axis = ax.transData + ax.transAxes.inverted()
axis_to_data = ax.transAxes + ax.transData.inverted()

for rect in ax.patches:
height = rect.get_height()
ypos_ax = data_to_axis.transform([1.0, height])
ypos = axis_to_data.transform(ypos_ax - 0.1)[1]
ax.text(
rect.get_x() + rect.get_width() / 2,
ypos,
fmt.format(height),
size=14,
weight="bold",
ha="center",
va="bottom",
color="white",
)
32 changes: 32 additions & 0 deletions tests/test_bar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import matplotlib.pyplot as plt

import dufte


def test_bar(filename=None, light=True):
with plt.style.context(dufte.style_bar):
labels = ["Australia", "Brazil", "China", "Germany", "Mexico", "United\nStates"]
vals = [21.65, 24.5, 6.95, 8.40, 21.00, 8.55]
xpos = range(len(vals))
plt.bar(xpos, vals)
plt.xticks(xpos, labels)
dufte.show_bar_values("{:.2f}")
plt.title("average temperature [°C]")

if not light:
gh_dark_bg = "#0d1117"
plt.gca().set_facecolor(gh_dark_bg)
plt.gcf().patch.set_facecolor(gh_dark_bg)

if filename:
plt.savefig(filename, transparent=True, bbox_inches="tight")
else:
plt.show()


if __name__ == "__main__":
# test_bar("bar-light.svg", True)
# plt.close()
# test_bar("bar-dark.svg", False)
test_bar("bar.svg", False)
plt.close()
20 changes: 8 additions & 12 deletions tests/test_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@
def test_plot(filename, light: bool, noise, offsets):
plt.style.use(dufte.style)

np.random.seed(0)
rng = np.random.default_rng(0)

x0 = np.linspace(0.0, 3.0, 100)
labels = ["no balancing", "CRV-27", "CRV-27*"]
for label, offset in zip(labels, offsets):
y0 = offset * x0 / (x0 + 1)
y0 += noise * np.random.rand(len(y0))
y0 += noise * rng.random(len(y0))
plt.plot(x0, y0, label=label)

plt.xlabel("distance [m]")
Expand All @@ -31,13 +32,7 @@ def test_plot(filename, light: bool, noise, offsets):
plt.gcf().patch.set_facecolor(gh_dark_bg)

if filename:
# <https://github.com/matplotlib/matplotlib/issues/17321>
plt.savefig(
filename,
transparent=True,
bbox_inches="tight",
facecolor=plt.gcf().get_facecolor(),
)
plt.savefig(filename, transparent=True, bbox_inches="tight")
else:
plt.show()

Expand Down Expand Up @@ -87,7 +82,8 @@ def test_all_nan():

if __name__ == "__main__":
# test_plot(None, True, 0.1, (1.0, 1.5, 1.6))
test_plot("ex1-light.svg", True, 0.1, (1.0, 1.5, 1.6))
# test_plot("ex1-light.svg", True, 0.1, (1.0, 1.5, 1.6))
# plt.close()
# test_plot("ex1-dark.svg", False, 0.1, (1.0, 1.5, 1.6))
test_plot("ex1.svg", False, 0.1, (1.0, 1.5, 1.6))
plt.close()
test_plot("ex1-dark.svg", False, 0.1, (1.0, 1.5, 1.6))
# test_nan()

0 comments on commit 67a051b

Please sign in to comment.