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

bar plots #41

Merged
merged 11 commits into from
Jul 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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 = nico.schloemer@gmail.com
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()