From b6c7f4203dc717abf1b95513785feb56f6258ddc Mon Sep 17 00:00:00 2001 From: sarahclaude Date: Tue, 2 Apr 2024 15:27:30 -0400 Subject: [PATCH 1/8] correct edgecolor showing with NaN values --- docs/notebooks/figanos_docs.ipynb | 21 ++++++++++++++------- figanos/matplotlib/plot.py | 11 ++++++----- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/docs/notebooks/figanos_docs.ipynb b/docs/notebooks/figanos_docs.ipynb index 2f54c9b7..6759c40f 100644 --- a/docs/notebooks/figanos_docs.ipynb +++ b/docs/notebooks/figanos_docs.ipynb @@ -623,11 +623,16 @@ "outputs": [], "source": [ "# create a fictional observational dataset from scratch\n", - "names = ['station_' + str(i) for i in np.arange(10)]\n", - "lat = 45 + np.random.rand(10)*3\n", - "lon = np.linspace(-76,-70, 10)\n", - "tas = 20 + np.random.rand(10)*7\n", - "yrs = (30 * np.random.rand(10)).astype(int)\n", + "nb = 10\n", + "\n", + "names = ['station_' + str(i) for i in np.arange(nb)]\n", + "lat = 45 + np.random.rand(nb)*3\n", + "lon = np.linspace(-76,-70, nb)\n", + "tas = 20 + np.random.rand(9)*7\n", + "tas = np.append(tas, np.nan)\n", + "yrs = (10+30 * np.random.rand(9)).astype(int)\n", + "yrs = np.append( np.nan, yrs)\n", + "\n", "attrs = {'units': 'degC', 'standard_name': 'air_temperature', 'long_name': 'Near-Surface Daily Maximum Air Temperature'}\n", "\n", "tas = xr.DataArray(data=tas,\n", @@ -655,11 +660,13 @@ " transform=ccrs.PlateCarree(),\n", " sizes ='years',\n", " size_range=(15, 100),\n", - " divergent=23.5,\n", + " #divergent=23.5,\n", " features=features,\n", " plot_kw={\n", " \"xlim\": (-78,-68),\n", - " \"ylim\": (43,50)},\n", + " \"ylim\": (43,50),\n", + " \"edgecolor\": 'black'\n", + " },\n", " fig_kw={'figsize': (9,6)},\n", " legend_kw={'loc': 'lower left',\n", " 'title': 'Number of years of data'},\n", diff --git a/figanos/matplotlib/plot.py b/figanos/matplotlib/plot.py index 0d20c178..47e52b51 100644 --- a/figanos/matplotlib/plot.py +++ b/figanos/matplotlib/plot.py @@ -1635,7 +1635,7 @@ def scattermap( if hasattr(data, "name") and getattr(data, "name") == sizes: sdata = plot_data elif sizes in list(data.coords.keys()): - sdata = data[sizes] + sdata = plot_data[sizes] else: raise ValueError(f"{sizes} not found") else: @@ -1650,7 +1650,7 @@ def scattermap( mask = smask pt_sizes = norm2range( - data=sdata.values, + data=sdata[mask].values, target_range=size_range, data_range=None, ) @@ -1689,7 +1689,8 @@ def scattermap( plot_kw_pop = {"x": "lon", "y": "lat", "hue": plot_data.name} | plot_kw_pop if ax: plot_kw_pop.setdefault("ax", ax) - im = data.plot.scatter(**plot_kw_pop) + v = plot_data[mask].to_dataset() + im = v.plot.scatter(**plot_kw_pop) # add features if ax: @@ -1749,8 +1750,8 @@ def scattermap( # size legend if sizes: legend_elements = size_legend_elements( - np.resize(sdata.values[mask], (sdata.values[mask].size, 1)), - np.resize(pt_sizes[mask], (pt_sizes[mask].size, 1)), + sdata[mask].values, + pt_sizes, max_entries=6, marker=plot_kw["marker"], ) From bee56cc4751ed0eb58ed7612b521ae2c92e5b479 Mon Sep 17 00:00:00 2001 From: sarahclaude Date: Wed, 3 Apr 2024 10:50:47 -0400 Subject: [PATCH 2/8] add mask to colorbar, correct extreme values in legend sizes, correct example --- docs/notebooks/figanos_docs.ipynb | 18 ++++++++---------- figanos/matplotlib/plot.py | 4 ++-- figanos/matplotlib/utils.py | 2 +- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/docs/notebooks/figanos_docs.ipynb b/docs/notebooks/figanos_docs.ipynb index 6759c40f..67c4467b 100644 --- a/docs/notebooks/figanos_docs.ipynb +++ b/docs/notebooks/figanos_docs.ipynb @@ -623,15 +623,13 @@ "outputs": [], "source": [ "# create a fictional observational dataset from scratch\n", - "nb = 10\n", - "\n", - "names = ['station_' + str(i) for i in np.arange(nb)]\n", - "lat = 45 + np.random.rand(nb)*3\n", - "lon = np.linspace(-76,-70, nb)\n", - "tas = 20 + np.random.rand(9)*7\n", - "tas = np.append(tas, np.nan)\n", - "yrs = (10+30 * np.random.rand(9)).astype(int)\n", - "yrs = np.append( np.nan, yrs)\n", + "names = ['station_' + str(i) for i in np.arange(10)]\n", + "lat = 45 + np.random.rand(10)*3\n", + "lon = np.linspace(-76,-70, 10)\n", + "tas = 20 + np.random.rand(10)*7\n", + "tas[9] = np.nan\n", + "yrs = (10+30 * np.random.rand(10))\n", + "yrs[0] = np.nan\n", "\n", "attrs = {'units': 'degC', 'standard_name': 'air_temperature', 'long_name': 'Near-Surface Daily Maximum Air Temperature'}\n", "\n", @@ -660,7 +658,7 @@ " transform=ccrs.PlateCarree(),\n", " sizes ='years',\n", " size_range=(15, 100),\n", - " #divergent=23.5,\n", + " divergent=23.5,\n", " features=features,\n", " plot_kw={\n", " \"xlim\": (-78,-68),\n", diff --git a/figanos/matplotlib/plot.py b/figanos/matplotlib/plot.py index 47e52b51..1fc1e19d 100644 --- a/figanos/matplotlib/plot.py +++ b/figanos/matplotlib/plot.py @@ -1661,8 +1661,8 @@ def scattermap( plot_kw.setdefault("s", pt_sizes[0]) # norm - plot_kw.setdefault("vmin", np.nanmin(plot_data.values)) - plot_kw.setdefault("vmax", np.nanmax(plot_data.values)) + plot_kw.setdefault("vmin", np.nanmin(plot_data[mask].values)) + plot_kw.setdefault("vmax", np.nanmax(plot_data[mask].values)) norm = custom_cmap_norm( cmap, diff --git a/figanos/matplotlib/utils.py b/figanos/matplotlib/utils.py index 984f00db..268dcc65 100644 --- a/figanos/matplotlib/utils.py +++ b/figanos/matplotlib/utils.py @@ -1338,7 +1338,7 @@ def size_legend_elements( lw=0, markerfacecolor="w", label=label, - markersize=np.sqrt(s), + markersize=np.sqrt(np.abs(s)), ) ) From fca4c968e4e84996bef990b867bdc5572efacb38 Mon Sep 17 00:00:00 2001 From: sarahclaude Date: Wed, 3 Apr 2024 11:54:54 -0400 Subject: [PATCH 3/8] add CHANGES.rst, debug changes for facetgrids --- CHANGES.rst | 3 ++- figanos/matplotlib/plot.py | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 1119ce07..ae0d897b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,11 +4,12 @@ Changelog 0.4.0 (unreleased) ------------------ -Contributors to this version: Trevor James Smith (:user:`Zeitsperre`), Marco Braun (:user:`vindelico`) +Contributors to this version: Trevor James Smith (:user:`Zeitsperre`), Marco Braun (:user:`vindelico`), Sarah-Claude Bourdeau-Goulet (:user:`Sarahclaude`) New features and enhancements ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Use list or ndarray as levels for colorbar in gridmap and small bug fixes (:pull:`176`). +* Fix NaN issues in ``fg.matplotlib.scattermap`` and extreme values in sizes legend (:pull:`184`). Internal changes ^^^^^^^^^^^^^^^^ diff --git a/figanos/matplotlib/plot.py b/figanos/matplotlib/plot.py index 1fc1e19d..85d1707e 100644 --- a/figanos/matplotlib/plot.py +++ b/figanos/matplotlib/plot.py @@ -1650,7 +1650,7 @@ def scattermap( mask = smask pt_sizes = norm2range( - data=sdata[mask].values, + data=sdata.where(mask).values, target_range=size_range, data_range=None, ) @@ -1661,8 +1661,8 @@ def scattermap( plot_kw.setdefault("s", pt_sizes[0]) # norm - plot_kw.setdefault("vmin", np.nanmin(plot_data[mask].values)) - plot_kw.setdefault("vmax", np.nanmax(plot_data[mask].values)) + plot_kw.setdefault("vmin", np.nanmin(plot_data.values[mask])) + plot_kw.setdefault("vmax", np.nanmax(plot_data.values[mask])) norm = custom_cmap_norm( cmap, @@ -1689,7 +1689,7 @@ def scattermap( plot_kw_pop = {"x": "lon", "y": "lat", "hue": plot_data.name} | plot_kw_pop if ax: plot_kw_pop.setdefault("ax", ax) - v = plot_data[mask].to_dataset() + v = plot_data.where(mask).to_dataset() im = v.plot.scatter(**plot_kw_pop) # add features @@ -1750,8 +1750,8 @@ def scattermap( # size legend if sizes: legend_elements = size_legend_elements( - sdata[mask].values, - pt_sizes, + np.resize(sdata.values[mask], (sdata.values[mask].size, 1)), + np.resize(pt_sizes[mask], (pt_sizes[mask].size, 1)), max_entries=6, marker=plot_kw["marker"], ) From 1410fabb99a1ba5c56fda772cfe1919d7aec4498 Mon Sep 17 00:00:00 2001 From: sarahclaude Date: Thu, 4 Apr 2024 10:01:09 -0400 Subject: [PATCH 4/8] added edgecolors --- figanos/matplotlib/plot.py | 60 +++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/figanos/matplotlib/plot.py b/figanos/matplotlib/plot.py index 85d1707e..35f858f9 100644 --- a/figanos/matplotlib/plot.py +++ b/figanos/matplotlib/plot.py @@ -1526,15 +1526,17 @@ def scattermap( if "row" not in plot_kw and "col" not in plot_kw: use_attrs.setdefault("title", "description") + # extract plot_kw from dict if needed + if isinstance(data, dict) and plot_kw and list(data.keys())[0] in plot_kw.keys(): + plot_kw = plot_kw[list(data.keys())[0]] + + plot_kw_pop = plot_kw.copy() # copy plot_kw to modify and pop info in it + # figanos does not use xr.plot.scatter default markersize if "markersize" in plot_kw.keys(): if not sizes: sizes = plot_kw["markersize"] - plot_kw.pop("markersize") - - # extract plot_kw from dict if needed - if isinstance(data, dict) and plot_kw and list(data.keys())[0] in plot_kw.keys(): - plot_kw = plot_kw[list(data.keys())[0]] + plot_kw_pop.pop("markersize") # if data is dict, extract if isinstance(data, dict): @@ -1574,13 +1576,13 @@ def scattermap( elif ax is not None and ("col" in plot_kw or "row" in plot_kw): raise ValueError("Cannot use 'ax' and 'col'/'row' at the same time.") elif ax is None: - plot_kw = {"subplot_kws": {"projection": projection}} | plot_kw + plot_kw_pop = {"subplot_kws": {"projection": projection}} | plot_kw_pop cfig_kw = fig_kw.copy() if "figsize" in fig_kw: # add figsize to plot_kw for facetgrid - plot_kw.setdefault("figsize", fig_kw["figsize"]) + plot_kw_pop.setdefault("figsize", fig_kw["figsize"]) cfig_kw.pop("figsize") if len(cfig_kw) >= 1: - plot_kw = {"subplot_kws": {"projection": projection}} | plot_kw + plot_kw_pop = {"subplot_kws": {"projection": projection}} | plot_kw_pop warnings.warn( "Only figsize and figure.add_subplot() arguments can be passed to fig_kw when using facetgrid." ) @@ -1600,9 +1602,9 @@ def scattermap( cbar_label = get_attributes(use_attrs["cbar_label"], data) if "add_colorbar" not in plot_kw or plot_kw["add_colorbar"] is not False: - plot_kw.setdefault("cbar_kwargs", {}) - plot_kw["cbar_kwargs"].setdefault("label", wrap_text(cbar_label)) - plot_kw["cbar_kwargs"].setdefault("pad", 0.015) + plot_kw_pop.setdefault("cbar_kwargs", {}) + plot_kw_pop["cbar_kwargs"].setdefault("label", wrap_text(cbar_label)) + plot_kw_pop["cbar_kwargs"].setdefault("pad", 0.015) # colormap if isinstance(cmap, str): @@ -1654,20 +1656,20 @@ def scattermap( target_range=size_range, data_range=None, ) - plot_kw.setdefault("add_legend", False) + plot_kw_pop.setdefault("add_legend", False) if ax: - plot_kw.setdefault("s", pt_sizes) + plot_kw_pop.setdefault("s", pt_sizes) else: - plot_kw.setdefault("s", pt_sizes[0]) + plot_kw_pop.setdefault("s", pt_sizes[0]) # norm - plot_kw.setdefault("vmin", np.nanmin(plot_data.values[mask])) - plot_kw.setdefault("vmax", np.nanmax(plot_data.values[mask])) + plot_kw_pop.setdefault("vmin", np.nanmin(plot_data.values[mask])) + plot_kw_pop.setdefault("vmax", np.nanmax(plot_data.values[mask])) norm = custom_cmap_norm( cmap, - vmin=plot_kw["vmin"], - vmax=plot_kw["vmax"], + vmin=plot_kw_pop["vmin"], + vmax=plot_kw_pop["vmax"], levels=levels, divergent=divergent, ) @@ -1679,10 +1681,28 @@ def scattermap( "transform": transform, "zorder": 8, "marker": "o", - "edgecolor": "none", } | plot_kw - plot_kw_pop = plot_kw.copy() + # chek if edgecolors in plot_kw and match len of plot_data + if "edgecolors" in plot_kw: + if matplotlib.colors.is_color_like(plot_kw["edgecolors"]): + plot_kw_pop["edgecolors"] = np.repeat( + plot_kw["edgecolors"], len(plot_data.where(mask).values) + ) + elif len(plot_kw["edgecolors"]) != len(plot_data.values): + plot_kw_pop["edgecolors"] = np.repeat( + plot_kw["edgecolors"][0], len(plot_data.where(mask).values) + ) + warnings.warn( + "Length of edgecolors does not match length of data. Only first edgecolor is used for plotting." + ) + else: + if isinstance(plot_kw["edgecolors"], list): + plot_kw_pop["edgecolors"] = np.array(plot_kw["edgecolors"]) + plot_kw_pop["edgecolors"] = plot_kw_pop["edgecolors"][mask] + else: + plot_kw_pop.setdefault("edgecolors", "none") + for key in ["vmin", "vmax"]: plot_kw_pop.pop(key) # plot From ecd17ea8fde3b18e91043e01d382cd5917a57ee3 Mon Sep 17 00:00:00 2001 From: sarahclaude Date: Thu, 4 Apr 2024 10:10:19 -0400 Subject: [PATCH 5/8] plot_kw_pop added --- figanos/matplotlib/plot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/figanos/matplotlib/plot.py b/figanos/matplotlib/plot.py index 35f858f9..7a476624 100644 --- a/figanos/matplotlib/plot.py +++ b/figanos/matplotlib/plot.py @@ -1675,13 +1675,13 @@ def scattermap( ) # set defaults and create copy without vmin, vmax (conflicts with norm) - plot_kw = { + plot_kw_pop = { "cmap": cmap, "norm": norm, "transform": transform, "zorder": 8, "marker": "o", - } | plot_kw + } | plot_kw_pop # chek if edgecolors in plot_kw and match len of plot_data if "edgecolors" in plot_kw: @@ -1773,7 +1773,7 @@ def scattermap( np.resize(sdata.values[mask], (sdata.values[mask].size, 1)), np.resize(pt_sizes[mask], (pt_sizes[mask].size, 1)), max_entries=6, - marker=plot_kw["marker"], + marker=plot_kw_pop["marker"], ) # legend spacing if size_range[1] > 200: From ade2a4b0783fe641d68e974d1782f89588907586 Mon Sep 17 00:00:00 2001 From: sarahclaude Date: Thu, 4 Apr 2024 10:47:02 -0400 Subject: [PATCH 6/8] setdefault edgecolors to edgecolor --- figanos/matplotlib/plot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/figanos/matplotlib/plot.py b/figanos/matplotlib/plot.py index 7a476624..917a6f0a 100644 --- a/figanos/matplotlib/plot.py +++ b/figanos/matplotlib/plot.py @@ -1701,7 +1701,7 @@ def scattermap( plot_kw_pop["edgecolors"] = np.array(plot_kw["edgecolors"]) plot_kw_pop["edgecolors"] = plot_kw_pop["edgecolors"][mask] else: - plot_kw_pop.setdefault("edgecolors", "none") + plot_kw_pop.setdefault("edgecolor", "none") for key in ["vmin", "vmax"]: plot_kw_pop.pop(key) From f335958eaa8e4fcffa451e1f6a9e95cd16c5df56 Mon Sep 17 00:00:00 2001 From: sarahclaude Date: Thu, 4 Apr 2024 11:05:53 -0400 Subject: [PATCH 7/8] merge main --- CHANGES.rst | 2 +- docs/notebooks/figanos_docs.ipynb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 81439432..cc2d9a83 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,7 +10,7 @@ New features and enhancements ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Use list or ndarray as levels for colorbar in gridmap and small bug fixes (:pull:`176`). * Added style sheet ``transparent.mplstyle`` (:issue:`183`, :pull:`185`) -* Fix NaN issues, extreme values in sizes legend and added edcolors in ``fg.matplotlib.scattermap`` (:pull:`184`). +* Fix NaN issues, extreme values in sizes legend and added edgecolors in ``fg.matplotlib.scattermap`` (:pull:`184`). Internal changes ^^^^^^^^^^^^^^^^ diff --git a/docs/notebooks/figanos_docs.ipynb b/docs/notebooks/figanos_docs.ipynb index 3f8901f0..ec8d6596 100644 --- a/docs/notebooks/figanos_docs.ipynb +++ b/docs/notebooks/figanos_docs.ipynb @@ -664,7 +664,7 @@ " plot_kw={\n", " \"xlim\": (-78,-68),\n", " \"ylim\": (43,50),\n", - " \"edgecolor\": 'black'\n", + " \"edgecolor\": \"black\",\n", " },\n", " fig_kw={'figsize': (9,6)},\n", " legend_kw={'loc': 'lower left',\n", From ab73f5e9b9ad9618455d2b73fcc0e416b8b8767f Mon Sep 17 00:00:00 2001 From: sarahclaude Date: Thu, 4 Apr 2024 14:19:22 -0400 Subject: [PATCH 8/8] use deepcopy instead of .copy() --- figanos/matplotlib/plot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/figanos/matplotlib/plot.py b/figanos/matplotlib/plot.py index 917a6f0a..858f888e 100644 --- a/figanos/matplotlib/plot.py +++ b/figanos/matplotlib/plot.py @@ -1526,11 +1526,11 @@ def scattermap( if "row" not in plot_kw and "col" not in plot_kw: use_attrs.setdefault("title", "description") + plot_kw_pop = copy.deepcopy(plot_kw) # copy plot_kw to modify and pop info in it + # extract plot_kw from dict if needed if isinstance(data, dict) and plot_kw and list(data.keys())[0] in plot_kw.keys(): - plot_kw = plot_kw[list(data.keys())[0]] - - plot_kw_pop = plot_kw.copy() # copy plot_kw to modify and pop info in it + plot_kw_pop = plot_kw_pop[list(data.keys())[0]] # figanos does not use xr.plot.scatter default markersize if "markersize" in plot_kw.keys():