Search code examples
matplotlib

Matplotlib.scatter documentation covers some parameters unclearly


I was searching for how to plot a scatter plot with points that have a transparent face but coloured edges. This page gave some ideas, but I find the behaviour of some arguments to differ than reported there. For example, one answer describes argument c as affecting both face color and edge color, causing arguments for the latter two to be ignored. In contrast I found the following to be synonymous: c, color, facecolor, facecolors.

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
xy = np.random.randint(10,size=[2,10])
colors = sns.color_palette('deep',10)
plt.rcParams['figure.dpi']=100

# These have the same effect (solid faces)
plt.clf(); plt.scatter( *xy, s=10000, c=colors )
plt.clf(); plt.scatter( *xy, s=10000, color=colors )
plt.clf(); plt.scatter( *xy, s=10000, facecolor=colors )
plt.clf(); plt.scatter( *xy, s=10000, facecolors=colors )

# These have the same effect (transparent faces, solid edges)
plt.clf(); plt.scatter( *xy, s=10000, edgecolors=colors, c='None' )
plt.clf(); plt.scatter( *xy, s=10000, edgecolors=colors, color='None' )
plt.clf(); plt.scatter( *xy, s=10000, edgecolors=colors, facecolor='None' )
plt.clf(); plt.scatter( *xy, s=10000, edgecolors=colors, facecolors='None' )

I also found that all 4 arguments are mentioned in the documentation but only c is listed as a pyplot.scatter() argument. Hence, the others don't get a detailed description.

I thought that the documentation is definitive and covers all possible arguments. Why do the other arguments not appear in the argument list? I think that's part of the reason why there is confusion about their interactions, e.g., when some arguments override others.


Solution

  • TLDR: c applies to both fc and ec, but specifying them overrides c "inheritance".

    I agree that there are multiple possibilities and that not every interaction is documented.

    However, I think this is because that way the beginner will find the most obvious use case more easily, and if they needs more complex usage they can dig deeper and in the end a few lines of codes will give the answer, as demonstrated below:

    1. First row demonstrates that facecolors, facecolor and fc are synonyms, as for edgecolors, edgecolor and ec;
    2. Second row demonstrates the interaction when fc and/or ec is 'none';
    3. Third row demonstrates that c applies to both fc and ec when they are not specified. You can see that when only c=fc is specified then the markers are bigger than when c='none' and fc=fc, because in the last case c='none' implies ec='none' since ec is not explicitly defined.

    scatter c fc ec

    Code to reproduce:

    import numpy as np
    import matplotlib.pyplot as plt
    
    N, s, lw = 10, 90, 2
    
    xy = np.random.randint(N, size=(2, N))
    facecolors = np.random.rand(N, 3)
    edgecolors = np.random.rand(N, 3)
    
    fig, axes = plt.subplots(nrows=3, ncols=3, figsize=(10, 7))
    
    axes[0, 0].set_title("facecolors=fc, edgecolors=ec")
    axes[0, 0].scatter(*xy, s=s, lw=lw, facecolors=facecolors, edgecolors=edgecolors)
    
    axes[0, 1].set_title("facecolor=fc, edgecolor=ec")
    axes[0, 1].scatter(*xy, s=s, lw=lw, facecolor=facecolors, edgecolor=edgecolors)
    
    axes[0, 2].set_title("fc=fc, ec=ec")
    axes[0, 2].scatter(*xy, s=s, lw=lw, fc=facecolors, ec=edgecolors)
    
    axes[1, 0].set_title("fc=fc, ec='none'")
    axes[1, 0].scatter(*xy, s=s, lw=lw, fc=facecolors, ec="none")
    
    axes[1, 1].set_title("fc='none', ec=ec")
    axes[1, 1].scatter(*xy, s=s, lw=lw, fc="none", ec=edgecolors)
    
    axes[1, 2].set_title("fc='none', ec='none'")
    axes[1, 2].scatter(*xy, s=s, lw=lw, fc="none", ec="none")
    
    axes[2, 0].set_title("c=fc, ec=ec")
    axes[2, 0].scatter(*xy, s=s, lw=lw, c=facecolors, ec=edgecolors)
    
    axes[2, 1].set_title("c=fc")
    axes[2, 1].scatter(*xy, s=s, lw=lw, c=facecolors)
    
    axes[2, 2].set_title("c=fc, ec='none'")
    axes[2, 2].scatter(*xy, s=s, lw=lw, c=facecolors, ec="none")
    
    axes[2, 2].set_title("c='none', fc=fc")
    axes[2, 2].scatter(*xy, s=s, lw=lw, c="none", fc=facecolors)
    
    fig.tight_layout()
    fig.show()