newsworthycharts 1.72.1__py3-none-any.whl → 1.73.1__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.72.1"
1
+ __version__ = "1.73.1"
2
2
 
3
3
  from .chart import Chart
4
4
  from .choroplethmap import ChoroplethMap
newsworthycharts/chart.py CHANGED
@@ -427,7 +427,7 @@ class Chart(object):
427
427
  self.ax.xaxis.set_major_formatter(DateFormatter('%Y'))
428
428
 
429
429
  else:
430
- loc = get_best_locator(delta, len(dates), self.interval)
430
+ loc = get_best_locator(delta, len(dates), self.interval, max_ticks=self.max_ticks)
431
431
  self.ax.xaxis.set_major_locator(loc)
432
432
  formatter = Formatter(self._language)
433
433
 
@@ -776,7 +776,13 @@ class Chart(object):
776
776
  'Creator': f"NWCharts {__version__}",
777
777
  }
778
778
  """
779
-
779
+ """
780
+ if img_format == "avif":
781
+ from matplotlib.animation import FuncAnimation, PillowWriter
782
+ ani = FuncAnimation(self._fig, lambda x: [], frames=[0], blit=True)
783
+ ani.save(buf, writer=PillowWriter(fps=1), dpi=self._fig.dpi * factor)
784
+ else:
785
+ """
780
786
  self._fig.savefig(buf, **args) # , bbox_inches="tight")
781
787
  buf.seek(0)
782
788
  self._storage.save(key, buf, img_format, storage_options)
@@ -174,6 +174,11 @@ class ChoroplethMap(Map):
174
174
  # We need to add it ourselves
175
175
  label_order = self.label_order or color_map.keys()
176
176
  for label in reversed(label_order):
177
+ if label not in color_map:
178
+ if str(label) in color_map:
179
+ label = str(label)
180
+ else:
181
+ continue
177
182
  color = color_map[label]
178
183
  # A bit of an hack:
179
184
  # Check if this corresponds to one of our predefined
@@ -229,4 +234,4 @@ class ChoroplethMap(Map):
229
234
  handles=patches,
230
235
  **label_kwargs,
231
236
  )
232
- #self.ax.get_figure().savefig("TST.png", bbox_inches="tight")
237
+ # self.ax.get_figure().savefig("TST.png", bbox_inches="tight")
@@ -1,7 +1,9 @@
1
1
  """ Custom locators and related methods
2
2
  """
3
- from matplotlib.dates import (YearLocator, MonthLocator, DayLocator,
4
- WeekdayLocator)
3
+ from matplotlib.dates import (YearLocator, DayLocator,
4
+ WeekdayLocator, AutoDateLocator)
5
+ import matplotlib.dates as mdates
6
+
5
7
  from datetime import datetime
6
8
  from dateutil.rrule import MO
7
9
 
@@ -36,7 +38,7 @@ def get_year_ticks(start_date, end_date, max_ticks=5):
36
38
  return selected_dates
37
39
 
38
40
 
39
- def get_best_locator(delta, points, interval=None):
41
+ def get_best_locator(delta, points, interval=None, max_ticks=None):
40
42
  """ Get the optimal locator given a time delta and number of points.
41
43
  This methods will be much more conservative than Matplotlib's AutoLocator,
42
44
  trying to keep the x axis as clean as possible, while still including
@@ -61,10 +63,12 @@ def get_best_locator(delta, points, interval=None):
61
63
  else:
62
64
  # Less than a year:
63
65
  if interval in ["monthly", "quarterly"]:
64
- if points > 12:
65
- return MonthLocator()
66
- else:
67
- return MonthLocator()
66
+ locator = AutoDateLocator(maxticks=max_ticks)
67
+ locator.intervald = {
68
+ mdates.MONTHLY: [1, 2, 3, 4, 6], # only allow monthly intervals
69
+ }
70
+ return locator
71
+ # return MonthLocator()
68
72
 
69
73
  elif interval == "weekly":
70
74
  # NB The threshold are not tested thoroughly. Consider adjusting.
@@ -82,7 +86,12 @@ def get_best_locator(delta, points, interval=None):
82
86
 
83
87
  elif interval == "daily" or interval is None:
84
88
  if delta.days > 30:
85
- return MonthLocator()
89
+ locator = AutoDateLocator(maxticks=max_ticks)
90
+ locator.intervald = {
91
+ mdates.MONTHLY: [1, 2, 3, 4, 6], # only allow monthly intervals
92
+ }
93
+ return locator
94
+ # return MonthLocator()
86
95
  elif delta.days > 21:
87
96
  return DayLocator(interval=10)
88
97
  elif delta.days > 7:
@@ -37,7 +37,7 @@ class SerialChart(Chart):
37
37
 
38
38
  self.grid = True
39
39
  self.point_marker = "."
40
- self.line_marker = "-"
40
+ self.line_marker = None
41
41
  self.line_marker_size = 3
42
42
 
43
43
  self.max_ticks = 10
@@ -234,16 +234,26 @@ class SerialChart(Chart):
234
234
  highlight_value)
235
235
  highlight_diff['y1'] = max(highlight_diff['y1'],
236
236
  highlight_value)
237
- if self.type[i] == "line":
237
+ if self.type[i] in ["line", "markers"]:
238
238
  # Put first series on top
239
239
  zo = len(series) - i + 1
240
240
  zo += 10 # Make sure lines are on top of bars
241
241
 
242
- if self.line_width is None:
243
- lw = self._nwc_style.get("lines.linewidth", 2)
242
+ lw = self.line_width
243
+ mz = self.line_marker_size
244
+ lm = self.line_marker
245
+ if self.type[i] == "markers":
246
+ if lm is None:
247
+ lm = "o"
248
+ if lw is None:
249
+ lw = self._nwc_style.get("lines.linewidth", 2) / 2
250
+ mz *= 3
244
251
  else:
245
- lw = self.line_width
246
-
252
+ if lm is None:
253
+ lm = "-"
254
+ if lw is None:
255
+ lw = self._nwc_style.get("lines.linewidth", 2)
256
+
247
257
  if hasattr(self, "_get_line_colors"):
248
258
  # Hook for sub classes
249
259
  color = self._get_line_colors(i)
@@ -252,6 +262,9 @@ class SerialChart(Chart):
252
262
  color = self._nwc_style["qualitative_colors"][i]
253
263
  else:
254
264
  color = self.colors[i]
265
+ elif self.highlight in dates_str and self.type[i] == "markers":
266
+ # For markers, will will use a logic similar to bars
267
+ color = self._nwc_style["neutral_color"]
255
268
  elif i == 0:
256
269
  color = self._nwc_style["strong_color"]
257
270
  else:
@@ -263,44 +276,67 @@ class SerialChart(Chart):
263
276
  line, = self.ax.plot(
264
277
  dates,
265
278
  values,
266
- self.line_marker,
267
- markersize=self.line_marker_size,
279
+ lm,
280
+ markersize=mz,
268
281
  color=color,
269
282
  markerfacecolor=marker_fill,
270
283
  markeredgewidth=0,
271
284
  zorder=zo,
272
285
  lw=lw,
273
286
  )
274
- # Add single, orphaned data points as markers
275
- # None, 1, None, 1, 1, 1 => . ---
276
- num_values = len(values)
277
- if num_values == 1:
287
+ if self.highlight in dates_str and self.type[i] == "markers":
288
+ # Highlight specific date (Motplotlib has no better option than to overwrite the marker)
278
289
  self.ax.plot(
279
- dates[0],
280
- values[0],
281
- c=color,
282
- marker=self.point_marker,
283
- zorder=12,
290
+ highlight_date,
291
+ highlight_value,
292
+ c=self._nwc_style["strong_color"],
293
+ marker=lm,
294
+ markersize=mz,
295
+ markerfacecolor=marker_fill,
296
+ markeredgewidth=0,
297
+ zorder=zo + 1,
298
+ )
299
+
300
+ if self.type[i] == "markers":
301
+ # Join the dots with a line
302
+ self.ax.plot(
303
+ dates,
304
+ values,
305
+ color=color,
306
+ zorder=zo - 1,
307
+ lw=lw,
284
308
  )
285
- elif num_values > 1:
286
- for j, v in enumerate(values):
287
- def nullish(val):
288
- return val is None or np.isnan(val)
289
- plot_me = False
290
- if not nullish(v):
291
- if j == 0 and nullish(values[1]):
292
- plot_me = True
293
- elif j == num_values - 1 and nullish(values[j - 1]):
294
- plot_me = True
295
- elif nullish(values[j - 1]) and nullish(values[j + 1]):
296
- plot_me = True
297
- if plot_me:
298
- self.ax.plot(
299
- dates[j], v,
300
- c=color,
301
- marker=self.point_marker,
302
- zorder=12
303
- )
309
+ else:
310
+ # Add single, orphaned data points as markers
311
+ # None, 1, None, 1, 1, 1 => . ---
312
+ num_values = len(values)
313
+ if num_values == 1:
314
+ self.ax.plot(
315
+ dates[0],
316
+ values[0],
317
+ c=color,
318
+ marker=self.point_marker,
319
+ zorder=12,
320
+ )
321
+ elif num_values > 1:
322
+ for j, v in enumerate(values):
323
+ def nullish(val):
324
+ return val is None or np.isnan(val)
325
+ plot_me = False
326
+ if not nullish(v):
327
+ if j == 0 and nullish(values[1]):
328
+ plot_me = True
329
+ elif j == num_values - 1 and nullish(values[j - 1]):
330
+ plot_me = True
331
+ elif nullish(values[j - 1]) and nullish(values[j + 1]):
332
+ plot_me = True
333
+ if plot_me:
334
+ self.ax.plot(
335
+ dates[j], v,
336
+ c=color,
337
+ marker=self.point_marker,
338
+ zorder=12
339
+ )
304
340
 
305
341
  if len(self.labels) > i and any([x[1] for x in serie]):
306
342
  line.set_label(self.labels[i])
@@ -460,7 +496,7 @@ class SerialChart(Chart):
460
496
  dir = "up"
461
497
  else:
462
498
  dir = "down"
463
- if self.type[i] == "line":
499
+ if self.type[i] in ["line", "markers"]:
464
500
  if len(highlight_values) > 1:
465
501
  # When highlighting two values on the same point,
466
502
  # put them in opposite direction
@@ -519,7 +555,7 @@ class SerialChart(Chart):
519
555
  y=self.baseline,
520
556
  linewidth=1,
521
557
  color="#444444",
522
- zorder=11,
558
+ zorder=11 if "bars" in self.type else 9,
523
559
  linestyle="--" if self.baseline else "-",
524
560
  )
525
561
  if self.baseline_annotation:
@@ -531,7 +567,7 @@ class SerialChart(Chart):
531
567
  xy,
532
568
  direction="down" if first_val and first_val >= self.baseline else "up",
533
569
  color=self._nwc_style["neutral_color"],
534
- zorder=11,
570
+ zorder=12,
535
571
  )
536
572
 
537
573
  # Shade area between lines if there are exactly 2 series
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: newsworthycharts
3
- Version: 1.72.1
3
+ Version: 1.73.1
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.72.1.tar.gz
6
+ Download-URL: https://github.com/jplusplus/newsworthycharts/archive/1.73.1.tar.gz
7
7
  Author: Jens Finnäs and Leo Wallentin, J++ Stockholm
8
8
  Author-email: stockholm@jplusplus.org
9
9
  License: MIT
@@ -154,12 +154,14 @@ These settings are available for all chart types:
154
154
 
155
155
  **SerialChart**
156
156
 
157
- - type = 'line' # line|bars, or a list of types for each data serie
157
+ - type = 'line' # line|bars|markers, or a list of types for each data serie
158
158
  - bar_width = 0.9 # percent of data point width
159
159
  - allow_broken_y_axis = True|False # default depends on chart type
160
160
  - baseline = 0 # The “zero” line. Useful for plotting deviations, e.g. temperatures above/below mean
161
161
  - baseline_annotation = None # A label for the baseline
162
162
  - line_width = None # To override style settings
163
+ - line_marker = "-"" # Matplotlib line marker type.
164
+ - line_marker_size = 3 # Matplotlib line marker size
163
165
  - max_ticks = 10 # Maximum number of ticks on the x axis. *Only used with yearly|decennial data* (this is yet to be fixed)
164
166
  - ticks = None # Custom ticks on the x axis, as a list of lists or tuples: `[(2013-01-01, "-13"), (2014-01-01, "-14"), (2015-01-01, "-15")]`
165
167
  - ymin = None # Minimum value on y axis. If None, it will be calculated from data
@@ -271,6 +273,17 @@ Roadmap
271
273
  Changelog
272
274
  ---------
273
275
 
276
+ - 1.73.1
277
+
278
+ - More leniant label parsing in ChoroplethMap
279
+ - Make max_ticks work in monthly time series charts.
280
+
281
+ - 1.73.0
282
+
283
+ - Add `type: "markers"` to serial charts. Uses a thin line with a marker at each data point as default.
284
+ - Make sure `baseline` goes behind lines in serial charts
285
+ - Make sure baseline annotation goes above lines in serial charts
286
+
274
287
  - 1.72.1
275
288
 
276
289
  - Better interval detection in SerialChart when years are all in different decades
@@ -1,15 +1,15 @@
1
- newsworthycharts/__init__.py,sha256=PQRGF5fVut2-N573MdBk5WLnTRKz9d3WWjMgw_Pru44,1221
1
+ newsworthycharts/__init__.py,sha256=j6K16TGBlydhzNJ9jIftxz03lGGYkpR8E1DcEXHuZPk,1221
2
2
  newsworthycharts/bubblemap.py,sha256=nkocWmpiFgfjEuJGAsthjY5X7Q56jXWsZHUGXw4PwgE,2587
3
3
  newsworthycharts/categoricalchart.py,sha256=Vr-0yFms0hEVCeUa3vLt3FYBqpX4xLQ8YGPc4LGQN_A,18368
4
- newsworthycharts/chart.py,sha256=eXgrNCmfE52rFsvTDkMpCZsmL0oSezdkH---LGYsES8,35062
5
- newsworthycharts/choroplethmap.py,sha256=PEhBOw9UD0OyeFRjpCFFoxa2ztT5NeGSYOubo2PaNtA,9210
4
+ newsworthycharts/chart.py,sha256=ebPLEIFAlUrE7N7n0aIqkzayKHHguuF0rVkXDK3OpJU,35393
5
+ newsworthycharts/choroplethmap.py,sha256=dMJYmep3Qt3pC09N3-9dSUxUO6EI3QmLktQRJ-D0bdM,9404
6
6
  newsworthycharts/datawrapper.py,sha256=RRkAVTpfP4updKxUIBaSmKuBi2RUVPaBRF8HDQhlGGA,11250
7
7
  newsworthycharts/map.py,sha256=EGh96tU10y7Kgu1e83_VWQSeABdjP6V-0VM6VTHDxu4,6089
8
8
  newsworthycharts/rangeplot.py,sha256=NE1W9TnmlpK6T3RvBJOU3nd73EXqkj17OY9i5zlw_cQ,8366
9
9
  newsworthycharts/rankchart.py,sha256=vMzNMVOv1jae6wuHC_riAdb1lm9oPltO3TT3YQpc0Oc,9376
10
10
  newsworthycharts/scatterplot.py,sha256=weHubdMsDGaBTXejg2TqBNPTQ1K-QBpZqJiyQ8EOEc4,5084
11
11
  newsworthycharts/seasonalchart.py,sha256=rr55yqJUkaYDR9Ik98jes6574oY1U8t8LwoLE3gClW4,1967
12
- newsworthycharts/serialchart.py,sha256=x28ldRdba3JEmat-cnQH1XQQXrPEhx-S55_1yTvNIDI,30133
12
+ newsworthycharts/serialchart.py,sha256=HQv3zAGBhlwbrH6HsslaM-c7VAvl9rg5CYCv1ok_Acw,31826
13
13
  newsworthycharts/storage.py,sha256=myERhlpvXyExXxUByBq9eW1bWkCyfH9SwTZbsWSyy3Q,4301
14
14
  newsworthycharts/stripechart.py,sha256=9B6PX2MyLuKNQ8W0OGdKbP0-U32kju0K_NHHwwz_J68,1547
15
15
  newsworthycharts/custom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -20,7 +20,7 @@ newsworthycharts/lib/colors.py,sha256=U04TDkvoMQkcldRFXfnwyLOTwq1SWW2se-Ad-DNcw9
20
20
  newsworthycharts/lib/datalist.py,sha256=-LdGFE0SOvjLnIRZ6eoJoijDaG3SeUPNIDPBjbm5A2U,6433
21
21
  newsworthycharts/lib/formatter.py,sha256=GNH43hE0bC17OgiV8LYH3YUrEhm7OJh9XzfSV4HVtHo,4838
22
22
  newsworthycharts/lib/geography.py,sha256=K0_teFmuPJwXX7Py-amJB_1YY5_gL2kBYhz1LrRCyTg,584
23
- newsworthycharts/lib/locator.py,sha256=rJHdgiA0LASRzdLINhTeU5zOoq1TCMgge4KOBWSagnM,3041
23
+ newsworthycharts/lib/locator.py,sha256=9iCow6GZDdKTqvY1HIkW60p5xFmBc1rMiPqh-kodT-s,3459
24
24
  newsworthycharts/lib/mimetypes.py,sha256=bL9HtVWbn2Of39LcBt4u4yelkr4bGZiyebq3OfLnfFY,237
25
25
  newsworthycharts/lib/utils.py,sha256=zk2hfbZluRcwwxa-N9LocP65HKiHmvgkBw9saAq1lgU,7403
26
26
  newsworthycharts/maps/se-4.gpkg,sha256=oWw5j7FPVpI0ig67jNDim8qSn5SG8rcHp0014-uTKZM,290816
@@ -29,8 +29,8 @@ newsworthycharts/rc/newsworthy,sha256=yOIZvYS6PG1u19VMcdtfj9vbihKQsey5IprwqK59Kg
29
29
  newsworthycharts/translations/datawrapper_regions.csv,sha256=fzZcQRX6RFMlNNP8mpgfYNdR3Y0QAlQxDXk8FXTaWWI,9214
30
30
  newsworthycharts/translations/regions.py,sha256=Nv1McQjggD4S3JRu82rDMTG3pqUVR13E5-FBpSYbm98,239
31
31
  newsworthycharts/translations/se_municipalities.csv,sha256=br_mm-IvzQtj_W55_ATREhJ97jWnCweBFlDAVY2EBxA,7098
32
- newsworthycharts-1.72.1.dist-info/LICENSE.txt,sha256=Sq6kGICrehbhC_FolNdXf0djKjTpv3YqjFCIYsxdQN4,1069
33
- newsworthycharts-1.72.1.dist-info/METADATA,sha256=Vz1Yjzk7s9RfcTHDpTIkSB5fb-meY0zgrKRVWENQnxA,34137
34
- newsworthycharts-1.72.1.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
35
- newsworthycharts-1.72.1.dist-info/top_level.txt,sha256=dn_kzIj8UgUCMsh1PHdVEQJHVGSsN7Z8YJF-8xXa8n0,17
36
- newsworthycharts-1.72.1.dist-info/RECORD,,
32
+ newsworthycharts-1.73.1.dist-info/LICENSE.txt,sha256=Sq6kGICrehbhC_FolNdXf0djKjTpv3YqjFCIYsxdQN4,1069
33
+ newsworthycharts-1.73.1.dist-info/METADATA,sha256=W7n5cgzx9DCRCvG8vbkqplsQw9dheDi67yAxUFbUYz8,34612
34
+ newsworthycharts-1.73.1.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
35
+ newsworthycharts-1.73.1.dist-info/top_level.txt,sha256=dn_kzIj8UgUCMsh1PHdVEQJHVGSsN7Z8YJF-8xXa8n0,17
36
+ newsworthycharts-1.73.1.dist-info/RECORD,,