RhodiumGroup/rhg_compute_tools

Design tools

Closed this issue · 2 comments

Color palettes & chart prep

Color schemes

  • RHG Colors
  • Impactlab colors

Fonts

  • Auto-loaded fonts
  • Tools for creating correct sizes, fonts, etc based on application, use case

Chart creation tools

  • Automatic binning by application type, color scheme
  • Automatic formatting with labels, etc. for different types of charts

Options for implementation

  • Seaborn configuration
  • Manual functions accepting a fig or ax handle

Color palettes

CIL palettes

import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap, ListedColormap

custom_continuous_cmaps = {
    'cil_RdBu': [
        "#e0603f", "#ed7453", "#f68f50", "#ffaa4d",
        "#ffcb58", "#ffea80", "#ffffad", "#d9ffd9",
        "#bbeae4", "#88d8da", "#55c7d2", "#41bad1",
        "#41a9c1", "#4197b0"],

    'cil_Reds': [
        "#FFDB6C", "#ffcb58", "#FFBB53", "#ffaa4d",
        "#FB9D4F", "#f68f50", "#F28252", "#ed7453",
        "#E76A49", "#e0603f", "#D45433", "#c84726",
        "#b53919"],

    'cil_Blues': [
        "#A2E1DF", "#88d8da", "#6FD0D6", "#55c7d2",
        "#4BC1D2", "#41bad1", "#41B2C9", "#41a9c1",
        "#41A0B9", "#4197b0", "#398AA1", "#307c92",
        "#065b74"],

    'rhg_Blues': ["#ACD8F1", "#63AAD6", "#0078AD", "#055A7F", "#023B56"],
    'rhg_Greens': ["#C1E399", "#A0D55F", "#77B530", "#59901B", "#366108"],
    'rhg_Yellows': ["#FDE8A5", "#FBD568", "#E7B731", "#C49C20", "#926E00"],
    'rhg_Oranges': ["#F9C7A2", "#FEA569", "#E97625", "#C45506", "#913F05"],
    'rhg_Reds': ["#ECA5AB", "#E66967", "#C32524", "#920605", "#6C0405"],
    'rhg_Purples': ["#D7BBE3", "#BE95CF", "#915FA4", "#633A76", "#6C0405"],
}

for cmap_name, cmap_colors in custom_continuous_cmaps.items():
    cmap = LinearSegmentedColormap.from_list(cmap_name, cmap_colors)
    plt.register_cmap(cmap=cmap)
    cmap_r = cmap.reversed()
    plt.register_cmap(cmap=cmap_r)

custom_discrete_cmaps = {
    'rhg_standard': ["#0078AD","#77B530","#E7B731","#E97625","#C32524","#915FA4"],
    'rhg_light': ["#63AAD6","#A0D55F","#FBD568","#FEA569","#E66967","#BE95CF"],
}

for cmap_name, cmap_colors in custom_discrete_cmaps.items():
    cmap = ListedColormap(cmap_colors, name=cmap_name)
    plt.register_cmap(cmap=cmap)
    cmap_r = cmap.reversed()
    plt.register_cmap(cmap=cmap_r)

Plotting helpers

eq_hist implementation

import matplotlib.pyplot as plt
import matplotlib
from matplotlib.colors import LinearSegmentedColormap
import numpy as np
import pandas as pd

try:
    string_types = (str, unicode)
except NameError:
    string_types = (str, )

def get_color_scheme(values, cmap=None, colors=None, levels=None, how=None):
    
    mini, maxi = float(values.min()), float(values.max())
    amax = max(abs(mini), abs(maxi))

    if (cmap is None) and (colors is not None):
        cmap = LinearSegmentedColormap.from_list('custom_cmap', colors)
    elif cmap is None:
        if (mini < 0) and (maxi > 0):
            cmap = matplotlib.cm.RdBu_r
        else:
            cmap = matplotlib.cm.viridis
    elif isinstance(cmap, string_types):
        cmap = matplotlib.cm.get_cmap(cmap)

    if how is None and levels is not None:
        norm = matplotlib.colors.BoundaryNorm(
            levels,
            cmap.N)

    elif (how is None) or (how == 'eq_hist'):
        if levels is None:
            levels = 11

        bin_edges = np.percentile(
            values[~np.isnan(values)], np.linspace(0, 100, levels))

        norm = matplotlib.colors.BoundaryNorm(
            bin_edges,
            cmap.N)
        
    elif how == 'log':
        norm = matplotlib.colors.LogNorm(vmin=mini, vmax=maxi)

    elif how == 'symlog':
        norm = matplotlib.colors.SymLogNorm(
            vmin=-amax, vmax=amax, linthresh=(amax / 100))

    elif how == 'linear':
        norm = matplotlib.colors.Normalize(vmin=mini, vmax=maxi)
        
    else:
        raise ValueError(
            'color scheme `how` argument {} not recognized. '
            'choose from {eq_hist, log, symlog, linear} or '
            'provide `levels`'
            .format(how))
    
    return cmap, norm

geopandas - add colorbar with correct norm/cmap

def add_colorbar(ax, cmap, norm, orientation='vertical', **kwargs):
    n_cmap = matplotlib.cm.ScalarMappable(norm=norm, cmap=cmap)
    n_cmap.set_array([])
    cbar = ax.get_figure().colorbar(n_cmap, ax=ax, orientation=orientation, **kwargs)

    return cbar

Example plotting with geopandas:

fig, ax = plt.subplots(1, 1, figsize=(16, 7), subplot_kw={'projection': ccrs.PlateCarree()})

cmap, norm = get_color_scheme(msas.val, cmap='cil_RdBu_r', how='eq_hist')
msas[msas.val.notnull()].plot('val', vmin=norm.vmin, vmax=norm.vmax, cmap=cmap, norm=norm, ax=ax)
ax.set_xlim(-127, -67)
ax.set_ylim(23, 51)
add_colorbar(ax, cmap=cmap, norm=norm)
import functools

def rhg_common():
    matplotlib.rcParams['axes.spines.top'] = False
    matplotlib.rcParams['axes.spines.right'] = False

def use_common(func):
    @functools.wraps(func)
    def inner(*args, **kwargs):
        rhg_common()
        return func(*args, **kwargs)
    return inner

@use_common
def rhg_powerpoint():
    matplotlib.rcParams['axes.prop_cycle'] = cycler(color=custom_discrete_cmaps['rhg_standard'])
    matplotlib.rc('lines', linewidth=2, color='g')
    matplotlib.rc('font', family='monospace', weight='bold', size=11)
    matplotlib.rcParams['figure.figsize'] = (9.5, 5.5)

@use_common
def rhg_powerpoint_widescreen():
    matplotlib.rcParams['axes.prop_cycle'] = cycler(color=custom_discrete_cmaps['rhg_standard'])
    matplotlib.rc('lines', linewidth=2, color='g')
    matplotlib.rc('font', family='fantasy', weight='bold', size=11)
    matplotlib.rcParams['figure.figsize'] = (9.5, 5.5)

@use_common
def rhg_note():
    matplotlib.rcParams['axes.prop_cycle'] = cycler(color=custom_discrete_cmaps['rhg_standard'])
    matplotlib.rc('lines', linewidth=1, color='g')
    matplotlib.rc('font', family='serif', weight='bold', size=11)
    matplotlib.rcParams['figure.figsize'] = (5.4, 3.21)