Point and figure chart with matplotlib


I’m trying to make a point and figure chart. I can get it to work printing out on the terminal but I want to graph it with matplotlib. What would be the best way of doing something like this? I was thinking scatter, but when I do this the columns are spread out too far. I would like to get something much like the chart from the link I provided where the columns are as close to one another as possible. Is there a parameter I can overwrite to force this? First time using matplotlib so please excuse me if this is trivial. Thanks.

Asked By: Jeff



No personal experience, but maybe set_view_interval() or set_data_interval() from here? I have used Matplotlib for a project, but didn’t have to play with fixing the x-axis width.

Answered By: Ambidextrous

You can adjust the size of the symbols used in a scatter plot by choosing the s parameter. You also will likely need to adjust the size of your figure (with figsize) or the dimensions of your axes (with add_axes). This is because the symbols for scatter are square, in display units, and the x and y axis are not automatically adjusted so that width-of-one-change = height-of-one-box.

In other words, the example you provided is a rectangular plot with the height > width, and the height and width are chosen to make the width-of-one-change == height-of-one-box.

Here’s an example of apply these techniques:

import matplotlib.pyplot as plt

BOX = 5
START = 365
changes = (8, -3, 4, -4, 12, -3, 7, -3, 5, -9, 3)

# one way to force dimensions is to set the figure size:
fig = plt.figure(figsize=(5, 10))

# another way is to control the axes dimensions
# for axes to have specific dimensions:
#                  [ x0,  y0,   w,   h]  in figure units, from 0 to 1
#ax = fig.add_axes([.15, .15, .7*.5, .7])
ax = fig.add_axes([.15, .15, .7, .7])

def sign(val):
    return val / abs(val)

pointChanges = []
for chg in changes:
    pointChanges += [sign(chg)] * abs(chg)

symbol = {-1:'o',

chgStart = START
for ichg, chg in enumerate(changes):
    x = [ichg+1] * abs(chg)
    y = [chgStart + i * BOX * sign(chg) for i in range(abs(chg))] 
    chgStart += BOX * sign(chg) * (abs(chg)-2)
    ax.scatter(x, y,
               s=175)   #<----- control size of scatter symbol

ax.set_xlim(0, len(changes)+1)

The method developed for each scatter plot is very hackish, but the key point is that I needed to play with the scatter s parameter and the figure size to get something of the desired effect.

The resulting plot is:

enter image description here

Ideally, one would make a custom method modeled after the scatter method. It would create a custom Collection instance that would include the x’s, o’s and month labels. It would also a) automatically adjust the axes/figure aspect or b) make asymmetric symbols. This is obviously an advanced option, intended for someone wishing to contribute, as a developer, to the Matplotlib project.

Answered By: Yann

Editing the colors was the best I could.

chgStart = START

for ichg, chg in enumerate(changes):
     x = [ichg+1] * abs(chg)
     y = [chgStart + i * BOX * sign(chg) for i in range(abs(chg))]
     chgStart += BOX * sign(chg) * (abs(chg)-2)
     ax.scatter(x, y,
            s=175, color = colors[int(sign(chg))] ) #<----- control size     of scatter symbol
Answered By: Elias Coutinho
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.