widgets.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. from matplotlib.colors import LinearSegmentedColormap
  4. try:
  5. from ipywidgets import interact, FloatSlider, IntSlider
  6. except ImportError:
  7. def interact(f):
  8. msg = "Interactive palettes require `ipywidgets`, which is not installed."
  9. raise ImportError(msg)
  10. from .miscplot import palplot
  11. from .palettes import (color_palette, dark_palette, light_palette,
  12. diverging_palette, cubehelix_palette)
  13. __all__ = ["choose_colorbrewer_palette", "choose_cubehelix_palette",
  14. "choose_dark_palette", "choose_light_palette",
  15. "choose_diverging_palette"]
  16. def _init_mutable_colormap():
  17. """Create a matplotlib colormap that will be updated by the widgets."""
  18. greys = color_palette("Greys", 256)
  19. cmap = LinearSegmentedColormap.from_list("interactive", greys)
  20. cmap._init()
  21. cmap._set_extremes()
  22. return cmap
  23. def _update_lut(cmap, colors):
  24. """Change the LUT values in a matplotlib colormap in-place."""
  25. cmap._lut[:256] = colors
  26. cmap._set_extremes()
  27. def _show_cmap(cmap):
  28. """Show a continuous matplotlib colormap."""
  29. from .rcmod import axes_style # Avoid circular import
  30. with axes_style("white"):
  31. f, ax = plt.subplots(figsize=(8.25, .75))
  32. ax.set(xticks=[], yticks=[])
  33. x = np.linspace(0, 1, 256)[np.newaxis, :]
  34. ax.pcolormesh(x, cmap=cmap)
  35. def choose_colorbrewer_palette(data_type, as_cmap=False):
  36. """Select a palette from the ColorBrewer set.
  37. These palettes are built into matplotlib and can be used by name in
  38. many seaborn functions, or by passing the object returned by this function.
  39. Parameters
  40. ----------
  41. data_type : {'sequential', 'diverging', 'qualitative'}
  42. This describes the kind of data you want to visualize. See the seaborn
  43. color palette docs for more information about how to choose this value.
  44. Note that you can pass substrings (e.g. 'q' for 'qualitative.
  45. as_cmap : bool
  46. If True, the return value is a matplotlib colormap rather than a
  47. list of discrete colors.
  48. Returns
  49. -------
  50. pal or cmap : list of colors or matplotlib colormap
  51. Object that can be passed to plotting functions.
  52. See Also
  53. --------
  54. dark_palette : Create a sequential palette with dark low values.
  55. light_palette : Create a sequential palette with bright low values.
  56. diverging_palette : Create a diverging palette from selected colors.
  57. cubehelix_palette : Create a sequential palette or colormap using the
  58. cubehelix system.
  59. """
  60. if data_type.startswith("q") and as_cmap:
  61. raise ValueError("Qualitative palettes cannot be colormaps.")
  62. pal = []
  63. if as_cmap:
  64. cmap = _init_mutable_colormap()
  65. if data_type.startswith("s"):
  66. opts = ["Greys", "Reds", "Greens", "Blues", "Oranges", "Purples",
  67. "BuGn", "BuPu", "GnBu", "OrRd", "PuBu", "PuRd", "RdPu", "YlGn",
  68. "PuBuGn", "YlGnBu", "YlOrBr", "YlOrRd"]
  69. variants = ["regular", "reverse", "dark"]
  70. @interact
  71. def choose_sequential(name=opts, n=(2, 18),
  72. desat=FloatSlider(min=0, max=1, value=1),
  73. variant=variants):
  74. if variant == "reverse":
  75. name += "_r"
  76. elif variant == "dark":
  77. name += "_d"
  78. if as_cmap:
  79. colors = color_palette(name, 256, desat)
  80. _update_lut(cmap, np.c_[colors, np.ones(256)])
  81. _show_cmap(cmap)
  82. else:
  83. pal[:] = color_palette(name, n, desat)
  84. palplot(pal)
  85. elif data_type.startswith("d"):
  86. opts = ["RdBu", "RdGy", "PRGn", "PiYG", "BrBG",
  87. "RdYlBu", "RdYlGn", "Spectral"]
  88. variants = ["regular", "reverse"]
  89. @interact
  90. def choose_diverging(name=opts, n=(2, 16),
  91. desat=FloatSlider(min=0, max=1, value=1),
  92. variant=variants):
  93. if variant == "reverse":
  94. name += "_r"
  95. if as_cmap:
  96. colors = color_palette(name, 256, desat)
  97. _update_lut(cmap, np.c_[colors, np.ones(256)])
  98. _show_cmap(cmap)
  99. else:
  100. pal[:] = color_palette(name, n, desat)
  101. palplot(pal)
  102. elif data_type.startswith("q"):
  103. opts = ["Set1", "Set2", "Set3", "Paired", "Accent",
  104. "Pastel1", "Pastel2", "Dark2"]
  105. @interact
  106. def choose_qualitative(name=opts, n=(2, 16),
  107. desat=FloatSlider(min=0, max=1, value=1)):
  108. pal[:] = color_palette(name, n, desat)
  109. palplot(pal)
  110. if as_cmap:
  111. return cmap
  112. return pal
  113. def choose_dark_palette(input="husl", as_cmap=False):
  114. """Launch an interactive widget to create a dark sequential palette.
  115. This corresponds with the :func:`dark_palette` function. This kind
  116. of palette is good for data that range between relatively uninteresting
  117. low values and interesting high values.
  118. Requires IPython 2+ and must be used in the notebook.
  119. Parameters
  120. ----------
  121. input : {'husl', 'hls', 'rgb'}
  122. Color space for defining the seed value. Note that the default is
  123. different than the default input for :func:`dark_palette`.
  124. as_cmap : bool
  125. If True, the return value is a matplotlib colormap rather than a
  126. list of discrete colors.
  127. Returns
  128. -------
  129. pal or cmap : list of colors or matplotlib colormap
  130. Object that can be passed to plotting functions.
  131. See Also
  132. --------
  133. dark_palette : Create a sequential palette with dark low values.
  134. light_palette : Create a sequential palette with bright low values.
  135. cubehelix_palette : Create a sequential palette or colormap using the
  136. cubehelix system.
  137. """
  138. pal = []
  139. if as_cmap:
  140. cmap = _init_mutable_colormap()
  141. if input == "rgb":
  142. @interact
  143. def choose_dark_palette_rgb(r=(0., 1.),
  144. g=(0., 1.),
  145. b=(0., 1.),
  146. n=(3, 17)):
  147. color = r, g, b
  148. if as_cmap:
  149. colors = dark_palette(color, 256, input="rgb")
  150. _update_lut(cmap, colors)
  151. _show_cmap(cmap)
  152. else:
  153. pal[:] = dark_palette(color, n, input="rgb")
  154. palplot(pal)
  155. elif input == "hls":
  156. @interact
  157. def choose_dark_palette_hls(h=(0., 1.),
  158. l=(0., 1.), # noqa: E741
  159. s=(0., 1.),
  160. n=(3, 17)):
  161. color = h, l, s
  162. if as_cmap:
  163. colors = dark_palette(color, 256, input="hls")
  164. _update_lut(cmap, colors)
  165. _show_cmap(cmap)
  166. else:
  167. pal[:] = dark_palette(color, n, input="hls")
  168. palplot(pal)
  169. elif input == "husl":
  170. @interact
  171. def choose_dark_palette_husl(h=(0, 359),
  172. s=(0, 99),
  173. l=(0, 99), # noqa: E741
  174. n=(3, 17)):
  175. color = h, s, l
  176. if as_cmap:
  177. colors = dark_palette(color, 256, input="husl")
  178. _update_lut(cmap, colors)
  179. _show_cmap(cmap)
  180. else:
  181. pal[:] = dark_palette(color, n, input="husl")
  182. palplot(pal)
  183. if as_cmap:
  184. return cmap
  185. return pal
  186. def choose_light_palette(input="husl", as_cmap=False):
  187. """Launch an interactive widget to create a light sequential palette.
  188. This corresponds with the :func:`light_palette` function. This kind
  189. of palette is good for data that range between relatively uninteresting
  190. low values and interesting high values.
  191. Requires IPython 2+ and must be used in the notebook.
  192. Parameters
  193. ----------
  194. input : {'husl', 'hls', 'rgb'}
  195. Color space for defining the seed value. Note that the default is
  196. different than the default input for :func:`light_palette`.
  197. as_cmap : bool
  198. If True, the return value is a matplotlib colormap rather than a
  199. list of discrete colors.
  200. Returns
  201. -------
  202. pal or cmap : list of colors or matplotlib colormap
  203. Object that can be passed to plotting functions.
  204. See Also
  205. --------
  206. light_palette : Create a sequential palette with bright low values.
  207. dark_palette : Create a sequential palette with dark low values.
  208. cubehelix_palette : Create a sequential palette or colormap using the
  209. cubehelix system.
  210. """
  211. pal = []
  212. if as_cmap:
  213. cmap = _init_mutable_colormap()
  214. if input == "rgb":
  215. @interact
  216. def choose_light_palette_rgb(r=(0., 1.),
  217. g=(0., 1.),
  218. b=(0., 1.),
  219. n=(3, 17)):
  220. color = r, g, b
  221. if as_cmap:
  222. colors = light_palette(color, 256, input="rgb")
  223. _update_lut(cmap, colors)
  224. _show_cmap(cmap)
  225. else:
  226. pal[:] = light_palette(color, n, input="rgb")
  227. palplot(pal)
  228. elif input == "hls":
  229. @interact
  230. def choose_light_palette_hls(h=(0., 1.),
  231. l=(0., 1.), # noqa: E741
  232. s=(0., 1.),
  233. n=(3, 17)):
  234. color = h, l, s
  235. if as_cmap:
  236. colors = light_palette(color, 256, input="hls")
  237. _update_lut(cmap, colors)
  238. _show_cmap(cmap)
  239. else:
  240. pal[:] = light_palette(color, n, input="hls")
  241. palplot(pal)
  242. elif input == "husl":
  243. @interact
  244. def choose_light_palette_husl(h=(0, 359),
  245. s=(0, 99),
  246. l=(0, 99), # noqa: E741
  247. n=(3, 17)):
  248. color = h, s, l
  249. if as_cmap:
  250. colors = light_palette(color, 256, input="husl")
  251. _update_lut(cmap, colors)
  252. _show_cmap(cmap)
  253. else:
  254. pal[:] = light_palette(color, n, input="husl")
  255. palplot(pal)
  256. if as_cmap:
  257. return cmap
  258. return pal
  259. def choose_diverging_palette(as_cmap=False):
  260. """Launch an interactive widget to choose a diverging color palette.
  261. This corresponds with the :func:`diverging_palette` function. This kind
  262. of palette is good for data that range between interesting low values
  263. and interesting high values with a meaningful midpoint. (For example,
  264. change scores relative to some baseline value).
  265. Requires IPython 2+ and must be used in the notebook.
  266. Parameters
  267. ----------
  268. as_cmap : bool
  269. If True, the return value is a matplotlib colormap rather than a
  270. list of discrete colors.
  271. Returns
  272. -------
  273. pal or cmap : list of colors or matplotlib colormap
  274. Object that can be passed to plotting functions.
  275. See Also
  276. --------
  277. diverging_palette : Create a diverging color palette or colormap.
  278. choose_colorbrewer_palette : Interactively choose palettes from the
  279. colorbrewer set, including diverging palettes.
  280. """
  281. pal = []
  282. if as_cmap:
  283. cmap = _init_mutable_colormap()
  284. @interact
  285. def choose_diverging_palette(
  286. h_neg=IntSlider(min=0,
  287. max=359,
  288. value=220),
  289. h_pos=IntSlider(min=0,
  290. max=359,
  291. value=10),
  292. s=IntSlider(min=0, max=99, value=74),
  293. l=IntSlider(min=0, max=99, value=50), # noqa: E741
  294. sep=IntSlider(min=1, max=50, value=10),
  295. n=(2, 16),
  296. center=["light", "dark"]
  297. ):
  298. if as_cmap:
  299. colors = diverging_palette(h_neg, h_pos, s, l, sep, 256, center)
  300. _update_lut(cmap, colors)
  301. _show_cmap(cmap)
  302. else:
  303. pal[:] = diverging_palette(h_neg, h_pos, s, l, sep, n, center)
  304. palplot(pal)
  305. if as_cmap:
  306. return cmap
  307. return pal
  308. def choose_cubehelix_palette(as_cmap=False):
  309. """Launch an interactive widget to create a sequential cubehelix palette.
  310. This corresponds with the :func:`cubehelix_palette` function. This kind
  311. of palette is good for data that range between relatively uninteresting
  312. low values and interesting high values. The cubehelix system allows the
  313. palette to have more hue variance across the range, which can be helpful
  314. for distinguishing a wider range of values.
  315. Requires IPython 2+ and must be used in the notebook.
  316. Parameters
  317. ----------
  318. as_cmap : bool
  319. If True, the return value is a matplotlib colormap rather than a
  320. list of discrete colors.
  321. Returns
  322. -------
  323. pal or cmap : list of colors or matplotlib colormap
  324. Object that can be passed to plotting functions.
  325. See Also
  326. --------
  327. cubehelix_palette : Create a sequential palette or colormap using the
  328. cubehelix system.
  329. """
  330. pal = []
  331. if as_cmap:
  332. cmap = _init_mutable_colormap()
  333. @interact
  334. def choose_cubehelix(n_colors=IntSlider(min=2, max=16, value=9),
  335. start=FloatSlider(min=0, max=3, value=0),
  336. rot=FloatSlider(min=-1, max=1, value=.4),
  337. gamma=FloatSlider(min=0, max=5, value=1),
  338. hue=FloatSlider(min=0, max=1, value=.8),
  339. light=FloatSlider(min=0, max=1, value=.85),
  340. dark=FloatSlider(min=0, max=1, value=.15),
  341. reverse=False):
  342. if as_cmap:
  343. colors = cubehelix_palette(256, start, rot, gamma,
  344. hue, light, dark, reverse)
  345. _update_lut(cmap, np.c_[colors, np.ones(256)])
  346. _show_cmap(cmap)
  347. else:
  348. pal[:] = cubehelix_palette(n_colors, start, rot, gamma,
  349. hue, light, dark, reverse)
  350. palplot(pal)
  351. if as_cmap:
  352. return cmap
  353. return pal