-
-
Notifications
You must be signed in to change notification settings - Fork 404
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added option for dimension range padding #2293
Changes from all commits
98aa9d3
5e69327
cdccca0
3d23908
5579510
6155d92
bb1b811
5e1f8d5
6df0dd5
cc93f81
7bb1e21
c71147b
a993089
6760bee
65ed72b
3ae07da
3dd5123
6b77306
041854e
ff09ee0
2df0529
e8d28cd
fed3a1f
0cbe021
020fbe1
f1e247d
bf10ed9
a180d46
fa15bd6
76e24e1
e0b9567
091d87e
e627820
ae0e03a
c7fe120
607568e
543a25d
bd3e4d5
262bea1
b4fb02b
b4a15a9
458c0a5
c50200a
bbb547b
acd80fb
3c92124
cbbe3fa
bc2b8ec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -810,36 +810,80 @@ def find_range(values, soft_range=[]): | |
return (None, None) | ||
|
||
|
||
def max_range(ranges): | ||
def is_finite(value): | ||
""" | ||
Safe check whether a value is finite, only None, NaN and inf | ||
values are considered non-finite and allows checking all types not | ||
restricted to numeric types. | ||
""" | ||
if value is None: | ||
return False | ||
try: | ||
return np.isfinite(value) | ||
except: | ||
return True | ||
|
||
|
||
def max_range(ranges, combined=True): | ||
""" | ||
Computes the maximal lower and upper bounds from a list bounds. | ||
""" | ||
try: | ||
with warnings.catch_warnings(): | ||
warnings.filterwarnings('ignore', r'All-NaN (slice|axis) encountered') | ||
values = [r for r in ranges for v in r if v is not None] | ||
values = [tuple(np.NaN if v is None else v for v in r) for r in ranges] | ||
if pd and all(isinstance(v, pd.Timestamp) for r in values for v in r): | ||
values = [(v1.to_datetime64(), v2.to_datetime64()) for v1, v2 in values] | ||
arr = np.array(values) | ||
if arr.dtype.kind in 'OSU': | ||
arr = list(python2sort([v for v in arr.flat if not is_nan(v) and v is not None])) | ||
if not len(arr): | ||
return np.NaN, np.NaN | ||
elif arr.dtype.kind in 'OSU': | ||
arr = list(python2sort([v for r in values for v in r if not is_nan(v) and v is not None])) | ||
return arr[0], arr[-1] | ||
if arr.dtype.kind in 'M': | ||
return arr[:, 0].min(), arr[:, 1].max() | ||
return (np.nanmin(arr[:, 0]), np.nanmax(arr[:, 1])) | ||
elif arr.dtype.kind in 'M': | ||
return (arr.min(), arr.max()) if combined else (arr[:, 0].min(), arr[:, 1].min()) | ||
if combined: | ||
return (np.nanmin(arr), np.nanmax(arr)) | ||
else: | ||
return (np.nanmin(arr[:, 0]), np.nanmax(arr[:, 1])) | ||
except: | ||
return (np.NaN, np.NaN) | ||
|
||
|
||
def dimension_range(lower, upper, dimension): | ||
def range_pad(lower, upper, padding=None, log=False): | ||
""" | ||
Pads the range by a fraction of the interval | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't spot any unit tests explicitly for these utilities. I know they should be tested indirectly by the more comprehensive unit tests in this PR but a couple of utility unit tests might be useful as examples of the expected input/output... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed, should be straightforward. |
||
""" | ||
if padding is not None and not isinstance(padding, tuple): | ||
padding = (padding, padding) | ||
if is_number(lower) and is_number(upper) and padding is not None: | ||
if not isinstance(lower, datetime_types) and log and lower > 0 and upper > 0: | ||
log_min = np.log(lower) / np.log(10) | ||
log_max = np.log(upper) / np.log(10) | ||
lspan = (log_max-log_min)*(1+padding[0]*2) | ||
uspan = (log_max-log_min)*(1+padding[1]*2) | ||
center = (log_min+log_max) / 2.0 | ||
start, end = np.power(10, center-lspan/2.), np.power(10, center+uspan/2.) | ||
else: | ||
span = (upper-lower) | ||
lpad = span*(padding[0]) | ||
upad = span*(padding[1]) | ||
start, end = lower-lpad, upper+upad | ||
else: | ||
start, end = lower, upper | ||
return start, end | ||
|
||
|
||
def dimension_range(lower, upper, hard_range, soft_range, padding=None, log=False): | ||
""" | ||
Computes the range along a dimension by combining the data range | ||
with the Dimension soft_range and range. | ||
""" | ||
lower, upper = max_range([(lower, upper), dimension.soft_range]) | ||
dmin, dmax = dimension.range | ||
lower = dmin if isfinite(dmin) else lower | ||
upper = dmax if isfinite(dmax) else upper | ||
lower, upper = range_pad(lower, upper, padding, log) | ||
lower, upper = max_range([(lower, upper), soft_range]) | ||
dmin, dmax = hard_range | ||
lower = lower if dmin is None or not np.isfinite(dmin) else dmin | ||
upper = upper if dmax is None or not np.isfinite(dmax) else dmax | ||
return lower, upper | ||
|
||
|
||
|
@@ -1035,6 +1079,7 @@ def dimension_sort(odict, kdims, vdims, key_index): | |
# Copied from param should make param version public | ||
def is_number(obj): | ||
if isinstance(obj, numbers.Number): return True | ||
elif isinstance(obj, (np.str_, np.unicode_)): return False | ||
# The extra check is for classes that behave like numbers, such as those | ||
# found in numpy, gmpy, etc. | ||
elif (hasattr(obj, '__int__') and hasattr(obj, '__add__')): return True | ||
|
@@ -1671,8 +1716,7 @@ def validate_regular_sampling(values, rtol=10e-6): | |
Returns a boolean indicating whether the sampling is regular. | ||
""" | ||
diffs = np.diff(values) | ||
vals = np.unique(diffs) | ||
return not (len(vals) > 1 and np.abs(vals.min()-vals.max()) > abs(diffs.min()*rtol)) | ||
return (len(diffs) < 1) or abs(diffs.min()-diffs.max()) < abs(diffs.min()*rtol) | ||
|
||
|
||
def compute_density(start, end, length, time_unit='us'): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,7 +61,7 @@ def get_batched_data(self, element, ranges=None): | |
data[k].extend(eld) | ||
return data, elmapping, style | ||
|
||
def get_extents(self, element, ranges=None): | ||
def get_extents(self, element, ranges=None, range_type='combined'): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure where this should go, but one of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Presumably the baseclass should document this in detail, the full list includes:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And there is 'soft' and 'hard'? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again..github doesn't show your comments when you post them! |
||
return None, None, None, None | ||
|
||
|
||
|
@@ -139,7 +139,7 @@ def _init_glyph(self, plot, mapping, properties): | |
plot.renderers.append(box) | ||
return None, box | ||
|
||
def get_extents(self, element, ranges=None): | ||
def get_extents(self, element, ranges=None, range_type='combined'): | ||
return None, None, None, None | ||
|
||
|
||
|
@@ -241,7 +241,7 @@ def _init_glyph(self, plot, mapping, properties, key): | |
plot.renderers.append(glyph) | ||
return None, glyph | ||
|
||
def get_extents(self, element, ranges=None): | ||
def get_extents(self, element, ranges=None, range_type='combined'): | ||
return None, None, None, None | ||
|
||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be good to update the docstring to explain this new flag.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, should be done here and in the Dimensioned baseclass at least.