def group_values(chart_type, group_cols, map_group_cols, pathname, inputs, prev_group_vals): group_cols = make_list(group_cols) if show_input_handler(chart_type or 'line')('group') and not len(group_cols): return [], None elif chart_type == 'maps': # all maps have a group input group_cols = make_list(map_group_cols) if not len(group_cols): return [], None data_id = get_data_id(pathname) group_vals = run_query(global_state.get_data(data_id), inputs.get('query'), global_state.get_context_variables(data_id)) group_vals = build_group_val_options(group_vals, group_cols) selections = [] available_vals = [gv['value'] for gv in group_vals] if prev_group_vals is not None: selections = [ pgv for pgv in prev_group_vals if pgv in available_vals ] if not len(selections) and len(group_vals) <= MAX_GROUPS: selections = available_vals return group_vals, selections
def build_drilldown_title(data_id, all_inputs, click_point, props, val_prop): data = global_state.get_data(data_id) def _build_val(col, val): if classify_type(find_dtype(data[col])) == "D": return json_date(convert_date_val_to_date(val)) return val if "text" in click_point: # Heatmaps strs = [] for dim in click_point["text"].split("<br>"): prop, val = dim.split(": ") strs.append("{} ({})".format(prop, val)) return "{}: {}".format(text("Drilldown for"), ", ".join(strs)) strs = [] frame_col = all_inputs.get("animate_by") if frame_col: strs.append("{} ({})".format(frame_col, click_point.get("customdata"))) for prop in props: prop = make_list(prop) val_key = prop[0] if click_point.get(val_key) is not None: col = make_list(all_inputs.get(prop[-1]))[0] strs.append("{} ({})".format( col, _build_val(col, click_point.get(val_key)))) val_prop = make_list(val_prop) val_key = val_prop[0] val_col = make_list(all_inputs.get(val_prop[-1]))[0] agg = AGGS[all_inputs.get("agg") or "raw"] strs.append("{} {} ({})".format( agg, val_col, _build_val(val_col, click_point.get(val_key)))) return "{}: {}".format(text("Drilldown for"), ", ".join(strs))
def retrieve_chart_data(df, *args, **kwargs): """ Retrieves data from a dataframe for x, y, z & group inputs complete with date frequency formatting (:meth:`dtale.charts.utils.date_freq_handler`) if specified :param df: dataframe that contains data for chart :type df: :class:`pandas:pandas.DataFrame` :param args: columns to use :type args: iterable of str :return: dataframe of data required for chart construction :rtype: :class:`pandas:pandas.DataFrame` """ freq_handler = date_freq_handler(df) cols = flatten_lists([make_list(a) for a in args]) all_code = [] all_data = [] for col in cols: if col is not None: s, code = freq_handler(col) all_data.append(s) if code is not None: all_code.append(code) all_data = pd.concat(all_data, axis=1) all_code = ["chart_data = pd.concat(["] + all_code + ["], axis=1)"] if len(make_list(kwargs.get("group_val"))): filters = build_group_inputs_filter(all_data, kwargs["group_val"]) all_data = run_query(all_data, filters) all_code.append("chart_data = chart_data.query({})".format( triple_quote(filters))) return all_data, all_code
def on_data( _ts1, _ts2, _ts3, _ts4, pathname, inputs, chart_inputs, yaxis_data, map_data, last_chart_inputs, ): """ dash callback controlling the building of dash charts """ all_inputs = dict_merge(inputs, chart_inputs, dict(yaxis=yaxis_data or {}), map_data) if all_inputs == last_chart_inputs: raise PreventUpdate if is_app_root_defined(dash_app.server.config.get("APPLICATION_ROOT")): all_inputs["app_root"] = dash_app.server.config["APPLICATION_ROOT"] charts, range_data, code = build_chart(get_data_id(pathname), **all_inputs) return ( charts, all_inputs, range_data, "\n".join(make_list(code)), get_yaxis_type_tabs(make_list(inputs.get("y") or [])), )
def input_toggles(_ts, inputs): """ dash callback controlling showing/hiding of chart-specific inputs (for example z-axis) as well as chart formatting inputs (sorting for bars in bar chart, bar chart style (stacked) or y-axis ranges. """ [chart_type, x, y, group, agg] = [inputs.get(p) for p in ['chart_type', 'x', 'y', 'group', 'agg']] settings = CHART_INPUT_SETTINGS[chart_type] x_settings, y_settings, z_settings, group_settings = (settings.get(p) for p in ['x', 'y', 'z', 'group']) def show(cfg, input_type='single'): return cfg.get('display', True) and cfg.get('type', 'single') == input_type y_multi_style = {'display': 'block' if show(y_settings, 'multi') else 'none'} y_single_style = {'display': 'block' if show(y_settings) else 'none'} z_style = {'display': 'block' if show(z_settings) else 'none'} group_style = {'display': 'block' if show(group_settings) else 'none'} rolling_style = {'display': 'inherit' if agg == 'rolling' else 'none'} show_cpg = show(group_settings) and len(group or []) and chart_type not in ['pie', 'wordcloud'] cpg_style = {'display': 'block' if show_cpg else 'none'} bar_style = {'display': 'block' if chart_type == 'bar' else 'none'} barsort_options = make_list(x) if x is not None else [] barsort_options += make_list(y) if y is not None else [] barsort_options = [build_option(o) for o in barsort_options] yaxis_style, yaxis_options = {'display': 'none'}, [] if chart_type in YAXIS_CHARTS and len(y or []): yaxis_style, yaxis_options = {'display': 'block'}, [build_option(y2) for y2 in y] return ( y_multi_style, y_single_style, z_style, group_style, rolling_style, cpg_style, bar_style, bar_style, barsort_options, yaxis_style, yaxis_options )
def retrieve_chart_data(df, x, y, z, group=None): """ Retrieves data from a dataframe for x, y, z & group inputs complete with date frequency formatting (:meth:`dtale.charts.utils.date_freq_handler`) if specified :param df: dataframe that contains data for chart :type df: :class:`pandas:pandas.DataFrame` :param x: column to use for the X-Axis :type x: str :param y: columns to use for the Y-Axes :type y: list of str :param z: column to use for the Z-Axis :type z: str :param group: column(s) to use for grouping :type group: list of str or str :return: dataframe of data required for chart construction :rtype: :class:`pandas:pandas.DataFrame` """ freq_handler = date_freq_handler(df) cols = [x] + make_list(y) + [z] + make_list(group) all_code = [] all_data = [] for col in cols: if col is not None: s, code = freq_handler(col) all_data.append(s) if code is not None: all_code.append(code) all_code = ["chart_data = pd.concat(["] + all_code + ["], axis=1)"] return pd.concat(all_data, axis=1), all_code
def build_agg_data(df, x, y, inputs, agg, z=None): """ Builds aggregated data when an aggregation (sum, mean, max, min...) is selected from the front-end. :param df: dataframe that contains data for chart :type df: :class:`pandas:pandas.DataFrame` :param x: column to use for the X-Axis :type x: str :param y: columns to use for the Y-Axes :type y: list of str :param inputs: additional chart configurations (chart_type, group, rolling_win, rolling_comp...) :type inputs: dict :param agg: points to a specific function that can be applied to :func: pandas.core.groupby.DataFrameGroupBy. Possible values are: count, first, last mean, median, min, max, std, var, mad, prod, sum :type agg: str :param z: column to use for the Z-Axis :type z: str, optional :return: dataframe of aggregated data :rtype: :class:`pandas:pandas.DataFrame` """ if agg == 'raw': return df, [] z_exists = len(make_list(z)) if agg == 'corr': if not z_exists: raise NotImplementedError( 'Correlation aggregation is only available for 3-dimensional charts!' ) if agg == 'rolling': if z_exists: raise NotImplementedError( 'Rolling computations have not been implemented for 3-dimensional charts!' ) window, comp = map(inputs.get, ['rolling_win', 'rolling_comp']) agg_df = df.set_index(x).rolling(window=window) agg_df = pd.DataFrame({c: getattr(agg_df[c], comp)() for c in y}) agg_df = agg_df.reset_index() code = [ "chart_data = chart_data.set_index('{x}').rolling(window={window})" .format(x=x, window=window), "chart_data = pd.DataFrame({'" + ', '.join([ "'{c}': chart_data['{c}'].{comp}()".format(c=c, comp=comp) for c in y ]) + '})', "chart_data = chart_data.reset_index()" ] return agg_df, code if z_exists: groups = df.groupby([x] + make_list(y)) return getattr(groups[make_list(z)], agg)().reset_index(), [ "chart_data = chart_data.groupby(['{cols}'])[['{z}']].{agg}().reset_index()" .format(cols="', '".join([x] + make_list(y)), z=z, agg=agg) ] groups = df.groupby(x) return getattr(groups[y], agg)().reset_index(), [ "chart_data = chart_data.groupby('{x}')[['{y}']].{agg}().reset_index()" .format(x=x, y=make_list(y)[0], agg=agg) ]
def main_inputs_and_group_val_display(inputs): chart_type = inputs.get('chart_type') show_group = show_input_handler(inputs.get('chart_type', 'line'))('group') if chart_type == 'maps' and not len(make_list(inputs.get('map_group'))): return dict(display='none'), 'col-md-12' elif show_group and not len(make_list(inputs.get('group'))): return dict(display='none'), 'col-md-12' return dict(display='block'), 'col-md-8'
def populate_col_dropdowns(is_open, input_data): if not is_open: raise PreventUpdate y = make_list(input_data.get("y")) z = make_list(input_data.get("z")) col_options = [ build_option(sub_col) for sub_col in (y if not len(z) else z) ] return [col_options for _ in range(10)]
def build_final_chart_code(code): is_charts = (next( (c for c in make_list(code) if c.startswith("figure = go.Figure(data=charts,")), None, ) is not None) return "\n".join( make_list(code) + [CHARTS_EXPORT_CODE if is_charts else CHART_EXPORT_CODE])
def flatten_columns(df, columns=None): if columns is not None: return [ ' '.join([ '{}-{}'.format(c1, str(c2)) for c1, c2 in zip(make_list(columns), make_list(col_val)) ]).strip() for col_val in df.columns.values ] return [ ' '.join([str(c) for c in make_list(col)]).strip() for col in df.columns.values ]
def build_aggs(y, z=None, agg=None, extended_aggregation=[]): z_exists = len(make_list(z)) agg_cols = make_list(y) if z_exists: agg_cols = make_list(z) aggs = {} if not len(extended_aggregation or []): aggs[agg] = agg_cols else: for ext_agg in extended_aggregation: aggs[ext_agg["agg"]] = aggs.get(ext_agg["agg"], []) + [ext_agg["col"]] return aggs
def input_data(_ts, chart_type, x, y_multi, y_single, z, group, agg, window, rolling_comp, pathname, query): """ dash callback for maintaining chart input state and column-based dropdown options. This will guard against users selecting the same column for multiple axes. """ y_val = make_list(y_single if chart_type in ZAXIS_CHARTS else y_multi) inputs = dict(query=query, chart_type=chart_type, x=x, y=y_val, z=z, group=group, agg=agg, window=window, rolling_comp=rolling_comp) data_id = get_data_id(pathname) cols = DATA[data_id].columns dtypes = get_dtypes(DATA[data_id]) def build_selections(*args): return flatten_lists([[] if a is None else make_list(a) for a in args]) def build_cols(): for c in cols: if classify_type(dtypes[c]) == 'D': for freq in FREQS: if freq in FREQ_LABELS: yield '{}|{}'.format(c, freq), '{} ({})'.format(c, FREQ_LABELS[freq]) else: yield c, c else: yield c, c col_opts = list(build_cols()) group_val, z_val = (None, z) if chart_type in ZAXIS_CHARTS else (group, None) x_options = [build_option(c, l) for c, l in col_opts if c not in build_selections(y_val, z_val, group_val)] y_filter = build_selections(x, group_val, z_val) y_multi_options = [build_option(c, l) for c, l in col_opts if c not in y_filter] y_single_options = [build_option(c, l) for c, l in col_opts if c not in y_filter] z_options = [build_option(c) for c in cols if c not in build_selections(x, y_val, group_val)] group_options = [build_option(c, l) for c, l in col_opts if c not in build_selections(x, y_val, z_val)] return inputs, x_options, y_single_options, y_multi_options, z_options, group_options
def build_filter(self): if self.cfg is None: return super(NumericFilter, self).handle_missing(None) cfg_val, cfg_operand, cfg_min, cfg_max = ( self.cfg.get(p) for p in ["value", "operand", "min", "max"]) if cfg_operand in ["=", "ne"]: state = make_list(cfg_val or []) if not len(state): return super(NumericFilter, self).handle_missing(None) fltr = dict(value=cfg_val, operand=cfg_operand) if len(state) == 1: fltr["query"] = "{} {} {}".format( build_col_key(self.column), "==" if cfg_operand == "=" else "!=", state[0], ) else: fltr["query"] = "{} {} ({})".format( build_col_key(self.column), "in" if cfg_operand == "=" else "not in", ", ".join(map(str, state)), ) return super(NumericFilter, self).handle_missing(fltr) if cfg_operand in ["<", ">", "<=", ">="]: if cfg_val is None: return super(NumericFilter, self).handle_missing(None) fltr = dict( value=cfg_val, operand=cfg_operand, query="{} {} {}".format(build_col_key(self.column), cfg_operand, cfg_val), ) return super(NumericFilter, self).handle_missing(fltr) if cfg_operand in ["[]", "()"]: fltr = dict(operand=cfg_operand) queries = [] if cfg_min is not None: fltr["min"] = cfg_min queries.append("{} >{} {}".format( build_col_key(self.column), "=" if cfg_operand == "[]" else "", cfg_min, )) if cfg_max is not None: fltr["max"] = cfg_max queries.append("{} <{} {}".format( build_col_key(self.column), "=" if cfg_operand == "[]" else "", cfg_max, )) if len(queries) == 2 and cfg_max == cfg_min: queries = [ "{} == {}".format(build_col_key(self.column), cfg_max) ] if not len(queries): return super(NumericFilter, self).handle_missing(None) fltr["query"] = " and ".join(queries) return super(NumericFilter, self).handle_missing(fltr) return super(NumericFilter, self).handle_missing(None)
def startup(data=None, data_loader=None, port=None): """ Loads and stores data globally - If data has indexes then it will lock save those columns as locked on the front-end - If data has column named index it will be dropped so that it won't collide with row numbering (dtale_index) - Create location in memory for storing settings which can be manipulated from the front-end (sorts, filter, ...) :param data: pandas.DataFrame or pandas.Series :param data_loader: function which returns pandas.DataFrame :param port: integer port for running Flask process """ global DATA, SETTINGS if data_loader is not None: data = data_loader() elif data is None: logger.debug('pytest: {}, flask: {}'.format(running_with_pytest(), running_with_flask())) if data is not None: curr_index = [ i for i in make_list(data.index.name or data.index.names) if i is not None ] logger.debug('pre-locking index columns ({}) to settings[{}]'.format( curr_index, port)) SETTINGS[str(port)] = dict(locked=curr_index) DATA = data.reset_index().drop('index', axis=1, errors='ignore')
def notebook_charts(self, x, y, group=None, aggregation=None, width='100%', height=350): """ Helper function to build an `ipython:IPython.display.IFrame` pointing at the charts popup :param x: column to be used as x-axis of chart :type x: str :param y: column to be used as y-axis of chart :type y: str :param group: comma-separated string of columns to group chart data by :type group: str, optional :param aggregation: points to a specific function that can be applied to :func: pandas.core.groupby.DataFrameGroupBy. Possible values are: count, first, last mean, median, min, max, std, var, mad, prod, sum :type aggregation: str, optional :param width: width of the ipython cell :type width: str or int, optional :param height: height of the ipython cell :type height: str or int, optional :return: :class:`ipython:IPython.display.IFrame` """ params = dict(x=x, y=y) if group: params['group'] = ','.join(make_list(group)) if aggregation: params['aggregation'] = aggregation self.notebook('/dtale/popup/charts/', params=params, width=width, height=height)
def _build_iframe(self, route='/dtale/iframe/', params=None, width='100%', height=350): """ Helper function to build an :class:`ipython:IPython.display.IFrame` if that module exists within your environment :param route: the :class:`flask:flask.Flask` route to hit on D-Tale :type route: str, optional :param params: properties & values passed as query parameters to the route :type params: dict, optional :param width: width of the ipython cell :type width: str or int, optional :param height: height of the ipython cell :type height: str or int, optional :return: :class:`ipython:IPython.display.IFrame` """ try: from IPython.display import IFrame except ImportError: logger.info('in order to use this function, please install IPython') return None iframe_url = '{}{}{}'.format(self._url, route, self._data_id) if params is not None: formatted_params = ['{}={}'.format(k, ','.join(make_list(params[k]))) for k in sorted(params)] iframe_url = '{}?{}'.format(iframe_url, '&'.join(formatted_params)) return IFrame(iframe_url, width=width, height=height)
def group_values( chart_type, group_cols, map_group_cols, cs_group_cols, treemap_group_cols, pathname, inputs, prev_group_vals, ): data_id = get_data_id(pathname) group_cols = group_cols if chart_type == "maps": group_cols = map_group_cols elif chart_type == "candlestick": group_cols = cs_group_cols elif chart_type == "treemap": group_cols = treemap_group_cols group_cols = make_list(group_cols) group_types = get_group_types(inputs, data_id, group_cols) if "groups" not in group_types: return [], None group_vals = run_query( global_state.get_data(data_id), inputs.get("query"), global_state.get_context_variables(data_id), ) group_vals = build_group_val_options(group_vals, group_cols) selections = [] available_vals = [gv["value"] for gv in group_vals] if prev_group_vals is not None: selections = [pgv for pgv in prev_group_vals if pgv in available_vals] if not len(selections) and len(group_vals) <= MAX_GROUPS: selections = available_vals return group_vals, selections
def input_data(_ts, chart_type, x, y_multi, y_single, z, group, group_val, agg, window, rolling_comp, pathname, query): """ dash callback for maintaining chart input state and column-based dropdown options. This will guard against users selecting the same column for multiple axes. """ y_val = make_list(y_single if chart_type in ZAXIS_CHARTS else y_multi) if group_val is not None: group_val = [json.loads(gv) for gv in group_val] inputs = dict(query=query, chart_type=chart_type, x=x, y=y_val, z=z, group=group, group_val=group_val, agg=agg, window=window, rolling_comp=rolling_comp) data_id = get_data_id(pathname) options = build_input_options(global_state.get_data(data_id), **inputs) x_options, y_multi_options, y_single_options, z_options, group_options, barsort_options, yaxis_options = options show_map = chart_type == 'maps' map_style = {} if show_map else {'display': 'none'} non_map_style = {'display': 'none'} if show_map else {} cscale_style = colorscale_input_style(chart_type=chart_type) return (inputs, x_options, y_single_options, y_multi_options, z_options, group_options, barsort_options, yaxis_options, non_map_style, map_style, cscale_style)
def build_input(label, input, className="col-auto", label_class="input-group-addon", **kwargs): """ Helper function to build a standard label/input component in dash. :param label: name of the input you are displaying :type label: str :param input: dash component for storing state :param className: style class to be applied to encapsulating div :type className: str :param kwargs: Optional keyword arguments to be applied to encapsulating div (style, title, id...) :type kwargs: dict :return: dash components for label/input :rtype: :dash:`dash_html_components.Div <dash-html-components/div>` """ return html.Div([ html.Div( [html.Span(label, className=label_class)] + make_list(input), className="input-group mr-3", ) ], className=className, **kwargs)
def yaxis_min_max_values(yaxis_type, yaxis, inputs, yaxis_inputs, range_data): """ dash callback controlling values for selected y-axis in y-axis range editor """ y = make_list(inputs.get('y')) dd_style = dict(display='block' if yaxis_type == 'multi' and len(y) > 1 else 'none') type_style = { 'borderRadius': '0 0.25rem 0.25rem 0' } if yaxis_type == 'default' else None min_max_style = 'none' if (yaxis_type == 'default') or ( yaxis_type == 'multi' and yaxis is None) else 'block' label_style = dict(display=min_max_style) input_style = {'lineHeight': 'inherit', 'display': min_max_style} curr_min, curr_max = (None, None) range_min, range_max = ((range_data or {}).get(p) or {} for p in ['min', 'max']) if yaxis: curr_vals = (yaxis_inputs or {}).get('data', {}).get(yaxis) or {} curr_min = curr_vals.get('min') or range_min.get(yaxis) curr_max = curr_vals.get('max') or range_max.get(yaxis) elif yaxis_type == 'single': curr_vals = (yaxis_inputs or {}).get('data', {}).get('all') or {} curr_min = curr_vals.get('min') if curr_min is None: curr_min = get_default_range(range_min, y) curr_max = curr_vals.get('max') if curr_max is None: curr_max = get_default_range(range_max, y, max=True) return curr_min, curr_max, dd_style, label_style, input_style, label_style, input_style, type_style
def get_loader_options(key, properties, options): """ Filters dictionary of click parameters for ones which start with a certain prefix :param key: click option prefix :type key: str :param properties: sub-properties of the click command key :type properties: list of str :param options: click options :type options: dict :return: dictionary of click options with start with key :rtype: dict """ def _build_key(option): segs = option.split("_") if len(segs) == 1: return "" return option.split("{}_".format(key))[-1] final_key = "_".join(key.split("-")) selected_opts = ([final_key] if not len(make_list(properties)) else [ "{}_{}".format(final_key, prop["name"] if isinstance(prop, dict) else prop) for prop in properties ]) return dict(((_build_key(k), v) for k, v in options.items() if k in selected_opts if v is not None))
def update_yaxis_data(yaxis_type, yaxis_min, yaxis_max, yaxis, yaxis_data, range_data, inputs): """ dash callback controlling updates to y-axis range state """ yaxis_data = yaxis_data or dict(data={}) yaxis_data["type"] = yaxis_type yaxis_name = "all" if yaxis_type == "single" else yaxis if yaxis_name == "all": y = make_list(inputs.get("y")) mins = range_data.get("min", {}) maxs = range_data.get("max", {}) range_min = get_default_range(mins, y) range_max = get_default_range(maxs, y, max=True) elif yaxis is None: raise PreventUpdate else: range_min, range_max = (range_data[p].get(yaxis_name) for p in ["min", "max"]) if yaxis_name in yaxis_data["data"]: if (yaxis_min, yaxis_max) == (range_min, range_max): del yaxis_data["data"][yaxis_name] else: yaxis_data["data"][yaxis_name] = dict(min=yaxis_min, max=yaxis_max) else: if (yaxis_min, yaxis_max) != (range_min, range_max): yaxis_data["data"][yaxis_name] = dict(min=yaxis_min, max=yaxis_max) return yaxis_data
def build_title(x, y, group=None, z=None, agg=None): """ Helper function to build chart titles based on the inputs for x, y, z, group & aggregation. - (x='a', y='b') => 'b by a' - (x='a', y=['b','c']) => 'b, c by a' - (x='a', y='b', z='c') => 'b by a weighted by c' - (x='a', y='b', group='d') => 'd - b by a' - (x='a', y='b', agg='corr') => 'b by a (Correlation)' - (x='a', y='b', z='c', agg='sum') => 'b by a weighted by c (Sum)' :param x: column to use for the X-Axis :type x: str :param y: columns to use for the Y-Axes :type y: list of str :param group: column(s) to use for grouping :type group: list of str or str, optional :param z: column to use for the Z-Axis :type z: str, optional :param agg: specific aggregation that can be applied to y or z axes. Possible values are: count, first, last mean, median, min, max, std, var, mad, prod, sum. This is included in label of axis it is being applied to. :type agg: str, optional :return: chart title :rtype: str """ y_title = ', '.join([update_label_for_freq(y2) for y2 in make_list(y)]) x_title = update_label_for_freq(x) title = '{} by {}'.format(y_title, x_title) if z: title = '{} weighted by {}'.format(title, z) if agg: agg_title = AGGS[agg] title = '{} ({})'.format(title, agg_title) if group: title = '{} - {}'.format(group, title) return {'title': {'text': title}}
def update_yaxis_data(yaxis_type, yaxis_min, yaxis_max, yaxis, yaxis_data, range_data, inputs): """ dash callback controlling updates to y-axis range state """ yaxis_data = yaxis_data or dict(data={}) yaxis_data['type'] = yaxis_type yaxis_name = 'all' if yaxis_type == 'single' else yaxis if yaxis_name == 'all': y = make_list(inputs.get('y')) mins = range_data.get('min', {}) maxs = range_data.get('max', {}) range_min = get_default_range(mins, y) range_max = get_default_range(maxs, y, max=True) elif yaxis is None: raise PreventUpdate else: range_min, range_max = (range_data[p].get(yaxis_name) for p in ['min', 'max']) if yaxis_name in yaxis_data['data']: if (yaxis_min, yaxis_max) == (range_min, range_max): del yaxis_data['data'][yaxis_name] else: yaxis_data['data'][yaxis_name] = dict(min=yaxis_min, max=yaxis_max) else: if (yaxis_min, yaxis_max) != (range_min, range_max): yaxis_data['data'][yaxis_name] = dict(min=yaxis_min, max=yaxis_max) return yaxis_data
def on_data( _ts1, _ts2, _ts3, _ts4, _ts5, _ts6, load, inputs, chart_inputs, yaxis_data, map_data, cs_data, treemap_data, last_chart_inputs, auto_load, prev_load_clicks, ): """ dash callback controlling the building of dash charts """ all_inputs = dict_merge( inputs, chart_inputs, dict(yaxis=yaxis_data or {}), map_data, cs_data, treemap_data, ) if not auto_load and load == prev_load_clicks: raise PreventUpdate if all_inputs == last_chart_inputs: raise PreventUpdate if is_app_root_defined(dash_app.server.config.get("APPLICATION_ROOT")): all_inputs["app_root"] = dash_app.server.config["APPLICATION_ROOT"] charts, range_data, code = build_chart(**all_inputs) return ( charts, all_inputs, range_data, "\n".join(make_list(code) + [CHART_EXPORT_CODE]), get_yaxis_type_tabs(make_list(inputs.get("y") or [])), load, dict(display="block" if valid_chart(**all_inputs) else "none"), )
def funnel_callback(selected_value, selected_label, group, stacked, data_id): label_value_data, value_options, label_options = label_value_callback("funnel")( selected_value, selected_label, group, data_id, funnel_stacked=stacked ) return ( label_value_data, value_options, label_options, show_style(len(make_list(group)) > 0), )
def retrieve_chart_data(df, *args, **kwargs): """ Retrieves data from a dataframe for x, y, z & group inputs complete with date frequency formatting (:meth:`dtale.charts.utils.date_freq_handler`) if specified :param df: dataframe that contains data for chart :type df: :class:`pandas:pandas.DataFrame` :param args: columns to use :type args: iterable of str :return: dataframe of data required for chart construction :rtype: :class:`pandas:pandas.DataFrame` """ freq_handler = date_freq_handler(df) cols = flatten_lists([make_list(a) for a in args]) all_code = [] all_data = [] for col in cols: if col is not None: s, code = freq_handler(col) all_data.append(s) if code is not None: all_code.append(code) all_data = pd.concat(all_data, axis=1) all_code = ["chart_data = pd.concat(["] + all_code + ["], axis=1)"] if len(make_list(kwargs.get('group_val'))): dtypes = get_dtypes(all_data) def _group_filter(group_val): for gc, gv in group_val.items(): classifier = classify_type(dtypes[gc]) yield group_filter_handler(gc, gv, classifier) def _full_filter(): for group_val in kwargs['group_val']: group_filter = ' and '.join(list(_group_filter(group_val))) yield group_filter filters = list(_full_filter()) filters = '({})'.format(') or ('.join(filters)) all_data = all_data.query(filters) all_code.append('chart_data = chart_data.query({})'.format(filters)) return all_data, all_code
def build_final_cols(y, z, agg, extended_aggregation): if not len(extended_aggregation or []): z = make_list(z) cols = y if not len(z) else z if agg is not None and agg != "raw": return ["{}|{}".format(col, agg) for col in cols] return cols return [ "{}|{}".format(ext_agg["col"], ext_agg["agg"]) for ext_agg in extended_aggregation ]
def retrieve_chart_data(df, x, y, z, group=None): """ Retrieves data from a dataframe for x, y, z & group inputs complete with date frequency formatting (:meth:`dtale.charts.utils.date_freq_handler`) if specified :param df: dataframe that contains data for chart :type df: :class:`pandas:pandas.DataFrame` :param x: column to use for the X-Axis :type x: str :param y: columns to use for the Y-Axes :type y: list of str :param z: column to use for the Z-Axis :type z: str :param group: column(s) to use for grouping :type group: list of str or str :return: dataframe of data required for chart constructiuon :rtype: :class:`pandas:pandas.DataFrame` """ freq_handler = date_freq_handler(df) cols = [x] + make_list(y) + [z] + make_list(group) return pd.concat([freq_handler(c) for c in cols if c is not None], axis=1)