def _create_week_area( plot: plt.Subplot, week: models.PotentialWeek, pattern: models.PricePatterns, ) -> None: """Add the price par chart for a specific potential week.""" min_prices: List[int] = list() max_prices: List[int] = list() for prices in week.prices: min_prices.append(prices.min) max_prices.append(prices.max) # We need to double up the first and last point so areas for the first and last # period hit the edge of the graph min_prices.insert(0, min_prices[0]) max_prices.insert(0, max_prices[0]) min_prices.append(min_prices[-1]) max_prices.append(max_prices[-1]) # the x locations for the bars price_color = colors.PATTERN_COLORS[pattern] price_color = colors.color(*price_color, alpha=utils.chance_alpha(week.chance)) plot.fill_between( PRICE_PERIODS_EXTENDED, min_prices, max_prices, step="mid", facecolor=price_color, linewidth=0, edgecolor="none", )
def _create_weekday_grid(weekday_labels: plt.Subplot, ) -> None: weekday_labels.grid( axis="x", linestyle="-", linewidth=2, color=colors.DAY_GRID_COLOR, alpha=colors.DAY_GRID_ALPHA, zorder=1, )
def _plot_current_prices(plot_prices: plt.Subplot, ticker: models.Ticker) -> int: current_period = -1 prices: List[Optional[int]] = [ None for _ in range(0, PRICE_PERIOD_COUNT + 2) ] if ticker.purchase_price != 0: prices[0] = ticker.purchase_price for i in range(0, PRICE_PERIOD_COUNT): try: this_price = ticker.prices[i] except IndexError: continue if this_price != 0: prices[i + 1] = this_price current_period = i if ticker.current_period > current_period: current_period = ticker.current_period plot_prices.step( [i for i in range(-1, PRICE_PERIOD_COUNT + 1)], prices, where="mid", linestyle="--", linewidth=4, dash_capstyle="projecting", color=colors.CURRENT_PRICE_COLOR, ) # Annotate the prices for i, this_price in enumerate(ticker.prices): # Zero means unknown, skip it. if this_price == 0: continue plot_prices.annotate( str(this_price), (i, this_price), textcoords="offset points", xytext=(0, 10), ha="center", va="bottom", fontsize=LABEL_SIZE * 1.2, color=colors.CURRENT_PRICE_COLOR, ) return current_period
def _create_price_watermark( plot: plt.Subplot, name: str, price: int, va_top: bool, pattern: Optional["models.PricePatterns"], ) -> None: y_text_offset = 5 if va_top: vertical_alignment = "top" y_text_offset *= -1 else: vertical_alignment = "bottom" if pattern is None: pattern_color = colors.CURRENT_PRICE_COLOR else: pattern_color = colors.PATTERN_COLORS[pattern] # Create a dotted line line at the break-even point that matches the price # progression price_line = mlines.Line2D( [0, 4], [price, price], color=pattern_color, linewidth=2, linestyle="--", ) plot.add_line(price_line) plot.annotate( f"{name}: {price}", (2, price), textcoords="offset points", xytext=(0, y_text_offset), horizontalalignment="center", verticalalignment=vertical_alignment, color=pattern_color, fontsize=LABEL_SIZE, bbox={ "boxstyle": "Square,pad=0.3", "facecolor": colors.color(0, 0, 0, alpha=0.08), "edgecolor": "none", "linewidth": 0, }, )
def _set_legend(labels: np.ndarray, cmap: colors.ListedColormap, ax: plt.Subplot): unique_labels = np.unique(labels) legend_elements = [Patch(facecolor=cmap.colors[i], label=unique_label) for i, unique_label in enumerate(unique_labels)] legend = ax.legend(handles=legend_elements, title='Clusters', fontsize=14, title_fontsize=14, loc="lower left") legend.get_frame().set_alpha(None) legend.get_frame().set_facecolor((1, 1, 1, 0.25))
def show_image(img: np.array, ax: plt.Subplot = None, title: str = ''): if ax: ax.set_title(title) ax.set_yticklabels([]) ax.set_xticklabels([]) ax.axes.get_xaxis().set_visible(False) ax.axes.get_yaxis().set_visible(False) ax.imshow(img) else: plt.imshow(img)
def _add_cursor(plot_prices: plt.Subplot, current_period: int) -> None: # Create a shaded region for the current period rect = patches.Rectangle( (current_period - 0.5, 0), 1, 701, linewidth=0, color=colors.CURRENT_PRICE_COLOR, alpha=0.1, ) # Add the patch to the Axes plot_prices.add_patch(rect) # Create a line at the head of the current price period current_period_line = mlines.Line2D( [current_period - 0.5, current_period - 0.5], [0, 700], color=colors.CURRENT_PRICE_COLOR, ) plot_prices.add_line(current_period_line)
def draw(self, ax: plt.Subplot, c: str = 'k', lw: int = 1, **kwargs): """ draw this rect Parameters ---------- ax subplot object to use for the plotting c color argument passed to plot lw linewidth argument passed to plot """ ax.plot([ self.west_edge, self.east_edge, self.east_edge, self.west_edge, self.west_edge ], [ self.north_edge, self.north_edge, self.south_edge, self.south_edge, self.north_edge ], c=c, lw=lw, **kwargs)
def add_proportion(ax1: plt.Subplot, ax2: plt.Subplot, series: pd.Series, name: str) -> list: for key in series.keys(): if series[key] < .05 * sum(series): if 'other' not in series.keys(): series['other'] = 0 series['other'] += series.pop(key) wedges, _, autotexts = ax2.pie(series.values, autopct='%1.1f%%', startangle=90, colors=COLORS, counterclock=False) ax1.legend(wedges, series.keys(), title=name, loc="center") ax2.axis('equal') ax1.axis('off') return autotexts
def plot_price_periods( plot_prices: plt.Subplot, options: ForecastOptions, ) -> None: # set bg color for all graphs for plot in [plot_prices]: plot.set_facecolor(options.bg_color) # Remove all spines for spine in plot.spines.values(): spine.set_visible(False) plot_prices.set_facecolor(options.bg_color) plot_prices.set_ylim(PRICE_Y_LIM) plot_prices.set_xlim([-1, 12]) plot_prices.axes.set_yticks(np.arange(0, 701, 100)) plot_prices.grid( axis="y", linestyle="-", linewidth=0.5, color=colors.PRICE_GRID_COLOR, alpha=colors.PRICE_GRID_ALPHA, ) forecast = options.forecast for pattern in forecast.patterns: for week in pattern.potential_weeks: _create_week_area(plot_prices, week, pattern.pattern) big_pattern = utils.get_pattern(forecast, models.PricePatterns.BIGSPIKE) if big_pattern.chance > 0: _create_pattern_line( plot_prices, spike_breakdown=[x for x in forecast.spikes.big.breakdown], spike_pattern=big_pattern, spike_color=colors.BIG_SPIKE_COLOR, ) small_pattern = utils.get_pattern(forecast, models.PricePatterns.SMALLSPIKE) if small_pattern.chance > 0: _create_pattern_line( plot_prices, spike_breakdown=[x for x in forecast.spikes.small.breakdown], spike_pattern=small_pattern, spike_color=colors.SMALL_SPIKE_COLOR, ) bottom_axis = plot_prices.axes # the x locations for the bars indTods = np.arange(PRICE_PERIOD_COUNT) # Add the time of day labels for each bar bottom_axis.axes.set_xlim(plot_prices.get_xlim()) bottom_axis.axes.set_xticks(indTods) bottom_axis.axes.set_xticklabels(PRICE_TODS) bottom_axis.spines["bottom"].set_position(("axes", -0.01)) # Create weekday labels weekday_labels = bottom_axis.twiny() # We need to make sure the limits match to line up with the bars weekday_labels.set_xlim(plot_prices.get_xlim()) # Place the weekdays between the AM / PM values weekday_labels.set_xticks([0.5, 2.5, 4.5, 6.5, 8.5, 10.5]) # Set the labels weekday_labels.set_xticklabels(PRICE_DAYS) # Move the weekday labels down a littls weekday_labels.spines["bottom"].set_position(("axes", -0.06)) _create_weekday_grid(weekday_labels) # style price axes plot_prices.tick_params( axis="y", labelcolor=colors.PRICE_LABEL_COLOR, labelsize=LABEL_SIZE, labeltop=False, labelbottom=False, labelright=True, labelleft=True, bottom=False, top=False, left=False, ) # style TOD labels bottom_axis.tick_params( axis="x", labelcolor=colors.DAY_LABEL_COLOR, labelsize=LABEL_SIZE, labeltop=False, labelbottom=True, labelright=False, labelleft=False, bottom=False, top=False, left=False, ) # style weekday labels weekday_labels.tick_params( axis="both", labelcolor=colors.DAY_LABEL_COLOR, labelsize=LABEL_SIZE, labelbottom=True, labeltop=False, bottom=False, top=False, left=False, ) # Style tod and day label for current price period current_period = _plot_current_prices(plot_prices, options.ticker) if current_period != -1: bbox = dict( boxstyle="round,pad=0.5", color=colors.DAY_LABEL_COLOR, ) current_tod_label = bottom_axis.xaxis.get_ticklabels()[current_period] current_tod_label.set_color("white") current_tod_label.set_bbox(bbox) _add_cursor(plot_prices, current_period) all_spines = itertools.chain( plot_prices.spines.values(), weekday_labels.spines.values(), ) for spine in all_spines: spine.set_visible(False)
def plot_pattern_chances(plot: plt.Subplot, forecast: models.Forecast) -> None: total = 0.0 valid_patterns = sorted( (x for x in forecast.patterns if x.chance != 0), key=lambda x: x.chance, reverse=True, ) for i, potential_pattern in enumerate(valid_patterns): if i > 0: # Add a little bit of space between sections total += 0.01 chance = potential_pattern.chance # If the chance gets below 2.5%, the bar disappears, so we need to have a # minimum visual value for it. adjusted_chance = max(potential_pattern.chance, 0.015) pattern_color = colors.PATTERN_COLORS[potential_pattern.pattern] plot.barh( 0.5, height=0.8, width=adjusted_chance, left=total, color=colors.color(*pattern_color, alpha=chance), linewidth=0, ) patten_name = PATTERN_NAMES[potential_pattern.pattern] plot.annotate( f"{patten_name}: {utils.format_chance(chance)}", (total + (adjusted_chance / 2), 0.5), color="white", alpha=0.75, fontsize=LABEL_SIZE * chance**0.9 + LABEL_SIZE / 2, fontweight="bold", ha="center", va="center", ) total += adjusted_chance plot.set_xlim((0, total)) plot.set_ylim((0, 1)) # remove axis labels plot.tick_params( axis="both", labeltop=False, labelbottom=False, labelright=False, labelleft=False, top=False, bottom=False, left=False, right=False, ) plot.patch.set_visible(False) for spine in plot.spines.values(): spine.set_visible(False)
def plot_prices_range( plot: plt.Subplot, ticker: models.Ticker, forecast: models.Forecast, ) -> None: max_pattern = models.PricePatterns.UNKNOWN guaranteed_pattern = models.PricePatterns.UNKNOWN min_pattern = models.PricePatterns.UNKNOWN break_even = ticker.purchase_price plot.axes.set_ylim(PRICE_Y_LIM) plot.axes.set_xlim([0, 4]) bar_anchors = np.arange(4) for i, potential_pattern in enumerate(forecast.patterns): bar_position = bar_anchors[i] if len(potential_pattern.potential_weeks) == 0: continue min_price = potential_pattern.prices_future.min max_price = potential_pattern.prices_future.max pattern_color = colors.PATTERN_COLORS[potential_pattern.pattern] bar_color = colors.color(*pattern_color, alpha=potential_pattern.chance) plot.bar( [bar_position], [max_price - min_price], bottom=min_price, width=0.4, align="edge", color=bar_color, ) if potential_pattern.prices_future.max == forecast.prices_future.max: max_pattern = potential_pattern.pattern if (potential_pattern.prices_future.guaranteed == forecast.prices_future.guaranteed): guaranteed_pattern = potential_pattern.pattern if potential_pattern.prices_future.min == forecast.prices_future.min: min_pattern = potential_pattern.pattern # Create a price grid that matches the price progression plot.grid( axis="y", linestyle="-", linewidth=0.5, color=colors.PRICE_GRID_COLOR, alpha=colors.PRICE_GRID_ALPHA, ) # Create a dotted line line at the breakeven point that matches the price # progression if break_even != 0: _create_price_watermark(plot, "break-even", break_even, va_top=False, pattern=None) # Create a dotted line line at the max profit point _create_price_watermark( plot, "potential", forecast.prices_future.max, va_top=False, pattern=max_pattern, ) # Create a dotted line line at the max guaranteed point _create_price_watermark( plot, "guaranteed", forecast.prices_future.guaranteed, va_top=True, pattern=guaranteed_pattern, ) if forecast.prices_summary.min != forecast.prices_summary.guaranteed: # Create a dotted line line at the min price _create_price_watermark( plot, "minimum", forecast.prices_future.min, va_top=True, pattern=min_pattern, ) # remove axis labels plot.tick_params( axis="both", labeltop=False, labelbottom=False, labelright=False, labelleft=False, top=False, bottom=False, left=False, right=False, ) plot.patch.set_visible(False) for position, spine in plot.spines.items(): spine.set_visible(False)
try: results.append(all_results[hyp][metric_index]) # choose index based on 'returns', 'action_entropy_trajectory', etc. except: print('missing') results.append(np.zeros([50, 100])) results = np.array(results) results = results.transpose([1, 0, 2]) data_ent = results N = 20 # Valentin's plots from matplotlib.pyplot import Subplot fig = plt.figure(figsize = (9, 6.5)) ax = Subplot(fig, 111) fig.add_subplot(ax) # ax.axis["right"].set_visible(False) # ax.axis["top"].set_visible(False) save_freq = 5 vec = np.arange(data_ent.mean(0).T[:, 0].shape[0]) * save_freq # vec = # vec = np.logspace(0, np.log10(50000), data_ent.mean(0).T[:, 0].shape[0]) # T = 10 # fig_names = ['return', ''] # colors = [u'b', u'g', u'r', u'c', u'm', u'y'] #['blue', 'dodgerblue', 'black', 'orange', 'red'] ylabels = ['returns', 'discounted returns', 'Action entropy trajectory', 'Online state visitation entropy', 'Offline state visitation entropy'] ylabels = ['Returns', 'discounted returns', 'Action Entropy', 'State Entropy', 'Offline State Entropy'] # colors = plt.cm.seismic(np.linspace(0, 1, n))
def plot_stop_accessibility_hist(subplot: Subplot, result: CrossStopRemovalResult): """Plot the accessibility difference in ``result`` as a subplot onto ``subplot``.""" # ----- Plot histogram # https://datavizpyr.com/overlapping-histograms-with-matplotlib-in-python/ # Configure plot subplot.set_xlabel("Distance to stop (km)", size=14) subplot.set_ylabel("Agent count", size=14) subplot.set_title( f"Distance to stop between before and after removing {result.stop_removed.cross_name}" ) # Plot histograms subplot.hist(result.metrics_before.data, bins=20, alpha=0.5, label="Before") subplot.hist(result.metrics_after.data, bins=20, alpha=0.5, label="After") # Post-configure the plot subplot.legend(loc="upper right")
def _remove_axes(ax: plt.Subplot): ax.set(yticklabels=[], xticklabels=[]) ax.tick_params(left=False, bottom=False) plt.tight_layout()
def _create_pattern_line( price_plot: plt.Subplot, spike_breakdown: List[float], spike_pattern: models.PotentialPattern, spike_color: List[float], ) -> None: """Create a dashed line on the max price for periods with a potential spike""" # get highest possible price for each period first_period = spike_pattern.spike.start last_period = spike_pattern.spike.end high_price = spike_pattern.prices_summary.max period_highs: List[Optional[int]] = [ high_price for _ in range(first_period, last_period + 1) ] price_periods: List[float] = [ p for p in range(first_period, last_period + 1) ] # Instead of using a plot, we are just going to draw the line directly. # Create a line at the head of the current price period current_period_line = mlines.Line2D( [first_period - 0.5, last_period + 0.5], [high_price, high_price], color=spike_color, linestyle="--", linewidth=4, # We want this line to be always visible so our alpha floor is going to be .1 alpha=max(utils.chance_alpha(spike_pattern.chance), 0.1), ) price_plot.add_line(current_period_line) y_annotation_distance = 10 for period in price_periods: if period == 11.5: continue try: chance = spike_breakdown[int(period)] except TypeError: # If this is period '11.5', then we don't need to annotate it, that value # is just here for line continuation continue # We need to render the numbers ABOVE the spike patter so that they don't # get illegible as the spike placement becomes more certain. price_plot.annotate( utils.format_chance(chance), (period, high_price), textcoords="offset points", xytext=(0, y_annotation_distance), ha="center", va="bottom", fontsize=LABEL_SIZE, color=spike_color, ) # Annotate the total chance of this spike price_plot.annotate( utils.format_chance(spike_pattern.chance), (first_period - 0.5 - 0.1, high_price), ha="right", va="center", fontsize=LABEL_SIZE, color="white", alpha=0.75, bbox={ "boxstyle": "Circle,pad=0.6", "facecolor": colors.color(*spike_color), "edgecolor": spike_color, "linewidth": 0, }, ) # Add a label to the daily chances for clarity middle = first_period + (last_period - first_period) / 2 middle_price = next(p for p in period_highs if p) price_plot.annotate( "daily:", (middle, middle_price + y_annotation_distance * 2.5), textcoords="offset points", xytext=(0, y_annotation_distance), ha="center", va="bottom", fontsize=LABEL_SIZE * 1.1, color=spike_color, )
def plot_stop_accessibility_cdf(subplot: Subplot, result: CrossStopRemovalResult): """Plot the accessibility difference in ``result`` as a subplot onto ``subplot``.""" # pylint: disable=invalid-name # Configure plot subplot.set_xlabel("Distance to stop (km)", size=20) subplot.set_ylabel("Percentile", size=20) subplot.set_title(result.stop_removed.cross_name, size=24) # Plot CDF x, y = result.metrics_before.get_quantile_cdf(20) subplot.plot(x, y, label="Before") x, y = result.metrics_after.get_quantile_cdf(20) subplot.plot(x, y, label="After") # Post-configure the plot subplot.legend(loc="upper right")