class DateRangeSlider(_SliderBase): value = param.Tuple(default=(None, None), length=2) value_throttled = param.Tuple(default=None, length=2, constant=True) start = param.Date(default=None) end = param.Date(default=None) step = param.Number(default=1) _source_transforms = {'value': None, 'value_throttled': None, 'start': None, 'end': None, 'step': None} _widget_type = _BkDateRangeSlider def __init__(self, **params): if 'value' not in params: params['value'] = (params.get('start', self.start), params.get('end', self.end)) super(DateRangeSlider, self).__init__(**params) def _process_property_change(self, msg): msg = super(DateRangeSlider, self)._process_property_change(msg) if 'value' in msg: v1, v2 = msg['value'] msg['value'] = (value_as_datetime(v1), value_as_datetime(v2)) if 'value_throttled' in msg: v1, v2 = msg['value_throttled'] msg['value_throttled'] = (value_as_datetime(v1), value_as_datetime(v2)) return msg
class DateSlider(_SliderBase): value = param.Date(default=None) value_throttled = param.Date(default=None, constant=True) start = param.Date(default=None) end = param.Date(default=None) _source_transforms = { 'value': None, 'value_throttled': None, 'start': None, 'end': None } _widget_type = _BkDateSlider def __init__(self, **params): if 'value' not in params: params['value'] = params.get('start', self.start) super(DateSlider, self).__init__(**params) def _process_property_change(self, msg): msg = super(_SliderBase, self)._process_property_change(msg) if 'value' in msg: msg['value'] = value_as_date(msg['value']) if 'value_throttled' in msg: msg['value_throttled'] = value_as_date(msg['value_throttled']) return msg
class DatetimeRangeInput(CompositeWidget): value = param.Tuple(default=(None, None), length=2) start = param.Date(default=None) end = param.Date(default=None) format = param.String(default='%Y-%m-%d %H:%M:%S', doc=""" Datetime format used for parsing and formatting the datetime.""") _composite_type = Column def __init__(self, **params): self._text = StaticText(margin=(5, 0, 0, 0), style={'white-space': 'nowrap'}) self._start = DatetimeInput(sizing_mode='stretch_width', margin=(5, 0, 0, 0)) self._end = DatetimeInput(sizing_mode='stretch_width', margin=(5, 0, 0, 0)) if 'value' not in params: params['value'] = (params['start'], params['end']) super().__init__(**params) self._msg = '' self._composite.extend([self._text, self._start, self._end]) self._updating = False self._update_widgets() self._update_label() @param.depends('name', '_start.name', '_end.name', watch=True) def _update_label(self): self._text.value = f'{self.name}{self._start.name}{self._end.name}{self._msg}' @param.depends('_start.value', '_end.value', watch=True) def _update(self): if self._updating: return if (self._start.value is not None and self._end.value is not None and self._start.value > self._end.value): self._msg = ' (start of range must be <= end)' self._update_label() return elif self._msg: self._msg = '' self._update_label() try: self._updating = True self.value = (self._start.value, self._end.value) finally: self._updating = False @param.depends('value', 'start', 'end', 'name', 'format', watch=True) def _update_widgets(self): if self._updating: return try: self._updating = True self._start.param.set_param(value=self.value[0], start=self.start, end=self.end, format=self.format) self._end.param.set_param(value=self.value[1], start=self.start, end=self.end, format=self.format) finally: self._updating = False
class DatetimeInput(LiteralInput): """ DatetimeInput allows declaring Python literals using a text input widget. Optionally a type may be declared. """ format = param.String(default='%Y-%m-%d %H:%M:%S', doc=""" Datetime format used for parsing and formatting the datetime.""") value = param.Date(default=None) start = param.Date(default=None) end = param.Date(default=None) type = datetime def __init__(self, **params): super(DatetimeInput, self).__init__(**params) if self.value is not None and ((self.start is not None and self.start < self.value) or (self.end is not None and self.end < self.value)): value = datetime.strftime(self.value, self.format) start = datetime.strftime(self.start, self.format) end = datetime.strftime(self.end, self.format) raise ValueError('DatetimeInput value must be between {start} and {end}, ' 'supplied value is {value}'.format(start=start, end=end, value=value)) def _process_property_change(self, msg): msg = Widget._process_property_change(self, msg) new_state = '' if 'value' in msg: value = msg.pop('value') try: value = datetime.strptime(value, self.format) except: new_state = ' (invalid)' value = self.value else: if value is not None and ((self.start is not None and self.start > value) or (self.end is not None and self.end < value)): new_state = ' (out of bounds)' value = self.value msg['value'] = value msg['name'] = msg.get('title', self.name).replace(self._state, '') + new_state self._state = new_state return msg def _process_param_change(self, msg): msg = {k: v for k, v in msg.items() if k not in ('type', 'format', 'start', 'end')} if 'value' in msg: value = msg['value'] if value is None: value = '' else: value = datetime.strftime(msg['value'], self.format) msg['value'] = value msg['title'] = self.name return msg
class DateSlider(_SliderBase): value = param.Date(default=None) start = param.Date(default=None) end = param.Date(default=None) _widget_type = _BkDateSlider
class TimePeriodServiceBase(ServiceBase): start = param.Date(default=None, precedence=2, doc='start date') end = param.Date(default=None, precedence=3, doc='end date') @property def start_string(self): return self.start.strftime('%Y-%m-%d') @property def end_string(self): return self.end.strftime('%Y-%m-%d')
class DateSlider(_SliderBase): value = param.Date(default=None) start = param.Date(default=None) end = param.Date(default=None) _widget_type = _BkDateSlider def _process_property_change(self, msg): msg = super(_SliderBase, self)._process_property_change(msg) if 'value' in msg: msg['value'] = value_as_date(msg['value']) return msg
class StockScreener(param.Parameterized): df = param.DataFrame(precedence=-1) index = param.ListSelector() normalize = param.Boolean(default=True) start = param.Date() def __init__(self, df, **params): start = dt.date(year=df.index[0].year, month=df.index[0].month, day=df.index[0].day) end = dt.date(year=df.index[-1].year, month=df.index[-1].month, day=df.index[-1].day) super(StockScreener, self).__init__(df=df, start=start, **params) self.param.start.bounds = (start, end) columns = list(self.df.columns) self.param.index.objects = columns self.index = columns[:5] @param.depends('index', 'normalize', 'start') def update_plot(self): pos = self.df.index.get_loc(self.start, method='bfill') dfp = self.df.iloc[pos:][self.index] if self.normalize: dfp = 100 * dfp / dfp.iloc[0] return dfp.hvplot(group_label='Ticker') def panel(self): return pn.Row( pn.panel(self.param, widgets={'start': pn.widgets.DateSlider}), self.update_plot)
class TestSet(param.Parameterized): numpy_params = ['r'] pandas_params = ['s','t','u'] conditionally_unsafe = ['f', 'o'] a = param.Integer(default=5, doc='Example doc', bounds=(2,30), inclusive_bounds=(True, False)) b = param.Number(default=4.3, allow_None=True) c = param.String(default='foo') d = param.Boolean(default=False) e = param.List([1,2,3], class_=int) f = param.List([1,2,3]) g = param.Date(default=datetime.datetime.now()) h = param.Tuple(default=(1,2,3), length=3) i = param.NumericTuple(default=(1,2,3,4)) j = param.XYCoordinates(default=(32.1, 51.5)) k = param.Integer(default=1) l = param.Range(default=(1.1,2.3), bounds=(1,3)) m = param.String(default='baz', allow_None=True) n = param.ObjectSelector(default=3, objects=[3,'foo'], allow_None=False) o = param.ObjectSelector(default=simple_list, objects=[simple_list], allow_None=False) p = param.ListSelector(default=[1,4,5], objects=[1,2,3,4,5,6]) q = param.CalendarDate(default=datetime.date.today()) r = None if np is None else param.Array(default=ndarray) s = None if pd is None else param.DataFrame(default=df1, columns=2) t = None if pd is None else param.DataFrame(default=pd.DataFrame( {'A':[1,2,3], 'B':[1.1,2.2,3.3]}), columns=(1,4), rows=(2,5)) u = None if pd is None else param.DataFrame(default=df2, columns=['A', 'B']) v = param.Dict({'1':2})
def test_get_soft_bounds(self): q = param.Date(dt.datetime(2017, 2, 25), bounds=(dt.datetime(2017, 2, 1), dt.datetime(2017, 2, 26)), softbounds=(dt.datetime(2017, 2, 1), dt.datetime(2017, 2, 25))) self.assertEqual(q.get_soft_bounds(), (dt.datetime(2017, 2, 1), dt.datetime(2017, 2, 25)))
class DateRangeSlider(_RangeSliderBase): value = param.Tuple(default=(None, None), length=2) value_start = param.Date(default=None, readonly=True) value_end = param.Date(default=None, readonly=True) value_throttled = param.Tuple(default=None, length=2, constant=True) start = param.Date(default=None) end = param.Date(default=None) step = param.Number(default=1) _source_transforms = { 'value': None, 'value_throttled': None, 'start': None, 'end': None, 'step': None } _rename = {'name': 'title', 'value_start': None, 'value_end': None} _widget_type = _BkDateRangeSlider def _process_param_change(self, msg): msg = super()._process_param_change(msg) if msg.get('value') == (None, None): del msg['value'] if msg.get('value_throttled') == (None, None): del msg['value_throttled'] return msg def _process_property_change(self, msg): msg = super()._process_property_change(msg) if 'value' in msg: v1, v2 = msg['value'] msg['value'] = (value_as_datetime(v1), value_as_datetime(v2)) if 'value_throttled' in msg: v1, v2 = msg['value_throttled'] msg['value_throttled'] = (value_as_datetime(v1), value_as_datetime(v2)) return msg
class DatePicker(Widget): value = param.Date(default=None) start = param.Date(default=None) end = param.Date(default=None) _widget_type = _BkDatePicker _rename = {'start': 'min_date', 'end': 'max_date', 'name': 'title'} def _process_property_change(self, msg): msg = super(DatePicker, self)._process_property_change(msg) if 'value' in msg: msg['value'] = datetime.strptime(msg['value'][4:], '%b %d %Y') return msg
class DateRangeSlider(_SliderBase): value = param.Tuple(default=None, length=2) start = param.Date(default=None) end = param.Date(default=None) step = param.Number(default=1) _widget_type = _BkDateRangeSlider def _process_property_change(self, msg): msg = super(DateRangeSlider, self)._process_property_change(msg) if 'value' in msg: v1, v2 = msg['value'] msg['value'] = (value_as_datetime(v1), value_as_datetime(v2)) return msg
class DatePicker(Widget): value = param.Date(default=None) start = param.Date(default=None) end = param.Date(default=None) _widget_type = _BkDatePicker _rename = {'start': 'min_date', 'end': 'max_date', 'name': 'title'} def _process_property_change(self, msg): msg = super(DatePicker, self)._process_property_change(msg) if msg.get('value', False): if not isiterable(msg['value']): msg['value'] = datetime.combine(msg['value'], datetime.min.time()) return msg
class DatePicker(Widget): value = param.Date(default=None) start = param.Date(default=None) end = param.Date(default=None) _source_transforms = {'value': None, 'start': None, 'end': None} _rename = {'start': 'min_date', 'end': 'max_date', 'name': 'title'} _widget_type = _BkDatePicker def _process_property_change(self, msg): msg = super(DatePicker, self)._process_property_change(msg) if 'value' in msg: if isinstance(msg['value'], string_types): msg['value'] = datetime.strptime(msg['value'][4:], '%b %d %Y') return msg
class _BigDumbParams(param.Parameterized): action = param.Action(default_action, allow_None=True) array = param.Array(np.array([1.0, 2.0])) boolean = param.Boolean(True, allow_None=True) callable = param.Callable(default_action, allow_None=True) class_selector = param.ClassSelector(int, is_instance=False, allow_None=True) color = param.Color("#FFFFFF", allow_None=True) composite = param.Composite(["action", "array"], allow_None=True) try: data_frame = param.DataFrame( pd.DataFrame({"A": 1.0, "B": np.arange(5)}), allow_None=True ) except TypeError: data_frame = param.DataFrame(pd.DataFrame({"A": 1.0, "B": np.arange(5)})) date = param.Date(datetime.now(), allow_None=True) date_range = param.DateRange((datetime.min, datetime.max), allow_None=True) dict_ = param.Dict({"foo": "bar"}, allow_None=True, doc="dict means dictionary") dynamic = param.Dynamic(default=default_action, allow_None=True) file_selector = param.FileSelector( os.path.join(FILE_DIR_DIR, "LICENSE"), path=os.path.join(FILE_DIR_DIR, "*"), allow_None=True, ) filename = param.Filename( os.path.join(FILE_DIR_DIR, "LICENSE"), allow_None=True ) foldername = param.Foldername(os.path.join(FILE_DIR_DIR), allow_None=True) hook_list = param.HookList( [CallableObject(), CallableObject()], class_=CallableObject, allow_None=True ) integer = param.Integer(10, allow_None=True) list_ = param.List([1, 2, 3], allow_None=True, class_=int) list_selector = param.ListSelector([2, 2], objects=[1, 2, 3], allow_None=True) magnitude = param.Magnitude(0.5, allow_None=True) multi_file_selector = param.MultiFileSelector( [], path=os.path.join(FILE_DIR_DIR, "*"), allow_None=True, check_on_set=True, ) number = param.Number(-10.0, allow_None=True, doc="here is a number") numeric_tuple = param.NumericTuple((5.0, 10.0), allow_None=True) object_selector = param.ObjectSelector( False, objects={"False": False, "True": 1}, allow_None=True ) path = param.Path(os.path.join(FILE_DIR_DIR, "LICENSE"), allow_None=True) range_ = param.Range((-1.0, 2.0), allow_None=True) series = param.Series(pd.Series(range(5)), allow_None=True) string = param.String("foo", allow_None=True, doc="this is a string") tuple_ = param.Tuple((3, 4, "fi"), allow_None=True) x_y_coordinates = param.XYCoordinates((1.0, 2.0), allow_None=True)
class Example(BaseClass): """An example Parameterized class""" timestamps = [] boolean = param.Boolean(True, doc="A sample Boolean parameter") color = param.Color(default="#FFFFFF") date = param.Date(dt.date(2017, 1, 1), bounds=wired.DATE_BOUNDS) dataframe = param.DataFrame(pd.util.testing.makeDataFrame().iloc[:3]) select_string = param.ObjectSelector(default="yellow", objects=["red", "yellow", "green"]) select_fn = param.ObjectSelector(default=list, objects=[list, set, dict]) int_list = param.ListSelector(default=[3, 5], objects=[1, 3, 5, 7, 9], precedence=0.5) single_file = param.FileSelector(path="../../*/*.py*", precedence=0.5) multiple_files = param.MultiFileSelector(path="../../*/*.py?", precedence=0.5) record_timestamp = param.Action( lambda x: x.timestamps.append(dt.datetime.utcnow()), doc="""Record timestamp.""", precedence=0.7, )
class Simulation(param.Parameterized): """Basic example of wrapping a GSSHA-based rainfall simulation.""" elevation_service = param.Parameter(default='svc://usgs-ned:13-arc-second', precedence=0.1) land_use_service = param.Parameter(default='svc://usgs-nlcd:2011', precedence=0.2) land_use_grid_id = param.Parameter(default='nlcd', precedence=0.3) model_creator = param.ClassSelector(CreateModel, default=CreateGSSHAModel(), precedence=-1) # TODO: switch to date range... simulation_start = param.Date(default=datetime(2017, 5, 9), precedence=0.5) # ...or at least a timedelta parameter type simulation_duration = param.Integer(default=120, bounds=(0, None), softbounds=(0, 1000000), precedence=0.51, doc=""" Simulated-time duration to run the simulator, in seconds.""") rain_intensity = param.Integer(default=24, bounds=(0, None), softbounds=(0, 75), precedence=0.6, doc=""" Intensity for simulated rain, in mm/hr.""") rain_duration = param.Integer(default=60, bounds=(0, None), softbounds=(0, 1000000), precedence=0.61, doc=""" Simulated-time duration for simulated rain, in seconds.""")
class Athlete(param.Parameterized): """A model of an Athlete""" name_ = param.String("P.A. Nelson") birthday = param.Date( datetime.date( 1976, 9, 17, ), bounds=DATE_BOUNDS, ) weight = param.Number( default=82, bounds=( 20, 300, ), ) power_curve = param.ClassSelector( class_=PowerCurve, default=PowerCurve(), )
class DatetimePicker(_DatetimePickerBase): value = param.Date(default=None) mode = param.String('single', constant=True) def _serialize_value(self, value): if isinstance(value, string_types) and value: value = datetime.strptime(value, r'%Y-%m-%d %H:%M:%S') # Hour, minute and seconds can be increased after end is reached. # This forces the hours, minute and second to be 0. end = self._date_to_datetime(self.end) if end is not None and value > end: value = end return value def _deserialize_value(self, value): if isinstance(value, (datetime, date)): value = value.strftime(r'%Y-%m-%d %H:%M:%S') return value
class MyParameterized(param.Parameterized): enable = param.Boolean(True, doc="A sample Boolean parameter", allow_None=True) what_proportion = param.Magnitude(default=0.9) age = param.Number(49, bounds=(0, 100), doc="Any Number between 0 to 100") how_many = param.Integer() favorite_quote = param.String(default="Hello, world!") choose_file_or_folder = param.Path(search_paths='./') choose_folder = param.Foldername(search_paths="./") choose_file = param.Filename(search_paths="./") select_a_file = param.FileSelector(path='./*') select_multiple_files = param.MultiFileSelector(path='./*') favorite_color = param.ObjectSelector( default="green", objects=["red", "yellow", "green"]) favorite_fruit = param.Selector(default="Apple", objects=["Orange", "Apple", "Mango"]) select_multiple = param.ListSelector(default=[3, 5], objects=[1, 2, 3, 4, 5]) birthday = param.CalendarDate(dt.date(2017, 1, 1), bounds=(dt.date(2017, 1, 1), dt.date(2017, 2, 1))) appointment = param.Date(dt.datetime(2017, 1, 1), bounds=(dt.datetime(2017, 1, 1), dt.datetime(2017, 2, 1))) least_favorite_color = param.Color(default='#FF0000') dataset = param.DataFrame(pd.util.testing.makeDataFrame().iloc[:3]) this_strange_thing = param.Tuple(default=(False, ), allow_None=True) some_numbers = param.NumericTuple(default=(1, 2, 3.0, 4.0)) home_city = param.XYCoordinates(default=(-111.65, 40.23)) bounds = param.Range(default=(-10, 10))
class DatetimePicker(_DatetimePickerBase): """ The `DatetimePicker` allows selecting selecting a `datetime` value using a textbox and a datetime-picking utility. Reference: https://panel.holoviz.org/reference/widgets/DatetimePicker.html :Example: >>> DatetimePicker( ... value=datetime(2025,1,1,22,0), ... start=date(2025,1,1), end=date(2025,12,31), ... military_time=True, name='Date and time' ... ) """ value = param.Date(default=None) mode = param.String('single', constant=True) def _serialize_value(self, value): if isinstance(value, str) and value: value = datetime.strptime(value, r'%Y-%m-%d %H:%M:%S') # Hour, minute and seconds can be increased after end is reached. # This forces the hours, minute and second to be 0. end = self._date_to_datetime(self.end) if end is not None and value > end: value = end return value def _deserialize_value(self, value): if isinstance(value, (datetime, date)): value = value.strftime(r'%Y-%m-%d %H:%M:%S') return value
class DateRangeSlider(_RangeSliderBase): """ The DateRangeSlider widget allows selecting a date range using a slider with two handles. Supports datetime.datetime, datetime.data and np.datetime64 ranges. Reference: https://panel.holoviz.org/reference/widgets/DateRangeSlider.html :Example: >>> import datetime as dt >>> DateRangeSlider( ... value=(dt.datetime(2025, 1, 9), dt.datetime(2025, 1, 16)), ... start=dt.datetime(2025, 1, 1), ... end=dt.datetime(2025, 1, 31), ... step=2, ... name="A tuple of datetimes" ... ) """ value = param.Tuple( default=(None, None), length=2, doc= """The selected range as a tuple of values. Updated when one of the handles is dragged. Supports datetime.datetime, datetime.date, and np.datetime64 ranges.""" ) value_start = param.Date(default=None, readonly=True, doc=""" The lower value of the selected range.""") value_end = param.Date(default=None, readonly=True, doc=""" The upper value of the selected range.""") value_throttled = param.Tuple(default=None, length=2, constant=True, doc=""" The selected range as a tuple of values. Updated one of the handles is released. Supports datetime.datetime, datetime.date and np.datetime64 ranges""") start = param.Date(default=None, doc=""" The lower bound.""") end = param.Date(default=None, doc=""" The upper bound.""") step = param.Number(default=1, doc=""" The step size. Default is 1 (day).""") _source_transforms = { 'value': None, 'value_throttled': None, 'start': None, 'end': None, 'step': None } _rename = {'name': 'title', 'value_start': None, 'value_end': None} _widget_type = _BkDateRangeSlider def _process_param_change(self, msg): msg = super()._process_param_change(msg) if msg.get('value') == (None, None): del msg['value'] elif 'value' in msg: v1, v2 = msg['value'] if isinstance(v1, dt.datetime): v1 = datetime_as_utctimestamp(v1) if isinstance(v2, dt.datetime): v2 = datetime_as_utctimestamp(v2) msg['value'] = (v1, v2) if msg.get('value_throttled') == (None, None): del msg['value_throttled'] return msg def _process_property_change(self, msg): msg = super()._process_property_change(msg) if 'value' in msg: v1, v2 = msg['value'] msg['value'] = (value_as_datetime(v1), value_as_datetime(v2)) if 'value_throttled' in msg: v1, v2 = msg['value_throttled'] msg['value_throttled'] = (value_as_datetime(v1), value_as_datetime(v2)) return msg
class DateSlider(_SliderBase): """ The DateSlider widget allows selecting a value within a set of bounds using a slider. Supports datetime.datetime, datetime.date and np.datetime64 values. The step size is fixed at 1 day. Reference: https://panel.holoviz.org/reference/widgets/DateSlider.html :Example: >>> import datetime as dt >>> DateSlider( ... value=dt.datetime(2025, 1, 1), ... start=dt.datetime(2025, 1, 1), ... end=dt.datetime(2025, 1, 7), ... name="A datetime value" ... ) """ value = param.Date(default=None, doc=""" The selected date value of the slider. Updated when the slider handle is dragged. Supports datetime.datetime, datetime.date or np.datetime64 types.""") value_throttled = param.Date(default=None, constant=True, doc=""" The value of the slider. Updated when the slider handle is released.""" ) start = param.Date(default=None, doc=""" The lower bound.""") end = param.Date(default=None, doc=""" The upper bound.""") as_datetime = param.Boolean(default=False, doc=""" Whether to store the date as a datetime.""") _rename = {'name': 'title', 'as_datetime': None} _source_transforms = { 'value': None, 'value_throttled': None, 'start': None, 'end': None } _widget_type = _BkDateSlider def __init__(self, **params): if 'value' not in params: params['value'] = params.get('start', self.start) super().__init__(**params) def _process_param_change(self, msg): msg = super()._process_param_change(msg) if 'value' in msg: value = msg['value'] if isinstance(value, dt.datetime): value = datetime_as_utctimestamp(value) msg['value'] = value return msg def _process_property_change(self, msg): msg = super()._process_property_change(msg) transform = value_as_datetime if self.as_datetime else value_as_date if 'value' in msg: msg['value'] = transform(msg['value']) if 'value_throttled' in msg: msg['value_throttled'] = transform(msg['value_throttled']) return msg
class Q(param.Parameterized): q = param.Date(bounds=(dt.date(2017, 2, 1), dt.date(2017, 2, 26)))
class HistoryPage(Page): """Provides an illustration of the `Ticker.history` method""" period_type = param.ObjectSelector( default="Period", objects=[ "Dates", "Period", ], ) # pylint: disable=protected-access interval = param.ObjectSelector( default="1d", objects=Ticker._INTERVALS, ) period = param.ObjectSelector( default="1y", objects=Ticker._PERIODS, ) # pylint: enable=protected-access start = param.Date( default=PERIOD_START_DATE, bounds=DATE_BOUNDS, ) end = param.Date( default=PERIOD_END_DATE, bounds=DATE_BOUNDS, ) @staticmethod def _help(): return pnx_help(Ticker.history) @param.depends("period_type") def _param_view( self, ): if self.period_type == "Period": return pn.Param( self, parameters=[ "period_type", "interval", "period", ], default_layout=GridBoxWithTwoColumns, width=400, show_name=False, ) return pn.Param( self, parameters=[ "period_type", "interval", "start", "end", ], default_layout=GridBoxWithTwoColumns, widgets={ "start": pn.widgets.DatePicker, "end": pn.widgets.DatePicker, }, width=600, show_name=False, ) @param.depends( "symbols", "period_type", "interval", "period", "start", "end", ) def _code( self, ): code = f"""Ticker("{self.symbols}").history({', '.join(self._args_string)})""" return code_card(code=code) @property def _history_args( self, ): history_args = {} if self.period_type == "Period": history_args["period"] = self.period history_args["start"] = None history_args["end"] = None else: history_args["period"] = None history_args["start"] = self.start history_args["end"] = self.end history_args["interval"] = self.interval return history_args @property def _args_string( self, ): return [ str(k) + "='" + str(v) + "'" for k, v in self._history_args.items() if v is not None ] # I cannot make the chart responsive. There is some starting information here # See https://stackoverflow.com/questions/55169344/how-to-make-altair-plots-responsive @staticmethod def _history_plot( dataframe: pd.DataFrame, ): if "symbol" in dataframe.columns: chart = ( alt.Chart(dataframe.reset_index()) .mark_line() .encode( alt.Y( "close:Q", scale=alt.Scale(zero=False), ), x="dates", color="symbol", tooltip=["dates", "close", "symbol"], ) ) else: chart = ( alt.Chart(dataframe.reset_index()) .mark_line() .encode( alt.Y( "close:Q", scale=alt.Scale(zero=False), ), x="dates:T", tooltip=["dates", "close"], ) ) chart = chart.properties( width="container", height=300, ) return chart @param.depends("symbols") @PROGRESS.report(message="Requesting Price History from Yahoo Finance") def _data( self, ): tickers = YahooQueryService.to_ticker(self.symbols) data = tickers.history(**self._history_args) if isinstance( data, pd.DataFrame, ): return pn.Column( pnx.SubHeader(" Response"), pn.pane.Vega( self._history_plot(data), sizing_mode="stretch_width", height=325, ), sizing_mode="stretch_width", ) return pnx_json(data) def view( self, ) -> pn.viewable.Viewable: """The main view of the OptionsPage Returns: pn.viewable.Viewable: The main view of the OptionsPage """ return pn.Column( self._param_view, self._data, self._code, self._help, sizing_mode="stretch_width", )
class Q(param.Parameterized): q = param.Date(bounds=(dt.date(2017, 2, 1), dt.date(2017, 2, 26)), inclusive_bounds=(True, False))
class DatetimeInput(LiteralInput): """ DatetimeInput allows declaring Python literals using a text input widget. Optionally a type may be declared. """ format = param.String(default='%Y-%m-%d %H:%M:%S', doc=""" Datetime format used for parsing and formatting the datetime.""") value = param.Date(default=None) start = param.Date(default=None) end = param.Date(default=None) type = datetime _source_transforms = {'value': None, 'start': None, 'end': None} _rename = { 'format': None, 'type': None, 'name': 'title', 'start': None, 'end': None, 'serializer': None } def __init__(self, **params): super(DatetimeInput, self).__init__(**params) self.param.watch(self._validate, 'value') self._validate(None) def _validate(self, event): new = self.value if new is not None and ((self.start is not None and self.start > new) or (self.end is not None and self.end < new)): value = datetime.strftime(new, self.format) start = datetime.strftime(self.start, self.format) end = datetime.strftime(self.end, self.format) if event: self.value = event.old raise ValueError( 'DatetimeInput value must be between {start} and {end}, ' 'supplied value is {value}'.format(start=start, end=end, value=value)) def _process_property_change(self, msg): msg = Widget._process_property_change(self, msg) new_state = '' if 'value' in msg: value = msg.pop('value') try: value = datetime.strptime(value, self.format) except Exception: new_state = ' (invalid)' value = self.value else: if value is not None and ( (self.start is not None and self.start > value) or (self.end is not None and self.end < value)): new_state = ' (out of bounds)' value = self.value msg['value'] = value msg['name'] = msg.get('title', self.name).replace(self._state, '') + new_state self._state = new_state return msg def _process_param_change(self, msg): msg = Widget._process_param_change(self, msg) if 'value' in msg: value = msg['value'] if value is None: value = '' else: value = datetime.strftime(msg['value'], self.format) msg['value'] = value msg['title'] = self.name return msg
class PowerCurve(param.Parameterized): """A Model of a Power Curve of an Athlete The PowerCurve is a recording of the athletes maximum power output in Watt per kg for fixed durations. """ ten_sec = param.Number(1079) ten_sec_date = param.Date( datetime.date( 2018, 8, 21, ), bounds=DATE_BOUNDS, ) one_min = param.Number(684) one_min_date = param.Date( datetime.date( 2017, 8, 31, ), bounds=DATE_BOUNDS, ) ten_min = param.Number(419) ten_min_date = param.Date( datetime.date( 2017, 9, 22, ), bounds=DATE_BOUNDS, ) twenty_min = param.Number(398) twenty_min_date = param.Date( datetime.date( 2017, 9, 22, ), bounds=DATE_BOUNDS, ) one_hour = param.Number(319) one_hour_date = param.Date( datetime.date( 2017, 8, 6, ), bounds=DATE_BOUNDS, ) @param.depends( "ten_sec", "one_min", "ten_min", "twenty_min", "one_hour", ) def plot(self, ): """A plot of the power curve: duration vs power""" data = { "duration": [ 10 / 60, 1, 10, 20, 60, ], "power": [ self.ten_sec, self.one_min, self.ten_min, self.twenty_min, self.one_hour, ], } dataframe = pd.DataFrame(data) line_plot = dataframe.hvplot.line( x="duration", y="power", width=300, line_color="#007BFF", line_width=3, ) scatter_plot = dataframe.hvplot.scatter( x="duration", y="power", width=300, ).opts( marker="o", size=6, color="#007BFF", ) fig = line_plot * scatter_plot gridstyle = { "grid_line_color": "black", "grid_line_width": 0.1, } fig = fig.opts( responsive=True, toolbar=None, yticks=list(range( 0, 1600, 200, )), ylim=( 0, 1500, ), gridstyle=gridstyle, show_grid=True, ) return fig
class OWI(param.Parameterized): grid = None field1 = pd.DataFrame() field2 = pd.DataFrame() delta = None number_fields = param.Integer(2, bounds=(1, 2), precedence=2) ilat = param.Integer(0, precedence=2) ilon = param.Integer(0, precedence=2) dx = param.Number(0.25, precedence=2) dy = param.Number(0.25, precedence=2) swlat = param.Number(5.8, precedence=2) swlon = param.Number(5.8, precedence=2) time_range = param.DateRange(default=None, doc=""" Start and end datetime""") date_start = param.Date(default=datetime.datetime.now(), precedence=-1) date_stop = param.Date(default=datetime.datetime.now() + timedelta(hours=5), precedence=-1) def get_file_metadata(self, path, filename): """ Method to get extract only time between snaps from an OWI file using the first two snaps. Parameters ---------- path - str directory where the file is located filename - str filename of the owi file Returns ------- delta - datetime.timedelta time between snaps """ # construct full path full_path = os.path.join(path, filename) # open the file fp = open(full_path) # get the first line line1 = fp.readline().split() # date_start = datetime.datetime.strptime(line1[-2], '%Y%m%d%H') # date_stop = datetime.datetime.strptime(line1[-1], '%Y%m%d%H') # get the second line line2 = fp.readline().rstrip('\n') # get the first snap time start_time = datetime.datetime.strptime(line2[68:80], '%Y%m%d%H') for line in fp: # if this is a header line if line[0:2] == 'iL': line.rstrip('\n') # get the second snap time snap_time = datetime.datetime.strptime( line.rstrip('\n')[68:80], '%Y%m%d%H') # calculate the time between snaps delta = snap_time - start_time fp.close() return delta fp.close() print( 'An accurate delta could not be calculated because only one snap ' 'was found in the file. Default of 0.0 will be used') delta = datetime.timedelta(seconds=0.0) return delta def read_owi_wind(self, path, filename, fields): """ Method to read owi wind field from file Parameters ---------- path - str Directory where the file is located filename - str Filename to be read (with extension) delta - datetime.timedelta time between time snaps fields - int Number of fields in this file (1 for single data, 2 for x and y data) """ if self.delta is None: self.delta = self.get_file_metadata(path, filename) # construct full path full_path = os.path.join(path, filename) # # convert the provided delta to a timedelta interval # interval = datetime.timedelta(minutes=delta) # set the nodes per line (fixed OWI format) nodes_per_line = 8 # instantiate variables snaps = [] data = [] next_header = 0 header_index = 0 field1_df = pd.DataFrame() field2_df = pd.DataFrame() # open the file fp = open(full_path) # loop over all the lines in the file (with an index) for i, line in enumerate(fp): # first line has file header information if i == 0: # parse the line spar = line.split() # record the start time time_start = datetime.datetime.strptime(spar[3], '%Y%m%d%H') # record the stop time time_stop = datetime.datetime.strptime(spar[4], '%Y%m%d%H') # calculate the number of snaps in the file (number_snaps, remainder) = divmod( (time_stop - time_start).total_seconds(), self.delta.total_seconds()) # check for even division if remainder != 0.0: raise UserWarning( 'Warning: The time delta did not evenly match with the start and ' 'stop time in the file. The results may contain bad data.' ) # calculate the datetime of each snap snaps = [ time_start + self.delta * x for x in range(0, int(number_snaps) + 1, 1) ] # the first data header line elif i == 1: # parse the line # get number of latitude nodes ilat = int(line[5:9]) # get number of longitude nodes ilon = int(line[15:19]) # get dx dx = float(line[22:28]) # get dy dy = float(line[31:37]) # get the SW corner of the grid latitude swlat = float(line[43:51]) # get the sW corner of the grid longitude swlon = float(line[57:65]) # get the snap time as a string snap_time_str = line[68:80] # calculate the number of nodes number_nodes = ilat * ilon # calculate the number of lines per snap (per field) lines_per_snap = int(np.ceil(number_nodes / nodes_per_line)) # calculate the indices for the header lines header_lines = [ x for x in range( 1, 1 + (len(snaps) * (1 + lines_per_snap * fields)), ( 1 + lines_per_snap * fields)) ] # increment the header line index header_index += 1 # get the next header line next_header = header_lines[header_index] # stop at the next header line elif i == next_header: # flatten the data list column_data = list(chain.from_iterable(data)) if fields == 1: # store the previous data for field1 field1_df[snap_time_str] = column_data[0:len(column_data)] # if there are two fields in the file else: # store the previous data for field1 field1_df[snap_time_str] = column_data[ 0:int(len(column_data) / 2)] # store the field2 data field2_df[snap_time_str] = column_data[ int(len(column_data) / 2):len(column_data)] # record the snap time for the next interval snap_time_str = line[68:80] # clear the data list data = [] # increment the header flag header_index += 1 # if we've just moved beyond the last header in the list, stop if header_index == len(header_lines): # break next_header = -1 else: # get the next header line next_header = header_lines[header_index] # if this is not a header line, it is data, append to the data list else: data.append([float(x) for x in line.split()]) # Store the data from the last timestep # flatten the data list column_data = list(chain.from_iterable(data)) if fields == 1: # store the previous data for field1 field1_df[snap_time_str] = column_data[0:len(column_data)] # if there are two fields in the file else: # store the previous data for field1 field1_df[snap_time_str] = column_data[0:int(len(column_data) / 2)] # store the field2 data field2_df[snap_time_str] = column_data[int(len(column_data) / 2):len(column_data)] # close the file fp.close() # store data in the object # self.delta = delta self.number_fields = fields self.ilat = ilat self.ilon = ilon self.dx = dx self.dy = dy self.swlat = swlat self.swlon = swlon # self.start_time = time_start # self.stop_time = time_stop self.time_range = (time_start, time_stop) # todo # if there are two fields, return both if fields == 2: self.field1 = field1_df self.field2 = field2_df # otherwise return field1 else: self.field1 = field1_df def set_grid_parameters(self, swLat, swLon, neLat, neLon, dx, dy): """Method to set the actual grid values based on a bounding box. dx and dy default to 0.25. If the number of cells cannot be evenly divided, an extra grid cell is added. Values are set into self. Latitude = y Longitude = x """ # calculate the estimated grid size delta_lat = abs(swLat - neLat) delta_lon = abs(swLon - neLon) # calculate the number of cells required ilat, rem = divmod(delta_lat, dy) # if the delta cannot be evenly divided, add a cell if rem != 0: ilat += 1 # if the ilat is even, add a cell if ilat % 2 == 0: ilat += 1 # calculate the number of cells required ilon, rem = divmod(delta_lon, dx) # if the delta cannot be evenly divided, add a cell if rem != 0: ilon += 1 # if the ilon is even, add a cell if ilon % 2 == 0: ilon += 1 # set values into self self.ilat = int(ilat) self.ilon = int(ilon) self.dx = dx self.dy = dy self.swlat = swLat self.swlon = swLon def make_owi_grid(self): """ Method to construct the list of grid nodes (latitude and longitude) from the construction parameters. The indexes of these grid nodes correspond to the indexing of the data in the OWI file. Parameters ---------- swlon - float Longitude of the SouthWestern Corner of the grid swlat - float Latitude of the SouthWestern Corner of the grid dx - float grid spacing in the x direction (Longitude) dy - float grid spacing in the y direction (Latitude) ilon - int number of grid cells in the x direction (Longitude) ilat - int number of grid cells in the y direction (Latitude) Returns ------- self.grid - df DataFrame containing the Latitude and Longitude lists for all the grid points. The indexing corresponds to the indexing of the data arrays from the file. """ # ensure that all the required values have been set if self.ilat is None or self.ilon is None or self.dx is None or self.dy is None or \ self.swlat is None or self.swlon is None: raise IOError( 'Parmeters defining the grid must be set into the OWI object (ilat, ' 'ilon, dx, dy, swlat, swlon)') # create array of x values xs = np.arange(self.swlon, self.swlon + self.ilon * self.dx, self.dx) # create array of y values ys = np.arange(self.swlat, self.swlat + self.ilat * self.dy, self.dy) # construct grid data = np.stack([[x, y] for y in ys for x in xs], axis=1) # construct the dataframe for the grid self.grid = pd.DataFrame({'Longitude': data[0], 'Latitude': data[1]}) # ensure all grid values are valid self.check_grid() def check_grid(self): """Method to check that the grid does not cross a pole or an antimeridian with invalid values. Corrects as needed and sets values back into self.grid.""" if self.grid.Longitude.min() < -180.0: bool = self.grid.Longitude < -180.0 self.grid.Longitude[bool] = self.grid.Longitude[bool] + 180.0 if self.grid.Longitude.max() > 180.0: bool = self.grid.Longitude > 180.0 self.grid.Longitude[bool] = self.grid.Longitude[bool] - 180 if self.grid.Latitude.min() < -90.0: bool = self.grid.Latitude < -90.0 self.grid.Latitude[bool] = self.grid.Latitude[bool] + 90.0 if self.grid.Latitude.min() > 90.0: bool = self.grid.Latitude > 90.0 self.grid.Latitude[bool] = self.grid.Latitude[bool] - 90.0 def create_wind_field(self, wind_df, directory, filename): """Method to generate wind field. Derived from Holland 1980""" #ensure data is present if wind_df.empty: raise IOError('No data provided. Unable to create wind field.') # Create the grid for this wind field (from the dx, dy, ilat, ilon, swlat, swlon) if self.grid is None: self.make_owi_grid() for index in wind_df.index: # date = wind_df['Datetime'][index] # lat = wind_df['Latitude'][index] # lon = wind_df['Longitude'][index] # sf_kt = wind_df['sf_kt'][index] # Rw_nm = wind_df['Rw_nm'][index] # pc_mb = wind_df['pc_mb'][index] # deg = wind_df['deg'][index] # v_kt = wind_df['v_kt'][index] date = wind_df['Datetime'][index] lat = wind_df['Latitude'][index] lon = wind_df['Longitude'][index] sf_kt = wind_df['Max_Wind_Speed'][index] Rw_nm = wind_df['Radius'][index] pc_mb = wind_df['Pressure'][index] deg = wind_df['Direction_Degrees'][index] v_kt = wind_df['Direction_Speed'][index] # convert grid to np array format # todo there is probably a better way to do this gridarray = np.array( list(zip(self.grid.Longitude, self.grid.Latitude))) # set the hurricane center point pt = np.array([lon, lat]) # force the second axis for matrix multiplication pt = pt[np.newaxis, ...] # calculate the distances from the hurricane point degrees = distance.cdist(gridarray, pt) degrees = degrees.reshape(len(degrees)) # set the avg radius of the earth earth_radius = 6371 * 1000 # meters # convert degrees to radius r_array = (np.pi * earth_radius) * degrees / 180 delta_lon = (np.pi * earth_radius) * (self.grid.Longitude.values - lon) / 180 # todo check this delta_lat = (np.pi * earth_radius) * (self.grid.Latitude.values - lat) / 180 # ignore div by zero errors np.seterr(divide='ignore') # calculate thea theta_array = np.arctan2(delta_lat, delta_lon) ####################################### # set standard assumptions # boundary layer adjustment factor beta = 0.9 # density of air den_air = 1.15 # kgm^-3 # density of water den_water = 997. # kgm^-3 # ambient atmospheric pressure https://www.britannica.com/science/atmospheric-pressure pn = 101325 # Euler's number e = 2.718281 # gravity grav = 9.80665 # ms^2 # rotation rate of the earth omega = 7.2921e-5 # rad/s (2*pi/day) # sampling adjustment (converts 1 min winds to 10 min winds) ct = 0.88 ######################################################## # apply conversions # convert knots to mps sf = sf_kt * 0.5144444 v = v_kt * 0.5144444 # convert nm to meters Rw = Rw_nm * 1852 # convert millibars to pascal pc = pc_mb * 100 # convert translational speed to # velocity east vte = v * np.sin(deg * np.pi / 180) # velocity west vtn = v * np.cos(deg * np.pi / 180) ###################################################### # calculate maximum storm wind speed at 10m sm = sf - np.sqrt(np.power(vte, 2) + np.power(vtn, 2)) # calculate maximum velocity at the top of the atmospheric boundary layer Vm = sm / beta # calculate Holland's B parameter holland_b = den_air * e * np.power(Vm, 2) / (pn - pc) # ensure Holland's B parameter is within reasonable limits 1 < B < 2.5 if holland_b < 1: # set to lower limit of 1 holland_b = 1 elif holland_b > 2.5: # set to upper limit of 2.5 holland_b = 2.5 # coriolis parameter for the storm's current location f = 2 * omega * np.sin( lat * np.pi / 180 ) # todo replace with array # typical values about 10^-4 rads/s # for each node from the center of storm and it's radial angle # calculate pressure at each node # pr_array = (pc + (pn - pc) * np.exp(-np.power(Rw / r_array, holland_b))) / (den_water * grav) # calculate the raw gradient wid speed at each node # Vg = np.sqrt((np.power(Rw / r, holland_b) - np.exp(1 - np.power(Rw / r, holland_b)) * np.power(Vm, 2) + # (np.power(float(r), 2) * np.power(f, 2) / 4)) # ) g- (r * f / 2) # orig - suspected typo Vg = np.sqrt((np.power(Rw / r_array, holland_b) * np.exp(1 - np.power(Rw / r_array, holland_b)) * np.power(Vm, 2) + (np.power(r_array, 2) * np.power(f, 2) / 4))) - ( r_array * f / 2) # derived from Holland 1980 # translational adjustments to final wind speed in north and east directions vtan = np.abs(Vg / Vm) * vtn vtae = np.abs(Vg / Vm) * vte # for each node location, i, separate the velocity into components and apply beta Vei = -Vg * beta * np.sin( theta_array ) # todo: does this need to be adjusted for southern hemi? Vni = Vg * beta * np.cos(theta_array) # multiply by sampling time adjustment to convert 1min winds to 10min winds and add translation velocity Vfei = ct * Vei + vtae Vfni = ct * Vni + vtan date_string = datetime.datetime.strftime(date, '%Y%m%d%H%M') self.field1[date_string] = Vfei self.field2[date_string] = Vfni # set start and stop dates self.date_start = wind_df['Datetime'].min() self.date_stop = wind_df['Datetime'].max() self.write_owi_file(directory, filename) def write_owi_file(self, path, filename): """ Method to write owi formatted wind file from values in the owi object. Parameters ---------- path - str Output directory filename - str Output filename (with extension) Returns ------- """ # construct full path full_path = os.path.join(path, filename) # ensure that all the required values have been set if self.ilat is None or self.ilon is None or self.dx is None or self.dy is None or \ self.swlat is None or self.swlon is None: raise IOError( 'Parmeters defining the grid must be set into the OWI object (ilat, ' 'ilon, dx, dy, swlat, swlon)') if self.grid is None: raise IOError( 'Grid must be constructed and set in the OWI object.') if self.field1 is None: raise IOError( 'At least one field of data must be set in the OWI object') if self.number_fields == 2 and self.field2 is None: raise IOError( 'Number of fields specified as 2, but no field2 data was found in the OWI object' ) # if self.start_time is None and self.stop_time is None: # raise IOError('Start time and stop time must be specified as %Y%m%d%H') # set the number of columns in the file (fixed OWI format) columns = 8 # start_time = self.time_range[0] # stop_time = self.time_range[1] start_time = self.date_start # todo stop_time = self.date_stop # todo # open the file fp = open(full_path, 'w+', newline='') # construct file header file_header = 'Oceanweather WIN/PRE Format {0} {1}\n'.\ format(datetime.datetime.strftime(start_time, '%Y%m%d%H'), datetime.datetime.strftime(stop_time, '%Y%m%d%H')) # write the file header fp.write(file_header) # loop over the timesteps in the data for time_string in self.field1.__iter__(): # construct the timestep header timestep_header = 'iLat={:>4}iLong={:>4}DX={:01.4f}DY={:01.4f}SWLat={:08.5f}SWLon={:02.4f}DT={}\n'.format( self.ilat, self.ilon, self.dx, self.dy, self.swlat, self.swlon, time_string) # write the timestep header fp.write(timestep_header) # loop over the data fields for data in [self.field1[time_string].values, self.field2[time_string].values] if self.number_fields == 2 \ else [self.field1[time_string].values]: # determine the number of rows required per field and the remaining extra data columns (num_rows, rem_columns) = divmod(len(data), columns) # reformat into fixed width strings and construct the columns for the file fullrows = [[ '{:>10.4f}'.format(data[i + j]) for j in range(columns) ] for i in range(0, len(data) - rem_columns, 8)] # print each full row for row in fullrows: row = ''.join(row) fp.write(row + '\n') # print the last row of data (doesn't contain all the columns) lastrow = [ '{:>10.4f}'.format(data[num_rows * 8 + j]) for j in range(rem_columns) ] fp.write(''.join(lastrow) + '\n') def write_grid_xy(self, path, filename): """ Method to write owi grid file as XY from values in the owi object. Parameters ---------- path - str Output directory filename - str Output filename (with extension) Returns ------- """ # construct full path full_path = os.path.join(path, filename) if self.grid is None: raise IOError( 'Grid must be constructed and set in the OWI object.') self.grid.to_csv(full_path, sep='\t', index=False) def view(self): def time_field(time): vx = self.field1[time].values vy = self.field2[time].values xs = self.grid.Longitude.values ys = self.grid.Latitude.values with np.errstate(divide='ignore', invalid='ignore'): angle = np.arctan2(vy, vx) mag = np.sqrt(vx**2 + vy**2) return gv.VectorField( (xs, ys, angle, mag), vdims=['Angle', 'Magnitude'], crs=ccrs.PlateCarree()).redim.range(Magnitude=(0, 50)) # tiles = gv.WMTS('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{Z}/{Y}/{X}') vectors = hv.DynamicMap( time_field, kdims='Time').redim.values(Time=sorted(self.field1.keys())) return vectors.opts(size_index='Magnitude', color_index='Magnitude', pivot='tail', width=700, height=500, colorbar=True, scale=1, cmap='OrRd')