0

I'm new to matplotlib and numpy and am trying to plot some array data to something like this:

plotting an array

but it currently looks like this:

enter image description here

My code is extremely basic but basically the image above is what I'd like to be able to do. I'm not too concerned yet with drawing gridlines but the plot should be proportional. I've looked at some examples on the mathplotlib website but can't find something similar enough. I wonder if pyplot is not the correct procedure or if my array is not formatted correctly?

import numpy as np
from matplotlib import pyplot as plt

data = [(2, 2), (3, 2), (4, 1), (4, 0), (3, 0), (3, 1), (4, 2),
        (4, 3), (4, 4), (3, 3), (3, 4), (2, 4), (2, 3), (1, 4),
        (0, 4), (0, 3), (1, 3), (0, 2), (1, 2), (0, 1), (0, 0),
        (1, 0), (1, 1), (2, 0), (2, 1)]

data = np.asarray(data)
print(data)
plt.ylim(0, 4)
plt.xlim(0, 4)
plt.axis([0, 4, 0, 4])
plt.plot(data)
plt.show()
cpsharp
  • 25
  • 7

4 Answers4

5

Try

plt.plot(*data.transpose())

instead of

plt.plot(data)

The plt.plot function takes 2 arrays, one with all the x-values and one with all the y-values. You cannot pass it a mixture of x and y values. The transpose method will reorder the values so that all x values are together and all y values are togheter and the star * operator will unpack those arrays into tow separate function arguments.

Jakob Stark
  • 3,346
  • 6
  • 22
  • 2
    Additionally, add some keyword arguments like `marker='.', markersize=10` in order to make the plot a bit more like the desired outcome. – jfaccioni Feb 07 '22 at 13:51
  • Ah, I see. I noticed that most of the examples at matplotlib were shown using separate data for both x and y coordinates, but sometimes in [x, y] form, however not exactly like I have done. Much thanks! – cpsharp Feb 07 '22 at 14:01
2

You need to unpack the x and y components into arrays like this:

plt.plot([p[0] for p in data], [p[1] for p in data], 'o-')

Use format style "o-": "o" for a symbol marker and "-" for a solid line.

To also format the x/y ticks here is the full code:

from matplotlib import pyplot as plt

data = [(2, 2), (3, 2), (4, 1), (4, 0), (3, 0), (3, 1), (4, 2),
        (4, 3), (4, 4), (3, 3), (3, 4), (2, 4), (2, 3), (1, 4),
        (0, 4), (0, 3), (1, 3), (0, 2), (1, 2), (0, 1), (0, 0),
        (1, 0), (1, 1), (2, 0), (2, 1)]

x = [p[0] for p in data]
y = [p[1] for p in data]
_, ax = plt.subplots()
ax.set_xticks(range(5))
ax.set_yticks(range(5))
ax.plot(x, y, 'o-', color='blue')
plt.grid() # add grid lines
plt.show()

Output: matplotlib image

CodeMonkey
  • 22,825
  • 4
  • 35
  • 75
2

To complete other answers, here is the closest I got in a reasonable amount of time to your desired output :

import numpy as np
from matplotlib import pyplot as plt

data = [(2, 2), (3, 2), (4, 1), (4, 0), (3, 0), (3, 1), (4, 2),
        (4, 3), (4, 4), (3, 3), (3, 4), (2, 4), (2, 3), (1, 4),
        (0, 4), (0, 3), (1, 3), (0, 2), (1, 2), (0, 1), (0, 0),
        (1, 0), (1, 1), (2, 0), (2, 1)]

data = np.asarray(data)
print(data)

# make axis equal and turn them off
plt.axis('equal')
plt.axis('off')

for i in range(5):
    # horizontal and vertical background lines
    plt.plot([i,i], [0,4], linewidth=0.5, color='black')
    plt.plot([0,4], [i,i], linewidth=0.5, color='black')
plt.plot(data[:,0], data[:,1], 'b') # line segments
plt.scatter(data[:,0], data[:,1], c='b') # markers

plt.show()

Output:

output


Edit

From this question, you can add numbers at each marker through plt.annotate. Just add this after plt.scatter :

offset = np.array([0.1, 0.1])
for i in range(data.shape[0]):
    # For each marker
    plt.annotate(str(i+1), data[i]+offset)

Output:

plot with annotations


Edit #2:

As a bonus, here is my attempt as getting the closest possible to the reference figure:

import numpy as np
from matplotlib import pyplot as plt

data = [(2, 2), (3, 2), (4, 1), (4, 0), (3, 0), (3, 1), (4, 2),
        (4, 3), (4, 4), (3, 3), (3, 4), (2, 4), (2, 3), (1, 4),
        (0, 4), (0, 3), (1, 3), (0, 2), (1, 2), (0, 1), (0, 0),
        (1, 0), (1, 1), (2, 0), (2, 1)]

data = np.asarray(data)
print(data)

# make axis equal and turn them off
plt.axis('equal')
plt.axis('off')

for i in range(5):
    # horizontal and vertical background lines
    plt.plot([i,i], [0,4], linewidth=1.5, color='#b3b3b3', zorder=1)
    plt.plot([0,4], [i,i], linewidth=1.5, color='#b3b3b3', zorder=1)

plt.plot(data[:,0], data[:,1], '#274b8b', linewidth=2.5, zorder=2) # line segments
plt.scatter(data[np.arange(len(data))!=7,0], data[np.arange(len(data))!=7,1], s=60, c='#4472c4', edgecolors='#2e569c', zorder=3) # markers
# marker for point 7 is not plotted in reference figure

# Individual points numbers, not in reference figure so commented
# offset = np.array([0.1, 0.1])
# for i in range(data.shape[0]):
    # # For each marker
    # plt.annotate(str(i+1), data[i]+offset)

for i in range(5):
    plt.annotate(str(i), [i-0.05, -0.25]) # X axis numbers
    plt.annotate(str(i), [-0.2, i-0.07]) # Y axis numbers


plt.show()

enter image description here

Jenny
  • 601
  • 3
  • 11
  • Thank you. This is great! Can the circles at each point become numbers, i.e 1 ... 25? I'm not sure yet in understanding the way you wrote the code: data[:,0], data[:, 1] but maybe the indices of 'x' [0] and 'y' [1]? – cpsharp Feb 07 '22 at 14:51
  • 1
    @cpsharp your data array is 2D, first dimension being the point indexes, second dimension being x and y values, so data[:,0] takes all (':') points values for the x (0) axis. – Jenny Feb 07 '22 at 14:56
  • 1
    @cpsharp I edited the answer with annotated points. You can make the offset a 2D array as well, so that for some of the points where the lines and the annotation are overlapping the text is positioned differently (for points 6, 10, 18 and 20). – Jenny Feb 07 '22 at 15:03
  • Your help and all others have saved me hours upon hours of trial and error. Thanks!! – cpsharp Feb 07 '22 at 15:47
  • You're welcome ! Try to understand the different parts of it, and check the matplotlib documentation, not only the examples: https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.html – Jenny Feb 07 '22 at 16:06
1

You could try something like this:

import numpy as np
from matplotlib import pyplot as plt

data = [(2, 2), (3, 2), (4, 1), (4, 0), (3, 0), (3, 1), (4, 2),
        (4, 3), (4, 4), (3, 3), (3, 4), (2, 4), (2, 3), (1, 4),
        (0, 4), (0, 3), (1, 3), (0, 2), (1, 2), (0, 1), (0, 0),
        (1, 0), (1, 1), (2, 0), (2, 1)]

for i in range(len(data) -1): 
  x1 = data[i][0]
  y1 = data[i][1]
  x2 = data[i+1][0]
  y2 = data[i+1][1]
  plt.plot([x1, x2], [y1, y2], c='blue')
  plt.scatter(x1, y1, c='blue')
plt.show()
Hiibb
  • 333
  • 1
  • 6