newsworthycharts 1.65.4__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 +89 -37
- newsworthycharts/chart.py +2 -2
- newsworthycharts/lib/datalist.py +5 -0
- {newsworthycharts-1.65.4.dist-info → newsworthycharts-1.67.0.dist-info}/METADATA +14 -2
- {newsworthycharts-1.65.4.dist-info → newsworthycharts-1.67.0.dist-info}/RECORD +9 -9
- {newsworthycharts-1.65.4.dist-info → newsworthycharts-1.67.0.dist-info}/LICENSE.txt +0 -0
- {newsworthycharts-1.65.4.dist-info → newsworthycharts-1.67.0.dist-info}/WHEEL +0 -0
- {newsworthycharts-1.65.4.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,32 +26,49 @@ class CategoricalChart(Chart):
|
|
25
26
|
return self.stacked
|
26
27
|
|
27
28
|
def _add_pie_data(self):
|
28
|
-
if len(self.data) > 1:
|
29
|
-
raise ValueError("Pie chart takes one data series only.")
|
30
|
-
self.legend = False
|
31
29
|
self.show_ticks = False
|
32
30
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
explode = [0.1 if x == self.highlight else 0 for x in labels]
|
31
|
+
if len(self.data) > 1:
|
32
|
+
subaxes = self._fig.subplots(1, len(self.data))
|
33
|
+
patches = []
|
34
|
+
for idx, serie in enumerate(self.data):
|
35
|
+
if len(self.data) == 1:
|
36
|
+
subax = self.ax
|
37
|
+
else:
|
38
|
+
subax = subaxes[idx]
|
43
39
|
|
44
|
-
|
45
|
-
|
46
|
-
labels=labels,
|
47
|
-
startangle=90,
|
48
|
-
colors=colors,
|
49
|
-
explode=explode
|
50
|
-
)
|
51
|
-
self.ax.axis('equal')
|
40
|
+
values = [to_float(x[1]) for x in serie]
|
41
|
+
labels = [x[0] for x in serie]
|
52
42
|
|
53
|
-
|
43
|
+
if self.colors is not None:
|
44
|
+
colors = self.colors
|
45
|
+
else:
|
46
|
+
colors = [self._nwc_style["qualitative_colors"][i] for i in range(len(values))]
|
47
|
+
|
48
|
+
explode = [0.1 if x == self.highlight else 0 for x in labels]
|
49
|
+
legend_placement = self.label_placement or "inline"
|
50
|
+
_pie = subax.pie(
|
51
|
+
values,
|
52
|
+
labels=labels if legend_placement == "inline" else None,
|
53
|
+
startangle=90,
|
54
|
+
colors=colors,
|
55
|
+
explode=explode
|
56
|
+
)
|
57
|
+
if idx == 0:
|
58
|
+
patches += _pie[0]
|
59
|
+
subax.axis('equal')
|
60
|
+
# `labels` in pie charts are used to label the series, not the values
|
61
|
+
if self.labels:
|
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)
|
70
|
+
|
71
|
+
self.ax.axis('off')
|
54
72
|
|
55
73
|
def _add_data(self):
|
56
74
|
if self.type == "pie":
|
@@ -84,7 +102,7 @@ class CategoricalChart(Chart):
|
|
84
102
|
serie_values.append(_values)
|
85
103
|
|
86
104
|
cum_values = np.cumsum(serie_values, axis=0).tolist()
|
87
|
-
|
105
|
+
self._bars = []
|
88
106
|
for i, data in enumerate(self.data):
|
89
107
|
|
90
108
|
# Replace None values with 0's to be able to plot bars
|
@@ -160,6 +178,7 @@ class CategoricalChart(Chart):
|
|
160
178
|
bar_pos = [x + i * bar_width
|
161
179
|
for x in np.arange(len(values))]
|
162
180
|
|
181
|
+
_bar = None
|
163
182
|
if self.bar_orientation == "horizontal":
|
164
183
|
kwargs = dict(align='center', height=bar_width,
|
165
184
|
color=colors, zorder=2)
|
@@ -167,7 +186,7 @@ class CategoricalChart(Chart):
|
|
167
186
|
# To make stacked bars we need to set bottom value
|
168
187
|
kwargs["left"] = cum_values[i - 1]
|
169
188
|
|
170
|
-
self.ax.barh(bar_pos, values, **kwargs)
|
189
|
+
_bar = self.ax.barh(bar_pos, values, **kwargs)
|
171
190
|
|
172
191
|
elif self.bar_orientation == "vertical":
|
173
192
|
kwargs = dict(
|
@@ -178,7 +197,8 @@ class CategoricalChart(Chart):
|
|
178
197
|
if self.stacked and i > 0:
|
179
198
|
# To make stacked bars we need to set bottom value
|
180
199
|
kwargs["bottom"] = cum_values[i - 1]
|
181
|
-
self.ax.bar(bar_pos, values, **kwargs)
|
200
|
+
_bar = self.ax.bar(bar_pos, values, **kwargs)
|
201
|
+
self._bars.append(_bar)
|
182
202
|
|
183
203
|
if self.bar_orientation == "horizontal":
|
184
204
|
margin = 0.02 # above and below first/last bar on horizontal
|
@@ -200,9 +220,6 @@ class CategoricalChart(Chart):
|
|
200
220
|
self.ax.set_xticklabels(categories)
|
201
221
|
self.ax.xaxis.set_ticks_position('none')
|
202
222
|
|
203
|
-
if len(self.data) > 1:
|
204
|
-
self.ax.legend(self.labels, loc='best')
|
205
|
-
|
206
223
|
self._setup_legend()
|
207
224
|
|
208
225
|
def _setup_legend(self):
|
@@ -211,6 +228,31 @@ class CategoricalChart(Chart):
|
|
211
228
|
legend = self.ax.get_legend()
|
212
229
|
if legend:
|
213
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
|
+
)
|
214
256
|
|
215
257
|
|
216
258
|
class CategoricalChartWithReference(CategoricalChart):
|
@@ -236,6 +278,7 @@ class CategoricalChartWithReference(CategoricalChart):
|
|
236
278
|
self.value_axis.grid(True)
|
237
279
|
|
238
280
|
bar_width = 0.8 / len(self.data)
|
281
|
+
self._bars = []
|
239
282
|
for i, data in enumerate(self.data):
|
240
283
|
|
241
284
|
# Replace None values with 0's to be able to plot bars
|
@@ -263,18 +306,30 @@ class CategoricalChartWithReference(CategoricalChart):
|
|
263
306
|
|
264
307
|
zorder = len(self.data) - i
|
265
308
|
if self.bar_orientation == "horizontal":
|
266
|
-
self.ax.barh(
|
267
|
-
|
309
|
+
bar = self.ax.barh(
|
310
|
+
bar_pos,
|
311
|
+
values,
|
312
|
+
height=bar_width,
|
313
|
+
align='center',
|
314
|
+
color=color,
|
315
|
+
zorder=zorder,
|
316
|
+
)
|
268
317
|
self.ax.set_yticks(tick_pos)
|
269
318
|
self.ax.set_yticklabels(categories)
|
270
319
|
# self.ax.invert_yaxis()
|
271
320
|
|
272
321
|
elif self.bar_orientation == "vertical":
|
273
|
-
self.ax.bar(
|
274
|
-
|
322
|
+
bar = self.ax.bar(
|
323
|
+
bar_pos,
|
324
|
+
values,
|
325
|
+
width=bar_width,
|
326
|
+
color=color,
|
327
|
+
zorder=zorder,
|
328
|
+
)
|
275
329
|
self.ax.set_xticks(tick_pos)
|
276
330
|
self.ax.set_xticklabels(categories)
|
277
331
|
self.ax.xaxis.set_ticks_position('none')
|
332
|
+
self._bars.append(bar)
|
278
333
|
|
279
334
|
# Make sure labels are not cropped
|
280
335
|
yaxis_bbox = self.ax.yaxis.get_tightbbox(self._fig.canvas.get_renderer())
|
@@ -282,9 +337,6 @@ class CategoricalChartWithReference(CategoricalChart):
|
|
282
337
|
margin -= yaxis_bbox.min[0] / float(self._w)
|
283
338
|
self._fig.subplots_adjust(left=margin)
|
284
339
|
|
285
|
-
if self.labels:
|
286
|
-
self.ax.legend(self.labels, loc='best')
|
287
|
-
|
288
340
|
self._setup_legend()
|
289
341
|
|
290
342
|
|
@@ -339,7 +391,7 @@ class ProgressChart(CategoricalChart):
|
|
339
391
|
|
340
392
|
# rect.set_linewidth(1)
|
341
393
|
# rect.set_edgecolor(color_progress)
|
342
|
-
|
394
|
+
|
343
395
|
# LABELING: Target
|
344
396
|
if self.target_label:
|
345
397
|
offset = 25
|
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,18 @@ 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
|
+
|
269
|
+
- 1.66.0
|
270
|
+
|
271
|
+
- Support multiple data series in pie charts (displayed as small multiples)
|
272
|
+
|
273
|
+
|
262
274
|
- 1.65.4
|
263
275
|
|
264
276
|
- Revert some of the title margin changes
|
@@ -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
|