How to pick a new color for each plotted line within a figure in matplotlib? - matplotlib

I'd like to NOT specify a color for each plotted line, and have each line get a distinct color. But if I run:
from matplotlib import pyplot as plt
for i in range(20):
plt.plot([0, 1], [i, i])
plt.show()
then I get this output:
If you look at the image above, you can see that matplotlib attempts to pick colors for each line that are different, but eventually it re-uses colors - the top ten lines use the same colors as the bottom ten. I just want to stop it from repeating already used colors AND/OR feed it a list of colors to use.

I usually use the second one of these:
from matplotlib.pyplot import cm
import numpy as np
#variable n below should be number of curves to plot
#version 1:
color = cm.rainbow(np.linspace(0, 1, n))
for i, c in zip(range(n), color):
plt.plot(x, y, c=c)
#or version 2:
color = iter(cm.rainbow(np.linspace(0, 1, n)))
for i in range(n):
c = next(color)
plt.plot(x, y, c=c)
Example of 2:

matplotlib 1.5+
You can use axes.set_prop_cycle (example).
matplotlib 1.0-1.4
You can use axes.set_color_cycle (example).
matplotlib 0.x
You can use Axes.set_default_color_cycle.

You can use a predefined "qualitative colormap" like this:
from matplotlib.cm import get_cmap
name = "Accent"
cmap = get_cmap(name) # type: matplotlib.colors.ListedColormap
colors = cmap.colors # type: list
axes.set_prop_cycle(color=colors)
Tested on matplotlib 3.0.3. See https://github.com/matplotlib/matplotlib/issues/10840 for discussion on why you can't call axes.set_prop_cycle(color=cmap).
A list of predefined qualititative colormaps is available at https://matplotlib.org/gallery/color/colormap_reference.html :

prop_cycle
color_cycle was deprecated in 1.5 in favor of this generalization: http://matplotlib.org/users/whats_new.html#added-axes-prop-cycle-key-to-rcparams
# cycler is a separate package extracted from matplotlib.
from cycler import cycler
import matplotlib.pyplot as plt
plt.rc('axes', prop_cycle=(cycler('color', ['r', 'g', 'b'])))
plt.plot([1, 2])
plt.plot([2, 3])
plt.plot([3, 4])
plt.plot([4, 5])
plt.plot([5, 6])
plt.show()
Also shown in the (now badly named) example: http://matplotlib.org/1.5.1/examples/color/color_cycle_demo.html mentioned at: https://stackoverflow.com/a/4971431/895245
Tested in matplotlib 1.5.1.

I don't know if you can automatically change the color, but you could exploit your loop to generate different colors:
for i in range(20):
ax1.plot(x, y, color = (0, i / 20.0, 0, 1)
In this case, colors will vary from black to 100% green, but you can tune it if you want.
See the matplotlib plot() docs and look for the color keyword argument.
If you want to feed a list of colors, just make sure that you have a list big enough and then use the index of the loop to select the color
colors = ['r', 'b', ...., 'w']
for i in range(20):
ax1.plot(x, y, color = colors[i])

You can also change the default color cycle in your matplotlibrc file.
If you don't know where that file is, do the following in python:
import matplotlib
matplotlib.matplotlib_fname()
This will show you the path to your currently used matplotlibrc file.
In that file you will find amongst many other settings also the one for axes.color.cycle. Just put in your desired sequence of colors and you will find it in every plot you make.
Note that you can also use all valid html color names in matplotlib.

As Ciro's answer notes, you can use prop_cycle to set a list of colors for matplotlib to cycle through. But how many colors? What if you want to use the same color cycle for lots of plots, with different numbers of lines?
One tactic would be to use a formula like the one from https://gamedev.stackexchange.com/a/46469/22397, to generate an infinite sequence of colors where each color tries to be significantly different from all those that preceded it.
Unfortunately, prop_cycle won't accept infinite sequences - it will hang forever if you pass it one. But we can take, say, the first 1000 colors generated from such a sequence, and set it as the color cycle. That way, for plots with any sane number of lines, you should get distinguishable colors.
Example:
from matplotlib import pyplot as plt
from matplotlib.colors import hsv_to_rgb
from cycler import cycler
# 1000 distinct colors:
colors = [hsv_to_rgb([(i * 0.618033988749895) % 1.0, 1, 1])
for i in range(1000)]
plt.rc('axes', prop_cycle=(cycler('color', colors)))
for i in range(20):
plt.plot([1, 0], [i, i])
plt.show()
Output:
Now, all the colors are different - although I admit that I struggle to distinguish a few of them!

Related

How to se BG color over an Histogram graph in matplotlb [duplicate]

I am making a scatter plot in matplotlib and need to change the background of the actual plot to black. I know how to change the face color of the plot using:
fig = plt.figure()
fig.patch.set_facecolor('xkcd:mint green')
My issue is that this changes the color of the space around the plot. How to I change the actual background color of the plot?
Use the set_facecolor(color) method of the axes object, which you've created one of the following ways:
You created a figure and axis/es together
fig, ax = plt.subplots(nrows=1, ncols=1)
You created a figure, then axis/es later
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1) # nrows, ncols, index
You used the stateful API (if you're doing anything more than a few lines, and especially if you have multiple plots, the object-oriented methods above make life easier because you can refer to specific figures, plot on certain axes, and customize either)
plt.plot(...)
ax = plt.gca()
Then you can use set_facecolor:
ax.set_facecolor('xkcd:salmon')
ax.set_facecolor((1.0, 0.47, 0.42))
As a refresher for what colors can be:
matplotlib.colors
Matplotlib recognizes the following formats to specify a color:
an RGB or RGBA tuple of float values in [0, 1] (e.g., (0.1, 0.2, 0.5) or (0.1, 0.2, 0.5, 0.3));
a hex RGB or RGBA string (e.g., '#0F0F0F' or '#0F0F0F0F');
a string representation of a float value in [0, 1] inclusive for gray level (e.g., '0.5');
one of {'b', 'g', 'r', 'c', 'm', 'y', 'k', 'w'};
a X11/CSS4 color name;
a name from the xkcd color survey; prefixed with 'xkcd:' (e.g., 'xkcd:sky blue');
one of {'tab:blue', 'tab:orange', 'tab:green', 'tab:red', 'tab:purple', 'tab:brown', 'tab:pink', 'tab:gray', 'tab:olive', 'tab:cyan'} which are the Tableau Colors from the ‘T10’ categorical palette (which is the default color cycle);
a “CN” color spec, i.e. 'C' followed by a single digit, which is an index into the default property cycle (matplotlib.rcParams['axes.prop_cycle']); the indexing occurs at artist creation time and defaults to black if the cycle does not include color.
All string specifications of color, other than “CN”, are case-insensitive.
One method is to manually set the default for the axis background color within your script (see Customizing matplotlib):
import matplotlib.pyplot as plt
plt.rcParams['axes.facecolor'] = 'black'
This is in contrast to Nick T's method which changes the background color for a specific axes object. Resetting the defaults is useful if you're making multiple different plots with similar styles and don't want to keep changing different axes objects.
Note: The equivalent for
fig = plt.figure()
fig.patch.set_facecolor('black')
from your question is:
plt.rcParams['figure.facecolor'] = 'black'
Something like this? Use the axisbg keyword to subplot:
>>> from matplotlib.figure import Figure
>>> from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
>>> figure = Figure()
>>> canvas = FigureCanvas(figure)
>>> axes = figure.add_subplot(1, 1, 1, axisbg='red')
>>> axes.plot([1,2,3])
[<matplotlib.lines.Line2D object at 0x2827e50>]
>>> canvas.print_figure('red-bg.png')
(Granted, not a scatter plot, and not a black background.)
Simpler answer:
ax = plt.axes()
ax.set_facecolor('silver')
If you already have axes object, just like in Nick T's answer, you can also use
ax.patch.set_facecolor('black')
The easiest thing is probably to provide the color when you create the plot :
fig1 = plt.figure(facecolor=(1, 1, 1))
or
fig1, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, facecolor=(1, 1, 1))
One suggestion in other answers is to use ax.set_axis_bgcolor("red"). This however is deprecated, and doesn't work on MatPlotLib >= v2.0.
There is also the suggestion to use ax.patch.set_facecolor("red") (works on both MatPlotLib v1.5 & v2.2). While this works fine, an even easier solution for v2.0+ is to use
ax.set_facecolor("red")
In addition to the answer of NickT, you can also delete the background frame by setting it to "none" as explain here: https://stackoverflow.com/a/67126649/8669161
import matplotlib.pyplot as plt
plt.rcParams['axes.facecolor'] = 'none'
I think this might be useful for some people:
If you want to change the color of the background that surrounds the figure, you can use this:
fig.patch.set_facecolor('white')
So instead of this:
you get this:
Obviously you can set any color you'd want.
P.S. In case you accidentally don't see any difference between the two plots, try looking at StackOverflow using darkmode.

add text on plt.imshow

I have two ndarrays: Mat, labels
Currently I display Mat:
plt.imshow(Mat, cmap='gray', vmin=0, vmax=1, interpolation='None')
labels has the same shape as Mat, and lables[(i,j)] contains a label of Mat[(i,j)].
How can I show the label on each pixel?
The easiest approach uses Seaborn's heatmap. When annot=True it prints the data values into the cells. But annot= can also be a matrix of labels. In that case it is important to set the print format to string (fmt='s'). annot_kws= can set additional keywords, such as fontsize or color. x and yticklabels can be incorporated in the call to heatmap(), or be set afterwards using matplotlib.
An important benefit of the default coloring is that Sorn uses black on the light colored cells and white on the dark cells.
Here is an example that uses some utf8 characters as labels.
from matplotlib import pyplot as plt
import numpy as np
import seaborn as sns
M, N = 5, 10
mat = np.random.rand(M, N)
labels = np.random.choice(['X', '☀', '★', '♛'], size=(M, N))
ax = sns.heatmap(mat, cmap="inferno", annot=labels, annot_kws={'fontsize': 16}, fmt='s')
plt.show()
PS: There is a matplotlib example in the documentation to create something similar without Seaborn. It can be easily adapted to print strings from a different matrix, and also a test can be added to change the color depending on the cell darkness.

Different level of transparency for edgeline and fill in matplotlib or seaborn distribution plot

I would like to set different levels of transparency (= alpha) for the edge line and fill of a distribution plot that I created in matplotlib/seaborn. For example:
ax1 = sns.distplot(BSRDI_DF, label="BsrDI", bins=newBins, kde=False,
hist_kws={"edgecolor": (1,0,0,1), "color":(1,0,0,0.25)})
The above approach does not work, unfortunately. Does anybody have any idea how I could accomplish this?
The problem seems to be that seaborn sets an alpha parameter for the histogram. While alpha defaults to None for a usual histogram, such that something like
plt.hist(x, lw=3, edgecolor=(1,0,0,0.75), color=(1,0,0,0.25))
works as expected, seaborn sets this alpha to some given value. This overwrites the alpha that is set in the RGBA tuples.
The solution is to set alpha explicitely to None:
ax = sns.distplot(x, kde=False, hist_kws={"lw":3, "edgecolor": (1,0,0,0.75),
"color":(1,0,0,0.25),"alpha":None})
A complete example:
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
x = np.random.randn(60)
ax = sns.distplot(x, label="BsrDI", bins=np.linspace(-3,3,10), kde=False,
hist_kws={"lw":3, "edgecolor": (1,0,0,0.75),
"color":(1,0,0,0.25),"alpha":None})
plt.show()
EDIT Nevermind, I thought using color instead of facecolor was causing the problem but it seems the output that I got only looked right because the patches were overlapping, giving seemingly darker edges.
After investigating the issue further, it looks like seaborn is hard-setting the alpha level at 0.4, which supersedes the arguments passed to hist_kws=
sns.distplot(x, kde=False, hist_kws={"edgecolor": (1,0,0,1), "lw":5, "facecolor":(0,1,0,0.1), "rwidth":0.8})
While using the same parameters to plt.hist() gives:
plt.hist(x, edgecolor=(1,0,0,1), lw=5, facecolor=(0,1,0,0.1), rwidth=0.8)
Conclusion: if you want different alpha levels for edges and face colors, you'll have to use matplotlib directly, and not seaborn.

Seaborn stripplot set edgecolor based on hue/palette

I'm trying to create a figure like this one from the seaborn documentation but with the edgecolor of the stripplot determined by the hue. This is my attempt:
import seaborn as sns
df = sns.load_dataset("tips")
ax = sns.stripplot(x="sex", y="tip", hue="day", data=df, jitter=True,
edgecolor=sns.color_palette("hls", 4),
facecolors="none", split=False, alpha=0.7)
But the color palettes for male and female appear to be different. How do I use the same color palette for both categories?
I'm using seaborn 0.6.dev
The edgecolor parameter is just passed straight through to plt.scatter. Currently you're giving it a list of 4 colors. I'm not exactly sure what I would expect it to do in that case (and I am not exactly sure why you end up with what you're seeing here), but I would not have expected it to "work".
The ideal way to have this work would be to have a "hollow circle" marker glyph that colors the edges based on the color (or facecolor) attribute rather than the edges. While it would be nice to have this as an option in core matplotlib, there are some inconsistencies that might make that unworkable. However, it's possible to hack together a custom glyph that will do the trick:
import numpy as np
import matplotlib as mpl
import seaborn as sns
sns.set_style("whitegrid")
df = sns.load_dataset("tips")
pnts = np.linspace(0, np.pi * 2, 24)
circ = np.c_[np.sin(pts) / 2, -np.cos(pts) / 2]
vert = np.r_[circ, circ[::-1] * .7]
open_circle = mpl.path.Path(vert)
sns.stripplot(x="sex", y="tip", hue="day", data=df,
jitter=True, split=False,
palette="hls", marker=open_circle, linewidth=0)
FWIW I should also mention that it's important to be careful when using this approach because the colors become much harder to distinguish. The hls palette exacerbates the problem as the lime green and cyan middle colors end up quite similar. I can imagine situations where this would work nicely, though, for instance a hue variable with two levels represented by gray and a bright color, where you want to emphasize the latter.

Coloring Intersection of Circles/Patches in Matplotlib

The following code:
# in ipython notebook, enable inline plotting with:
# %pylab inline --no-import-all
import matplotlib.pyplot as plt
# create some circles
circle1 = plt.Circle((-.5,0), 1, color='r', alpha=.2)
circle2 = plt.Circle(( .5,0), 1, color='b', alpha=.2)
# add them to the plot (bad form to use ;, but saving space)
# and control the display a bit
ax = plt.gca()
ax.add_artist(circle1); ax.add_artist(circle2)
ax.set_xlim(-2, 2); ax.set_ylim(-2, 2)
ax.set_aspect('equal')
# display it
plt.plot()
Produces the following plot:
I would like to specify the colors of the four regions (1) the background (currently white), (2 and 3) each individual event (the non-overlapping areas, currently blue and red), and (4) the intersection event (currently blended to purple). For example, I might color them red, green, blue, yellow -or- I might give them four different, precisely specified grayscale values (the later is more likely). [The colors will be generated based on characteristics of the underlying data.]
I specifically do not want to use alpha blending to "infer" a color in the intersection. I need to explicitly control the colors of all four regions.
I can think of a few strategies to solve this:
Ask mpl to extract the "primitive" patch objects that make up the three distinctly colored graphical regions (and do something similar to operate on the background) and then color them.
Given the circles, manually compute their intersections and color that intersection (somehow). Going point by point seems ugly.
Thanks!
I'm not 100% sure but I think matplotlib does not have the functionality to intersect polygons. But you could use shapely:
import shapely.geometry as sg
import matplotlib.pyplot as plt
import descartes
# create the circles with shapely
a = sg.Point(-.5,0).buffer(1.)
b = sg.Point(0.5,0).buffer(1.)
# compute the 3 parts
left = a.difference(b)
right = b.difference(a)
middle = a.intersection(b)
# use descartes to create the matplotlib patches
ax = plt.gca()
ax.add_patch(descartes.PolygonPatch(left, fc='b', ec='k', alpha=0.2))
ax.add_patch(descartes.PolygonPatch(right, fc='r', ec='k', alpha=0.2))
ax.add_patch(descartes.PolygonPatch(middle, fc='g', ec='k', alpha=0.2))
# control display
ax.set_xlim(-2, 2); ax.set_ylim(-2, 2)
ax.set_aspect('equal')
plt.show()