_style.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. # being a bit too dynamic
  2. # pylint: disable=E1101
  3. from __future__ import division
  4. from contextlib import contextmanager
  5. import warnings
  6. import numpy as np
  7. import pandas.compat as compat
  8. from pandas.compat import lmap, lrange
  9. from pandas.core.dtypes.common import is_list_like
  10. def _get_standard_colors(num_colors=None, colormap=None, color_type='default',
  11. color=None):
  12. import matplotlib.pyplot as plt
  13. if color is None and colormap is not None:
  14. if isinstance(colormap, compat.string_types):
  15. import matplotlib.cm as cm
  16. cmap = colormap
  17. colormap = cm.get_cmap(colormap)
  18. if colormap is None:
  19. raise ValueError("Colormap {0} is not recognized".format(cmap))
  20. colors = lmap(colormap, np.linspace(0, 1, num=num_colors))
  21. elif color is not None:
  22. if colormap is not None:
  23. warnings.warn("'color' and 'colormap' cannot be used "
  24. "simultaneously. Using 'color'")
  25. colors = list(color) if is_list_like(color) else color
  26. else:
  27. if color_type == 'default':
  28. # need to call list() on the result to copy so we don't
  29. # modify the global rcParams below
  30. try:
  31. colors = [c['color']
  32. for c in list(plt.rcParams['axes.prop_cycle'])]
  33. except KeyError:
  34. colors = list(plt.rcParams.get('axes.color_cycle',
  35. list('bgrcmyk')))
  36. if isinstance(colors, compat.string_types):
  37. colors = list(colors)
  38. colors = colors[0:num_colors]
  39. elif color_type == 'random':
  40. import pandas.core.common as com
  41. def random_color(column):
  42. """ Returns a random color represented as a list of length 3"""
  43. # GH17525 use common._random_state to avoid resetting the seed
  44. rs = com.random_state(column)
  45. return rs.rand(3).tolist()
  46. colors = lmap(random_color, lrange(num_colors))
  47. else:
  48. raise ValueError("color_type must be either 'default' or 'random'")
  49. if isinstance(colors, compat.string_types):
  50. import matplotlib.colors
  51. conv = matplotlib.colors.ColorConverter()
  52. def _maybe_valid_colors(colors):
  53. try:
  54. [conv.to_rgba(c) for c in colors]
  55. return True
  56. except ValueError:
  57. return False
  58. # check whether the string can be convertible to single color
  59. maybe_single_color = _maybe_valid_colors([colors])
  60. # check whether each character can be convertible to colors
  61. maybe_color_cycle = _maybe_valid_colors(list(colors))
  62. if maybe_single_color and maybe_color_cycle and len(colors) > 1:
  63. hex_color = [c['color']
  64. for c in list(plt.rcParams['axes.prop_cycle'])]
  65. colors = [hex_color[int(colors[1])]]
  66. elif maybe_single_color:
  67. colors = [colors]
  68. else:
  69. # ``colors`` is regarded as color cycle.
  70. # mpl will raise error any of them is invalid
  71. pass
  72. # Append more colors by cycling if there is not enough color.
  73. # Extra colors will be ignored by matplotlib if there are more colors
  74. # than needed and nothing needs to be done here.
  75. if len(colors) < num_colors:
  76. try:
  77. multiple = num_colors // len(colors) - 1
  78. except ZeroDivisionError:
  79. raise ValueError("Invalid color argument: ''")
  80. mod = num_colors % len(colors)
  81. colors += multiple * colors
  82. colors += colors[:mod]
  83. return colors
  84. class _Options(dict):
  85. """
  86. Stores pandas plotting options.
  87. Allows for parameter aliasing so you can just use parameter names that are
  88. the same as the plot function parameters, but is stored in a canonical
  89. format that makes it easy to breakdown into groups later
  90. """
  91. # alias so the names are same as plotting method parameter names
  92. _ALIASES = {'x_compat': 'xaxis.compat'}
  93. _DEFAULT_KEYS = ['xaxis.compat']
  94. def __init__(self, deprecated=False):
  95. self._deprecated = deprecated
  96. # self['xaxis.compat'] = False
  97. super(_Options, self).__setitem__('xaxis.compat', False)
  98. def __getitem__(self, key):
  99. key = self._get_canonical_key(key)
  100. if key not in self:
  101. raise ValueError(
  102. '{key} is not a valid pandas plotting option'.format(key=key))
  103. return super(_Options, self).__getitem__(key)
  104. def __setitem__(self, key, value):
  105. key = self._get_canonical_key(key)
  106. return super(_Options, self).__setitem__(key, value)
  107. def __delitem__(self, key):
  108. key = self._get_canonical_key(key)
  109. if key in self._DEFAULT_KEYS:
  110. raise ValueError(
  111. 'Cannot remove default parameter {key}'.format(key=key))
  112. return super(_Options, self).__delitem__(key)
  113. def __contains__(self, key):
  114. key = self._get_canonical_key(key)
  115. return super(_Options, self).__contains__(key)
  116. def reset(self):
  117. """
  118. Reset the option store to its initial state
  119. Returns
  120. -------
  121. None
  122. """
  123. self.__init__()
  124. def _get_canonical_key(self, key):
  125. return self._ALIASES.get(key, key)
  126. @contextmanager
  127. def use(self, key, value):
  128. """
  129. Temporarily set a parameter value using the with statement.
  130. Aliasing allowed.
  131. """
  132. old_value = self[key]
  133. try:
  134. self[key] = value
  135. yield self
  136. finally:
  137. self[key] = old_value
  138. plot_params = _Options()