newsworthycharts 1.71.0__tar.gz → 1.71.3__tar.gz
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-1.71.0 → newsworthycharts-1.71.3}/PKG-INFO +32 -5
- newsworthycharts-1.71.0/newsworthycharts.egg-info/PKG-INFO → newsworthycharts-1.71.3/README.rst +28 -27
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/__init__.py +1 -1
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/categoricalchart.py +5 -1
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/chart.py +37 -50
- newsworthycharts-1.71.3/newsworthycharts/rankchart.py +115 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/scatterplot.py +7 -2
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/serialchart.py +51 -14
- newsworthycharts-1.71.0/README.rst → newsworthycharts-1.71.3/newsworthycharts.egg-info/PKG-INFO +54 -1
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts.egg-info/requires.txt +2 -2
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/setup.py +2 -2
- newsworthycharts-1.71.0/newsworthycharts/rankchart.py +0 -35
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/LICENSE.txt +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/MANIFEST.in +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/bubblemap.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/choroplethmap.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/custom/__init__.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/custom/climate_cars.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/datawrapper.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/lib/__init__.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/lib/color_fn.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/lib/colors.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/lib/datalist.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/lib/formatter.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/lib/geography.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/lib/locator.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/lib/mimetypes.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/lib/utils.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/map.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/maps/se-4.gpkg +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/maps/se-7.gpkg +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/rangeplot.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/rc/newsworthy +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/seasonalchart.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/storage.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/stripechart.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/translations/datawrapper_regions.csv +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/translations/regions.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/translations/se_municipalities.csv +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts.egg-info/SOURCES.txt +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts.egg-info/dependency_links.txt +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts.egg-info/not-zip-safe +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts.egg-info/top_level.txt +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/setup.cfg +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/test/test_categorical_chart.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/test/test_choropleth_maps.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/test/test_custom_climate_cars.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/test/test_data_list.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/test/test_datawrapper.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/test/test_main.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/test/test_rangeplot.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/test/test_scatterplot.py +0 -0
- {newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/test/test_serial_chart.py +0 -0
@@ -1,9 +1,9 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: newsworthycharts
|
3
|
-
Version: 1.71.
|
3
|
+
Version: 1.71.3
|
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.71.
|
6
|
+
Download-URL: https://github.com/jplusplus/newsworthycharts/archive/1.71.3.tar.gz
|
7
7
|
Author: Jens Finnäs and Leo Wallentin, J++ Stockholm
|
8
8
|
Author-email: stockholm@jplusplus.org
|
9
9
|
License: MIT
|
@@ -15,13 +15,13 @@ Requires-Dist: matplotlib==3.10.0
|
|
15
15
|
Requires-Dist: langcodes>=3.3
|
16
16
|
Requires-Dist: Babel<3,>=2.14.0
|
17
17
|
Requires-Dist: PyYAML>=3
|
18
|
-
Requires-Dist: adjustText==
|
18
|
+
Requires-Dist: adjustText==1.3.0
|
19
19
|
Requires-Dist: numpy>2
|
20
20
|
Requires-Dist: python-dateutil<3,>=2
|
21
21
|
Requires-Dist: Pillow==11.1.0
|
22
22
|
Requires-Dist: requests>=2.22
|
23
23
|
Requires-Dist: matplotlib-label-lines==0.5.1
|
24
|
-
Requires-Dist: geopandas
|
24
|
+
Requires-Dist: geopandas==1.0.1
|
25
25
|
Requires-Dist: mapclassify==2.8.1
|
26
26
|
|
27
27
|
This module contains methods for producing graphs and publishing them on Amazon S3, or in the location of your choice.
|
@@ -150,6 +150,7 @@ These settings are available for all chart types:
|
|
150
150
|
- logo = None # Path to image that will be embedded in the caption area. Can also be set though a style property
|
151
151
|
- color_fn = None # Custom coloring function
|
152
152
|
- legend_title = None # Title for the legend
|
153
|
+
- revert_value_axis = False # Revert the value axis to put 0 top
|
153
154
|
|
154
155
|
**SerialChart**
|
155
156
|
|
@@ -178,7 +179,9 @@ _ Inherits from SerialChart _
|
|
178
179
|
|
179
180
|
_ Inherits from SerialChart _
|
180
181
|
|
181
|
-
- highlight
|
182
|
+
- highlight = None # The label value to of a line to highlight
|
183
|
+
- line_marker = "o-" # Matplotlib line marker type.
|
184
|
+
- line_marker_size = 5 # Matplotlib line marker size
|
182
185
|
|
183
186
|
**BubbleMap**
|
184
187
|
|
@@ -267,6 +270,30 @@ Roadmap
|
|
267
270
|
Changelog
|
268
271
|
---------
|
269
272
|
|
273
|
+
- 1.71.3
|
274
|
+
|
275
|
+
- Revert 1.71.2 changes to rendering, to make file sizes predictable again
|
276
|
+
|
277
|
+
- 1.71.2
|
278
|
+
|
279
|
+
- Annotates all isolated value sequences in BumpChart
|
280
|
+
- Allows shared ranks in BumpChart
|
281
|
+
- Prefers showing all ticks in BumpChart
|
282
|
+
- Fixes some padding issues with reverted value axis
|
283
|
+
- Adds `revert_value_axis` option for all charts
|
284
|
+
- New custom label collision algorithm for serial charts
|
285
|
+
- Removes unused(?) label collision algorithm for categorical charts
|
286
|
+
- Upgrades adjustText (now used in ScatterPlot only) to 1.3.0
|
287
|
+
- Adds `_after_add_data()` hook for subclasses and extensions
|
288
|
+
- Pins Geopandas version (currently 1.0.1)
|
289
|
+
- Smaller vertical annotation offset (partially reverting 1.71.1)
|
290
|
+
|
291
|
+
- 1.71.1
|
292
|
+
|
293
|
+
- Allow setting line marker size and style in `BumpChart`
|
294
|
+
- Various fixes in `BumpChart`
|
295
|
+
- Take font size into account when positioning annotations
|
296
|
+
|
270
297
|
- 1.71.0
|
271
298
|
|
272
299
|
- Add `BumpChart`
|
newsworthycharts-1.71.0/newsworthycharts.egg-info/PKG-INFO → newsworthycharts-1.71.3/README.rst
RENAMED
@@ -1,29 +1,3 @@
|
|
1
|
-
Metadata-Version: 2.1
|
2
|
-
Name: newsworthycharts
|
3
|
-
Version: 1.71.0
|
4
|
-
Summary: Matplotlib wrapper to create charts and publish them on Amazon S3
|
5
|
-
Home-page: https://github.com/jplusplus/newsworthycharts
|
6
|
-
Download-URL: https://github.com/jplusplus/newsworthycharts/archive/1.71.0.tar.gz
|
7
|
-
Author: Jens Finnäs and Leo Wallentin, J++ Stockholm
|
8
|
-
Author-email: stockholm@jplusplus.org
|
9
|
-
License: MIT
|
10
|
-
Requires-Python: >=3.9
|
11
|
-
Description-Content-Type: text/x-rst
|
12
|
-
License-File: LICENSE.txt
|
13
|
-
Requires-Dist: boto3>=1.26
|
14
|
-
Requires-Dist: matplotlib==3.10.0
|
15
|
-
Requires-Dist: langcodes>=3.3
|
16
|
-
Requires-Dist: Babel<3,>=2.14.0
|
17
|
-
Requires-Dist: PyYAML>=3
|
18
|
-
Requires-Dist: adjustText==0.7.3
|
19
|
-
Requires-Dist: numpy>2
|
20
|
-
Requires-Dist: python-dateutil<3,>=2
|
21
|
-
Requires-Dist: Pillow==11.1.0
|
22
|
-
Requires-Dist: requests>=2.22
|
23
|
-
Requires-Dist: matplotlib-label-lines==0.5.1
|
24
|
-
Requires-Dist: geopandas>=1
|
25
|
-
Requires-Dist: mapclassify==2.8.1
|
26
|
-
|
27
1
|
This module contains methods for producing graphs and publishing them on Amazon S3, or in the location of your choice.
|
28
2
|
|
29
3
|
It is written and maintained for `Newsworthy <https://www.newsworthy.se/en/>`_, but could possibly come in handy for other people as well.
|
@@ -150,6 +124,7 @@ These settings are available for all chart types:
|
|
150
124
|
- logo = None # Path to image that will be embedded in the caption area. Can also be set though a style property
|
151
125
|
- color_fn = None # Custom coloring function
|
152
126
|
- legend_title = None # Title for the legend
|
127
|
+
- revert_value_axis = False # Revert the value axis to put 0 top
|
153
128
|
|
154
129
|
**SerialChart**
|
155
130
|
|
@@ -178,7 +153,9 @@ _ Inherits from SerialChart _
|
|
178
153
|
|
179
154
|
_ Inherits from SerialChart _
|
180
155
|
|
181
|
-
- highlight
|
156
|
+
- highlight = None # The label value to of a line to highlight
|
157
|
+
- line_marker = "o-" # Matplotlib line marker type.
|
158
|
+
- line_marker_size = 5 # Matplotlib line marker size
|
182
159
|
|
183
160
|
**BubbleMap**
|
184
161
|
|
@@ -267,6 +244,30 @@ Roadmap
|
|
267
244
|
Changelog
|
268
245
|
---------
|
269
246
|
|
247
|
+
- 1.71.3
|
248
|
+
|
249
|
+
- Revert 1.71.2 changes to rendering, to make file sizes predictable again
|
250
|
+
|
251
|
+
- 1.71.2
|
252
|
+
|
253
|
+
- Annotates all isolated value sequences in BumpChart
|
254
|
+
- Allows shared ranks in BumpChart
|
255
|
+
- Prefers showing all ticks in BumpChart
|
256
|
+
- Fixes some padding issues with reverted value axis
|
257
|
+
- Adds `revert_value_axis` option for all charts
|
258
|
+
- New custom label collision algorithm for serial charts
|
259
|
+
- Removes unused(?) label collision algorithm for categorical charts
|
260
|
+
- Upgrades adjustText (now used in ScatterPlot only) to 1.3.0
|
261
|
+
- Adds `_after_add_data()` hook for subclasses and extensions
|
262
|
+
- Pins Geopandas version (currently 1.0.1)
|
263
|
+
- Smaller vertical annotation offset (partially reverting 1.71.1)
|
264
|
+
|
265
|
+
- 1.71.1
|
266
|
+
|
267
|
+
- Allow setting line marker size and style in `BumpChart`
|
268
|
+
- Various fixes in `BumpChart`
|
269
|
+
- Take font size into account when positioning annotations
|
270
|
+
|
270
271
|
- 1.71.0
|
271
272
|
|
272
273
|
- Add `BumpChart`
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from .chart import Chart
|
2
2
|
from .lib.utils import to_float
|
3
|
-
from adjustText import adjust_text
|
3
|
+
# from adjustText import adjust_text
|
4
4
|
import numpy as np
|
5
5
|
|
6
6
|
|
@@ -251,11 +251,15 @@ class CategoricalChart(Chart):
|
|
251
251
|
)
|
252
252
|
if idx > 0:
|
253
253
|
texts += [*_texts]
|
254
|
+
"""
|
254
255
|
adjust_text(
|
255
256
|
texts,
|
256
257
|
ax=self.ax,
|
257
258
|
autoalign="x" if self.bar_orientation == "horizontal" else "y",
|
259
|
+
only_move="x" if self.bar_orientation == "horizontal" else "y",
|
260
|
+
# will replace autoalign in newer versions
|
258
261
|
)
|
262
|
+
"""
|
259
263
|
|
260
264
|
|
261
265
|
class CategoricalChartWithReference(CategoricalChart):
|
@@ -207,9 +207,14 @@ class Chart(object):
|
|
207
207
|
c = color_name
|
208
208
|
return c
|
209
209
|
|
210
|
-
def _annotate_point(
|
211
|
-
|
212
|
-
|
210
|
+
def _annotate_point(
|
211
|
+
self,
|
212
|
+
text,
|
213
|
+
xy,
|
214
|
+
direction,
|
215
|
+
offset=12,
|
216
|
+
**kwargs
|
217
|
+
):
|
213
218
|
"""Add a label to a given point.
|
214
219
|
|
215
220
|
:param text: text content of label
|
@@ -237,11 +242,11 @@ class Chart(object):
|
|
237
242
|
elif direction == "left":
|
238
243
|
opts["verticalalignment"] = "center"
|
239
244
|
opts["horizontalalignment"] = "right"
|
240
|
-
opts["xytext"] = (-offset,
|
245
|
+
opts["xytext"] = (-offset, -self._nwc_style["annotation.fontsize"] / 4)
|
241
246
|
elif direction == "right":
|
242
247
|
opts["verticalalignment"] = "center"
|
243
248
|
opts["horizontalalignment"] = "left"
|
244
|
-
opts["xytext"] = (offset,
|
249
|
+
opts["xytext"] = (offset, -self._nwc_style["annotation.fontsize"] / 4)
|
245
250
|
else:
|
246
251
|
msg = f"'{direction}' is an unknown direction for an annotation"
|
247
252
|
raise Exception(msg)
|
@@ -250,7 +255,7 @@ class Chart(object):
|
|
250
255
|
opts.update(kwargs)
|
251
256
|
|
252
257
|
ann = self.ax.annotate(text, xy=xy, **opts)
|
253
|
-
# ann = self.ax.text(text, xy[0], xy[1])
|
258
|
+
# ann = self.ax.text(text, xy[0], xy[1], **opts)
|
254
259
|
self._annotations.append(ann)
|
255
260
|
|
256
261
|
return ann
|
@@ -338,6 +343,13 @@ class Chart(object):
|
|
338
343
|
pass
|
339
344
|
# raise NotImplementedError("This method should be overridden")
|
340
345
|
|
346
|
+
def _after_add_data(self):
|
347
|
+
""" Extra operations after data has been added.
|
348
|
+
Typically used by subclasses and extensions
|
349
|
+
"""
|
350
|
+
pass
|
351
|
+
# raise NotImplementedError("This method should be overridden")
|
352
|
+
|
341
353
|
def _mark_broken_axis(self, axis="y"):
|
342
354
|
"""Adds a symbol to mark that an axis is broken
|
343
355
|
"""
|
@@ -472,6 +484,8 @@ class Chart(object):
|
|
472
484
|
# Apply all changes, in the correct order for consistent rendering
|
473
485
|
if len(self.data):
|
474
486
|
self._add_data()
|
487
|
+
# Extra hook for extensions and subclasses
|
488
|
+
self._after_add_data()
|
475
489
|
|
476
490
|
# Calculate size in inches
|
477
491
|
# Until 1.45 we did this on init, but now we'd like to enable dynamic heights
|
@@ -668,7 +682,6 @@ class Chart(object):
|
|
668
682
|
+ self._footer_rel_height
|
669
683
|
)
|
670
684
|
# print(sub_canvas_height, self._note_rel_height, self._footer_rel_height)
|
671
|
-
self._fig.subplots_adjust(bottom=sub_canvas_height)
|
672
685
|
|
673
686
|
if self.revert_value_axis:
|
674
687
|
if hasattr(self, "orientation") and self.orientation == "horizontal":
|
@@ -679,9 +692,10 @@ class Chart(object):
|
|
679
692
|
self.ax.invert_yaxis()
|
680
693
|
value_axis = self.ax.get_yaxis()
|
681
694
|
value_ticks = self.ax.get_yticks()
|
682
|
-
value_ticks = [x for x in value_ticks if x <= self.data.max_val]
|
695
|
+
value_ticks = [x for x in value_ticks if x <= self.data.max_val and x >= self.ymin]
|
683
696
|
value_axis.set_ticks(value_ticks)
|
684
697
|
# self.ax.set_ylim(self.data.max_val, self.ymin)
|
698
|
+
self._fig.subplots_adjust(bottom=sub_canvas_height)
|
685
699
|
|
686
700
|
@classmethod
|
687
701
|
def init_from(cls, args: dict, storage=LocalStorage(),
|
@@ -719,7 +733,7 @@ class Chart(object):
|
|
719
733
|
chart.ticks = args["ticks"]
|
720
734
|
return chart
|
721
735
|
|
722
|
-
def
|
736
|
+
def _render(
|
723
737
|
self,
|
724
738
|
key: str,
|
725
739
|
img_format: str,
|
@@ -728,9 +742,6 @@ class Chart(object):
|
|
728
742
|
storage_options: dict={}
|
729
743
|
):
|
730
744
|
"""Render file, and send to storage."""
|
731
|
-
# Apply all changes, in the correct order for consistent rendering
|
732
|
-
self._apply_changes_before_rendering(factor=factor, transparent=transparent)
|
733
|
-
|
734
745
|
# Save plot in memory, to write it directly to storage
|
735
746
|
buf = BytesIO()
|
736
747
|
args = {
|
@@ -770,6 +781,19 @@ class Chart(object):
|
|
770
781
|
buf.seek(0)
|
771
782
|
self._storage.save(key, buf, img_format, storage_options)
|
772
783
|
|
784
|
+
def render(
|
785
|
+
self,
|
786
|
+
key: str,
|
787
|
+
img_format: str,
|
788
|
+
transparent: bool=False,
|
789
|
+
factor: int=1,
|
790
|
+
storage_options: dict={}
|
791
|
+
):
|
792
|
+
"""Render file, and send to storage."""
|
793
|
+
# Apply all changes, in the correct order for consistent rendering
|
794
|
+
self._apply_changes_before_rendering(factor=factor, transparent=transparent)
|
795
|
+
self._render(key, img_format, transparent, factor, storage_options)
|
796
|
+
|
773
797
|
def render_all(self, key: str, transparent=False, factor=1, storage_options={}):
|
774
798
|
"""
|
775
799
|
Render all available formats
|
@@ -780,44 +804,7 @@ class Chart(object):
|
|
780
804
|
for file_format in self.file_types:
|
781
805
|
if file_format == "dw":
|
782
806
|
continue
|
783
|
-
|
784
|
-
# Save plot in memory, to write it directly to storage
|
785
|
-
buf = BytesIO()
|
786
|
-
args = {
|
787
|
-
'format': file_format,
|
788
|
-
'transparent': transparent,
|
789
|
-
'dpi': self._fig.dpi * factor,
|
790
|
-
}
|
791
|
-
if file_format == "pdf":
|
792
|
-
args["metadata"] = {
|
793
|
-
'Creator': "Newsworthy",
|
794
|
-
'Producer': f"NWCharts {__version__}",
|
795
|
-
}
|
796
|
-
elif file_format == "png":
|
797
|
-
args["metadata"] = {
|
798
|
-
'Author': "Newsworthy",
|
799
|
-
'Software': f"NWCharts {__version__}",
|
800
|
-
}
|
801
|
-
elif file_format == "svg":
|
802
|
-
args["metadata"] = {
|
803
|
-
'Publisher': "Newsworthy",
|
804
|
-
'Creator': f"NWCharts {__version__}",
|
805
|
-
}
|
806
|
-
elif file_format in ["jpg", "jpeg"]:
|
807
|
-
args["pil_kwargs"] = {
|
808
|
-
"quality": 100,
|
809
|
-
"optimize": True,
|
810
|
-
}
|
811
|
-
"""
|
812
|
-
Not yet implemented in Pillow
|
813
|
-
args["metadata"] = {
|
814
|
-
'Publisher': "Newsworthy",
|
815
|
-
'Creator': f"NWCharts {__version__}",
|
816
|
-
}
|
817
|
-
"""
|
818
|
-
self._fig.savefig(buf, **args) # bbox_inches="tight")
|
819
|
-
buf.seek(0)
|
820
|
-
self._storage.save(key, buf, file_format, storage_options)
|
807
|
+
self._render(key, file_format, transparent, factor, storage_options)
|
821
808
|
|
822
809
|
@property
|
823
810
|
def title(self):
|
@@ -0,0 +1,115 @@
|
|
1
|
+
"""
|
2
|
+
A chart showing ranking over time (like ”most popular baby names”)
|
3
|
+
"""
|
4
|
+
from .serialchart import SerialChart
|
5
|
+
from .lib.utils import to_date
|
6
|
+
import numpy as np
|
7
|
+
|
8
|
+
|
9
|
+
class BumpChart(SerialChart):
|
10
|
+
"""Plot a rank chart
|
11
|
+
|
12
|
+
Data should be a list of iterables of (rank, date string) tuples, eg:
|
13
|
+
`[ [("2010-01-01", 2), ("2011-01-01", 3)] ]`, combined with a list of
|
14
|
+
labels in the same order
|
15
|
+
"""
|
16
|
+
|
17
|
+
def __init__(self, *args, **kwargs):
|
18
|
+
super(BumpChart, self).__init__(*args, **kwargs)
|
19
|
+
|
20
|
+
if self.line_width is None:
|
21
|
+
self.line_width = 0.9
|
22
|
+
self.label_placement = 'none'
|
23
|
+
self.type = "line"
|
24
|
+
self.decimals = 0
|
25
|
+
self.revert_value_axis = True
|
26
|
+
self.ymin = 1
|
27
|
+
self.allow_broken_y_axis = False
|
28
|
+
self.grid = False
|
29
|
+
self.accentuate_baseline = False
|
30
|
+
|
31
|
+
self.line_marker = "o-"
|
32
|
+
self.line_marker_size = 5
|
33
|
+
|
34
|
+
def _get_line_colors(self, i, *args):
|
35
|
+
if not self.data:
|
36
|
+
# Don't waste time
|
37
|
+
return None
|
38
|
+
if self.highlight and self.highlight in self.labels and i == self.labels.index(self.highlight):
|
39
|
+
return self._nwc_style["strong_color"]
|
40
|
+
elif self.colors and i < len(self.colors):
|
41
|
+
return self.colors[i]
|
42
|
+
return self._nwc_style["neutral_color"]
|
43
|
+
|
44
|
+
def _after_add_data(self):
|
45
|
+
# Print out every rank
|
46
|
+
if self.data.max_val < 30:
|
47
|
+
_range = list(range(1, int(self.data.max_val) + 1))
|
48
|
+
self.ax.yaxis.set_ticks(_range, _range)
|
49
|
+
# Add labels
|
50
|
+
slots_occupied = {
|
51
|
+
to_date(k): [] for k in self.data.x_points
|
52
|
+
}
|
53
|
+
for i, serie in enumerate(self.data):
|
54
|
+
values = np.array(self.serie_values[i], dtype=np.float64)
|
55
|
+
dates = [to_date(x[0]) for x in serie]
|
56
|
+
color = self._get_line_colors(i)
|
57
|
+
|
58
|
+
endpoints = [
|
59
|
+
(d, values[idx])
|
60
|
+
for (idx, d) in enumerate(dates) if idx == len(dates) - 1 or np.isnan(values[idx + 1])
|
61
|
+
]
|
62
|
+
for ep in endpoints:
|
63
|
+
position = ep[1]
|
64
|
+
while position in slots_occupied[ep[0]]:
|
65
|
+
position += 1
|
66
|
+
slots_occupied[ep[0]].append(position)
|
67
|
+
self._annotate_point(
|
68
|
+
self.labels[i],
|
69
|
+
(ep[0], position),
|
70
|
+
"right",
|
71
|
+
offset=15,
|
72
|
+
color=color,
|
73
|
+
va="center",
|
74
|
+
# arrowprops=dict(arrowstyle="->", color=color),
|
75
|
+
)
|
76
|
+
"""
|
77
|
+
labels = []
|
78
|
+
for i, serie in enumerate(self.data):
|
79
|
+
values = np.array(self.serie_values[i], dtype=np.float64)
|
80
|
+
dates = [to_date(x[0]) for x in serie]
|
81
|
+
color = self._get_line_colors(i)
|
82
|
+
|
83
|
+
endpoints = [
|
84
|
+
(d, values[idx])
|
85
|
+
for (idx, d) in enumerate(dates) if idx == len(dates) - 1 or np.isnan(values[idx + 1])
|
86
|
+
]
|
87
|
+
for ep in endpoints:
|
88
|
+
lbl = self._annotate_point(
|
89
|
+
self.labels[i],
|
90
|
+
(ep[0], ep[1]),
|
91
|
+
"right",
|
92
|
+
offset=15,
|
93
|
+
color=color,
|
94
|
+
va="center",
|
95
|
+
# arrowprops=dict(arrowstyle="->", color=color),
|
96
|
+
)
|
97
|
+
loops = 0
|
98
|
+
overlap = True if len(labels) > 0 else False
|
99
|
+
while overlap:
|
100
|
+
for i, bb in enumerate(labels):
|
101
|
+
if i == len(labels) - 1:
|
102
|
+
overlap = False
|
103
|
+
break
|
104
|
+
bbox1 = lbl.get_window_extent()
|
105
|
+
bbox2 = labels[i].get_window_extent()
|
106
|
+
print(bbox1, bbox2)
|
107
|
+
if bbox1.y1 < bbox2.y0 + 10 and bbox1.x1 > bbox2.x0 + 5: # allow for some overlap
|
108
|
+
xy1 = lbl.xyann
|
109
|
+
lbl.xyann = (xy1[0], xy1[1] + 1)
|
110
|
+
break
|
111
|
+
loops += 1
|
112
|
+
if loops > 500:
|
113
|
+
break
|
114
|
+
labels.append(lbl)
|
115
|
+
"""
|
@@ -114,8 +114,13 @@ class ScatterPlot(Chart):
|
|
114
114
|
|
115
115
|
# These settings could be fine-tuned
|
116
116
|
# Weren't able to add lines between points and labels for example
|
117
|
-
adjust_text(
|
118
|
-
|
117
|
+
adjust_text(
|
118
|
+
self._annotations,
|
119
|
+
ax=self.ax,
|
120
|
+
autoalign="y",
|
121
|
+
only_move="y", # replacing autoalign i newer versions
|
122
|
+
expand_points=(1, 1),
|
123
|
+
)
|
119
124
|
|
120
125
|
if self.ymin is not None:
|
121
126
|
self.ax.set_ylim(self.ymin)
|
@@ -4,7 +4,7 @@ from .lib.utils import to_float, to_date, guess_date_interval
|
|
4
4
|
import numpy as np
|
5
5
|
from math import inf
|
6
6
|
from dateutil.relativedelta import relativedelta
|
7
|
-
from adjustText import adjust_text
|
7
|
+
# from adjustText import adjust_text
|
8
8
|
from labellines import labelLines
|
9
9
|
|
10
10
|
|
@@ -28,6 +28,7 @@ class SerialChart(Chart):
|
|
28
28
|
# draw bars and cut ay axis from this line
|
29
29
|
self.baseline = kwargs.get("baseline", 0)
|
30
30
|
self.baseline_annotation = kwargs.get("baseline_annotation", None)
|
31
|
+
self.accentuate_baseline = True
|
31
32
|
|
32
33
|
self.color_labels = kwargs.get("color_labels", None)
|
33
34
|
|
@@ -37,6 +38,7 @@ class SerialChart(Chart):
|
|
37
38
|
self.grid = True
|
38
39
|
self.point_marker = "."
|
39
40
|
self.line_marker = "-"
|
41
|
+
self.line_marker_size = 3
|
40
42
|
|
41
43
|
self.max_ticks = 10
|
42
44
|
|
@@ -177,6 +179,7 @@ class SerialChart(Chart):
|
|
177
179
|
# Replace None values with 0's to be able to plot bars
|
178
180
|
_values = [0 if v is None else v for v in _values]
|
179
181
|
serie_values.append(_values)
|
182
|
+
self.serie_values = serie_values
|
180
183
|
|
181
184
|
# Select a date to highlight
|
182
185
|
highlight_date = None
|
@@ -256,7 +259,7 @@ class SerialChart(Chart):
|
|
256
259
|
dates,
|
257
260
|
values,
|
258
261
|
self.line_marker,
|
259
|
-
markersize=
|
262
|
+
markersize=self.line_marker_size,
|
260
263
|
color=color,
|
261
264
|
zorder=zo,
|
262
265
|
lw=lw,
|
@@ -303,7 +306,8 @@ class SerialChart(Chart):
|
|
303
306
|
"right",
|
304
307
|
offset=15,
|
305
308
|
color=color,
|
306
|
-
va="center"
|
309
|
+
va="center",
|
310
|
+
# arrowprops=dict(arrowstyle="->", color=color),
|
307
311
|
)
|
308
312
|
# store labels to check for overlap later
|
309
313
|
line_label_elems.append(lbl)
|
@@ -503,13 +507,14 @@ class SerialChart(Chart):
|
|
503
507
|
|
504
508
|
# Accentuate y=0 || y=baseline
|
505
509
|
# if (self.data.min_val < self.baseline) or self.baseline_annotation:
|
506
|
-
self.
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
510
|
+
if self.accentuate_baseline:
|
511
|
+
self.ax.axhline(
|
512
|
+
y=self.baseline,
|
513
|
+
linewidth=1,
|
514
|
+
color="#444444",
|
515
|
+
zorder=11,
|
516
|
+
linestyle="--" if self.baseline else "-"
|
517
|
+
)
|
513
518
|
if self.baseline_annotation:
|
514
519
|
xy = (to_date(self.data.outer_min_x), self.baseline)
|
515
520
|
# We only allow baseline to be set for single series bar charts
|
@@ -572,8 +577,11 @@ class SerialChart(Chart):
|
|
572
577
|
|
573
578
|
padding_top = ymax * 0.15
|
574
579
|
|
575
|
-
self.
|
576
|
-
|
580
|
+
if not self.revert_value_axis:
|
581
|
+
self.ax.set_ylim(
|
582
|
+
ymin=ymin - padding_bottom,
|
583
|
+
ymax=ymax + padding_top,
|
584
|
+
)
|
577
585
|
|
578
586
|
self.ax.yaxis.set_major_formatter(y_formatter)
|
579
587
|
self.ax.yaxis.grid(self.grid)
|
@@ -673,12 +681,13 @@ class SerialChart(Chart):
|
|
673
681
|
)
|
674
682
|
|
675
683
|
def _adust_texts_vertically(self, elements, ha="left"):
|
684
|
+
"""
|
685
|
+
from adjustText import get_bboxes
|
676
686
|
if len(elements) == 2:
|
677
687
|
# Hack: check for overlap and adjust labels only
|
678
688
|
# if such overlap exist.
|
679
689
|
# `adjust_text` tended to offset labels unnecessarily
|
680
690
|
# but it might just be that I haven't worked out how to use it properly
|
681
|
-
from adjustText import get_bboxes
|
682
691
|
bb1, bb2 = get_bboxes(elements, self._fig.canvas.get_renderer(), (1.0, 1.0), self.ax)
|
683
692
|
if (
|
684
693
|
# first label is above
|
@@ -689,4 +698,32 @@ class SerialChart(Chart):
|
|
689
698
|
adjust_text(elements, autoalign="y", ha=ha)
|
690
699
|
|
691
700
|
else:
|
692
|
-
adjust_text(
|
701
|
+
adjust_text(
|
702
|
+
elements,
|
703
|
+
autoalign="y",
|
704
|
+
only_move="y", # will replace autoalign in newer versions
|
705
|
+
ax=self.ax,
|
706
|
+
max_move=(0, 10), # (10, 10) is default
|
707
|
+
)
|
708
|
+
"""
|
709
|
+
overlap = True
|
710
|
+
loops = 0
|
711
|
+
while overlap:
|
712
|
+
for i, bb in enumerate(elements):
|
713
|
+
if i == len(elements) - 1:
|
714
|
+
overlap = False
|
715
|
+
break
|
716
|
+
bbox1 = elements[i].get_window_extent()
|
717
|
+
bbox2 = elements[i + 1].get_window_extent()
|
718
|
+
if bbox1.y1 > bbox2.y0 + 10 and bbox1.x1 > bbox2.x0 + 5: # allow for some overlap
|
719
|
+
loops += 1
|
720
|
+
xy1 = elements[i].xyann
|
721
|
+
# xy2 = elements[i + 1].xyann
|
722
|
+
# elements[i].update_positions((bbox1.x0, bbox1.y0 - 5))
|
723
|
+
# elements[i].update_positions((xy1[0], xy1[1] - 0.02))
|
724
|
+
elements[i].xyann = (xy1[0], xy1[1] - 0.01)
|
725
|
+
# elements[i].set(arrowprops=dict(arrowstyle="->"))
|
726
|
+
# elements[i + 1].xyann = (xy2[0], xy2[1] + 0.005)
|
727
|
+
break
|
728
|
+
if loops > 5_000:
|
729
|
+
break
|
newsworthycharts-1.71.0/README.rst → newsworthycharts-1.71.3/newsworthycharts.egg-info/PKG-INFO
RENAMED
@@ -1,3 +1,29 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: newsworthycharts
|
3
|
+
Version: 1.71.3
|
4
|
+
Summary: Matplotlib wrapper to create charts and publish them on Amazon S3
|
5
|
+
Home-page: https://github.com/jplusplus/newsworthycharts
|
6
|
+
Download-URL: https://github.com/jplusplus/newsworthycharts/archive/1.71.3.tar.gz
|
7
|
+
Author: Jens Finnäs and Leo Wallentin, J++ Stockholm
|
8
|
+
Author-email: stockholm@jplusplus.org
|
9
|
+
License: MIT
|
10
|
+
Requires-Python: >=3.9
|
11
|
+
Description-Content-Type: text/x-rst
|
12
|
+
License-File: LICENSE.txt
|
13
|
+
Requires-Dist: boto3>=1.26
|
14
|
+
Requires-Dist: matplotlib==3.10.0
|
15
|
+
Requires-Dist: langcodes>=3.3
|
16
|
+
Requires-Dist: Babel<3,>=2.14.0
|
17
|
+
Requires-Dist: PyYAML>=3
|
18
|
+
Requires-Dist: adjustText==1.3.0
|
19
|
+
Requires-Dist: numpy>2
|
20
|
+
Requires-Dist: python-dateutil<3,>=2
|
21
|
+
Requires-Dist: Pillow==11.1.0
|
22
|
+
Requires-Dist: requests>=2.22
|
23
|
+
Requires-Dist: matplotlib-label-lines==0.5.1
|
24
|
+
Requires-Dist: geopandas==1.0.1
|
25
|
+
Requires-Dist: mapclassify==2.8.1
|
26
|
+
|
1
27
|
This module contains methods for producing graphs and publishing them on Amazon S3, or in the location of your choice.
|
2
28
|
|
3
29
|
It is written and maintained for `Newsworthy <https://www.newsworthy.se/en/>`_, but could possibly come in handy for other people as well.
|
@@ -124,6 +150,7 @@ These settings are available for all chart types:
|
|
124
150
|
- logo = None # Path to image that will be embedded in the caption area. Can also be set though a style property
|
125
151
|
- color_fn = None # Custom coloring function
|
126
152
|
- legend_title = None # Title for the legend
|
153
|
+
- revert_value_axis = False # Revert the value axis to put 0 top
|
127
154
|
|
128
155
|
**SerialChart**
|
129
156
|
|
@@ -152,7 +179,9 @@ _ Inherits from SerialChart _
|
|
152
179
|
|
153
180
|
_ Inherits from SerialChart _
|
154
181
|
|
155
|
-
- highlight
|
182
|
+
- highlight = None # The label value to of a line to highlight
|
183
|
+
- line_marker = "o-" # Matplotlib line marker type.
|
184
|
+
- line_marker_size = 5 # Matplotlib line marker size
|
156
185
|
|
157
186
|
**BubbleMap**
|
158
187
|
|
@@ -241,6 +270,30 @@ Roadmap
|
|
241
270
|
Changelog
|
242
271
|
---------
|
243
272
|
|
273
|
+
- 1.71.3
|
274
|
+
|
275
|
+
- Revert 1.71.2 changes to rendering, to make file sizes predictable again
|
276
|
+
|
277
|
+
- 1.71.2
|
278
|
+
|
279
|
+
- Annotates all isolated value sequences in BumpChart
|
280
|
+
- Allows shared ranks in BumpChart
|
281
|
+
- Prefers showing all ticks in BumpChart
|
282
|
+
- Fixes some padding issues with reverted value axis
|
283
|
+
- Adds `revert_value_axis` option for all charts
|
284
|
+
- New custom label collision algorithm for serial charts
|
285
|
+
- Removes unused(?) label collision algorithm for categorical charts
|
286
|
+
- Upgrades adjustText (now used in ScatterPlot only) to 1.3.0
|
287
|
+
- Adds `_after_add_data()` hook for subclasses and extensions
|
288
|
+
- Pins Geopandas version (currently 1.0.1)
|
289
|
+
- Smaller vertical annotation offset (partially reverting 1.71.1)
|
290
|
+
|
291
|
+
- 1.71.1
|
292
|
+
|
293
|
+
- Allow setting line marker size and style in `BumpChart`
|
294
|
+
- Various fixes in `BumpChart`
|
295
|
+
- Take font size into account when positioning annotations
|
296
|
+
|
244
297
|
- 1.71.0
|
245
298
|
|
246
299
|
- Add `BumpChart`
|
@@ -3,11 +3,11 @@ matplotlib==3.10.0
|
|
3
3
|
langcodes>=3.3
|
4
4
|
Babel<3,>=2.14.0
|
5
5
|
PyYAML>=3
|
6
|
-
adjustText==
|
6
|
+
adjustText==1.3.0
|
7
7
|
numpy>2
|
8
8
|
python-dateutil<3,>=2
|
9
9
|
Pillow==11.1.0
|
10
10
|
requests>=2.22
|
11
11
|
matplotlib-label-lines==0.5.1
|
12
|
-
geopandas
|
12
|
+
geopandas==1.0.1
|
13
13
|
mapclassify==2.8.1
|
@@ -29,13 +29,13 @@ setup(
|
|
29
29
|
"langcodes>=3.3",
|
30
30
|
"Babel>=2.14.0,<3",
|
31
31
|
"PyYAML>=3",
|
32
|
-
"adjustText==
|
32
|
+
"adjustText==1.3.0",
|
33
33
|
"numpy>2",
|
34
34
|
"python-dateutil>=2,<3",
|
35
35
|
"Pillow==11.1.0",
|
36
36
|
"requests>=2.22",
|
37
37
|
"matplotlib-label-lines==0.5.1",
|
38
|
-
"geopandas
|
38
|
+
"geopandas==1.0.1",
|
39
39
|
"mapclassify==2.8.1",
|
40
40
|
],
|
41
41
|
setup_requires=["flake8"],
|
@@ -1,35 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
A chart showing ranking over time (like ”most popular baby names”)
|
3
|
-
"""
|
4
|
-
from .serialchart import SerialChart
|
5
|
-
|
6
|
-
|
7
|
-
class BumpChart(SerialChart):
|
8
|
-
"""Plot a rank chart
|
9
|
-
|
10
|
-
Data should be a list of iterables of (rank, date string) tuples, eg:
|
11
|
-
`[ [("2010-01-01", 2), ("2011-01-01", 3)] ]`, combined with a list of
|
12
|
-
labels in the same order
|
13
|
-
"""
|
14
|
-
|
15
|
-
def __init__(self, *args, **kwargs):
|
16
|
-
super(BumpChart, self).__init__(*args, **kwargs)
|
17
|
-
self.line_width = 0.5
|
18
|
-
self.label_placement = 'line'
|
19
|
-
self.type = "line"
|
20
|
-
self.decimals = 0
|
21
|
-
self.revert_value_axis = True
|
22
|
-
self.ymin = 1
|
23
|
-
self.allow_broken_y_axis = False
|
24
|
-
self.grid = False
|
25
|
-
self.line_marker = "o-"
|
26
|
-
|
27
|
-
def _get_line_colors(self, i, *args):
|
28
|
-
if not self.data:
|
29
|
-
# Don't waste time
|
30
|
-
return None
|
31
|
-
if self.highlight and self.highlight in self.labels and i == self.labels.index(self.highlight):
|
32
|
-
return self._nwc_style["strong_color"]
|
33
|
-
elif self.colors and i < len(self.colors):
|
34
|
-
return self.colors[i]
|
35
|
-
return self._nwc_style["neutral_color"]
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts/translations/regions.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{newsworthycharts-1.71.0 → newsworthycharts-1.71.3}/newsworthycharts.egg-info/dependency_links.txt
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|