From 03d8d2eb6058dbc9c2bcbf1d4c3833db1cf4529c Mon Sep 17 00:00:00 2001 From: kvyh Date: Mon, 8 Aug 2016 17:16:38 -0700 Subject: [PATCH 01/13] improved docstring and generalised _rescale_minmax --- astroplan/constraints.py | 51 ++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/astroplan/constraints.py b/astroplan/constraints.py index 364008dc..5cfc73de 100644 --- a/astroplan/constraints.py +++ b/astroplan/constraints.py @@ -345,7 +345,7 @@ def compute_constraint(self, times, observer, targets): mi = 1 if self.min is None else self.min # we reverse order so that airmass close to 1/min is good - return _rescale_airmass(secz, mi, mx) + return _rescale_minmax(secz, mi, mx, better_than=0, worse_than=0) class AtNightConstraint(Constraint): @@ -997,24 +997,35 @@ def observability_table(constraints, observer, targets, times=None, return tab -def _rescale_minmax(vals, min_val, max_val): - """ Rescale altitude into an observability score.""" - rescaled = (vals - min_val) / (max_val - min_val) - below = rescaled < 0 - above = rescaled > 1 - rescaled[below] = 0 - rescaled[above] = 1 - - return rescaled - +def _rescale_minmax(vals, worst_val, best_val, better_than=1, worse_than=0): + """ + rescales the input ``vals`` between 0 and 1 + Parameters + ---------- + vals : array of values + worst_val : value + worst acceptable value (rescales to 0) + best_val : value + best value cared about (rescales to 1) + better_than : 0 or 1 + What is returned for ``vals`` beyond than ``best_val`` + worse_than : 0 or 1 + What is returned for ``vals`` beyond than ``worst_val`` -def _rescale_airmass(vals, min_val, max_val): - """ Rescale airmass into an observability score.""" - rescaled = (vals - min_val) / (max_val - min_val) - below = rescaled < 0 - above = rescaled > 1 - # In both cases, we want out-of-range airmasses to return a 0 score - rescaled[below] = 1 - rescaled[above] = 1 + Returns + ------- + array of floats between 0 and 1 inclusive rescaled so that + ``vals`` equal to ``worst_val`` equal 0 and those equal to + ``best_val`` equal 1 + """ + rescaled = (vals - worst_val) / (best_val - worst_val) + if best_val - worst_val > 0: + worse = vals < worst_val + better = vals > best_val + else: + worse = vals < best_val + better = vals > worst_val + rescaled[worse] = worse_than + rescaled[better] = better_than - return 1 - rescaled + return rescaled From 0ddf6811a6ab29340c11ceb4d1bf47183f8c9c76 Mon Sep 17 00:00:00 2001 From: kvyh Date: Mon, 8 Aug 2016 17:28:17 -0700 Subject: [PATCH 02/13] fixed a few typos and formatting issues --- astroplan/constraints.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/astroplan/constraints.py b/astroplan/constraints.py index 5cfc73de..4385305c 100644 --- a/astroplan/constraints.py +++ b/astroplan/constraints.py @@ -345,7 +345,7 @@ def compute_constraint(self, times, observer, targets): mi = 1 if self.min is None else self.min # we reverse order so that airmass close to 1/min is good - return _rescale_minmax(secz, mi, mx, better_than=0, worse_than=0) + return _rescale_minmax(secz, mi, mx, better_than=0) class AtNightConstraint(Constraint): @@ -1000,17 +1000,19 @@ def observability_table(constraints, observer, targets, times=None, def _rescale_minmax(vals, worst_val, best_val, better_than=1, worse_than=0): """ rescales the input ``vals`` between 0 and 1 + Parameters ---------- vals : array of values + the values that need to be rescaled to be between 0 and 1 worst_val : value worst acceptable value (rescales to 0) best_val : value best value cared about (rescales to 1) better_than : 0 or 1 - What is returned for ``vals`` beyond than ``best_val`` + what is returned for ``vals`` beyond the ``best_val`` worse_than : 0 or 1 - What is returned for ``vals`` beyond than ``worst_val`` + what is returned for ``vals`` beyond the ``worst_val`` Returns ------- @@ -1029,3 +1031,5 @@ def _rescale_minmax(vals, worst_val, best_val, better_than=1, worse_than=0): rescaled[better] = better_than return rescaled + +_rescale_minmax() \ No newline at end of file From 1bee05bcbcd58a0fb0e0c21777be4d4020164283 Mon Sep 17 00:00:00 2001 From: kvyh Date: Mon, 8 Aug 2016 19:04:42 -0700 Subject: [PATCH 03/13] removed an extra line --- astroplan/constraints.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/astroplan/constraints.py b/astroplan/constraints.py index 4385305c..0dd75237 100644 --- a/astroplan/constraints.py +++ b/astroplan/constraints.py @@ -1031,5 +1031,3 @@ def _rescale_minmax(vals, worst_val, best_val, better_than=1, worse_than=0): rescaled[better] = better_than return rescaled - -_rescale_minmax() \ No newline at end of file From 0af7c43eb45ebb01fcf447d10648ebfebfc70a4b Mon Sep 17 00:00:00 2001 From: kvyh Date: Tue, 9 Aug 2016 11:25:41 -0700 Subject: [PATCH 04/13] added test and examples --- astroplan/constraints.py | 26 ++++++++++++++++++++++++-- astroplan/tests/test_constraints.py | 13 ++++++++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/astroplan/constraints.py b/astroplan/constraints.py index 0dd75237..8bbdb5a1 100644 --- a/astroplan/constraints.py +++ b/astroplan/constraints.py @@ -1019,14 +1019,36 @@ def _rescale_minmax(vals, worst_val, best_val, better_than=1, worse_than=0): array of floats between 0 and 1 inclusive rescaled so that ``vals`` equal to ``worst_val`` equal 0 and those equal to ``best_val`` equal 1 + + Examples + -------- + rescale altitude = [20, 30, 40, 45, 55, 70] to between + 0 and 1, with the best at 60->1 and worst at 35->0. 0 + below 35 and 1 above 60. + >>> from astroplan.constraints import _rescale_minmax + >>> import numpy as np + >>> airmasses = np.array([20, 30, 40, 45, 55, 70]) + >>> _rescale_minmax(airmasses, 35, 60) + array([ 0. , 0. , 0.2, 0.4, 0.8, 1. ]) + + rescale airmasses = [1,1,1.5,2,2,2,3,3,3,2,2,1] to between + 0 and 1, with the best at 1->1 and worst at 2.25->0. 0 beyond + 1 and 2.25. + >>> from astroplan.constraints import _rescale_minmax + >>> import numpy as np + >>> airmasses = np.array([1,1,1.5,2,2,2,3,3,3,2,2,1,0]) + >>> _rescale_minmax(airmasses, 2.25, 1, better_than = 0) + array([ 1. , 1. , 0.6, 0.2, 0.2, 0.2, 0. , 0. , 0. , + 0.2, 0.2, 1. , 0. ]) + """ rescaled = (vals - worst_val) / (best_val - worst_val) if best_val - worst_val > 0: worse = vals < worst_val better = vals > best_val else: - worse = vals < best_val - better = vals > worst_val + worse = vals > worst_val + better = vals < best_val rescaled[worse] = worse_than rescaled[better] = better_than diff --git a/astroplan/tests/test_constraints.py b/astroplan/tests/test_constraints.py index 6c26145b..24c9dec1 100644 --- a/astroplan/tests/test_constraints.py +++ b/astroplan/tests/test_constraints.py @@ -15,7 +15,8 @@ is_observable, is_always_observable, observability_table, time_grid_from_range, SunSeparationConstraint, MoonSeparationConstraint, MoonIlluminationConstraint, - TimeConstraint, LocalTimeConstraint, months_observable) + TimeConstraint, LocalTimeConstraint, months_observable, + _rescale_minmax) APY_LT104 = not minversion('astropy','1.0.4') @@ -369,6 +370,16 @@ def test_months_observable(): assert months == should_be +def test_rescale_minmax(): + a = np.array([2]) + rescaled = np.zeros(5) + rescaled[0] = (_rescale_minmax(a, 6, 1))[0] + rescaled[1] = (_rescale_minmax(a, 1, 6))[0] + rescaled[2] = (_rescale_minmax(a, 0, 1))[0] + rescaled[3] = (_rescale_minmax(a, 1, 0))[0] + rescaled[4] = (_rescale_minmax(a, 0, 1, better_than=0))[0] + assert all(np.array([0.8, 0.2, 1, 0, 0]) == rescaled) + constraint_tests = [ AltitudeConstraint(), AirmassConstraint(2), From 5aa82ed97b95d22fe4f09e297ba639d8a594a295 Mon Sep 17 00:00:00 2001 From: kvyh Date: Tue, 9 Aug 2016 12:42:04 -0700 Subject: [PATCH 05/13] reworded and simplified examples --- astroplan/constraints.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/astroplan/constraints.py b/astroplan/constraints.py index 8bbdb5a1..ea81f0a6 100644 --- a/astroplan/constraints.py +++ b/astroplan/constraints.py @@ -1022,24 +1022,24 @@ def _rescale_minmax(vals, worst_val, best_val, better_than=1, worse_than=0): Examples -------- - rescale altitude = [20, 30, 40, 45, 55, 70] to between - 0 and 1, with the best at 60->1 and worst at 35->0. 0 - below 35 and 1 above 60. + rescale an array of altitudes to be between 0 and 1, + with the best (60) going to 1 and worst (35) going to + 0. For values outside the range, the rescale should + return 0 below 35 and 1 above 60. >>> from astroplan.constraints import _rescale_minmax >>> import numpy as np - >>> airmasses = np.array([20, 30, 40, 45, 55, 70]) - >>> _rescale_minmax(airmasses, 35, 60) + >>> altitudes = np.array([20, 30, 40, 45, 55, 70]) + >>> _rescale_minmax(altitudes, 35, 60) array([ 0. , 0. , 0.2, 0.4, 0.8, 1. ]) - rescale airmasses = [1,1,1.5,2,2,2,3,3,3,2,2,1] to between - 0 and 1, with the best at 1->1 and worst at 2.25->0. 0 beyond - 1 and 2.25. + rescale airmasses to between 0 and 1, with the best (1) + and worst (2.25). All values outside the range should + return 0. >>> from astroplan.constraints import _rescale_minmax >>> import numpy as np - >>> airmasses = np.array([1,1,1.5,2,2,2,3,3,3,2,2,1,0]) + >>> airmasses = np.array([1, 1.5, 2, 3, 0]) >>> _rescale_minmax(airmasses, 2.25, 1, better_than = 0) - array([ 1. , 1. , 0.6, 0.2, 0.2, 0.2, 0. , 0. , 0. , - 0.2, 0.2, 1. , 0. ]) + array([ 1. , 0.6, 0.2, 0. , 0. ]) """ rescaled = (vals - worst_val) / (best_val - worst_val) From 23ed9f6fc34679ae1e07a758ef67f49e76521ce9 Mon Sep 17 00:00:00 2001 From: kvyh Date: Fri, 12 Aug 2016 15:59:36 -0700 Subject: [PATCH 06/13] changing rescale_minmax to not be private --- astroplan/constraints.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/astroplan/constraints.py b/astroplan/constraints.py index ea81f0a6..4242bae3 100644 --- a/astroplan/constraints.py +++ b/astroplan/constraints.py @@ -28,7 +28,8 @@ "is_observable", "is_always_observable", "time_grid_from_range", "SunSeparationConstraint", "MoonSeparationConstraint", "MoonIlluminationConstraint", "LocalTimeConstraint", "Constraint", - "TimeConstraint", "observability_table", "months_observable"] + "TimeConstraint", "observability_table", "months_observable", + "rescale_minmax"] def _get_altaz(times, observer, targets, @@ -286,7 +287,7 @@ def compute_constraint(self, times, observer, targets): uppermask = alt <= self.max return lowermask & uppermask else: - return _rescale_minmax(alt, self.min, self.max) + return rescale_minmax(alt, self.min, self.max) class AirmassConstraint(AltitudeConstraint): @@ -345,7 +346,7 @@ def compute_constraint(self, times, observer, targets): mi = 1 if self.min is None else self.min # we reverse order so that airmass close to 1/min is good - return _rescale_minmax(secz, mi, mx, better_than=0) + return rescale_minmax(secz, mi, mx, better_than=0) class AtNightConstraint(Constraint): @@ -997,9 +998,9 @@ def observability_table(constraints, observer, targets, times=None, return tab -def _rescale_minmax(vals, worst_val, best_val, better_than=1, worse_than=0): +def rescale_minmax(vals, worst_val, best_val, better_than=1, worse_than=0): """ - rescales the input ``vals`` between 0 and 1 + rescales an input array ``vals`` between zero and one Parameters ---------- @@ -1026,19 +1027,19 @@ def _rescale_minmax(vals, worst_val, best_val, better_than=1, worse_than=0): with the best (60) going to 1 and worst (35) going to 0. For values outside the range, the rescale should return 0 below 35 and 1 above 60. - >>> from astroplan.constraints import _rescale_minmax + >>> from astroplan.constraints import rescale_minmax >>> import numpy as np >>> altitudes = np.array([20, 30, 40, 45, 55, 70]) - >>> _rescale_minmax(altitudes, 35, 60) + >>> rescale_minmax(altitudes, 35, 60) array([ 0. , 0. , 0.2, 0.4, 0.8, 1. ]) rescale airmasses to between 0 and 1, with the best (1) and worst (2.25). All values outside the range should return 0. - >>> from astroplan.constraints import _rescale_minmax + >>> from astroplan.constraints import rescale_minmax >>> import numpy as np >>> airmasses = np.array([1, 1.5, 2, 3, 0]) - >>> _rescale_minmax(airmasses, 2.25, 1, better_than = 0) + >>> rescale_minmax(airmasses, 2.25, 1, better_than = 0) array([ 1. , 0.6, 0.2, 0. , 0. ]) """ From c2ec0ce61315840dcfbace23377b16f1700ac5aa Mon Sep 17 00:00:00 2001 From: kvyh Date: Sat, 13 Aug 2016 08:12:03 -0700 Subject: [PATCH 07/13] updated the test to the new function name --- astroplan/tests/test_constraints.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/astroplan/tests/test_constraints.py b/astroplan/tests/test_constraints.py index 24c9dec1..184aa9c4 100644 --- a/astroplan/tests/test_constraints.py +++ b/astroplan/tests/test_constraints.py @@ -16,7 +16,7 @@ time_grid_from_range, SunSeparationConstraint, MoonSeparationConstraint, MoonIlluminationConstraint, TimeConstraint, LocalTimeConstraint, months_observable, - _rescale_minmax) + rescale_minmax) APY_LT104 = not minversion('astropy','1.0.4') @@ -373,11 +373,11 @@ def test_months_observable(): def test_rescale_minmax(): a = np.array([2]) rescaled = np.zeros(5) - rescaled[0] = (_rescale_minmax(a, 6, 1))[0] - rescaled[1] = (_rescale_minmax(a, 1, 6))[0] - rescaled[2] = (_rescale_minmax(a, 0, 1))[0] - rescaled[3] = (_rescale_minmax(a, 1, 0))[0] - rescaled[4] = (_rescale_minmax(a, 0, 1, better_than=0))[0] + rescaled[0] = (rescale_minmax(a, 6, 1))[0] + rescaled[1] = (rescale_minmax(a, 1, 6))[0] + rescaled[2] = (rescale_minmax(a, 0, 1))[0] + rescaled[3] = (rescale_minmax(a, 1, 0))[0] + rescaled[4] = (rescale_minmax(a, 0, 1, better_than=0))[0] assert all(np.array([0.8, 0.2, 1, 0, 0]) == rescaled) constraint_tests = [ From a4653b44dfb8ed327b5c55ee60d26d76ca21a225 Mon Sep 17 00:00:00 2001 From: kvyh Date: Mon, 15 Aug 2016 12:53:34 -0700 Subject: [PATCH 08/13] removed empty line --- astroplan/constraints.py | 1 - 1 file changed, 1 deletion(-) diff --git a/astroplan/constraints.py b/astroplan/constraints.py index 4242bae3..9aba194a 100644 --- a/astroplan/constraints.py +++ b/astroplan/constraints.py @@ -1041,7 +1041,6 @@ def rescale_minmax(vals, worst_val, best_val, better_than=1, worse_than=0): >>> airmasses = np.array([1, 1.5, 2, 3, 0]) >>> rescale_minmax(airmasses, 2.25, 1, better_than = 0) array([ 1. , 0.6, 0.2, 0. , 0. ]) - """ rescaled = (vals - worst_val) / (best_val - worst_val) if best_val - worst_val > 0: From c4f26a89cfe2c197944150c8840eeb706774d7c9 Mon Sep 17 00:00:00 2001 From: kvyh Date: Wed, 17 Aug 2016 12:47:55 -0700 Subject: [PATCH 09/13] splitting rescale into two functions --- astroplan/constraints.py | 100 ++++++++++++++++++---------- astroplan/tests/test_constraints.py | 12 ++-- 2 files changed, 72 insertions(+), 40 deletions(-) diff --git a/astroplan/constraints.py b/astroplan/constraints.py index 9aba194a..799aebdf 100644 --- a/astroplan/constraints.py +++ b/astroplan/constraints.py @@ -29,7 +29,7 @@ "SunSeparationConstraint", "MoonSeparationConstraint", "MoonIlluminationConstraint", "LocalTimeConstraint", "Constraint", "TimeConstraint", "observability_table", "months_observable", - "rescale_minmax"] + "max_best_rescale", "min_best_rescale"] def _get_altaz(times, observer, targets, @@ -287,7 +287,7 @@ def compute_constraint(self, times, observer, targets): uppermask = alt <= self.max return lowermask & uppermask else: - return rescale_minmax(alt, self.min, self.max) + return max_best_rescale(alt, self.min, self.max) class AirmassConstraint(AltitudeConstraint): @@ -345,8 +345,8 @@ def compute_constraint(self, times, observer, targets): mx = self.max mi = 1 if self.min is None else self.min - # we reverse order so that airmass close to 1/min is good - return rescale_minmax(secz, mi, mx, better_than=0) + # values below 1 should be disregarded + return min_best_rescale(secz, mi, mx, less_than=0) class AtNightConstraint(Constraint): @@ -998,28 +998,73 @@ def observability_table(constraints, observer, targets, times=None, return tab -def rescale_minmax(vals, worst_val, best_val, better_than=1, worse_than=0): +def min_best_rescale(vals, min_val, max_val, greater_than=0, less_than=1): """ - rescales an input array ``vals`` between zero and one + rescales an input array ``vals`` between zero and one, + where the ``min_val`` is the best. Parameters ---------- vals : array of values the values that need to be rescaled to be between 0 and 1 - worst_val : value + min_val : value worst acceptable value (rescales to 0) - best_val : value + max_val : value best value cared about (rescales to 1) - better_than : 0 or 1 - what is returned for ``vals`` beyond the ``best_val`` - worse_than : 0 or 1 - what is returned for ``vals`` beyond the ``worst_val`` + greater_than : 0 or 1 + what is returned for ``vals`` above max_val + less_than : 0 or 1 + what is returned for ``vals`` below min_val Returns ------- array of floats between 0 and 1 inclusive rescaled so that - ``vals`` equal to ``worst_val`` equal 0 and those equal to - ``best_val`` equal 1 + ``vals`` equal to ``max_val`` equal 0 and those equal to + ``min_val`` equal 1 + + Examples + -------- + rescale airmasses to between 0 and 1, with the best (1) + and worst (2.25). All values outside the range should + return 0. + >>> from astroplan.constraints import min_best_rescale + >>> import numpy as np + >>> airmasses = np.array([1, 1.5, 2, 3, 0]) + >>> min_best_rescale(airmasses, 1, 2.25, less_than = 0) + array([ 1. , 0.6, 0.2, 0. , 0. ]) + """ + rescaled = (vals - max_val) / (min_val - max_val) + below = vals < min_val + above = vals > max_val + rescaled[below] = less_than + rescaled[above] = greater_than + + return rescaled + + +def max_best_rescale(vals, min_val, max_val, greater_than=1, less_than=0): + """ + rescales an input array ``vals`` between zero and one, + where the ``max_val`` is the best. + + Parameters + ---------- + vals : array of values + the values that need to be rescaled to be between 0 and 1 + min_val : value + worst acceptable value (rescales to 0) + max_val : value + best value cared about (rescales to 1) + greater_than : 0 or 1 + what is returned for ``vals`` above max_val + less_than : 0 or 1 + what is returned for ``vals`` below min_val + + Returns + ------- + array of floats between 0 and 1 inclusive rescaled so that + ``vals`` equal to ``min_val`` equal 0 and those equal to + ``max_val`` equal 1 Examples -------- @@ -1027,29 +1072,16 @@ def rescale_minmax(vals, worst_val, best_val, better_than=1, worse_than=0): with the best (60) going to 1 and worst (35) going to 0. For values outside the range, the rescale should return 0 below 35 and 1 above 60. - >>> from astroplan.constraints import rescale_minmax + >>> from astroplan.constraints import max_best_rescale >>> import numpy as np >>> altitudes = np.array([20, 30, 40, 45, 55, 70]) - >>> rescale_minmax(altitudes, 35, 60) + >>> max_best_rescale(altitudes, 35, 60) array([ 0. , 0. , 0.2, 0.4, 0.8, 1. ]) - - rescale airmasses to between 0 and 1, with the best (1) - and worst (2.25). All values outside the range should - return 0. - >>> from astroplan.constraints import rescale_minmax - >>> import numpy as np - >>> airmasses = np.array([1, 1.5, 2, 3, 0]) - >>> rescale_minmax(airmasses, 2.25, 1, better_than = 0) - array([ 1. , 0.6, 0.2, 0. , 0. ]) """ - rescaled = (vals - worst_val) / (best_val - worst_val) - if best_val - worst_val > 0: - worse = vals < worst_val - better = vals > best_val - else: - worse = vals > worst_val - better = vals < best_val - rescaled[worse] = worse_than - rescaled[better] = better_than + rescaled = (vals - min_val) / (max_val - min_val) + below = vals < min_val + above = vals > max_val + rescaled[below] = less_than + rescaled[above] = greater_than return rescaled diff --git a/astroplan/tests/test_constraints.py b/astroplan/tests/test_constraints.py index 184aa9c4..b0ec9dc8 100644 --- a/astroplan/tests/test_constraints.py +++ b/astroplan/tests/test_constraints.py @@ -16,7 +16,7 @@ time_grid_from_range, SunSeparationConstraint, MoonSeparationConstraint, MoonIlluminationConstraint, TimeConstraint, LocalTimeConstraint, months_observable, - rescale_minmax) + max_best_rescale, min_best_rescale) APY_LT104 = not minversion('astropy','1.0.4') @@ -373,11 +373,11 @@ def test_months_observable(): def test_rescale_minmax(): a = np.array([2]) rescaled = np.zeros(5) - rescaled[0] = (rescale_minmax(a, 6, 1))[0] - rescaled[1] = (rescale_minmax(a, 1, 6))[0] - rescaled[2] = (rescale_minmax(a, 0, 1))[0] - rescaled[3] = (rescale_minmax(a, 1, 0))[0] - rescaled[4] = (rescale_minmax(a, 0, 1, better_than=0))[0] + rescaled[0] = (min_best_rescale(a, 1, 6))[0] + rescaled[1] = (max_best_rescale(a, 1, 6))[0] + rescaled[2] = (max_best_rescale(a, 0, 1))[0] + rescaled[3] = (min_best_rescale(a, 0, 1))[0] + rescaled[4] = (max_best_rescale(a, 0, 1, greater_than=0))[0] assert all(np.array([0.8, 0.2, 1, 0, 0]) == rescaled) constraint_tests = [ From 5a1e59224f35c7429475fc8a727d4766561800c6 Mon Sep 17 00:00:00 2001 From: kvyh Date: Thu, 18 Aug 2016 17:34:28 -0700 Subject: [PATCH 10/13] added score to the docstring and removed the unnecessary parameter --- astroplan/constraints.py | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/astroplan/constraints.py b/astroplan/constraints.py index 799aebdf..d78e8af0 100644 --- a/astroplan/constraints.py +++ b/astroplan/constraints.py @@ -346,7 +346,7 @@ def compute_constraint(self, times, observer, targets): mi = 1 if self.min is None else self.min # values below 1 should be disregarded - return min_best_rescale(secz, mi, mx, less_than=0) + return min_best_rescale(secz, mi, mx, less_than_min=0) class AtNightConstraint(Constraint): @@ -998,10 +998,10 @@ def observability_table(constraints, observer, targets, times=None, return tab -def min_best_rescale(vals, min_val, max_val, greater_than=0, less_than=1): +def min_best_rescale(vals, min_val, max_val, less_than_min=1): """ - rescales an input array ``vals`` between zero and one, - where the ``min_val`` is the best. + rescales an input array ``vals`` to be a score (between zero and one), + where the ``min_val`` goes to one, and the ``max_val`` goes to zero. Parameters ---------- @@ -1011,10 +1011,10 @@ def min_best_rescale(vals, min_val, max_val, greater_than=0, less_than=1): worst acceptable value (rescales to 0) max_val : value best value cared about (rescales to 1) - greater_than : 0 or 1 - what is returned for ``vals`` above max_val - less_than : 0 or 1 - what is returned for ``vals`` below min_val + less_than_min : 0 or 1 + what is returned for ``vals`` below min_val. (in some cases + anything less than ``min_val`` should also return one, + in some cases it should return zero) Returns ------- @@ -1030,22 +1030,22 @@ def min_best_rescale(vals, min_val, max_val, greater_than=0, less_than=1): >>> from astroplan.constraints import min_best_rescale >>> import numpy as np >>> airmasses = np.array([1, 1.5, 2, 3, 0]) - >>> min_best_rescale(airmasses, 1, 2.25, less_than = 0) + >>> min_best_rescale(airmasses, 1, 2.25, less_than_min = 0) array([ 1. , 0.6, 0.2, 0. , 0. ]) """ rescaled = (vals - max_val) / (min_val - max_val) below = vals < min_val above = vals > max_val - rescaled[below] = less_than - rescaled[above] = greater_than + rescaled[below] = less_than_min + rescaled[above] = 0 return rescaled -def max_best_rescale(vals, min_val, max_val, greater_than=1, less_than=0): +def max_best_rescale(vals, min_val, max_val, greater_than_max=1): """ - rescales an input array ``vals`` between zero and one, - where the ``max_val`` is the best. + rescales an input array ``vals`` to be a score (between zero and one), + where the ``max_val`` goes to one, and the ``min_val`` goes to zero. Parameters ---------- @@ -1055,10 +1055,10 @@ def max_best_rescale(vals, min_val, max_val, greater_than=1, less_than=0): worst acceptable value (rescales to 0) max_val : value best value cared about (rescales to 1) - greater_than : 0 or 1 - what is returned for ``vals`` above max_val - less_than : 0 or 1 - what is returned for ``vals`` below min_val + greater_than_max : 0 or 1 + what is returned for ``vals`` above max_val. (in some cases + anything higher than ``max_val`` should also return one, + in some cases it should return zero) Returns ------- @@ -1081,7 +1081,7 @@ def max_best_rescale(vals, min_val, max_val, greater_than=1, less_than=0): rescaled = (vals - min_val) / (max_val - min_val) below = vals < min_val above = vals > max_val - rescaled[below] = less_than - rescaled[above] = greater_than + rescaled[below] = 0 + rescaled[above] = greater_than_max return rescaled From 44f8c99ff51842da005ca50b8dbe1a754dd866c9 Mon Sep 17 00:00:00 2001 From: kvyh Date: Fri, 19 Aug 2016 17:05:47 -0700 Subject: [PATCH 11/13] adding non-boolean to the user-defined constraint --- docs/tutorials/constraints.rst | 87 ++++++++++++++++++++++++++-------- 1 file changed, 67 insertions(+), 20 deletions(-) diff --git a/docs/tutorials/constraints.rst b/docs/tutorials/constraints.rst index 48f4f95e..e3bc3267 100644 --- a/docs/tutorials/constraints.rst +++ b/docs/tutorials/constraints.rst @@ -242,16 +242,21 @@ be within some angular separation from Vega – we'll call it ``compute_constraints`` method when you check if a target is observable using `~astroplan.is_observable` or `~astroplan.is_always_observable`. +* We also want to provide the option of having the constraint output + a non-boolean score. Where being closer to the minimum separation + returns a higher score than being closer to the maximum separation. + Here's our ``VegaSeparationConstraint`` implementation:: - from astroplan import Constraint, is_observable + from astroplan import Constraint, is_observable, min_best_rescale from astropy.coordinates import Angle + import astropy.units as u class VegaSeparationConstraint(Constraint): """ Constraint the separation from Vega """ - def __init__(self, min=None, max=None): + def __init__(self, min=None, max=None, boolean_constraint=True): """ min : `~astropy.units.Quantity` or `None` (optional) Minimum acceptable separation between Vega and target. `None` @@ -262,6 +267,7 @@ Here's our ``VegaSeparationConstraint`` implementation:: """ self.min = min self.max = max + self.boolean_constraint = boolean_constraint def compute_constraint(self, times, observer, targets): @@ -273,31 +279,51 @@ Here's our ``VegaSeparationConstraint`` implementation:: # Calculate separation between target and vega vega_separation = Angle([vega.separation(target.coord) for target in targets]) + if self.boolean_constraint: + # If a maximum is specified but no minimum + if self.min is None and self.max is not None: + mask = vega_separation < self.max - # If a maximum is specified but no minimum - if self.min is None and self.max is not None: - mask = vega_separation < self.max + # If a minimum is specified but no maximum + elif self.max is None and self.min is not None: + mask = self.min < vega_separation - # If a minimum is specified but no maximum - elif self.max is None and self.min is not None: - mask = self.min < vega_separation + # If both a minimum and a maximum are specified + elif self.min is not None and self.max is not None: + mask = ((self.min < vega_separation) & (vega_separation < self.max)) - # If both a minimum and a maximum are specified - elif self.min is not None and self.max is not None: - mask = ((self.min < vega_separation) & (vega_separation < self.max)) + # Otherwise, raise an error + else: + raise ValueError("No max and/or min specified in " + "VegaSeparationConstraint.") - # Otherwise, raise an error - else: - raise ValueError("No max and/or min specified in " - "VegaSeparationConstraint.") + # Return an array that is True where the target is observable and + # False where it is not + # Must have shape (len(targets), len(times)) - # Return an array that is True where the target is observable and - # False where it is not - # Must have shape (len(targets), len(times)) + # currently mask has shape (len(targets), 1) + return np.tile(mask, len(times)) + + # if we want to return a non-boolean score + else: + # no min and no max still should error + if self.min is None and self.max is None: + raise ValueError("No max and/or min specified in " + "VegaSeparationConstraint.") + if self.min is None: + # if no minimum is given, set it at 0 degrees + self.min = 0*u.deg + if self.max is None: + # if no maximum is given, set it to 180 degrees + self.max = 180*u.deg + + # rescale the vega_separation values so that they become + # scores between zero and one + rescale = min_best_rescale(vega_separation, self.min, + self.max, less_than_min=0) + return np.tile(rescale, len(times)) - # currently mask has shape (len(targets), 1) - return np.tile(mask, len(times)) Then as in the earlier example, we can call our constraint:: @@ -310,3 +336,24 @@ Then as in the earlier example, we can call our constraint:: The resulting list of booleans indicates that the only target separated by 5 and 30 degrees from Vega is Albireo. Following this pattern, you can design arbitrarily complex criteria for constraints. + +To see the (target x time) array for the non-boolean score:: + + >>> constraint = VegaSeparationConstraint(min=5*u.deg, max=30*u.deg, + ... boolean_constraint=False) + >>> print(constraint(subaru, targets, time_range=time_range) + [[ 0. 0. 0. 0. 0. 0. 0. + 0. 0. 0. 0. 0. ] + [ 0. 0. 0. 0. 0. 0. 0. + 0. 0. 0. 0. 0. ] + [ 0.57748686 0.57748686 0.57748686 0.57748686 0.57748686 0.57748686 + 0.57748686 0.57748686 0.57748686 0.57748686 0.57748686 0.57748686] + [ 0. 0. 0. 0. 0. 0. 0. + 0. 0. 0. 0. 0. ] + [ 0. 0. 0. 0. 0. 0. 0. + 0. 0. 0. 0. 0. ] + [ 0. 0. 0. 0. 0. 0. 0. + 0. 0. 0. 0. 0. ]] + +The score of .5775 for Albireo indicates that it is slightly closer to +the 5 degree minimum than to the 30 degree maximum. From 4cbc55b46cb5a6302021743faf06ef76a84e54e5 Mon Sep 17 00:00:00 2001 From: kvyh Date: Fri, 19 Aug 2016 20:00:39 -0700 Subject: [PATCH 12/13] fixed an incorrect parameter --- astroplan/tests/test_constraints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/astroplan/tests/test_constraints.py b/astroplan/tests/test_constraints.py index b0ec9dc8..b5873db7 100644 --- a/astroplan/tests/test_constraints.py +++ b/astroplan/tests/test_constraints.py @@ -377,7 +377,7 @@ def test_rescale_minmax(): rescaled[1] = (max_best_rescale(a, 1, 6))[0] rescaled[2] = (max_best_rescale(a, 0, 1))[0] rescaled[3] = (min_best_rescale(a, 0, 1))[0] - rescaled[4] = (max_best_rescale(a, 0, 1, greater_than=0))[0] + rescaled[4] = (max_best_rescale(a, 0, 1, greater_than_max=0))[0] assert all(np.array([0.8, 0.2, 1, 0, 0]) == rescaled) constraint_tests = [ From 0252eb1d451220fb5062ad8d404845bc2b9926a5 Mon Sep 17 00:00:00 2001 From: kvyh Date: Tue, 23 Aug 2016 12:31:41 -0700 Subject: [PATCH 13/13] clarifying docstring --- astroplan/constraints.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/astroplan/constraints.py b/astroplan/constraints.py index d78e8af0..ea8db24d 100644 --- a/astroplan/constraints.py +++ b/astroplan/constraints.py @@ -1005,14 +1005,14 @@ def min_best_rescale(vals, min_val, max_val, less_than_min=1): Parameters ---------- - vals : array of values + vals : array-like the values that need to be rescaled to be between 0 and 1 - min_val : value + min_val : float worst acceptable value (rescales to 0) - max_val : value + max_val : float best value cared about (rescales to 1) less_than_min : 0 or 1 - what is returned for ``vals`` below min_val. (in some cases + what is returned for ``vals`` below ``min_val``. (in some cases anything less than ``min_val`` should also return one, in some cases it should return zero) @@ -1049,14 +1049,14 @@ def max_best_rescale(vals, min_val, max_val, greater_than_max=1): Parameters ---------- - vals : array of values + vals : array-like the values that need to be rescaled to be between 0 and 1 - min_val : value + min_val : float worst acceptable value (rescales to 0) - max_val : value + max_val : float best value cared about (rescales to 1) greater_than_max : 0 or 1 - what is returned for ``vals`` above max_val. (in some cases + what is returned for ``vals`` above ``max_val``. (in some cases anything higher than ``max_val`` should also return one, in some cases it should return zero)