newsworthycharts 1.66.0__py3-none-any.whl → 1.67.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- newsworthycharts/__init__.py +1 -1
- newsworthycharts/categoricalchart.py +63 -18
- newsworthycharts/chart.py +2 -2
- newsworthycharts/lib/datalist.py +5 -0
- {newsworthycharts-1.66.0.dist-info → newsworthycharts-1.67.0.dist-info}/METADATA +9 -2
- {newsworthycharts-1.66.0.dist-info → newsworthycharts-1.67.0.dist-info}/RECORD +9 -9
- {newsworthycharts-1.66.0.dist-info → newsworthycharts-1.67.0.dist-info}/LICENSE.txt +0 -0
- {newsworthycharts-1.66.0.dist-info → newsworthycharts-1.67.0.dist-info}/WHEEL +0 -0
- {newsworthycharts-1.66.0.dist-info → newsworthycharts-1.67.0.dist-info}/top_level.txt +0 -0
newsworthycharts/__init__.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
from .chart import Chart
|
2
2
|
from .lib.utils import to_float
|
3
|
-
|
3
|
+
from adjustText import adjust_text
|
4
4
|
import numpy as np
|
5
5
|
|
6
6
|
|
@@ -16,6 +16,7 @@ class CategoricalChart(Chart):
|
|
16
16
|
self.stacked = False
|
17
17
|
self.legend = True
|
18
18
|
self.type = "bars"
|
19
|
+
self.label_placement = None # legend|inline|outside # defaults will vary
|
19
20
|
|
20
21
|
# Optional: specify a list of colors (for multiple datasets)
|
21
22
|
self.colors = None
|
@@ -25,11 +26,11 @@ class CategoricalChart(Chart):
|
|
25
26
|
return self.stacked
|
26
27
|
|
27
28
|
def _add_pie_data(self):
|
28
|
-
self.legend = False
|
29
29
|
self.show_ticks = False
|
30
30
|
|
31
31
|
if len(self.data) > 1:
|
32
32
|
subaxes = self._fig.subplots(1, len(self.data))
|
33
|
+
patches = []
|
33
34
|
for idx, serie in enumerate(self.data):
|
34
35
|
if len(self.data) == 1:
|
35
36
|
subax = self.ax
|
@@ -45,17 +46,27 @@ class CategoricalChart(Chart):
|
|
45
46
|
colors = [self._nwc_style["qualitative_colors"][i] for i in range(len(values))]
|
46
47
|
|
47
48
|
explode = [0.1 if x == self.highlight else 0 for x in labels]
|
48
|
-
|
49
|
+
legend_placement = self.label_placement or "inline"
|
50
|
+
_pie = subax.pie(
|
49
51
|
values,
|
50
|
-
labels=labels,
|
52
|
+
labels=labels if legend_placement == "inline" else None,
|
51
53
|
startangle=90,
|
52
54
|
colors=colors,
|
53
55
|
explode=explode
|
54
56
|
)
|
57
|
+
if idx == 0:
|
58
|
+
patches += _pie[0]
|
55
59
|
subax.axis('equal')
|
56
60
|
# `labels` in pie charts are used to label the series, not the values
|
57
61
|
if self.labels:
|
58
|
-
subax.set_title(self.labels[idx], loc='center', y=0
|
62
|
+
subax.set_title(self.labels[idx], loc='center', y=0)
|
63
|
+
if legend_placement == "inline":
|
64
|
+
self.legend = False
|
65
|
+
elif legend_placement == "legend":
|
66
|
+
self.ax.legend(patches, labels, loc='best', frameon=True, fancybox=True).set_zorder(100)
|
67
|
+
elif legend_placement == "outside":
|
68
|
+
_ = self.ax.legend(patches, labels, bbox_to_anchor=(1.04, 1), loc="upper left")
|
69
|
+
_.set(zorder=20)
|
59
70
|
|
60
71
|
self.ax.axis('off')
|
61
72
|
|
@@ -91,7 +102,7 @@ class CategoricalChart(Chart):
|
|
91
102
|
serie_values.append(_values)
|
92
103
|
|
93
104
|
cum_values = np.cumsum(serie_values, axis=0).tolist()
|
94
|
-
|
105
|
+
self._bars = []
|
95
106
|
for i, data in enumerate(self.data):
|
96
107
|
|
97
108
|
# Replace None values with 0's to be able to plot bars
|
@@ -167,6 +178,7 @@ class CategoricalChart(Chart):
|
|
167
178
|
bar_pos = [x + i * bar_width
|
168
179
|
for x in np.arange(len(values))]
|
169
180
|
|
181
|
+
_bar = None
|
170
182
|
if self.bar_orientation == "horizontal":
|
171
183
|
kwargs = dict(align='center', height=bar_width,
|
172
184
|
color=colors, zorder=2)
|
@@ -174,7 +186,7 @@ class CategoricalChart(Chart):
|
|
174
186
|
# To make stacked bars we need to set bottom value
|
175
187
|
kwargs["left"] = cum_values[i - 1]
|
176
188
|
|
177
|
-
self.ax.barh(bar_pos, values, **kwargs)
|
189
|
+
_bar = self.ax.barh(bar_pos, values, **kwargs)
|
178
190
|
|
179
191
|
elif self.bar_orientation == "vertical":
|
180
192
|
kwargs = dict(
|
@@ -185,7 +197,8 @@ class CategoricalChart(Chart):
|
|
185
197
|
if self.stacked and i > 0:
|
186
198
|
# To make stacked bars we need to set bottom value
|
187
199
|
kwargs["bottom"] = cum_values[i - 1]
|
188
|
-
self.ax.bar(bar_pos, values, **kwargs)
|
200
|
+
_bar = self.ax.bar(bar_pos, values, **kwargs)
|
201
|
+
self._bars.append(_bar)
|
189
202
|
|
190
203
|
if self.bar_orientation == "horizontal":
|
191
204
|
margin = 0.02 # above and below first/last bar on horizontal
|
@@ -207,9 +220,6 @@ class CategoricalChart(Chart):
|
|
207
220
|
self.ax.set_xticklabels(categories)
|
208
221
|
self.ax.xaxis.set_ticks_position('none')
|
209
222
|
|
210
|
-
if len(self.data) > 1:
|
211
|
-
self.ax.legend(self.labels, loc='best')
|
212
|
-
|
213
223
|
self._setup_legend()
|
214
224
|
|
215
225
|
def _setup_legend(self):
|
@@ -218,6 +228,31 @@ class CategoricalChart(Chart):
|
|
218
228
|
legend = self.ax.get_legend()
|
219
229
|
if legend:
|
220
230
|
legend.remove()
|
231
|
+
else:
|
232
|
+
if len(self.data) == 0 and self.type != "pie":
|
233
|
+
return
|
234
|
+
placement = self.label_placement or "legend"
|
235
|
+
if placement == "legend":
|
236
|
+
self.ax.legend(self.labels, loc='best')
|
237
|
+
elif placement == "outside":
|
238
|
+
_ = self.ax.legend(self.labels, bbox_to_anchor=(1.04, 1), loc="upper left")
|
239
|
+
_.set(zorder=20)
|
240
|
+
elif placement == "inline":
|
241
|
+
texts = []
|
242
|
+
for idx, _bar in enumerate(self._bars):
|
243
|
+
_texts = self.ax.bar_label(
|
244
|
+
_bar,
|
245
|
+
labels=[self.labels[idx]] * self.data.num_categories,
|
246
|
+
label_type="center",
|
247
|
+
backgroundcolor="#f0f0f099",
|
248
|
+
)
|
249
|
+
if idx > 0:
|
250
|
+
texts += [*_texts]
|
251
|
+
adjust_text(
|
252
|
+
texts,
|
253
|
+
ax=self.ax,
|
254
|
+
autoalign="x" if self.bar_orientation == "horizontal" else "y",
|
255
|
+
)
|
221
256
|
|
222
257
|
|
223
258
|
class CategoricalChartWithReference(CategoricalChart):
|
@@ -243,6 +278,7 @@ class CategoricalChartWithReference(CategoricalChart):
|
|
243
278
|
self.value_axis.grid(True)
|
244
279
|
|
245
280
|
bar_width = 0.8 / len(self.data)
|
281
|
+
self._bars = []
|
246
282
|
for i, data in enumerate(self.data):
|
247
283
|
|
248
284
|
# Replace None values with 0's to be able to plot bars
|
@@ -270,18 +306,30 @@ class CategoricalChartWithReference(CategoricalChart):
|
|
270
306
|
|
271
307
|
zorder = len(self.data) - i
|
272
308
|
if self.bar_orientation == "horizontal":
|
273
|
-
self.ax.barh(
|
274
|
-
|
309
|
+
bar = self.ax.barh(
|
310
|
+
bar_pos,
|
311
|
+
values,
|
312
|
+
height=bar_width,
|
313
|
+
align='center',
|
314
|
+
color=color,
|
315
|
+
zorder=zorder,
|
316
|
+
)
|
275
317
|
self.ax.set_yticks(tick_pos)
|
276
318
|
self.ax.set_yticklabels(categories)
|
277
319
|
# self.ax.invert_yaxis()
|
278
320
|
|
279
321
|
elif self.bar_orientation == "vertical":
|
280
|
-
self.ax.bar(
|
281
|
-
|
322
|
+
bar = self.ax.bar(
|
323
|
+
bar_pos,
|
324
|
+
values,
|
325
|
+
width=bar_width,
|
326
|
+
color=color,
|
327
|
+
zorder=zorder,
|
328
|
+
)
|
282
329
|
self.ax.set_xticks(tick_pos)
|
283
330
|
self.ax.set_xticklabels(categories)
|
284
331
|
self.ax.xaxis.set_ticks_position('none')
|
332
|
+
self._bars.append(bar)
|
285
333
|
|
286
334
|
# Make sure labels are not cropped
|
287
335
|
yaxis_bbox = self.ax.yaxis.get_tightbbox(self._fig.canvas.get_renderer())
|
@@ -289,9 +337,6 @@ class CategoricalChartWithReference(CategoricalChart):
|
|
289
337
|
margin -= yaxis_bbox.min[0] / float(self._w)
|
290
338
|
self._fig.subplots_adjust(left=margin)
|
291
339
|
|
292
|
-
if self.labels:
|
293
|
-
self.ax.legend(self.labels, loc='best')
|
294
|
-
|
295
340
|
self._setup_legend()
|
296
341
|
|
297
342
|
|
newsworthycharts/chart.py
CHANGED
@@ -285,7 +285,7 @@ class Chart(object):
|
|
285
285
|
def _add_title(self, title_text):
|
286
286
|
"""Add a title."""
|
287
287
|
text = self._fig.suptitle(
|
288
|
-
title_text, wrap=True,
|
288
|
+
title_text.strip(), wrap=True,
|
289
289
|
x=0,
|
290
290
|
y=0.985, # default: 0.98
|
291
291
|
horizontalalignment="left",
|
@@ -300,7 +300,7 @@ class Chart(object):
|
|
300
300
|
text = self._fig.text(
|
301
301
|
0,
|
302
302
|
y_pos,
|
303
|
-
subtitle_text,
|
303
|
+
subtitle_text.strip(),
|
304
304
|
wrap=True,
|
305
305
|
verticalalignment="top",
|
306
306
|
linespacing=1.4,
|
newsworthycharts/lib/datalist.py
CHANGED
@@ -196,6 +196,11 @@ class DataSet(MutableSequence):
|
|
196
196
|
values = array(self.values, dtype=float)
|
197
197
|
return np.nansum(values, axis=0).tolist()
|
198
198
|
|
199
|
+
@property
|
200
|
+
def num_categories(self):
|
201
|
+
categories = set([x[0] for s in self.list for x in s])
|
202
|
+
return len(categories)
|
203
|
+
|
199
204
|
def __len__(self):
|
200
205
|
return len(self.list)
|
201
206
|
|
@@ -1,9 +1,9 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: newsworthycharts
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.67.0
|
4
4
|
Summary: Matplotlib wrapper to create charts and publish them on Amazon S3
|
5
5
|
Home-page: https://github.com/jplusplus/newsworthycharts
|
6
|
-
Download-URL: https://github.com/jplusplus/newsworthycharts/archive/1.
|
6
|
+
Download-URL: https://github.com/jplusplus/newsworthycharts/archive/1.67.0.tar.gz
|
7
7
|
Author: Jens Finnäs and Leo Wallentin, J++ Stockholm
|
8
8
|
Author-email: stockholm@jplusplus.org
|
9
9
|
License: MIT
|
@@ -259,6 +259,13 @@ Roadmap
|
|
259
259
|
Changelog
|
260
260
|
---------
|
261
261
|
|
262
|
+
- 1.67.0
|
263
|
+
|
264
|
+
- Fixed label margins in pie charts
|
265
|
+
- Trim all titles and subtitles of leading and trailing whitespace
|
266
|
+
- Improved piechart annotation
|
267
|
+
- Support `label_placement` (legend|outside|inline) in categorical charts ('legend' is still buggy in pie charts)
|
268
|
+
|
262
269
|
- 1.66.0
|
263
270
|
|
264
271
|
- Support multiple data series in pie charts (displayed as small multiples)
|
@@ -1,7 +1,7 @@
|
|
1
|
-
newsworthycharts/__init__.py,sha256=
|
1
|
+
newsworthycharts/__init__.py,sha256=t7zoA4QcnDRPHIEjlFDnSD3BUJrVpJjyY2dxNvpp_MQ,1160
|
2
2
|
newsworthycharts/bubblemap.py,sha256=nkocWmpiFgfjEuJGAsthjY5X7Q56jXWsZHUGXw4PwgE,2587
|
3
|
-
newsworthycharts/categoricalchart.py,sha256=
|
4
|
-
newsworthycharts/chart.py,sha256=
|
3
|
+
newsworthycharts/categoricalchart.py,sha256=3XXBzIILkUl0721FN9j8cDK5mSjh8vK0JQGXCRu9ilo,18048
|
4
|
+
newsworthycharts/chart.py,sha256=9M4LE5UUN5kMaVQ1jnCAKpACU3oZBMPPUpodad9sBT4,34390
|
5
5
|
newsworthycharts/choroplethmap.py,sha256=bCLf4kcchp1C2djg5AxcOM8BdbaMj0xg7UHrZsDafhI,8013
|
6
6
|
newsworthycharts/datawrapper.py,sha256=RRkAVTpfP4updKxUIBaSmKuBi2RUVPaBRF8HDQhlGGA,11250
|
7
7
|
newsworthycharts/map.py,sha256=c409jEO4L8Yr780sJRC0RchR44roAlgOUDAkuk1SfRg,6057
|
@@ -16,7 +16,7 @@ newsworthycharts/custom/climate_cars.py,sha256=WyNLgjgRCv_zRrzVuk_BmcigFSzpzudjE
|
|
16
16
|
newsworthycharts/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
17
17
|
newsworthycharts/lib/color_fn.py,sha256=ck65GzTS9oPgHUCnKDwX2z8zlnNyzncw97MmhXwi8z8,1282
|
18
18
|
newsworthycharts/lib/colors.py,sha256=U04TDkvoMQkcldRFXfnwyLOTwq1SWW2se-Ad-DNcw9o,485
|
19
|
-
newsworthycharts/lib/datalist.py,sha256=
|
19
|
+
newsworthycharts/lib/datalist.py,sha256=ZyGHnHbn57npITgr2HmtfrS-Xv8U1muCMDOcOwQnlM8,6345
|
20
20
|
newsworthycharts/lib/formatter.py,sha256=GNH43hE0bC17OgiV8LYH3YUrEhm7OJh9XzfSV4HVtHo,4838
|
21
21
|
newsworthycharts/lib/geography.py,sha256=K0_teFmuPJwXX7Py-amJB_1YY5_gL2kBYhz1LrRCyTg,584
|
22
22
|
newsworthycharts/lib/locator.py,sha256=rJHdgiA0LASRzdLINhTeU5zOoq1TCMgge4KOBWSagnM,3041
|
@@ -28,8 +28,8 @@ newsworthycharts/rc/newsworthy,sha256=yOIZvYS6PG1u19VMcdtfj9vbihKQsey5IprwqK59Kg
|
|
28
28
|
newsworthycharts/translations/datawrapper_regions.csv,sha256=fzZcQRX6RFMlNNP8mpgfYNdR3Y0QAlQxDXk8FXTaWWI,9214
|
29
29
|
newsworthycharts/translations/regions.py,sha256=Nv1McQjggD4S3JRu82rDMTG3pqUVR13E5-FBpSYbm98,239
|
30
30
|
newsworthycharts/translations/se_municipalities.csv,sha256=br_mm-IvzQtj_W55_ATREhJ97jWnCweBFlDAVY2EBxA,7098
|
31
|
-
newsworthycharts-1.
|
32
|
-
newsworthycharts-1.
|
33
|
-
newsworthycharts-1.
|
34
|
-
newsworthycharts-1.
|
35
|
-
newsworthycharts-1.
|
31
|
+
newsworthycharts-1.67.0.dist-info/LICENSE.txt,sha256=Sq6kGICrehbhC_FolNdXf0djKjTpv3YqjFCIYsxdQN4,1069
|
32
|
+
newsworthycharts-1.67.0.dist-info/METADATA,sha256=drsWEzZU63lSRO05hy_VT2Mt13UGWyZNW8j_hwI43pc,31262
|
33
|
+
newsworthycharts-1.67.0.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
|
34
|
+
newsworthycharts-1.67.0.dist-info/top_level.txt,sha256=dn_kzIj8UgUCMsh1PHdVEQJHVGSsN7Z8YJF-8xXa8n0,17
|
35
|
+
newsworthycharts-1.67.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|