|
52 | 52 | import six |
53 | 53 | from six.moves import map, zip |
54 | 54 |
|
| 55 | +import warnings |
55 | 56 | import re |
56 | 57 | import numpy as np |
57 | 58 | from numpy import ma |
@@ -1148,6 +1149,80 @@ def autoscale_None(self, A): |
1148 | 1149 | self._transform_vmin_vmax() |
1149 | 1150 |
|
1150 | 1151 |
|
| 1152 | +class PowerNorm(Normalize): |
| 1153 | + """ |
| 1154 | + Normalize a given value to the ``[0, 1]`` interval with a power-law |
| 1155 | + scaling. This will clip any negative data points to 0. |
| 1156 | + """ |
| 1157 | + def __init__(self, gamma, vmin=None, vmax=None, clip=False): |
| 1158 | + Normalize.__init__(self, vmin, vmax, clip) |
| 1159 | + self.gamma = gamma |
| 1160 | + |
| 1161 | + def __call__(self, value, clip=None): |
| 1162 | + if clip is None: |
| 1163 | + clip = self.clip |
| 1164 | + |
| 1165 | + result, is_scalar = self.process_value(value) |
| 1166 | + |
| 1167 | + self.autoscale_None(result) |
| 1168 | + gamma = self.gamma |
| 1169 | + vmin, vmax = self.vmin, self.vmax |
| 1170 | + if vmin > vmax: |
| 1171 | + raise ValueError("minvalue must be less than or equal to maxvalue") |
| 1172 | + elif vmin == vmax: |
| 1173 | + result.fill(0) |
| 1174 | + else: |
| 1175 | + if clip: |
| 1176 | + mask = ma.getmask(result) |
| 1177 | + val = ma.array(np.clip(result.filled(vmax), vmin, vmax), |
| 1178 | + mask=mask) |
| 1179 | + resdat = result.data |
| 1180 | + resdat -= vmin |
| 1181 | + np.power(resdat, gamma, resdat) |
| 1182 | + resdat /= (vmax - vmin) ** gamma |
| 1183 | + result = np.ma.array(resdat, mask=result.mask, copy=False) |
| 1184 | + result[value < 0] = 0 |
| 1185 | + if is_scalar: |
| 1186 | + result = result[0] |
| 1187 | + return result |
| 1188 | + |
| 1189 | + def inverse(self, value): |
| 1190 | + if not self.scaled(): |
| 1191 | + raise ValueError("Not invertible until scaled") |
| 1192 | + gamma = self.gamma |
| 1193 | + vmin, vmax = self.vmin, self.vmax |
| 1194 | + |
| 1195 | + if cbook.iterable(value): |
| 1196 | + val = ma.asarray(value) |
| 1197 | + return ma.power(value, 1. / gamma) * (vmax - vmin) + vmin |
| 1198 | + else: |
| 1199 | + return pow(value, 1. / gamma) * (vmax - vmin) + vmin |
| 1200 | + |
| 1201 | + def autoscale(self, A): |
| 1202 | + """ |
| 1203 | + Set *vmin*, *vmax* to min, max of *A*. |
| 1204 | + """ |
| 1205 | + self.vmin = ma.min(A) |
| 1206 | + if self.vmin < 0: |
| 1207 | + self.vmin = 0 |
| 1208 | + warnings.warn("Power-law scaling on negative values is " |
| 1209 | + "ill-defined, clamping to 0.") |
| 1210 | + |
| 1211 | + self.vmax = ma.max(A) |
| 1212 | + |
| 1213 | + def autoscale_None(self, A): |
| 1214 | + ' autoscale only None-valued vmin or vmax' |
| 1215 | + if self.vmin is None and np.size(A) > 0: |
| 1216 | + self.vmin = ma.min(A) |
| 1217 | + if self.vmin < 0: |
| 1218 | + self.vmin = 0 |
| 1219 | + warnings.warn("Power-law scaling on negative values is " |
| 1220 | + "ill-defined, clamping to 0.") |
| 1221 | + |
| 1222 | + if self.vmax is None and np.size(A) > 0: |
| 1223 | + self.vmax = ma.max(A) |
| 1224 | + |
| 1225 | + |
1151 | 1226 | class BoundaryNorm(Normalize): |
1152 | 1227 | """ |
1153 | 1228 | Generate a colormap index based on discrete intervals. |
|
0 commit comments