##
# Special thanks to @krey for the python3 support
##
import numpy as np
import colorsys
import colorlover as cl
import operator
import copy
from collections import deque
from six import string_types
from IPython.display import HTML, display
from . import themes
from .utils import inverseDict
from .auth import get_config_file
[docs]class CufflinksError(Exception):
pass
[docs]def to_rgba(color, alpha):
"""
Converts from hex|rgb to rgba
Parameters:
-----------
color : string
Color representation on hex or rgb
alpha : float
Value from 0 to 1.0 that represents the
alpha value.
Example:
to_rgba('#E1E5ED',0.6)
to_rgba('#f03',0.7)
to_rgba('rgb(23,23,23)',.5)
"""
if type(color) == tuple:
color, alpha = color
color = color.lower()
if 'rgba' in color:
cl = list(eval(color.replace('rgba', '')))
if alpha:
cl[3] = alpha
return 'rgba' + str(tuple(cl))
elif 'rgb' in color:
r, g, b = eval(color.replace('rgb', ''))
return 'rgba' + str((r, g, b, alpha))
else:
return to_rgba(hex_to_rgb(color), alpha)
[docs]def hex_to_rgb(color):
"""
Converts from hex to rgb
Parameters:
-----------
color : string
Color representation on hex or rgb
Example:
hex_to_rgb('#E1E5ED')
hex_to_rgb('#f03')
"""
color = normalize(color)
color = color[1:]
# return 'rgb'+str(tuple(ord(c) for c in color.decode('hex')))
return 'rgb' + str((int(color[0:2], base=16), int(color[2:4], base=16), int(color[4:6], base=16)))
[docs]def normalize(color):
"""
Returns an hex color
Parameters:
-----------
color : string
Color representation in rgba|rgb|hex
Example:
normalize('#f03')
"""
if type(color) == tuple:
color = to_rgba(*color)
if 'rgba' in color:
return rgb_to_hex(rgba_to_rgb(color))
elif 'rgb' in color:
return rgb_to_hex(color)
elif '#' in color:
if len(color) == 7:
return color
else:
color = color[1:]
return '#' + ''.join([x * 2 for x in list(color)])
else:
try:
return normalize(cnames[color.lower()])
except:
raise CufflinksError('Not a valid color: ' + color)
[docs]def rgb_to_hex(color):
"""
Converts from rgb to hex
Parameters:
-----------
color : string
Color representation on hex or rgb
Example:
rgb_to_hex('rgb(23,25,24)')
"""
rgb = eval(color.replace('rgb', ''))
# return '#'+''.join(map(chr, rgb)).encode('hex')
return '#' + ''.join(['{0:02x}'.format(x).upper() for x in rgb])
[docs]def rgba_to_rgb(color, bg='rgb(255,255,255)'):
"""
Converts from rgba to rgb
Parameters:
-----------
color : string
Color representation in rgba
bg : string
Color representation in rgb
Example:
rgba_to_rgb('rgb(23,25,24,.4)''
"""
def c_tup(c):
return eval(c[c.find('('):])
color = c_tup(color)
bg = hex_to_rgb(normalize(bg))
bg = c_tup(bg)
a = color[3]
r = [int((1 - a) * bg[i] + a * color[i]) for i in range(3)]
return 'rgb' + str(tuple(r))
[docs]def hex_to_hsv(color):
"""
Converts from hex to hsv
Parameters:
-----------
color : string
Color representation on color
Example:
hex_to_hsv('#ff9933')
"""
color = normalize(color)
color = color[1:]
# color=tuple(ord(c)/255.0 for c in color.decode('hex'))
color = (int(color[0:2], base=16) / 255.0, int(color[2:4],
base=16) / 255.0, int(color[4:6], base=16) / 255.0)
return colorsys.rgb_to_hsv(*color)
[docs]def color_range(color, N=20):
"""
Generates a scale of colours from a base colour
Parameters:
-----------
color : string
Color representation in hex
N : int
number of colours to generate
Example:
color_range('#ff9933',20)
"""
color = normalize(color)
org = color
color = hex_to_hsv(color)
HSV_tuples = [(color[0], x, color[2]) for x in np.arange(0, 1, 2.0 / N)]
HSV_tuples.extend([(color[0], color[1], x)
for x in np.arange(0, 1, 2.0 / N)])
hex_out = []
for c in HSV_tuples:
c = colorsys.hsv_to_rgb(*c)
c = [int(_ * 255) for _ in c]
# hex_out.append("#"+"".join([chr(x).encode('hex') for x in c]))
hex_out.append("#" + "".join(['{0:02x}'.format(x) for x in c]))
if org not in hex_out:
hex_out.append(org)
hex_out.sort()
return hex_out
[docs]def color_table(color, N=1, sort=False, sort_values=False, inline=False, as_html=False):
"""
Generates a colour table
Parameters:
-----------
color : string | list | dict
Color representation in rgba|rgb|hex
If a list of colors is passed then these
are displayed in a table
N : int
number of colours to generate
When color is not a list then it generaes
a range of N colors
sort : bool
if True then items are sorted
sort_values : bool
if True then items are sorted by color values.
Only applies if color is a dictionary
inline : bool
if True it returns single line color blocks
as_html : bool
if True it returns the HTML code
Example:
color_table('#ff9933')
color_table(cufflinks.cnames)
color_table(['pink','salmon','yellow'])
Note:
This function only works in iPython Notebook
"""
if isinstance(color, list):
c_ = ''
rgb_tup = [normalize(c) for c in color]
if sort:
rgb_tup.sort()
elif isinstance(color, dict):
c_ = ''
items = [(k, normalize(v), hex_to_hsv(normalize(v)))
for k, v in list(color.items())]
if sort_values:
items = sorted(items, key=operator.itemgetter(2))
elif sort:
items = sorted(items, key=operator.itemgetter(0))
rgb_tup = [(k, v) for k, v, _ in items]
else:
c_ = normalize(color)
if N > 1:
rgb_tup = np.array(color_range(c_, N))[::-1]
else:
rgb_tup = [c_]
def _color(c):
if hex_to_hsv(c)[2] < .5:
color = "#ffffff"
shadow = '0 1px 0 #000'
else:
color = "#000000"
shadow = '0 1px 0 rgba(255,255,255,0.6)'
if c == c_:
border = " border: 1px solid #ffffff;"
else:
border = ''
return color, shadow, border
s = '<ul style="list-style-type: none;">' if not inline else ''
for c in rgb_tup:
if isinstance(c, tuple):
k, c = c
k += ' : '
else:
k = ''
if inline:
s += '<div style="background-color:{0};height:20px;width:20px;display:inline-block;"></div>'.format(
c)
else:
color, shadow, border = _color(c)
s += """<li style="text-align:center;""" + border + """line-height:30px;background-color:""" + c + """;">
<span style=" text-shadow:""" + shadow + """; color:""" + color + """;">""" + k + c.upper() + """</span>
</li>"""
s += '</ul>' if not inline else ''
if as_html:
return s
return display(HTML(s))
[docs]def colorgen(colors=None, n=None, scale=None, theme=None):
"""
Returns a generator with a list of colors
and gradients of those colors
Parameters:
-----------
colors : list(colors)
List of colors to use
Example:
colorgen()
colorgen(['blue','red','pink'])
colorgen(['#f03','rgb(23,25,25)'])
"""
step = .1
if not colors:
if not scale:
if not theme:
scale = get_config_file()['colorscale']
else:
scale = themes.THEMES[theme]['colorscale']
colors = get_scales(scale)
dq = deque(colors)
if n:
step = len(dq) * 0.8 / n if len(dq) * 8 < n else .1
for i in np.arange(.2, 1, step):
for y in dq:
yield to_rgba(y, 1 - i + .2)
dq.rotate(1)
# NEW STUFF
# Color Names
# ---------------------------------
cnames = {'aliceblue': '#F0F8FF',
'antiquewhite': '#FAEBD7',
'aqua': '#00FFFF',
'aquamarine': '#7FFFD4',
'azure': '#F0FFFF',
'beige': '#F5F5DC',
'bisque': '#FFE4C4',
'black': '#000000',
'blanchedalmond': '#FFEBCD',
'blue': '#3780bf',
'bluegray': '#565656',
'bluepurple': '#6432AB',
'blueviolet': '#8A2BE2',
'brick': '#E24A33',
'brightblue': '#0000FF',
'brightred': '#FF0000',
'brown': '#A52A2A',
'burlywood': '#DEB887',
'cadetblue': '#5F9EA0',
'charcoal': '#151516',
'chartreuse': '#7FFF00',
'chocolate': '#D2691E',
'coral': '#FF7F50',
'cornflowerblue': '#6495ED',
'cornsilk': '#FFF8DC',
'crimson': '#DC143C',
'cyan': '#00FFFF',
'darkblue': '#00008B',
'darkcyan': '#008B8B',
'darkgoldenrod': '#B8860B',
'darkgray': '#A9A9A9',
'darkgreen': '#006400',
'darkgrey': '#A9A9A9',
'darkkhaki': '#BDB76B',
'darkmagenta': '#8B008B',
'darkolivegreen': '#556B2F',
'darkorange': '#FF8C00',
'darkorchid': '#9932CC',
'darkred': '#8B0000',
'darksalmon': '#E9967A',
'darkseagreen': '#8FBC8F',
'darkslateblue': '#483D8B',
'darkslategray': '#2F4F4F',
'darkslategrey': '#2F4F4F',
'darkturquoise': '#00CED1',
'darkviolet': '#9400D3',
'deeppink': '#FF1493',
'deepskyblue': '#00BFFF',
'dimgray': '#696969',
'dimgrey': '#696969',
'dodgerblue': '#1E90FF',
'firebrick': '#B22222',
'floralwhite': '#FFFAF0',
'forestgreen': '#228B22',
'fuchsia': '#FF00FF',
'gainsboro': '#DCDCDC',
'ghostwhite': '#F8F8FF',
'gold': '#FFD700',
'goldenrod': '#DAA520',
'grassgreen': '#32ab60',
'gray': '#808080',
'green': '#008000',
'greenyellow': '#ADFF2F',
'grey': '#808080',
'grey01': '#0A0A0A',
'grey02': '#151516',
'grey03': '#1A1A1C',
'grey04': '#1E1E21',
'grey05': '#252529',
'grey06': '#36363C',
'grey07': '#3C3C42',
'grey08': '#434343',
'grey09': '#666570',
'grey10': '#666666',
'grey11': '#8C8C8C',
'grey12': '#C2C2C2',
'grey13': '#E2E2E2',
'grey14': '#E5E5E5',
'honeydew': '#F0FFF0',
'hotpink': '#FF69B4',
'indianred': '#CD5C5C',
'indigo': '#4B0082',
'ivory': '#FFFFF0',
'java': '#17BECF',
'khaki': '#F0E68C',
'lavender': '#E6E6FA',
'lavenderblush': '#FFF0F5',
'lawngreen': '#7CFC00',
'lemonchiffon': '#FFFACD',
'lightpink2': '#fccde5',
'lightpurple': '#bc80bd',
'lightblue': '#ADD8E6',
'lightcoral': '#F08080',
'lightcyan': '#E0FFFF',
'lightgoldenrodyellow': '#FAFAD2',
'lightgray': '#D3D3D3',
'lightgreen': '#90EE90',
'lightgrey': '#D3D3D3',
'lightivory': '#F6F6F6',
'lightpink': '#FFB6C1',
'lightsalmon': '#FFA07A',
'lightseagreen': '#20B2AA',
'lightskyblue': '#87CEFA',
'lightslategray': '#778899',
'lightslategrey': '#778899',
'lightsteelblue': '#B0C4DE',
'lightteal': '#8dd3c7',
'lightyellow': '#FFFFE0',
'lightblue2': '#80b1d3',
'lightviolet': '#8476CA',
'lime': '#00FF00',
'lime2': '#8EBA42',
'limegreen': '#32CD32',
'linen': '#FAF0E6',
'magenta': '#FF00FF',
'maroon': '#800000',
'mediumaquamarine': '#66CDAA',
'mediumblue': '#0000CD',
'mediumgray': '#656565',
'mediumorchid': '#BA55D3',
'mediumpurple': '#9370DB',
'mediumseagreen': '#3CB371',
'mediumslateblue': '#7B68EE',
'mediumspringgreen': '#00FA9A',
'mediumturquoise': '#48D1CC',
'mediumvioletred': '#C71585',
'midnightblue': '#191970',
'mintcream': '#F5FFFA',
'mistyrose': '#FFE4E1',
'moccasin': '#FFE4B5',
'mustard': '#FBC15E',
'navajowhite': '#FFDEAD',
'navy': '#000080',
'oldlace': '#FDF5E6',
'olive': '#808000',
'olivedrab': '#6B8E23',
'orange': '#ff9933',
'orangered': '#FF4500',
'orchid': '#DA70D6',
'palegoldenrod': '#EEE8AA',
'palegreen': '#98FB98',
'paleolive': '#b3de69',
'paleturquoise': '#AFEEEE',
'palevioletred': '#DB7093',
'papayawhip': '#FFEFD5',
'peachpuff': '#FFDAB9',
'pearl': '#D9D9D9',
'pearl02': '#F5F6F9',
'pearl03': '#E1E5ED',
'pearl04': '#9499A3',
'pearl05': '#6F7B8B',
'pearl06': '#4D5663',
'peru': '#CD853F',
'pink': '#ff0088',
'pinksalmon': '#FFB5B8',
'plum': '#DDA0DD',
'polar': '#ACAFB5',
'polarblue': '#0080F0',
'polarbluelight': '#46A0F0',
'polarcyan': '#ADFCFC',
'polardark': '#484848',
'polardiv': '#D5D8DB',
'polardust': '#F2F3F7',
'polargrey': '#505050',
'polargreen': '#309054',
'polarorange': '#EE7600',
'polarpurple': '#6262DE',
'polarred': '#D94255',
'powderblue': '#B0E0E6',
'purple': '#800080',
'red': '#db4052',
'rose': '#FFC0CB',
'rosybrown': '#BC8F8F',
'royalblue': '#4169E1',
'saddlebrown': '#8B4513',
'salmon': '#fb8072',
'sandybrown': '#FAA460',
'seaborn': '#EAE7E4',
'seagreen': '#2E8B57',
'seashell': '#FFF5EE',
'sienna': '#A0522D',
'silver': '#C0C0C0',
'skyblue': '#87CEEB',
'slateblue': '#6A5ACD',
'slategray': '#708090',
'slategrey': '#708090',
'smurf': '#3E6FB0',
'snow': '#FFFAFA',
'springgreen': '#00FF7F',
'steelblue': '#4682B4',
'tan': '#D2B48C',
'teal': '#008080',
'thistle': '#D8BFD8',
'tomato': '#FF6347',
'turquoise': '#40E0D0',
'violet': '#EE82EE',
'wheat': '#F5DEB3',
'white': '#FFFFFF',
'whitesmoke': '#F5F5F5',
'yellow': '#ffff33',
'yellowgreen': '#9ACD32',
"henanigans_bg": "#242424",
"henanigans_blue1": "#5F95DE",
"henanigans_blue2": "#93B6E6",
"henanigans_cyan1": "#7EC4CF",
"henanigans_cyan2": "#B6ECF3",
"henanigans_dark1": "#040404",
"henanigans_dark2": "#141414",
"henanigans_dialog1": "#444459",
"henanigans_dialog2": "#5D5D7A",
"henanigans_green1": "#8BD155",
"henanigans_green2": "#A0D17B",
"henanigans_grey1": "#343434",
"henanigans_grey2": "#444444",
"henanigans_light1": "#A4A4A4",
"henanigans_light2": "#F4F4F4",
"henanigans_orange1": "#EB9E58",
"henanigans_orange2": "#EBB483",
"henanigans_purple1": "#C98FDE",
"henanigans_purple2": "#AC92DE",
"henanigans_red1": "#F77E70",
"henanigans_red2": "#DE958E",
"henanigans_yellow1": "#E8EA7E",
"henanigans_yellow2": "#E9EABE"
}
# Custom Color Scales
# ---------------------------------
_custom_scales = {
'qual': {
# dflt only exists to keep backward compatibility after issue 91
'dflt': ['orange', 'blue', 'grassgreen', 'purple', 'red', 'teal', 'yellow', 'olive', 'salmon', 'lightblue2'],
'original': ['orange', 'blue', 'grassgreen', 'purple', 'red', 'teal', 'yellow', 'olive', 'salmon', 'lightblue2'],
'ggplot': ['brick', 'smurf', 'lightviolet', 'mediumgray', 'mustard', 'lime2', 'pinksalmon'],
'polar': ['polarblue', 'polarorange', 'polargreen', 'polarpurple', 'polarred', 'polarcyan', 'polarbluelight']
},
'div': {
},
'seq': {
}
}
# ---------------------------------------------------------------
# The below functions are based in colorlover by Jack Parmer
# https://github.com/jackparmer/colorlover/
# ---------------------------------------------------------------
_scales = None
_scales_names = None
[docs]def interp(colors, N):
def _interp(colors, N):
try:
return cl.interp(colors, N)
except:
return _interp(colors, N + 1)
c = _interp(colors, N)
return list(map(rgb_to_hex, cl.to_rgb(c)))
[docs]def scales(scale=None):
"""
Displays a color scale (HTML)
Parameters:
-----------
scale : str
Color scale name
If no scale name is provided then all scales are returned
(max number for each scale)
If scale='all' then all scale combinations available
will be returned
Example:
scales('accent')
scales('all')
scales()
"""
if scale:
if scale == 'all':
display(HTML(cl.to_html(_scales)))
else:
display(HTML(cl.to_html(get_scales(scale))))
else:
s = ''
keys = list(_scales_names.keys())
keys.sort()
for k in keys:
scale = get_scales(k)
s += '<div style="display:inline-block;padding:10px;"><div>{0}</div>{1}</div>'.format(
k, cl.to_html(scale))
display(HTML(s))
# Scales Dictionary
# ---------------------------------
[docs]def reset_scales():
global _scales
global _scales_names
scale_cpy = cl.scales.copy()
# Add custom scales
for k, v in list(_custom_scales.items()):
if v:
for k_, v_ in list(v.items()):
if str(len(v_)) not in scale_cpy:
scale_cpy[str(len(v_))] = {}
scale_cpy[str(len(v_))][k][k_] = [
hex_to_rgb(normalize(_)) for _ in v_]
# Dictionary by Type > Name > N
_scales = {}
for k, v in list(scale_cpy.items()):
for k_, v_ in list(v.items()):
if k_ not in _scales:
_scales[k_] = {}
for k__, v__ in list(v_.items()):
if k__ not in _scales[k_]:
_scales[k_][k__] = {}
_scales[k_][k__][k] = v__
# Dictionary by Name > N
_scales_names = {}
for k, v in list(scale_cpy.items()):
for k_, v_ in list(v.items()):
for k__, v__ in list(v_.items()):
k__ = k__.lower()
if k__ not in _scales_names:
_scales_names[k__] = {}
_scales_names[k__][k] = v__
[docs]def get_scales(scale=None, n=None):
"""
Returns a color scale
Parameters:
-----------
scale : str
Color scale name
If the color name is preceded by a minus (-)
then the scale is inversed
n : int
Number of colors
If n < number of colors available for a given scale then
the minimum number will be returned
If n > number of colors available for a given scale then
the maximum number will be returned
Example:
get_scales('accent',8)
get_scales('pastel1')
"""
if scale:
is_reverse = False
if scale[0] == '-':
scale = scale[1:]
is_reverse = True
d = copy.deepcopy(_scales_names[scale.lower()])
keys = list(map(int, list(d.keys())))
cs = None
if n:
if n in keys:
cs = d[str(n)]
elif n < min(keys):
cs = d[str(min(keys))]
if cs is None:
cs = d[str(max(keys))]
if is_reverse:
cs.reverse()
return cs
else:
d = {}
for k, v in list(_scales_names.items()):
if isinstance(v, dict):
keys = list(map(int, list(v.keys())))
d[k] = v[str(max(keys))]
else:
d[k] = v
return d
[docs]def get_colorscale(scale):
"""
Returns a color scale to be used for a plotly figure
Parameters:
-----------
scale : str or list
Color scale name
If the color name is preceded by a minus (-)
then the scale is inversed.
Also accepts a list of colors (rgb,rgba,hex)
Example:
get_colorscale('accent')
get_colorscale(['rgb(127,201,127)','rgb(190,174,212)','rgb(253,192,134)'])
"""
if type(scale) in string_types:
scale = get_scales(scale)
else:
if type(scale) != list:
raise Exception(
"scale needs to be either a scale name or list of colors")
cs = [[1.0 * c / (len(scale) - 1), scale[c]] for c in range(len(scale))]
cs.sort()
return cs
reset_scales()