newsworthycharts 1.71.1__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.1/newsworthycharts.egg-info → newsworthycharts-1.71.3}/PKG-INFO +23 -4
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/README.rst +19 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/__init__.py +1 -1
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/categoricalchart.py +5 -1
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/chart.py +28 -46
- newsworthycharts-1.71.3/newsworthycharts/rankchart.py +115 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/scatterplot.py +7 -2
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/serialchart.py +39 -5
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3/newsworthycharts.egg-info}/PKG-INFO +23 -4
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts.egg-info/requires.txt +2 -2
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/setup.py +2 -2
- newsworthycharts-1.71.1/newsworthycharts/rankchart.py +0 -40
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/LICENSE.txt +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/MANIFEST.in +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/bubblemap.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/choroplethmap.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/custom/__init__.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/custom/climate_cars.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/datawrapper.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/lib/__init__.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/lib/color_fn.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/lib/colors.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/lib/datalist.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/lib/formatter.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/lib/geography.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/lib/locator.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/lib/mimetypes.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/lib/utils.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/map.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/maps/se-4.gpkg +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/maps/se-7.gpkg +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/rangeplot.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/rc/newsworthy +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/seasonalchart.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/storage.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/stripechart.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/translations/datawrapper_regions.csv +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/translations/regions.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts/translations/se_municipalities.csv +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts.egg-info/SOURCES.txt +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts.egg-info/dependency_links.txt +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts.egg-info/not-zip-safe +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/newsworthycharts.egg-info/top_level.txt +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/setup.cfg +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/test/test_categorical_chart.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/test/test_choropleth_maps.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/test/test_custom_climate_cars.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/test/test_data_list.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/test/test_datawrapper.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/test/test_main.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/test/test_rangeplot.py +0 -0
- {newsworthycharts-1.71.1 → newsworthycharts-1.71.3}/test/test_scatterplot.py +0 -0
- {newsworthycharts-1.71.1 → 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
|
|
@@ -269,6 +270,24 @@ Roadmap
|
|
269
270
|
Changelog
|
270
271
|
---------
|
271
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
|
+
|
272
291
|
- 1.71.1
|
273
292
|
|
274
293
|
- Allow setting line marker size and style in `BumpChart`
|
@@ -124,6 +124,7 @@ These settings are available for all chart types:
|
|
124
124
|
- logo = None # Path to image that will be embedded in the caption area. Can also be set though a style property
|
125
125
|
- color_fn = None # Custom coloring function
|
126
126
|
- legend_title = None # Title for the legend
|
127
|
+
- revert_value_axis = False # Revert the value axis to put 0 top
|
127
128
|
|
128
129
|
**SerialChart**
|
129
130
|
|
@@ -243,6 +244,24 @@ Roadmap
|
|
243
244
|
Changelog
|
244
245
|
---------
|
245
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
|
+
|
246
265
|
- 1.71.1
|
247
266
|
|
248
267
|
- Allow setting line marker size and style in `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):
|
@@ -242,11 +242,11 @@ class Chart(object):
|
|
242
242
|
elif direction == "left":
|
243
243
|
opts["verticalalignment"] = "center"
|
244
244
|
opts["horizontalalignment"] = "right"
|
245
|
-
opts["xytext"] = (-offset, -self._nwc_style["annotation.fontsize"] /
|
245
|
+
opts["xytext"] = (-offset, -self._nwc_style["annotation.fontsize"] / 4)
|
246
246
|
elif direction == "right":
|
247
247
|
opts["verticalalignment"] = "center"
|
248
248
|
opts["horizontalalignment"] = "left"
|
249
|
-
opts["xytext"] = (offset, -self._nwc_style["annotation.fontsize"] /
|
249
|
+
opts["xytext"] = (offset, -self._nwc_style["annotation.fontsize"] / 4)
|
250
250
|
else:
|
251
251
|
msg = f"'{direction}' is an unknown direction for an annotation"
|
252
252
|
raise Exception(msg)
|
@@ -255,7 +255,7 @@ class Chart(object):
|
|
255
255
|
opts.update(kwargs)
|
256
256
|
|
257
257
|
ann = self.ax.annotate(text, xy=xy, **opts)
|
258
|
-
# ann = self.ax.text(text, xy[0], xy[1])
|
258
|
+
# ann = self.ax.text(text, xy[0], xy[1], **opts)
|
259
259
|
self._annotations.append(ann)
|
260
260
|
|
261
261
|
return ann
|
@@ -343,6 +343,13 @@ class Chart(object):
|
|
343
343
|
pass
|
344
344
|
# raise NotImplementedError("This method should be overridden")
|
345
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
|
+
|
346
353
|
def _mark_broken_axis(self, axis="y"):
|
347
354
|
"""Adds a symbol to mark that an axis is broken
|
348
355
|
"""
|
@@ -477,6 +484,8 @@ class Chart(object):
|
|
477
484
|
# Apply all changes, in the correct order for consistent rendering
|
478
485
|
if len(self.data):
|
479
486
|
self._add_data()
|
487
|
+
# Extra hook for extensions and subclasses
|
488
|
+
self._after_add_data()
|
480
489
|
|
481
490
|
# Calculate size in inches
|
482
491
|
# Until 1.45 we did this on init, but now we'd like to enable dynamic heights
|
@@ -673,7 +682,6 @@ class Chart(object):
|
|
673
682
|
+ self._footer_rel_height
|
674
683
|
)
|
675
684
|
# print(sub_canvas_height, self._note_rel_height, self._footer_rel_height)
|
676
|
-
self._fig.subplots_adjust(bottom=sub_canvas_height)
|
677
685
|
|
678
686
|
if self.revert_value_axis:
|
679
687
|
if hasattr(self, "orientation") and self.orientation == "horizontal":
|
@@ -687,6 +695,7 @@ class Chart(object):
|
|
687
695
|
value_ticks = [x for x in value_ticks if x <= self.data.max_val and x >= self.ymin]
|
688
696
|
value_axis.set_ticks(value_ticks)
|
689
697
|
# self.ax.set_ylim(self.data.max_val, self.ymin)
|
698
|
+
self._fig.subplots_adjust(bottom=sub_canvas_height)
|
690
699
|
|
691
700
|
@classmethod
|
692
701
|
def init_from(cls, args: dict, storage=LocalStorage(),
|
@@ -724,7 +733,7 @@ class Chart(object):
|
|
724
733
|
chart.ticks = args["ticks"]
|
725
734
|
return chart
|
726
735
|
|
727
|
-
def
|
736
|
+
def _render(
|
728
737
|
self,
|
729
738
|
key: str,
|
730
739
|
img_format: str,
|
@@ -733,9 +742,6 @@ class Chart(object):
|
|
733
742
|
storage_options: dict={}
|
734
743
|
):
|
735
744
|
"""Render file, and send to storage."""
|
736
|
-
# Apply all changes, in the correct order for consistent rendering
|
737
|
-
self._apply_changes_before_rendering(factor=factor, transparent=transparent)
|
738
|
-
|
739
745
|
# Save plot in memory, to write it directly to storage
|
740
746
|
buf = BytesIO()
|
741
747
|
args = {
|
@@ -775,6 +781,19 @@ class Chart(object):
|
|
775
781
|
buf.seek(0)
|
776
782
|
self._storage.save(key, buf, img_format, storage_options)
|
777
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
|
+
|
778
797
|
def render_all(self, key: str, transparent=False, factor=1, storage_options={}):
|
779
798
|
"""
|
780
799
|
Render all available formats
|
@@ -785,44 +804,7 @@ class Chart(object):
|
|
785
804
|
for file_format in self.file_types:
|
786
805
|
if file_format == "dw":
|
787
806
|
continue
|
788
|
-
|
789
|
-
# Save plot in memory, to write it directly to storage
|
790
|
-
buf = BytesIO()
|
791
|
-
args = {
|
792
|
-
'format': file_format,
|
793
|
-
'transparent': transparent,
|
794
|
-
'dpi': self._fig.dpi * factor,
|
795
|
-
}
|
796
|
-
if file_format == "pdf":
|
797
|
-
args["metadata"] = {
|
798
|
-
'Creator': "Newsworthy",
|
799
|
-
'Producer': f"NWCharts {__version__}",
|
800
|
-
}
|
801
|
-
elif file_format == "png":
|
802
|
-
args["metadata"] = {
|
803
|
-
'Author': "Newsworthy",
|
804
|
-
'Software': f"NWCharts {__version__}",
|
805
|
-
}
|
806
|
-
elif file_format == "svg":
|
807
|
-
args["metadata"] = {
|
808
|
-
'Publisher': "Newsworthy",
|
809
|
-
'Creator': f"NWCharts {__version__}",
|
810
|
-
}
|
811
|
-
elif file_format in ["jpg", "jpeg"]:
|
812
|
-
args["pil_kwargs"] = {
|
813
|
-
"quality": 100,
|
814
|
-
"optimize": True,
|
815
|
-
}
|
816
|
-
"""
|
817
|
-
Not yet implemented in Pillow
|
818
|
-
args["metadata"] = {
|
819
|
-
'Publisher': "Newsworthy",
|
820
|
-
'Creator': f"NWCharts {__version__}",
|
821
|
-
}
|
822
|
-
"""
|
823
|
-
self._fig.savefig(buf, **args) # bbox_inches="tight")
|
824
|
-
buf.seek(0)
|
825
|
-
self._storage.save(key, buf, file_format, storage_options)
|
807
|
+
self._render(key, file_format, transparent, factor, storage_options)
|
826
808
|
|
827
809
|
@property
|
828
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
|
|
@@ -179,6 +179,7 @@ class SerialChart(Chart):
|
|
179
179
|
# Replace None values with 0's to be able to plot bars
|
180
180
|
_values = [0 if v is None else v for v in _values]
|
181
181
|
serie_values.append(_values)
|
182
|
+
self.serie_values = serie_values
|
182
183
|
|
183
184
|
# Select a date to highlight
|
184
185
|
highlight_date = None
|
@@ -306,6 +307,7 @@ class SerialChart(Chart):
|
|
306
307
|
offset=15,
|
307
308
|
color=color,
|
308
309
|
va="center",
|
310
|
+
# arrowprops=dict(arrowstyle="->", color=color),
|
309
311
|
)
|
310
312
|
# store labels to check for overlap later
|
311
313
|
line_label_elems.append(lbl)
|
@@ -575,8 +577,11 @@ class SerialChart(Chart):
|
|
575
577
|
|
576
578
|
padding_top = ymax * 0.15
|
577
579
|
|
578
|
-
self.
|
579
|
-
|
580
|
+
if not self.revert_value_axis:
|
581
|
+
self.ax.set_ylim(
|
582
|
+
ymin=ymin - padding_bottom,
|
583
|
+
ymax=ymax + padding_top,
|
584
|
+
)
|
580
585
|
|
581
586
|
self.ax.yaxis.set_major_formatter(y_formatter)
|
582
587
|
self.ax.yaxis.grid(self.grid)
|
@@ -676,12 +681,13 @@ class SerialChart(Chart):
|
|
676
681
|
)
|
677
682
|
|
678
683
|
def _adust_texts_vertically(self, elements, ha="left"):
|
684
|
+
"""
|
685
|
+
from adjustText import get_bboxes
|
679
686
|
if len(elements) == 2:
|
680
687
|
# Hack: check for overlap and adjust labels only
|
681
688
|
# if such overlap exist.
|
682
689
|
# `adjust_text` tended to offset labels unnecessarily
|
683
690
|
# but it might just be that I haven't worked out how to use it properly
|
684
|
-
from adjustText import get_bboxes
|
685
691
|
bb1, bb2 = get_bboxes(elements, self._fig.canvas.get_renderer(), (1.0, 1.0), self.ax)
|
686
692
|
if (
|
687
693
|
# first label is above
|
@@ -692,4 +698,32 @@ class SerialChart(Chart):
|
|
692
698
|
adjust_text(elements, autoalign="y", ha=ha)
|
693
699
|
|
694
700
|
else:
|
695
|
-
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
|
@@ -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
|
|
@@ -269,6 +270,24 @@ Roadmap
|
|
269
270
|
Changelog
|
270
271
|
---------
|
271
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
|
+
|
272
291
|
- 1.71.1
|
273
292
|
|
274
293
|
- Allow setting line marker size and style in `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,40 +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
|
-
|
18
|
-
if self.line_width is None:
|
19
|
-
self.line_width = 0.9
|
20
|
-
self.label_placement = 'line'
|
21
|
-
self.type = "line"
|
22
|
-
self.decimals = 0
|
23
|
-
self.revert_value_axis = True
|
24
|
-
self.ymin = 1
|
25
|
-
self.allow_broken_y_axis = False
|
26
|
-
self.grid = False
|
27
|
-
self.accentuate_baseline = False
|
28
|
-
|
29
|
-
self.line_marker = "o-"
|
30
|
-
self.line_marker_size = 5
|
31
|
-
|
32
|
-
def _get_line_colors(self, i, *args):
|
33
|
-
if not self.data:
|
34
|
-
# Don't waste time
|
35
|
-
return None
|
36
|
-
if self.highlight and self.highlight in self.labels and i == self.labels.index(self.highlight):
|
37
|
-
return self._nwc_style["strong_color"]
|
38
|
-
elif self.colors and i < len(self.colors):
|
39
|
-
return self.colors[i]
|
40
|
-
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.1 → newsworthycharts-1.71.3}/newsworthycharts/translations/regions.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{newsworthycharts-1.71.1 → 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
|