newsworthycharts 1.59.0__py3-none-any.whl → 1.60.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.
@@ -1,4 +1,4 @@
1
- __version__ = "1.59.0"
1
+ __version__ = "1.60.0"
2
2
 
3
3
  from .chart import Chart
4
4
  from .choroplethmap import ChoroplethMap
@@ -6,6 +6,7 @@ import geopandas as gpd
6
6
  import numpy as np
7
7
  import pandas as pd
8
8
  import mapclassify
9
+ import matplotlib.patches as mpatches
9
10
 
10
11
 
11
12
  class ChoroplethMap(Map):
@@ -29,15 +30,27 @@ class ChoroplethMap(Map):
29
30
 
30
31
  df = self._prepare_map_data()
31
32
 
32
- if self.categorical:
33
- # We'll categorize manually further down the line,
34
- # to easier implement custom coloring
35
- pass
36
- # df["data"] = pd.Categorical(
37
- # df["data"],
38
- # ordered=True,
39
- # )
40
- else:
33
+ args = {
34
+ "categorical": True,
35
+ "legend": True, # bug in geopandas, fixed in master but not released
36
+ "legend_kwds": {
37
+ "loc": "upper left",
38
+ "bbox_to_anchor": (1.05, 1.0),
39
+ },
40
+ "edgecolor": "white",
41
+ "linewidth": 0.2,
42
+ "missing_kwds": {
43
+ "color": "gainsboro",
44
+ },
45
+ }
46
+ # This should be adjusted per basemap
47
+ label_kwargs = {
48
+ "bbox_to_anchor": (0.92, 0.95),
49
+ "loc": "upper left",
50
+ }
51
+
52
+ patches = [] # for legend
53
+ if not self.categorical:
41
54
  # mapclassify doesn't work well with nan values,
42
55
  # but we to keep them for plotting, hence
43
56
  # this hack with cutting out nan's and re-pasting them below
@@ -58,44 +71,57 @@ class ChoroplethMap(Map):
58
71
  _dict = _has_value[["id", "cats"]].set_index("id").to_dict()
59
72
  df["data"] = df["id"].map(_dict["cats"])
60
73
 
61
- args = {
62
- "categorical": True,
63
- "legend": True, # bug in geopandas, fixed in master but not released
64
- "legend_kwds": {
65
- "loc": "upper left",
66
- "bbox_to_anchor": (1.05, 1.0),
67
- },
68
- "edgecolor": "white",
69
- "linewidth": 0.2,
70
- "missing_kwds": {
71
- "color": "gainsboro",
72
- },
73
- }
74
- # This should be adjusted per basemap
75
- label_kwargs = {
76
- "bbox_to_anchor": (0.92, 0.95),
77
- "loc": "upper left",
78
- }
79
- if not self.categorical:
80
- args["cmap"] = self.color_ramp
81
- args["column"] = "data"
82
- if self.categorical:
83
- cat = df[~df["data"].isna()]["data"].unique()
74
+ # args["column"] = "data"
75
+ # args["cmap"] = self.color_ramp
76
+ # We can not provide vmin/vmax to geopandas, so we need to
77
+ # normalize the data ourselves, otherwise the inset maps will be off
78
+ import matplotlib.cm as cm
79
+ import matplotlib as mpl
80
+ norm = mpl.colors.Normalize(vmin=df["data"].min(), vmax=df["data"].max())
81
+ mapper = cm.ScalarMappable(norm=norm, cmap=self.color_ramp)
82
+ df["color"] = df["data"].apply(lambda x: mapper.to_rgba(x) if not np.isnan(x) else "gainsboro")
83
+ df["color"] = df["color"].fillna("gainsboro")
84
+ args["color"] = df["color"]
85
+
86
+ # Add labels legend (manually, Geopandas is too crude as of now)
87
+ fmt = self._get_value_axis_formatter()
88
+ for idx, cat in enumerate(binning.bins):
89
+ # cat is the upper limit of each category
90
+ if binning.counts[idx] == 1:
91
+ txt_val = fmt(cat)
92
+ elif idx == 0:
93
+ txt_val = f"– {fmt(cat)}"
94
+ else:
95
+ txt_val = f"{fmt(binning.bins[idx - 1])} – {fmt(cat)}"
96
+ patches.append(mpatches.Patch(color=mapper.to_rgba(cat), label=txt_val))
97
+
98
+ elif self.categorical:
99
+ # We'll categorize manually further down the line,
100
+ # to easier implement custom coloring
101
+ # df["data"] = pd.Categorical(
102
+ # df["data"],
103
+ # ordered=True,
104
+ # )
105
+
106
+ cat = df[~df["data"].isna()]["data"].astype(str).unique()
84
107
  args["categories"] = cat
85
108
  if self.colors:
86
109
  color_map = self.colors
87
110
  else:
88
111
  color_map = {}
112
+ if len(cat) > len(self._nwc_style["qualitative_colors"]):
113
+ raise ValueError(
114
+ "Too many categories for the available colors in the current style. " + # noqa:W504
115
+ "Add a custom color map, or use a style with more categorical colors!"
116
+ )
89
117
  for idx, cat in enumerate(cat):
90
118
  color_map[cat] = self._nwc_style["qualitative_colors"][idx]
91
- df["color"] = df["data"].map(color_map)
119
+ df["color"] = df["data"].astype(str).map(color_map)
92
120
  df["color"] = df["color"].fillna("gainsboro")
93
121
  args["color"] = df["color"]
94
122
 
95
123
  # Geopandas does not handle legend if color keyword is used
96
124
  # We need to add it ourselves
97
- import matplotlib.patches as mpatches
98
- patches = []
99
125
  for label, color in color_map.items():
100
126
  # A bit of an hack:
101
127
  # Check if this corresponds to one of our predefined
@@ -104,44 +130,20 @@ class ChoroplethMap(Map):
104
130
  color = self._nwc_style[f"{color}_color"]
105
131
  patch = mpatches.Patch(color=color, label=label)
106
132
  patches.append(patch)
107
- self.ax.legend(
108
- handles=patches,
109
- **label_kwargs
110
- )
133
+ if self.missing_label:
134
+ patches.append(mpatches.Patch(color="gainsboro", label=self.missing_label))
111
135
 
112
- fig = df.plot(ax=self.ax, **args)
136
+ df.plot(ax=self.ax, **args)
113
137
  # Add outer edge
114
- gpd.GeoSeries(df.unary_union).plot(
138
+ gpd.GeoSeries(df.union_all()).plot(
115
139
  ax=self.ax,
116
140
  edgecolor="lightgrey",
117
141
  linewidth=0.2,
118
142
  facecolor="none",
119
143
  color="none",
120
144
  )
121
- self.ax.axis("off")
122
-
123
- # Format numbers in legend
124
- if not self.categorical:
125
- leg = fig.get_legend()
126
- fmt = self._get_value_axis_formatter()
127
- remove_last = False
128
- for lbl in leg.get_texts():
129
- val = lbl.get_text()
130
- if val == "NaN": # as returned by mapclassify
131
- if self.missing_label is not None:
132
- val = self.missing_label
133
- else:
134
- remove_last = True
135
- val = ""
136
- else:
137
- val = float(val)
138
- val = fmt(val)
139
- lbl.set_text(val)
140
- if remove_last:
141
- del leg.legend_handles[-1]
142
- texts = [lbl.get_text() for lbl in leg.get_texts()]
143
- fig.legend(handles=leg.legend_handles, labels=texts, **label_kwargs)
144
145
 
146
+ self.ax.axis("off")
145
147
  for inset in self.insets:
146
148
  if "prefix" in inset:
147
149
  _df = df[df["id"].str.startswith(inset["prefix"])].copy()
@@ -150,12 +152,11 @@ class ChoroplethMap(Map):
150
152
  if _df["data"].isnull().all():
151
153
  # Skip if no data
152
154
  continue
153
- if self.categorical:
154
- # We need a series matching the filtered data
155
- args["color"] = _df["color"]
155
+
156
+ args["color"] = _df["color"]
156
157
  args["legend"] = False
157
158
  axin = self.ax.inset_axes(inset["axes"])
158
- gpd.GeoSeries(_df.unary_union).plot(
159
+ gpd.GeoSeries(_df.union_all()).plot(
159
160
  ax=axin,
160
161
  edgecolor="lightgrey",
161
162
  linewidth=0.3,
@@ -169,3 +170,8 @@ class ChoroplethMap(Map):
169
170
  r, (a, b, c, d) = self.ax.indicate_inset_zoom(axin)
170
171
  for _line in [a, b, c, d]:
171
172
  _line.set_visible(False)
173
+
174
+ self.ax.legend(
175
+ handles=patches,
176
+ **label_kwargs,
177
+ )
newsworthycharts/map.py CHANGED
@@ -87,10 +87,10 @@ class Map(Chart):
87
87
 
88
88
  def __init__(self, *args, **kwargs):
89
89
  super(Map, self).__init__(*args, **kwargs)
90
- self.bins = kwargs.get("bins", 9)
90
+ self.bins = kwargs.get("bins", 5)
91
91
  self.binning_method = kwargs.get("binning_method", "natural_breaks")
92
92
  self.colors = kwargs.get("colors", None)
93
- self.color_ramp = kwargs.get("color_ramp", "YlOrRd")
93
+ self.color_ramp = kwargs.get("color_ramp", "YlGn") # YlOrRd
94
94
  self.categorical = kwargs.get("categorical", False)
95
95
  self.base_map = None
96
96
  self.missing_label = None
@@ -1,28 +1,29 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: newsworthycharts
3
- Version: 1.59.0
3
+ Version: 1.60.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.59.0.tar.gz
6
+ Download-URL: https://github.com/jplusplus/newsworthycharts/archive/1.60.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
10
10
  Requires-Python: >=3.9
11
11
  Description-Content-Type: text/x-rst
12
12
  License-File: LICENSE.txt
13
+ Requires-Dist: boto3 >=1.26
14
+ Requires-Dist: matplotlib ==3.9.2
15
+ Requires-Dist: langcodes >=3.3
13
16
  Requires-Dist: Babel <3,>=2.14.0
14
- Requires-Dist: Pillow ==10.3.0
15
17
  Requires-Dist: PyYAML >=3
16
18
  Requires-Dist: adjustText ==0.7.3
17
- Requires-Dist: boto3 >=1.26
18
- Requires-Dist: geopandas ==0.14.4
19
- Requires-Dist: langcodes >=3.3
20
- Requires-Dist: mapclassify ==2.6.1
21
- Requires-Dist: matplotlib-label-lines ==0.5.1
22
- Requires-Dist: matplotlib ==3.9.0
23
- Requires-Dist: numpy <2,>=1.21.0
19
+ Requires-Dist: numpy >2
24
20
  Requires-Dist: python-dateutil <3,>=2
21
+ Requires-Dist: Pillow ==10.4.0
25
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.6.1
26
+ Requires-Dist: fiona >=1.0
26
27
 
27
28
  This module contains methods for producing graphs and publishing them on Amazon S3, or in the location of your choice.
28
29
 
@@ -136,8 +137,8 @@ These settings are available for all chart types:
136
137
  - yline = None # A horizontal line across the chart (Matplotlib: axhline)
137
138
  - labels = [] # Optionally one label for each dataset
138
139
  - annotations = [] # Manually added annotations
139
- - interval = None # yearly|quarterly|monthly|weekly|daily
140
- - units = 'number' # number|percent|degrees
140
+ - interval = None # yearly, quarterly, monthly, weekly, daily
141
+ - units = 'number' # number, percent, degrees
141
142
  - show_ticks = True # toggle category names, dates, etc
142
143
  - subtitle = None
143
144
  - note = None
@@ -236,13 +237,33 @@ To deploy a new version to [PyPi](https://pypi.org/project/newsworthycharts):
236
237
 
237
238
  Roadmap
238
239
  -------
239
- - Adding more base maps
240
- - Getting rid of custom settings-hack
240
+
241
+ - Get rid of custom settings-hack
242
+ - Remove custom charts (add missing api interfaces to Chart class instead)
243
+ - Remove DataWrapper class (out-of-scope)
241
244
  - Custom month locator with equal-width month bars
245
+ - Add color-ramp choropleth maps, as an alternative to binning
242
246
 
243
247
  Changelog
244
248
  ---------
245
249
 
250
+ - 1.60.0
251
+
252
+ - [BREAKING] The default number of bins in maps is now 5, not 9
253
+ - [BREAKING] Default color ramp for choropleth maps is now `YlGn`
254
+ - GeoPandas 1.x
255
+ - numpy 2.x
256
+ - matplotlib==3.9.2
257
+ - Pillow==10.4.0
258
+ - Improved nan handling in labelling continuous choropleth maps
259
+ - Replace deprecated `.unary_union` with `.union_all()`
260
+ - Choropleth map legends now print spans for binned data
261
+ - Use Matplotlib's legend for choropleth maps, to have the same style as other charts, and for much improved flexibility
262
+ - Inset maps now (finally!) work with continuous data
263
+ - Better error handling in categorical maps
264
+ - Cast all categorical values to strings in categorical maps
265
+ - Replace deprecated imghdr with puremagic in tests
266
+
246
267
  - 1.59.0
247
268
 
248
269
  - Added `.highlight_annotation` to enable turning off the textual annotation on the highlighted point
@@ -1,10 +1,10 @@
1
- newsworthycharts/__init__.py,sha256=ql3nVttdON3JXhF6EJyawP8Yqp7_5CVACZVg67qQnVk,1160
1
+ newsworthycharts/__init__.py,sha256=xHFzdcFCUfaPTmQ4A76S5gg13omTVjATWqkExpWYdk8,1160
2
2
  newsworthycharts/bubblemap.py,sha256=nkocWmpiFgfjEuJGAsthjY5X7Q56jXWsZHUGXw4PwgE,2587
3
3
  newsworthycharts/categoricalchart.py,sha256=LwOZ3VbNy9vzvoK0s77AkbfMt4CXVDSAhnsnBInUIrE,14764
4
4
  newsworthycharts/chart.py,sha256=VXEJe_g_KahyiUXqXnM3vDS7SQoW1Ppktcj13-A2_oc,30631
5
- newsworthycharts/choroplethmap.py,sha256=2b61MuYed8ccc7uZkzuSdZDbqVR7C3OP1ftb_khuaLw,6069
5
+ newsworthycharts/choroplethmap.py,sha256=dVRShxjkBKa57r_Kp6ysdNCXGwXh3eZK28MvVtLe7BQ,6677
6
6
  newsworthycharts/datawrapper.py,sha256=RRkAVTpfP4updKxUIBaSmKuBi2RUVPaBRF8HDQhlGGA,11250
7
- newsworthycharts/map.py,sha256=ruhFEFmGg8DwySDPnEbx_4n57DgHL5LlLKQEnoNmuIU,6225
7
+ newsworthycharts/map.py,sha256=c3VwhJFwmmWJyXJ7HvYdvJsWMTCKD9Eg5VnK6UmdDFc,6233
8
8
  newsworthycharts/rangeplot.py,sha256=NE1W9TnmlpK6T3RvBJOU3nd73EXqkj17OY9i5zlw_cQ,8366
9
9
  newsworthycharts/scatterplot.py,sha256=6iaMoiZx__Gc-2Hcdw-8Ga5dSonrFo3oexKNmSFuir4,4959
10
10
  newsworthycharts/seasonalchart.py,sha256=rr55yqJUkaYDR9Ik98jes6574oY1U8t8LwoLE3gClW4,1967
@@ -28,8 +28,8 @@ newsworthycharts/rc/newsworthy,sha256=X0btLNrmk2DRrfOsKj_WCSIgeD6btacEN2tRF_B4m8
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.59.0.dist-info/LICENSE.txt,sha256=Sq6kGICrehbhC_FolNdXf0djKjTpv3YqjFCIYsxdQN4,1069
32
- newsworthycharts-1.59.0.dist-info/METADATA,sha256=sCsOn00COBf8iSzh5TP71gP4-9vl1TnToi10UrMM4g8,27250
33
- newsworthycharts-1.59.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
34
- newsworthycharts-1.59.0.dist-info/top_level.txt,sha256=dn_kzIj8UgUCMsh1PHdVEQJHVGSsN7Z8YJF-8xXa8n0,17
35
- newsworthycharts-1.59.0.dist-info/RECORD,,
31
+ newsworthycharts-1.60.0.dist-info/LICENSE.txt,sha256=Sq6kGICrehbhC_FolNdXf0djKjTpv3YqjFCIYsxdQN4,1069
32
+ newsworthycharts-1.60.0.dist-info/METADATA,sha256=_muVSibMISGRnrB1Mm2AakHaLJoPtwgQlOKjIAsHZdI,28168
33
+ newsworthycharts-1.60.0.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
34
+ newsworthycharts-1.60.0.dist-info/top_level.txt,sha256=dn_kzIj8UgUCMsh1PHdVEQJHVGSsN7Z8YJF-8xXa8n0,17
35
+ newsworthycharts-1.60.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.42.0)
2
+ Generator: setuptools (74.1.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5