|
| 1 | +""" |
| 2 | +================= |
| 3 | +Grouped bar chart |
| 4 | +================= |
| 5 | +
|
| 6 | +This example serves to showcase and discuss the API. It's geared towards illustating |
| 7 | +API usage and design decisions only through the development phase. It's not intended |
| 8 | +to go into the final PR in this form. |
| 9 | +
|
| 10 | +Input data formats |
| 11 | +================== |
| 12 | +
|
| 13 | +Case 1: multiple separate datasets |
| 14 | +---------------------------------- |
| 15 | +
|
| 16 | +""" |
| 17 | +import matplotlib.pyplot as plt |
| 18 | +import numpy as np |
| 19 | + |
| 20 | +group_labels = ['A', 'B'] |
| 21 | +data1 = [1, 1.2] |
| 22 | +data2 = [2, 2.4] |
| 23 | +data3 = [3, 3.6] |
| 24 | + |
| 25 | + |
| 26 | +fig, axs = plt.subplots(1, 2) |
| 27 | + |
| 28 | +# current solution: manual positioning with multiple bar() calls |
| 29 | +label_pos = np.array([0, 1]) |
| 30 | +bar_width = 0.8 / 3 |
| 31 | +data_shift = -1*bar_width + np.array([0, bar_width, 2*bar_width]) |
| 32 | +axs[0].bar(label_pos + data_shift[0], data1, width=bar_width, label="data1") |
| 33 | +axs[0].bar(label_pos + data_shift[1], data2, width=bar_width, label="data2") |
| 34 | +axs[0].bar(label_pos + data_shift[2], data3, width=bar_width, label="data3") |
| 35 | +axs[0].set_xticks(label_pos, group_labels) |
| 36 | +axs[0].legend() |
| 37 | + |
| 38 | +# grouped_bar() with list of datasets |
| 39 | +axs[1].grouped_bar([data1, data2, data3], |
| 40 | + tick_labels=group_labels, labels=["data1", "data2", "data3"]) |
| 41 | +axs[1].legend() |
| 42 | + |
| 43 | + |
| 44 | +# %% |
| 45 | +# Case 1b: multiple datasets as dict |
| 46 | +# ---------------------------------- |
| 47 | +# instead of carrying a list of datasets and a list of dataset labels, users may |
| 48 | +# want to organize their datasets in a dict. |
| 49 | + |
| 50 | +datasets = { |
| 51 | + 'data1': data1, |
| 52 | + 'data2': data2, |
| 53 | + 'data3': data3, |
| 54 | +} |
| 55 | + |
| 56 | +# %% |
| 57 | +# While you can feed keys and values into the above API, it may be convenient to pass |
| 58 | +# the whole dict as "data" and automatically extract the labels from the keys: |
| 59 | + |
| 60 | +fig, axs = plt.subplots(1, 2) |
| 61 | + |
| 62 | +# explicitly extract values and labels from a dict and feed to grouped_bar(): |
| 63 | +axs[0].grouped_bar(datasets.values(), tick_labels=group_labels, labels=datasets.keys()) |
| 64 | +axs[0].legend() |
| 65 | +# accepting a dict as input |
| 66 | +axs[1].grouped_bar(datasets, tick_labels=group_labels) |
| 67 | +axs[1].legend() |
| 68 | + |
| 69 | +# %% |
| 70 | +# Case 2: 2D array data |
| 71 | +# --------------------- |
| 72 | +# When receiving a 2D array, we interpret the data as |
| 73 | +# |
| 74 | +# .. code-block:: none |
| 75 | +# |
| 76 | +# dataset_0 dataset_1 dataset_2 |
| 77 | +# x[0]='A' ds0_a ds1_a ds2_a |
| 78 | +# x[1]='B' ds0_b ds1_b ds2_b |
| 79 | +# |
| 80 | +# This is consistent with the standard data science interpretation of instances |
| 81 | +# on the vertical and features on the horizontal. And also matches how pandas is |
| 82 | +# interpreting rows and columns. |
| 83 | +# |
| 84 | +# Note that a list of individual datasets and a 2D array behave structurally different, |
| 85 | +# i.e. hen turning a list into a numpy array, you have to transpose that array to get |
| 86 | +# the correct representation. Those two behave the same:: |
| 87 | +# |
| 88 | +# grouped_bar([data1, data2]) |
| 89 | +# grouped_bar(np.array([data1, data2]).T) |
| 90 | +# |
| 91 | +# This is a conscious decision, because the commonly understood dimension ordering |
| 92 | +# semantics of "list of datasets" and 2D array of datasets is different. |
| 93 | + |
| 94 | +group_labels = ['A', 'B'] |
| 95 | +data = np.array([ |
| 96 | + [1, 2, 3], |
| 97 | + [1.2, 2.4, 3.6], |
| 98 | +]) |
| 99 | +columns = ["data1", "data2", "data3"] |
| 100 | + |
| 101 | +fig, ax = plt.subplots() |
| 102 | +ax.grouped_bar(data, tick_labels=group_labels, labels=columns) |
| 103 | + |
| 104 | +# %% |
| 105 | +# This creates the same plot as pandas (code cannot be executed because pandas |
| 106 | +# is not a doc dependency):: |
| 107 | +# |
| 108 | +# df = pd.DataFrame(data, index=group_labels, columns=columns) |
| 109 | +# df.plot.bar() |
| 110 | + |
| 111 | +# %% |
| 112 | +# Controlling bar group center positions |
| 113 | +# ====================================== |
| 114 | +# By default, bars groups are centered on integer positions 0, 1, 2, ... |
| 115 | +# This can be overridden using the *positions* parameter, but for simplicity |
| 116 | +# and clarity, we require that the positions are equidistant |
| 117 | + |
| 118 | +positions = [0, 2, 4] |
| 119 | +data = { |
| 120 | + 'data1': [1, 2, 3], |
| 121 | + 'data2': [1.2, 2.2, 3.2], |
| 122 | +} |
| 123 | + |
| 124 | +fig, ax = plt.subplots() |
| 125 | +ax.grouped_bar(data, positions=positions) |
| 126 | + |
| 127 | + |
| 128 | +# %% |
| 129 | +# Bar width and spacing |
| 130 | +# ===================== |
| 131 | +# The center positions of the bar groups are equidistantly spaced. We can still choose |
| 132 | +# two of the following properties: bar width, spacing between groups, and |
| 133 | +# spacing between bars. |
| 134 | +# |
| 135 | +# We believe the most convenient approach is defining spacing between groups |
| 136 | +# and spacing between bars as fraction of the bar width. |
| 137 | + |
| 138 | +data = { |
| 139 | + 'data1': [1, 2, 3], |
| 140 | + 'data2': [1.2, 2.2, 3.2], |
| 141 | + 'data3': [1.4, 2.4, 3.4], |
| 142 | + 'data4': [1.6, 2.6, 3.6], |
| 143 | +} |
| 144 | + |
| 145 | +fig, axs = plt.subplots(2, 2) |
| 146 | +axs[0, 0].grouped_bar(data) |
| 147 | +axs[0, 1].grouped_bar(data, group_spacing=0.5) |
| 148 | +axs[1, 0].grouped_bar(data, bar_spacing=0.2) |
| 149 | +axs[1, 1].grouped_bar(data, group_spacing=0.5, bar_spacing=0.1) |
| 150 | + |
| 151 | + |
| 152 | +# %% |
| 153 | +# Styling |
| 154 | +# ======= |
| 155 | +# The bars can be styled through additional keyword arguments. Currently, |
| 156 | +# the only per-dataset setting is ``colors``. Additionally, all |
| 157 | +# `.Rectangle` parameters are passed through and applied to all datasets. |
| 158 | + |
| 159 | +x = ['A', 'B', 'C'] |
| 160 | +data = { |
| 161 | + 'data1': [1, 2, 3], |
| 162 | + 'data2': [1.2, 2.2, 3.2], |
| 163 | + 'data3': [1.4, 2.4, 3.4], |
| 164 | + 'data4': [1.6, 2.6, 3.6], |
| 165 | +} |
| 166 | + |
| 167 | +fig, ax = plt.subplots() |
| 168 | +ax.grouped_bar(data, tick_labels=x, colors=["r", "g", "b", "m"], edgecolor="black") |
| 169 | + |
| 170 | + |
| 171 | +# %% |
| 172 | +# Horizontal grouped bars |
| 173 | +# ======================= |
| 174 | +# Use ``orientation="horizontal"`` to create horizontal grouped bar charts. |
| 175 | + |
| 176 | +x = ['A', 'B', 'C'] |
| 177 | +data = { |
| 178 | + 'data1': [1, 2, 3], |
| 179 | + 'data2': [1.2, 2.2, 3.2], |
| 180 | +} |
| 181 | + |
| 182 | +fig, ax = plt.subplots() |
| 183 | +ax.grouped_bar(data, tick_labels=x, orientation="horizontal") |
0 commit comments