@@ -1024,7 +1024,8 @@ def _is_color_like(self, color):
1024
1024
return False
1025
1025
1026
1026
def add_2d_contours (self , root , param1 = None , param2 = None , plotno = 0 , of = None , cols = None , contour_levels = None ,
1027
- add_legend_proxy = True , param_pair = None , density = None , alpha = None , ax = None , ** kwargs ):
1027
+ add_legend_proxy = True , param_pair = None , density = None , alpha = None , ax = None ,
1028
+ mask_function : callable = None , ** kwargs ):
1028
1029
"""
1029
1030
Low-level function to add 2D contours to plot for samples with given root name and parameters
1030
1031
@@ -1043,6 +1044,9 @@ def add_2d_contours(self, root, param1=None, param2=None, plotno=0, of=None, col
1043
1044
:param alpha: alpha for the contours added
1044
1045
:param ax: optional :class:`~matplotlib:matplotlib.axes.Axes` instance (or y,x subplot coordinate)
1045
1046
to add to (defaults to current plot or the first/main plot if none)
1047
+ :param mask_function: optional function, mask_function(minx, miny, stepx, stepy, mask),
1048
+ which which sets mask to zero for values of parameter name that are excluded by prior.
1049
+ See the example in the plot gallery.
1046
1050
:param kwargs: optional keyword arguments:
1047
1051
1048
1052
- **filled**: True to make filled contours
@@ -1055,7 +1059,13 @@ def add_2d_contours(self, root, param1=None, param2=None, plotno=0, of=None, col
1055
1059
if density is None :
1056
1060
param1 , param2 = self .get_param_array (root , param_pair or [param1 , param2 ])
1057
1061
ax .getdist_params = (param1 , param2 )
1058
- if isinstance (root , MixtureND ):
1062
+ if mask_function is not None :
1063
+ samples = self .samples_for_root (root )
1064
+ density = samples .get2DDensityGridData (param1 .name , param2 .name ,
1065
+ mask_function = mask_function ,
1066
+ num_plot_contours = self .settings .num_plot_contours ,
1067
+ meanlikes = self .settings .shade_meanlikes )
1068
+ elif isinstance (root , MixtureND ):
1059
1069
density = root .marginalizedMixture (params = [param1 , param2 ]).density2D ()
1060
1070
else :
1061
1071
density = self .sample_analyser .get_density_grid (root , param1 , param2 ,
@@ -1086,6 +1096,7 @@ def add_2d_contours(self, root, param1=None, param2=None, plotno=0, of=None, col
1086
1096
def clean_args (_args ):
1087
1097
return {k : v for k , v in _args .items () if k not in ('color' , 'ls' , 'lw' )}
1088
1098
1099
+ z = density .P if density .mask is None else np .ma .masked_where (density .mask , density .P )
1089
1100
if kwargs .get ('filled' ):
1090
1101
if cols is None :
1091
1102
color = kwargs .get ('color' )
@@ -1098,13 +1109,13 @@ def clean_args(_args):
1098
1109
else :
1099
1110
cols = color
1100
1111
levels = sorted (np .append ([density .P .max () + 1 ], contour_levels ))
1101
- cs = ax .contourf (density .x , density .y , density . P , levels , colors = cols , alpha = alpha , ** clean_args (kwargs ))
1112
+ cs = ax .contourf (density .x , density .y , z , levels , colors = cols , alpha = alpha , ** clean_args (kwargs ))
1102
1113
1103
1114
fc = tuple (cs .to_rgba (cs .cvalues [- 1 ], cs .alpha ))
1104
1115
if proxy_ix >= 0 :
1105
1116
self .contours_added [proxy_ix ] = (
1106
1117
matplotlib .patches .Rectangle ((0 , 0 ), 1 , 1 , fc = fc ))
1107
- ax .contour (density .x , density .y , density . P , levels [:1 ], colors = (fc ,),
1118
+ ax .contour (density .x , density .y , z , levels [:1 ], colors = (fc ,),
1108
1119
linewidths = self ._scaled_linewidth (self .settings .linewidth_contour
1109
1120
if kwargs .get ('lw' ) is None else kwargs ['lw' ]),
1110
1121
linestyles = kwargs .get ('ls' ),
@@ -1116,7 +1127,7 @@ def clean_args(_args):
1116
1127
lws = args ['lw' ] # note linewidth_contour is only used for filled contours
1117
1128
kwargs = self ._get_plot_args (plotno , ** kwargs )
1118
1129
kwargs ['alpha' ] = alpha
1119
- cs = ax .contour (density .x , density .y , density . P , sorted (contour_levels ), colors = cols , linestyles = linestyles ,
1130
+ cs = ax .contour (density .x , density .y , z , sorted (contour_levels ), colors = cols , linestyles = linestyles ,
1120
1131
linewidths = lws , ** clean_args (kwargs ))
1121
1132
dashes = args .get ('dashes' )
1122
1133
if dashes :
@@ -1658,14 +1669,15 @@ def plot_1d(self, roots, param, marker=None, marker_color=None, label_right=Fals
1658
1669
self .finish_plot ()
1659
1670
1660
1671
def plot_2d (self , roots , param1 = None , param2 = None , param_pair = None , shaded = False ,
1661
- add_legend_proxy = True , line_offset = 0 , proxy_root_exclude = (), ax = None , ** kwargs ):
1672
+ add_legend_proxy = True , line_offset = 0 , proxy_root_exclude = (), ax = None ,
1673
+ mask_function : callable = None , ** kwargs ):
1662
1674
"""
1663
1675
Create a single 2D line, contour or filled plot.
1664
1676
1665
1677
:param roots: root name or :class:`~.mcsamples.MCSamples` instance (or list of any of either of these) for
1666
1678
the samples to plot
1667
1679
:param param1: x parameter name
1668
- :param param2: y parameter name
1680
+ :param param2: y parameter name
1669
1681
:param param_pair: An [x,y] pair of params; can be set instead of param1 and param2
1670
1682
:param shaded: True or integer if plot should be a shaded density plot, where the integer specifies
1671
1683
the index of which contour is shaded (first samples shaded if True provided instead
@@ -1675,6 +1687,15 @@ def plot_2d(self, roots, param1=None, param2=None, param_pair=None, shaded=False
1675
1687
:param proxy_root_exclude: any root names not to include when adding to the legend proxy
1676
1688
:param ax: optional :class:`~matplotlib:matplotlib.axes.Axes` instance (or y,x subplot coordinate)
1677
1689
to add to (defaults to current plot or the first/main plot if none)
1690
+ :param mask_function: Function that defines regions in the 2D parameter space to exclude from the plot.
1691
+ Must have signature mask_function(minx, miny, stepx, stepy, mask), where:
1692
+ - minx, miny: minimum values of x and y parameters
1693
+ - stepx, stepy: step sizes in x and y directions
1694
+ - mask: 2D boolean numpy array (modified in-place)
1695
+ The function should set mask values to 0 where points should be excluded by the prior.
1696
+ Useful for implementing non-rectangular prior boundaries not aligned with parameter axes,
1697
+ - see the example in the plot gallery.
1698
+ Note it should not include simple axis-aligned range priors that are accounted for automatically.
1678
1699
:param kwargs: additional optional arguments:
1679
1700
1680
1701
* **filled**: True for filled contours
@@ -1711,6 +1732,7 @@ def plot_2d(self, roots, param1=None, param2=None, param_pair=None, shaded=False
1711
1732
contour_args = self ._make_contour_args (len (roots ), ** kwargs )
1712
1733
for i , root in enumerate (roots ):
1713
1734
res = self .add_2d_contours (root , param_pair [0 ], param_pair [1 ], line_offset + i , of = len (roots ), ax = ax ,
1735
+ mask_function = mask_function ,
1714
1736
add_legend_proxy = add_legend_proxy and root not in proxy_root_exclude ,
1715
1737
** contour_args [i ])
1716
1738
xbounds , ybounds = self ._update_limits (res , xbounds , ybounds )
0 commit comments