def test_relative_luminance(): """Test relative luminance.""" out1 = utils.relative_luminance("white") assert out1 == 1 out2 = utils.relative_luminance("#000000") assert out2 == 0 out3 = utils.relative_luminance((.25, .5, .75)) assert out3 == pytest.approx(0.201624536) rgbs = mpl.cm.RdBu(np.linspace(0, 1, 10)) lums1 = [utils.relative_luminance(rgb) for rgb in rgbs] lums2 = utils.relative_luminance(rgbs) for lum1, lum2 in zip(lums1, lums2): assert lum1 == pytest.approx(lum2)
def choropleth( self, gdf:gpd.GeoDataFrame, column, cmap=None, legend=True, vmin=None, vmax=None, labels=None, colorbar_fraction = 0.046, colorbar_pad = 0.04, colorbar_shrink = 0.75, **kwargs, ): if legend == 'manual': manual_legend = True legend = False else: manual_legend = False y = gdf.plot( ax=self.ax, column=column, cmap=cmap, legend=legend, vmin=vmin, vmax=vmax, **kwargs ) if manual_legend: mn = gdf[column].min() if vmin is None else vmin mx = gdf[column].max() if vmax is None else vmax from matplotlib.colors import Normalize from matplotlib import cm norm = Normalize(vmin=mn, vmax=mx) n_cmap = cm.ScalarMappable(norm=norm, cmap=cmap) n_cmap.set_array([]) self.fig.colorbar(n_cmap, fraction=colorbar_fraction, pad=colorbar_pad, shrink=colorbar_shrink) if labels is not None: from seaborn.utils import relative_luminance areacolors = y.collections[0].get_facecolors() label_col = labels.pop('column') formatter = labels.pop('formatter', lambda x: x) for r in range(len(gdf)): self.ax.annotate( s=str(formatter(gdf.iloc[r][label_col])), xy=gdf.iloc[r].geometry.representative_point().coords[0], ha='center', va='center', clip_on=True, color=".15" if relative_luminance(areacolors[r]) > .408 else "w", **labels ) return self
def _annotate_heatmap(self, ax, mesh): """Add textual labels with the value in each cell.""" mesh.update_scalarmappable() xpos, ypos = np.meshgrid(ax.get_xticks(), ax.get_yticks()) for x, y, m, color, val in zip(xpos.flat, ypos.flat, mesh.get_array(), mesh.get_facecolors(), self.annot_data.flat): if m is not np.ma.masked: l = relative_luminance(color) text_color = ".15" if l > .408 else "w" annotation = ("{:" + self.fmt + "}").format(val) text_kwargs = dict(color=text_color, ha="center", va="center") text_kwargs.update(self.annot_kws) ax.text(x, y, annotation, **text_kwargs)
def annotate_corr_plot(corr, cmap=None, clim=(-1, 1), ax=None): ax = ax if ax is not None else plt.gca() if isinstance(cmap, str): cmap = plt.cm.get_cmap(cmap) for i in range(corr.shape[0]): for j in range(corr.shape[1]): if cmap is None: invert = abs(corr[i, j]) > .5 else: color = cmap((corr[i, j] - clim[0]) / (clim[1] - clim[0])) invert = relative_luminance(color) <= .408 text_color = 'w' if invert else 'k' text = str(int(100. * corr[i, j])) ax.annotate(text, xy=(j, i), ha='center', va='center', color=text_color)
def create_formats_for_nucleosomes(workbook): """ Creates excel formats for the header that we will use :param workbook: :return: """ color_palette = pd.read_hdf(META_FILE, '/meta/color_palette') # These formats will be used for the color palette color_formats = {} for key, bg_color in color_palette.items(): # I love this hack from Seaborn: font_color = '#000000' if relative_luminance(bg_color) > .408 else '#FFFFFF' color_formats[key] = workbook.add_format(dict(font_color=font_color, shrink=True, align='center', valign='vcenter', bg_color=bg_color)) return color_formats
def annotate_heatmap_string(mesh, annot_data, **kwargs): """ Add textual labels with the value in each cell. (copied from seaborn so that I can pass an array of strings). """ from seaborn.utils import relative_luminance ax = mesh.axes mesh.update_scalarmappable() height, width = annot_data.shape xpos, ypos = np.meshgrid(np.arange(width) + 0.5, np.arange(height) + 0.5) for x, y, m, color, ann in zip(xpos.flat, ypos.flat, mesh.get_array(), mesh.get_facecolors(), annot_data.flat): if m is not np.ma.masked: lum = relative_luminance(color) text_color = ".15" if lum > 0.408 else "w" text_kwargs = dict(color=text_color, ha="center", va="center") text_kwargs.update(**kwargs) ax.text(x, y, ann.decode("UTF-8"), **text_kwargs)
def get_text_color(self, background_color): luminance = sb_utils.relative_luminance(background_color) text_color = ".15" if luminance > .408 else "w" return text_color
def _dotplot( dot_size, dot_color, dot_ax, cmap: str = 'Reds', color_on: Optional[str] = 'dot', y_label: Optional[str] = None, dot_max: Optional[float] = None, dot_min: Optional[float] = None, standard_scale: Literal['var', 'group'] = None, smallest_dot: Optional[float] = 0.0, largest_dot: Optional[float] = 200, size_exponent: Optional[float] = 2, edge_color: Optional[ColorLike] = None, edge_lw: Optional[float] = None, grid: Optional[bool] = False, x_padding: Optional[float] = 0.8, y_padding: Optional[float] = 1.0, vmin: Optional[float] = None, vmax: Optional[float] = None, vcenter: Optional[float] = None, norm: Optional[Normalize] = None, **kwds, ): """\ Makes a *dot plot* given two data frames, one containing the doc size and other containing the dot color. The indices and columns of the data frame are used to label the output image The dots are plotted using :func:`matplotlib.pyplot.scatter`. Thus, additional arguments can be passed. Parameters ---------- dot_size: Data frame containing the dot_size. dot_color: Data frame containing the dot_color, should have the same, shape, columns and indices as dot_size. dot_ax: matplotlib axis cmap String denoting matplotlib color map. color_on Options are 'dot' or 'square'. Be default the colomap is applied to the color of the dot. Optionally, the colormap can be applied to an square behind the dot, in which case the dot is transparent and only the edge is shown. y_label: String. Label for y axis dot_max If none, the maximum dot size is set to the maximum fraction value found (e.g. 0.6). If given, the value should be a number between 0 and 1. All fractions larger than dot_max are clipped to this value. dot_min If none, the minimum dot size is set to 0. If given, the value should be a number between 0 and 1. All fractions smaller than dot_min are clipped to this value. standard_scale Whether or not to standardize that dimension between 0 and 1, meaning for each variable or group, subtract the minimum and divide each by its maximum. smallest_dot If none, the smallest dot has size 0. All expression levels with `dot_min` are plotted with this size. edge_color Dot edge color. When `color_on='dot'` the default is no edge. When `color_on='square'`, edge color is white edge_lw Dot edge line width. When `color_on='dot'` the default is no edge. When `color_on='square'`, line width = 1.5 grid Adds a grid to the plot x_paddding Space between the plot left/right borders and the dots center. A unit is the distance between the x ticks. Only applied when color_on = dot y_paddding Space between the plot top/bottom borders and the dots center. A unit is the distance between the y ticks. Only applied when color_on = dot kwds Are passed to :func:`matplotlib.pyplot.scatter`. Returns ------- matplotlib.colors.Normalize, dot_min, dot_max """ assert dot_size.shape == dot_color.shape, ( 'please check that dot_size ' 'and dot_color dataframes have the same shape') assert list(dot_size.index) == list( dot_color.index), ('please check that dot_size ' 'and dot_color dataframes have the same index') assert list(dot_size.columns) == list(dot_color.columns), ( 'please check that the dot_size ' 'and dot_color dataframes have the same columns') if standard_scale == 'group': dot_color = dot_color.sub(dot_color.min(1), axis=0) dot_color = dot_color.div(dot_color.max(1), axis=0).fillna(0) elif standard_scale == 'var': dot_color -= dot_color.min(0) dot_color = (dot_color / dot_color.max(0)).fillna(0) elif standard_scale is None: pass # make scatter plot in which # x = var_names # y = groupby category # size = fraction # color = mean expression # +0.5 in y and x to set the dot center at 0.5 multiples # this facilitates dendrogram and totals alignment for # matrixplot, dotplot and stackec_violin using the same coordinates. y, x = np.indices(dot_color.shape) y = y.flatten() + 0.5 x = x.flatten() + 0.5 frac = dot_size.values.flatten() mean_flat = dot_color.values.flatten() cmap = pl.get_cmap(kwds.get('cmap', cmap)) if 'cmap' in kwds: del kwds['cmap'] if dot_max is None: dot_max = np.ceil(max(frac) * 10) / 10 else: if dot_max < 0 or dot_max > 1: raise ValueError("`dot_max` value has to be between 0 and 1") if dot_min is None: dot_min = 0 else: if dot_min < 0 or dot_min > 1: raise ValueError("`dot_min` value has to be between 0 and 1") if dot_min != 0 or dot_max != 1: # clip frac between dot_min and dot_max frac = np.clip(frac, dot_min, dot_max) old_range = dot_max - dot_min # re-scale frac between 0 and 1 frac = (frac - dot_min) / old_range size = frac**size_exponent # rescale size to match smallest_dot and largest_dot size = size * (largest_dot - smallest_dot) + smallest_dot normalize = check_colornorm(vmin, vmax, vcenter, norm) if color_on == 'square': if edge_color is None: from seaborn.utils import relative_luminance # use either black or white for the edge color # depending on the luminance of the background # square color edge_color = [] for color_value in cmap(normalize(mean_flat)): lum = relative_luminance(color_value) edge_color.append(".15" if lum > 0.408 else "w") edge_lw = 1.5 if edge_lw is None else edge_lw # first make a heatmap similar to `sc.pl.matrixplot` # (squares with the asigned colormap). Circles will be plotted # on top dot_ax.pcolor(dot_color.values, cmap=cmap, norm=normalize) for axis in ['top', 'bottom', 'left', 'right']: dot_ax.spines[axis].set_linewidth(1.5) kwds = fix_kwds( kwds, s=size, cmap=cmap, linewidth=edge_lw, facecolor='none', edgecolor=edge_color, norm=normalize, ) dot_ax.scatter(x, y, **kwds) else: edge_color = 'none' if edge_color is None else edge_color edge_lw = 0.0 if edge_lw is None else edge_lw color = cmap(normalize(mean_flat)) kwds = fix_kwds( kwds, s=size, cmap=cmap, color=color, linewidth=edge_lw, edgecolor=edge_color, norm=normalize, ) dot_ax.scatter(x, y, **kwds) y_ticks = np.arange(dot_color.shape[0]) + 0.5 dot_ax.set_yticks(y_ticks) dot_ax.set_yticklabels( [dot_color.index[idx] for idx, _ in enumerate(y_ticks)], minor=False) x_ticks = np.arange(dot_color.shape[1]) + 0.5 dot_ax.set_xticks(x_ticks) dot_ax.set_xticklabels( [dot_color.columns[idx] for idx, _ in enumerate(x_ticks)], rotation=90, ha='center', minor=False, ) dot_ax.tick_params(axis='both', labelsize='small') dot_ax.grid(False) dot_ax.set_ylabel(y_label) # to be consistent with the heatmap plot, is better to # invert the order of the y-axis, such that the first group is on # top dot_ax.set_ylim(dot_color.shape[0], 0) dot_ax.set_xlim(0, dot_color.shape[1]) if color_on == 'dot': # add padding to the x and y lims when the color is not in the square # default y range goes from 0.5 to num cols + 0.5 # and default x range goes from 0.5 to num rows + 0.5, thus # the padding needs to be corrected. x_padding = x_padding - 0.5 y_padding = y_padding - 0.5 dot_ax.set_ylim(dot_color.shape[0] + y_padding, -y_padding) dot_ax.set_xlim(-x_padding, dot_color.shape[1] + x_padding) if grid: dot_ax.grid(True, color='gray', linewidth=0.1) dot_ax.set_axisbelow(True) return normalize, dot_min, dot_max
def plot(self, ax, cax): """Draw the heatmap on the provided Axes.""" # Remove all the Axes spines despine(ax=ax, left=True, bottom=True) # Draw the heatmap and annotate height, width = self.plot_data.shape xpos, ypos = np.meshgrid(np.arange(width) + .5, np.arange(height) + .5) data = self.plot_data.data cellsize = self.cellsize mask = self.plot_data.mask if not isinstance(mask, np.ndarray) and not mask: mask = np.zeros(self.plot_data.shape, np.bool) annot_data = self.annot_data if not self.annot: annot_data = np.zeros(self.plot_data.shape) # Draw rectangles instead of using pcolormesh # Might be slower than original heatmap for x, y, m, val, s, an_val in zip(xpos.flat, ypos.flat, mask.flat, data.flat, cellsize.flat, annot_data.flat): if not m: vv = (val - self.vmin) / (self.vmax - self.vmin) size = np.clip(s / self.cellsize_vmax, 0.1, 1.0) color = self.cmap(vv) rect = plt.Rectangle([x - size / 2, y - size / 2], size, size, facecolor=color, **self.rect_kws) ax.add_patch(rect) if self.annot: annotation = ("{:" + self.fmt + "}").format(an_val) text = ax.text(x, y, annotation, **self.annot_kws) # add edge to text text_luminance = relative_luminance(text.get_color()) text_edge_color = ".15" if text_luminance > .408 else "w" text.set_path_effects([ mpl.patheffects.withStroke(linewidth=1, foreground=text_edge_color) ]) # Set the axis limits ax.set(xlim=(0, self.data.shape[1]), ylim=(0, self.data.shape[0])) # Set other attributes ax.set(**self.ax_kws) if self.cbar: norm = mpl.colors.Normalize(vmin=self.vmin, vmax=self.vmax) scalar_mappable = mpl.cm.ScalarMappable(cmap=self.cmap, norm=norm) scalar_mappable.set_array(self.plot_data.data) cb = ax.figure.colorbar(scalar_mappable, cax, ax, **self.cbar_kws) cb.outline.set_linewidth(0) # if kws.get('rasterized', False): # cb.solids.set_rasterized(True) # Add row and column labels if isinstance(self.xticks, string_types) and self.xticks == "auto": xticks, xticklabels = self._auto_ticks(ax, self.xticklabels, 0) else: xticks, xticklabels = self.xticks, self.xticklabels if isinstance(self.yticks, string_types) and self.yticks == "auto": yticks, yticklabels = self._auto_ticks(ax, self.yticklabels, 1) else: yticks, yticklabels = self.yticks, self.yticklabels ax.set(xticks=xticks, yticks=yticks) xtl = ax.set_xticklabels(xticklabels) ytl = ax.set_yticklabels(yticklabels, rotation="vertical") # Possibly rotate them if they overlap ax.figure.draw(ax.figure.canvas.get_renderer()) if axis_ticklabels_overlap(xtl): plt.setp(xtl, rotation="vertical") if axis_ticklabels_overlap(ytl): plt.setp(ytl, rotation="horizontal") # Add the axis labels ax.set(xlabel=self.xlabel, ylabel=self.ylabel) # Invert the y axis to show the plot in matrix form ax.invert_yaxis()