NumPy Art- A Tutorial in Scatter-Fun
By: Patrick Fuller
Tags: Numpy, Art, Matplotlib, Python, Fun

I was working on an assignment recently and arrived at an exponential distribution. One segment of the exercise was to graph part of the corresponding parabola bit by bit. It ended up looking something like this.

The true nature of the distribution.

The segmented chunks drawn one by one.

This was a nice little lightbulb moment for me. Nothing truly groundbreaking mind you. It was something akin to, “OOooh, look at the colors!”.

So far I have found it more common to plot a collection of points as two corresponding lists/arrays of x values and y values. It might like something like this for example:

x = np.linspace(-4,4,5)

y = x**2

plt.scatter(x, y)

This results in one color for all the points:

Less colorful, less pop.

The principle for graphing the different colors on the same graph is to call pyplot(as plt) multiple times.

for x in range(-2,3):

plt.scatter(x, x**2)

Each time x is reassigned in the for loop, pyplot is creating a new scatter plot with a different color. I can only presume this architecture is designed to allow to functions to appear distinct.

Different colors to help distinguish between functions.

I had recently also discovered passing the size of the scatter markers as a changing variable and my next experiment resulted in something more like this:

Colors with changing size for extra pop.

At this point I began to wonder what about adjusting more features in a graph with multiple different colors.

To take a step back, lets break down a few simple parameters of my new favorite function, matplotlib.pyplot.scatter.

Size- This adjusts the size of the markers. If you wanted to adjust the size of the graph, in an earlier line use plt.figure(figsize=(width, height)). Color- This adjusts the color of a given scatterplot’s markers. This parameter could take a number of different value types. It could take a string of one color by name, or a list of colors(with the same length as the distribution plotted). The color parameter will also accept RGB, RGBA and HEX colors. Alpha- This parameter adjusts the opacity of the markers plotted. This is very handy when analyzing distributions, and many of the points over lap. It allows us to see a ‘cloud-like’ representation of the data with regions with less data appearing lighter. Marker- This parameter adjusts the shape or style of the marker plotted. Common choices are circles, triangles, or diamonds, but there are quite a number of others the can be found here. A recent new favorite might be the ability to pass in any text you like by passing marker = “$text$”.

There are many other amazing parameters of the scatterplot and can be found at Matplotlib’s documentation, matplotlib.pyplot.scatter.

I initially began experimenting with hex colors. A hex color is just six numbers (single digit integers)preceded with a ‘#’. An example might look like ‘#000000’ or ‘#052884’. Also sometimes they can include letters and sometimes they can be three long instead of six. I have generally seen it passed a string in python. I initially landed on the code below as a way to randomize color:

hexlist = [str(np.random.randint(0,10)) for i in range(6)]

color = '#'+''.join(hexlist)

Ultimately I found my self randomizing color using an RGB/RGBA architechture more often than hex. RGB uses an array of three values ranging from 0 to 1. Each value corresponds to an intensity in the hues, red, green, and blue accordingly. An optional fourth value corresponds to the alpha value(opacity like above). I arrived at this code for my RGBA randomizer.

rgba = [np.random.random() for _ in range(4)]

color = np.array([rgba])

Why do we care a bout randomizing color, when calling new scatterplots will change the color for us? Well lets take a look.

Using the default colors that scatterplot provides.

Adding in randomized color.

The default colors provided when the scatterplot chooses its own are not bad. Many are pastel, and they really don’t seem to grab the full spectrum compared to full randomization. To be 100% transparent(forgive the pun), the second plot also has opacity randomized with all four values in RGBA. Full opacity would look so:

Only three values randomized: RGB

xs = np.linspace(-100,100,1000)

for x in xs:

rgb = [np.random.random() for _ in range(3)]

color = np.array([rgb])

plt.scatter(x,np.random.normal(),

s = (np.abs(x))/20,

c = color)

So far I have only tied size to a variable and randomized color, but both can randomized or assigned to a value in the distribution. The world is our multi-colored oyster. Lastly to randomize markers I found a dictionary online and used np.random.choice to select them as follows:

markers = {'.': 'point', ',': 'pixel', 'o': 'circle', 'v': 'triangle_down', '^': 'triangle_up',

'<': 'triangle_left', '>': 'triangle_right', '1': 'tri_down', '2': 'tri_up', '3': 'tri_left',

'4': 'tri_right', '8': 'octagon', 's': 'square', 'p': 'pentagon', '*': 'star', 'h': 'hexagon1',

'H': 'hexagon2', '+': 'plus', 'x': 'x', 'D': 'diamond', 'd': 'thin_diamond', '|': 'vline',

'_': 'hline', 'P': 'plus_filled', 'X': 'x_filled', 0: 'tickleft', 1: 'tickright', 2: 'tickup',

3: 'tickdown', 4: 'caretleft', 5: 'caretright', 6: 'caretup', 7: 'caretdown', 8: 'caretleftbase',

9: 'caretrightbase', 10: 'caretupbase', 11: 'caretdownbase', 'None': 'nothing', None: 'nothing',

' ': 'nothing', '': 'nothing'} plt.figure(figsize=(12,12))

for _ in range(1000):

a = np.random.randint(0,1000,5)

b = np.random.randint(0,1000,5)

rgba = [np.random.random() for _ in range(4)]

color = np.array([rgba])

marker = np.random.choice(list(markers.keys()))

alpha = np.random.random()**2.5

size = np.random.exponential()**5.3

plt.scatter(a,b, alpha=0.4, marker=marker,

color=color, s=size)

Resulting in something like:

So many shapes!

At this point, so many features have been randomized so many times, that each time the code is run, it produces something that will likely never be reproduced in the same way. In this light, the code starts to take on a strange beauty for its ephemerality, aside from its partial resemblance to some modern art.

Lastly but not least, here is my new favorite adding text for markers:

for x in range(5):

plt.scatter(x,x, marker="$yup$", s=1000)

Need I say more? …fine YUP!

To change the color of the background and remove axes I use:

plt.style.use('dark_background')

plt.axis('off')

The code for these experiments can be found at my repo and below are some of my favorites.