Search code examples
pythonmatplotlibplotpython-ggplot

Matplotlib: How to assign increasing shade of colors according to the size of text in a plot?


I have a bunch of words frequencies:

words = ['an', 'apple', 'in', 'a', 'tree']
counts = [23, 12, 45, 20, 9]

How can I plot the words in matplotlib according to their values and also color changing smoothly (for example cm.Blues) ?

My attempt is this:

import numpy as np
import matplotlib.pyplot as plt
plt.rc('font',**{'size':20, 'family':'fantasy'})

from collections import Counter
from matplotlib import colors as mcolors


words = ['an', 'apple', 'in', 'a', 'tree']
counts = [23,   12,      45,  20,   9]

plt.figure(figsize=(8,8))
c = list(mcolors.CSS4_COLORS.values())

for i in range(len(words)):
    x = np.random.uniform(low=0, high=1)
    y = np.random.uniform(low=0, high=1)
    plt.text(x, y, words[i], 
             size=counts[i]*2, 
             rotation=np.random.choice([-90, 0.0, 90]), 
             color=c[np.random.randint(0,len(c))])

plt.setp(plt.gca(), frame_on=False, xticks=(), yticks=())
plt.show()

There are random colors.

How can we assign increasing shade of color to make this picture more intuitive?

enter image description here


Solution

  • CSS4_colors are HTML colors defined using an hex code. This is not the best choice if you want to change the saturation or brightness. You may want to use the HSV color model.

    Thus you have to convert your color to hsv and then change either s or v according to your word count.

    import numpy as np
    import matplotlib.pyplot as plt
    plt.rc('font',**{'size':20, 'family':'fantasy'})
    
    from matplotlib import colors as mcolors
    
    words = ['an', 'apple', 'in', 'a', 'tree']
    counts = [23,   12,      45,  20,   9]
    maxcounts = max(counts)
    sat = [c / maxcounts for c in counts]
    
    plt.figure(figsize=(8,8))
    
    # convert colors to HSV
    css4_colors = list(mcolors.CSS4_COLORS.values())
    ncolors = len(c)
    rgb = [mcolors.to_rgb(c) for c in css4_colors]
    hsv = [mcolors.rgb_to_hsv(c) for c in rgb]
    
    
    for i in range(len(words)):
        x = np.random.uniform(low=0, high=1)
        y = np.random.uniform(low=0, high=1)
    
        # select a color
        icolor = np.random.randint(0, ncolors)
        color = hsv[icolor]
        color[1] = sat[i] # here change saturation, index 1 or brightness, index 2 according to the counts list
    
        plt.text(x, y, words[i], 
                 size=counts[i] * 2, 
                 rotation=np.random.choice([-90, 0.0, 90]), 
                 color=mcolors.hsv_to_rgb(color)) # don't forget to get back to rgb
    
    plt.setp(plt.gca(), frame_on=False, xticks=(), yticks=())
    plt.show()
    

    EDIT

    Ok, this is another version where you select a hue color (between 0 and 1), the brightness and you change only the saturation.

    Depending on what you want, you can change the brightness according to the counts and keep the saturation constant.

    import numpy as np
    import matplotlib.pyplot as plt
    plt.rc('font',**{'size':20, 'family':'fantasy'})
    
    from matplotlib import colors as mcolors
    
    words = ['an', 'apple', 'in', 'a', 'tree']
    counts = [23,   12,      45,  20,   9]
    maxcounts = max(counts)
    sat = [c / maxcounts for c in counts]
    
    plt.figure(figsize=(8,8))
    
    # select a hue value => define the color
    hue = 0.6
    # choose a brightness
    brightness = 1
    
    for i in range(len(words)):
        x = np.random.uniform(low=0, high=1)
        y = np.random.uniform(low=0, high=1)
    
        # select a color
        color = (hue, sat[i], brightness)
    
        plt.text(x, y, words[i],
                 size=counts[i] * 2,
                 rotation=np.random.choice([-90, 0.0, 90]),
                 color=mcolors.hsv_to_rgb(color)) # don't forget to get back to rgb
    
    plt.setp(plt.gca(), frame_on=False, xticks=(), yticks=())
    plt.show()