def customize_figure(fig: Figure) -> Figure: """Update the layout and style of plotly figures. Parameters ---------- fig : plotly.graph_objs._figure.Figure The figure to modify Returns ------- plotly.graph_objs._figure.Figure A customized figure. """ fig.update_xaxes(fixedrange=True, title="Values", title_font_size=12) fig.update_yaxes(fixedrange=True, title_font_size=12) fig.update_layout( font_family="Courier New", paper_bgcolor="#205050", plot_bgcolor="#205050", margin={ "l": 60, "t": 40, "r": 10, "b": 10 }, title_font_size=13, template="plotly_dark", ) return fig
def build(z: List[List[float]], scale: Union[str, List[List[Union[float, str]]]], path: str): """ Тут будується графік, після чого зображення зберігається у файл. """ figure = Figure() figure.add_trace( Heatmap(z=z, zmin=0, zmax=1, colorscale=scale, showscale=False)) figure.update_layout(showlegend=False, plot_bgcolor='white', margin={ 't': 0, 'r': 0, 'b': 0, 'l': 0 }) figure.update_xaxes(showticklabels=False, showgrid=False, showline=False, zeroline=False) figure.update_yaxes(scaleanchor='x', scaleratio=1, showticklabels=False, showgrid=False, showline=False, zeroline=False) figure.write_image(path, width=1600, height=800)
def build_mse_dependency( distribution_name: str, thetas: List[float], n_moments: int, n_samples: int, n_runs: int ) -> Figure: figure = Figure() figure.update_layout(title=f"Dependency between moment and MSE for {distribution_name} distribution") figure.update_xaxes(title="k") figure.update_yaxes(title="MSE") for theta in thetas: experiment = Experiment(distribution_name, theta) mse = experiment.run(n_moments, n_samples, n_runs) figure.add_trace(create_mse_scatter(mse, distribution_name, theta)) return figure
def customize_figure(fig: go.Figure) -> go.Figure: """Update the font style, plot background, axes labels, and more. Args: fig (plotly.graph_objs._figure.Figure): Figure to edit. Returns: plotly.graph_objs._figure.Figure: Customized figure. """ fig.update_layout( font_family="serif", plot_bgcolor="#fff", titlefont=dict(size=18, color="#444"), title_x=0.1, ) fig.update_xaxes(fixedrange=True, title_font_size=16) fig.update_yaxes(fixedrange=True, title_font_size=16) return fig
def formatted_fig_json(fig: go.Figure) -> dict: """Formats figure with styling and returns as JSON. :param fig: Plotly figure. :type fig: go.Figure :return: Formatted plotly figure as a JSON dict. :rtype: dict """ fig.update_xaxes(title_font=dict(size=16, color="black"), tickfont={"size": 14}) fig.update_yaxes(title_font=dict(size=16, color="black"), tickfont={"size": 14}) fig.update_layout( hoverlabel_align="right", font_family="Roboto", title_font_size=20, plot_bgcolor="white", paper_bgcolor="white", ) return json.loads(fig.to_json())
def update_yaxes(figure: go.Figure, plot_type: str, **kwargs: Any) -> go.Figure: return figure.update_yaxes( showline=kwargs.get("framed", True), linewidth=2, linecolor="black", mirror=True, gridwidth=1, gridcolor="#ECECEC", fixedrange=plot_type == "distribution", showticklabels=plot_type != "distribution", ).update_yaxes(**kwargs.get("yaxis", {}))
def __update_axes(self, fig: pgo.Figure): if self.properties.x_axis_name is not None: fig.update_xaxes( patch={ 'visible': True, 'title': { 'text': self.properties.x_axis_name, } }, overwrite=True, ) if self.properties.y_axis_name is not None: fig.update_yaxes( patch={ 'visible': True, 'title': { 'text': self.properties.y_axis_name, } }, overwrite=True, )
def timeseries_comparison_plot( series_1: pd.Series, series_2: pd.Series, traces_opts: dict = {}, layout_opts: dict = { "xaxis_title": "Time", "yaxis_title": "Values", "autosize": True, "height": 200, }, line_opts: dict = {}, ) -> Figure: """Create a single time series line plot Plotly figure Returns the plotly figure object. """ fig = Figure() # Only thing I figured is - I could do this s1 = series_1.sort_index() fig.add_scatter( x=s1.index, y=s1, mode="lines", name=s1.name if s1.name else "series_1" ) # Not what is desired - need a line s2 = series_2.sort_index() fig.add_scatter( x=s2.index, y=s2, mode="lines", name=s2.name if s2.name else "series_2" ) # Not what is desired - need a line fig.update_layout(**layout_opts) # see https://plotly.com/python/figure-labels/ fig.update_traces(traces_opts) # set line color? fig.update_layout(margin=dict(l=0, r=0, b=0, t=5, pad=0)) fig.update_yaxes(automargin=True) fig.update_xaxes(automargin=True) return fig
def multi_plotly_timeseries_plot( dataframe: pd.DataFrame, traces_opts: dict = {}, layout_opts: dict = { "xaxis_title": "Time", "yaxis_title": "Values", "autosize": True, "height": 200, }, line_opts: dict = {}, ) -> Figure: """Create a single time series line plot Plotly figure Returns the plotly figure object. """ fig = Figure() columns = list(dataframe.keys()) to_plot_df = dataframe.sort_index() # Only thing I figured is - I could do this for key in columns: fig.add_scatter(x=to_plot_df.index, y=to_plot_df[key], mode="lines", name=key) # Not what is desired - need a line fig.update_layout( **layout_opts) # see https://plotly.com/python/figure-labels/ fig.update_traces(traces_opts) # set line color? fig.update_layout(margin=dict(l=0, r=0, b=0, t=5, pad=0)) fig.update_yaxes(automargin=True) fig.update_xaxes(automargin=True) return fig
def budget_heatmap(df, datetime_column=DATETIME_COLUMN, category_column=CATEGORY_COLUMN, amount_column=AMOUNT_COLUMN, budget=None, moving_average_window=None, start_date=None, end_date=None, fig: go.Figure = None, annotations=True, annotation_text_size=8, delta_column=DELTA_COLUMN): """ TODO: - Docstring - Visually show if there's no data before X date (right now it just looks like spending-budget is great!) Args: df: datetime_column: category_column: amount_column: budget: (or really budget collection?) moving_average_window: start_date: end_date: fig: annotations (bool): If True, write the (amount-budget) value in each cell of the heatmap Returns: """ if fig is None: fig = go.Figure() start_date, end_date, date_range = _parse_dates(datetime_column, df, end_date, start_date, moving_average_window) # If we have no data here, we haven't selected any categories to plot. Escape if len(df) == 0: # no data! return fig # Construct plot zmid = 0 zmin, zmax = get_rounded_z_range_including_mid(df[delta_column], zmid, round_to=10) colorscale = make_centered_rg_colorscale(zmin, zmax, zmid) # For plotting, reverse the order of the rows. Rows picked on the sidebar are populated top to bottom, but y-values # on a heatmap show from bottom to top (increasing y direction) df_sums = _reverse_index_level_order(df, BUDGET_NAME_COLUMN) df_sums.sort_index() fig.add_trace( go.Heatmap( x=df_sums.index.get_level_values(datetime_column), y=df_sums.index.get_level_values(BUDGET_NAME_COLUMN), z=df_sums[DELTA_COLUMN], text=df_sums[DELTA_COLUMN], colorscale=colorscale, zmin=zmin, zmax=zmax, xgap=1, ygap=3, )) if annotations: annotations = _make_annotations(df_sums, annotation_text_size, delta_column) fig.update_layout(annotations=annotations, ) fig.update_layout(**FIGURE_BACKGROUND) fig.update_yaxes(dtick=1) # Ensure axes show full range of data, and pad by same amount as other figures fig.update_xaxes(range=[start_date - X_AXIS_PAD, end_date], dtick="M1") return fig
def main(): # get path to directory filepath = input('Path to directory (blank if PWD): ') # check if valid path if filepath != '': if not path.isdir(filepath): exit('Invalid path') data, lats, lons, flag, good, bad = [], [None, None], [None, None], False, 0, 0 # ask if user wants too show images' metadata (added for my zine) meta = input('\nShow metadata? (y/n): ') if meta.lower() in ('yes', 'y'): metaflag = True elif meta.lower() in ('no', 'n'): metaflag = False else: exit('Invalid response') # read all files in dir for file in glob(filepath + '*'): file = file.lower() # skip non-image files if not file.endswith('.jpg') and not file.endswith( '.jpeg') and not file.endswith('.png'): continue # extract Exif data from image exif = { TAGS[key]: val for key, val in Image.open(file).getexif().items() if key in TAGS } # extract GPS + datetime try: loc = exif['GPSInfo'] dt = exif['DateTimeOriginal'] good += 1 # skip if either missing except KeyError: bad += 1 continue # extract latitude and longitude lat = { 'dir': loc[1], 'deg': loc[2][0], 'min': loc[2][1], 'sec': loc[2][2] } lon = { 'dir': loc[3], 'deg': loc[4][0], 'min': loc[4][1], 'sec': loc[4][2] } # clean and print metadata if metaflag: cleanLat = str(lat['deg']) + '° ' + str(lat['min']) + '\' ' + str( lat['sec']) + '\" ' + str(lat['dir']) cleanLon = str(lon['deg']) + '° ' + str(lon['min']) + '\' ' + str( lon['sec']) + '\" ' + str(lon['dir']) print( f'File: {file} Latitude: {cleanLat} Longitude: {cleanLon} Time: {dt}' ) # calculate full coordinate with degree, minute, second truLat = float(lat['deg'] + (lat['min'] / 60.0) + (lat['sec'] / 3600.0)) truLon = float(lon['deg'] + (lon['min'] / 60.0) + (lon['sec'] / 3600.0)) # calculate mins and maxes if flag: lons[0], lons[1] = min(lons[0], truLon), max(lons[1], truLon) lats[0], lats[1] = min(lats[0], truLat), max(lats[1], truLat) # first time just assign values and flip flag else: lons[0], lons[1] = truLon, truLon lats[0], lats[1] = truLat, truLat flag = True data.append({ 'img': file, 'lat': lat, 'lon': lon, 'datetime': dt, 'truLat': truLat, 'truLon': truLon }) # not enough valid images if good <= 1: exit('Didn\'t find enough valid image files for a visualization.') print( f'\nExtracted metadata from {good} files. Unable to extract from {bad}.\n' ) # prompt for viz choice q = input( 'Please enter the number corresponding to your visualization of choice:\n1: Unsorted path\n2: Sorted path\n3: Both paths overlaid\n\n#: ' ) # validate user input while q not in ('1', '2', '3'): q = input('#: ') q = int(q) coords, sortedCoords, unSortedData = 'M ', 'M ', None # copy data, add first point if q == 1 or q == 3: unSortedData = data.copy() coords += str(unSortedData[0]['truLat']) + ',' + str( unSortedData[0]['truLon']) + ' ' # sort data, add first point if q == 2 or q == 3: data.sort(key=lambda x: x['datetime']) sortedCoords += str(data[0]['truLat']) + ',' + str( data[0]['truLon']) + ' ' # append rest of points for i in range(1, good): if q == 1 or q == 3: coords += ('L' + str(unSortedData[i]['truLat']) + ',' + str(unSortedData[i]['truLon']) + ' ') if q == 2 or q == 3: sortedCoords += ('L' + str(data[i]['truLat']) + ',' + str(data[i]['truLon']) + ' ') paths = [] # if using unsorted, append path if coords != 'M ': paths.append({'type': 'path', 'path': coords, 'line_color': '#3CB371'}) # if using sorted, append path if sortedCoords != 'M ': paths.append({ 'type': 'path', 'path': sortedCoords, 'line_color': '#6666FF' }) fig = Figure(layout=Layout(plot_bgcolor='RGBA(1,1,1,0)')) # draw axes from min to max fig.update_xaxes(range=[lats[0], lats[1]], color='#FFFFFF') fig.update_yaxes(range=[lons[0], lons[1]], color='#FFFFFF') fig.update_layout(shapes=paths) fig.show()
def substituted_data_plot( raw_values: pd.Series, substitutions: pd.Series, message_series: pd.Series = None, traces_opts: dict = {}, layout_opts: dict = { "xaxis_title": "Time", "yaxis_title": "Values", "autosize": True, "height": 200, }, line_opts: dict = {}, ) -> Figure: """Create a single time series line plot Plotly figure Returns the plotly figure object. """ fig = Figure() s1 = raw_values.sort_index() s1 = s1.loc[~s1.index.duplicated(keep="first")] s2 = substitutions.sort_index() s2 = s2.loc[~s2.index.duplicated(keep="first")] completely_handled_series, replaced_originals, replacements, new_values, deleted = handle_substitutions( s1, s2) fig.add_scatter( x=completely_handled_series.index, y=completely_handled_series, mode="markers+lines", name=raw_values.name + "_substituted" if raw_values.name else "raw_values_substituted", line_color="blue", opacity=0.6, ) # Not what is desired - need a line fig.add_scatter( x=replaced_originals.index, y=replaced_originals, mode="markers", name="replaced raw values", line_color="orange", marker=dict(size=10, opacity=0.6), ) # Not what is desired - need a line fig.add_scatter( x=deleted.index, y=deleted, mode="markers", name="ignored raw values", line_color="red", marker=dict(symbol="x", size=10, opacity=0.6), ) # Not what is desired - need a line fig.add_scatter( x=new_values.index, y=new_values, mode="markers", name="added values", line_color="green", marker=dict(symbol="cross", size=10, opacity=0.6), ) # Not what is desired - need a line fig.update_layout( **layout_opts) # see https://plotly.com/python/figure-labels/ fig.update_traces(traces_opts) # set line color? fig.update_layout(margin=dict(l=0, r=0, b=0, t=5, pad=0)) fig.update_yaxes(automargin=True) fig.update_xaxes(automargin=True) return fig
def timeseries_comparison_plot( series_1: pd.Series, series_2: pd.Series, limit_violation_timestamp: str, limit: float, traces_opts: dict = {}, layout_opts: dict = { "xaxis_title": "Time", "yaxis_title": "Values", "autosize": True, "height": 200, }, line_opts: dict = {}, ) -> Figure: """Create a single time series line plot Plotly figure Returns the plotly figure object. """ fig = Figure() # Only thing I figured is - I could do this s1 = series_1.sort_index() fig.add_scatter( x=s1.index, y=s1, mode="lines", name=s1.name if s1.name else "series_1" ) # Not what is desired - need a line s2 = series_2.sort_index() fig.add_scatter( x=s2.index, y=s2, mode="lines", name=s2.name if s2.name else "series_2" ) # Not what is desired - need a line fig.update_layout(**layout_opts) # see https://plotly.com/python/figure-labels/ fig.update_traces(traces_opts) # set line color? vline_y_min = np.min([series_1.min(), series_2.min(), limit]) vline_y_max = np.max([series_1.max(), series_2.max(), limit]) hline_x_min = np.min([series_1.index.min(), series_2.index.min()]) hline_x_max = np.max([series_1.index.max(), series_2.index.max()]) fig.update_layout( shapes=( [ dict( type="line", yref="y", y0=vline_y_min, y1=vline_y_max, xref="x", x0=limit_violation_timestamp, x1=limit_violation_timestamp, ) ] if limit_violation_timestamp is not None else [] ) + [ dict( type="line", yref="y", y0=limit, y1=limit, xref="x", x0=hline_x_min, x1=hline_x_max, line={"color": "red", "width": 1}, ) ] ) fig.update_layout(margin=dict(l=0, r=0, b=0, t=5, pad=0)) fig.update_yaxes(automargin=True) fig.update_xaxes(automargin=True) return fig
def timeseries_comparison_plot( series_1: pd.Series, series_2: pd.Series, freq, freq_factor, max_shifts_past, max_shifts_future, traces_opts: dict = {}, layout_opts: dict = { "xaxis_title": "Time", "yaxis_title": "Values", "autosize": True, "height": 480, }, line_opts: dict = {}, ) -> Figure: """Create a single time series line plot Plotly figure Returns the plotly figure object. """ if max_shifts_past < 0 or max_shifts_future < 0: raise ValueError("Shift maxima values must be greater equal zero.") fig = Figure() s1 = series_1.sort_index() fig.add_scatter( x=s1.index, y=s1, mode="lines", name=s1.name if (s1.name and s1.name != "measurement") else "series", ) s2 = series_2.sort_index() for step in np.arange(-max_shifts_past, max_shifts_future, 1): shifted = s2.copy() shifted.index = shifted.index.shift(periods=step * freq_factor, freq=freq) fig.add_scatter( x=shifted.index, y=shifted, mode="lines", name=shifted.name if (shifted.name and shifted.name != "measurement") else "series_shiftable", visible=False, ) fig.data[max_shifts_past + 1].visible = True # start somewhere steps = [] for i in range(len(fig.data)): step = dict( method="update", args=[ { "visible": [False] * (len(fig.data)) }, { "title": f"Shifted by x times frequency {freq_factor}{freq}: " + str(i - max_shifts_past - 1) }, ], # layout attribute ) step["args"][0]["visible"][i] = True # Toggle i'th trace to "visible" step["args"][0]["visible"][0] = True step["label"] = str(i - max_shifts_past - 1) steps.append(step) sliders = [ dict( active=max_shifts_past, currentvalue={ "prefix": f"Shifted by x times frequency {freq_factor}{freq}: " }, pad={ "t": 80, "b": 10 }, steps=steps[1:], ) ] fig.update_layout( **layout_opts) # see https://plotly.com/python/figure-labels/ fig.update_traces(traces_opts) # set line color? fig.update_layout(margin=dict(l=0, r=0, b=40, t=30, pad=10)) fig.update_yaxes(automargin=True) fig.update_xaxes(automargin=True) fig.update_layout(sliders=sliders) return fig