0

I am trying to plot points between ranges but it is not coming out well due to my lack of plotting library knowledge.If you look at the diagram below, sometimes my data is shown outside the rectangles i.e.ranges. My data is formatted as below:

d = {(x, y, [values between x and y]), (x, y, [values between x and y]) ....}
d = {(10, 15, [1, 4, 4, 4, 4, 4]), (20, 25, [4, 5, 5, 5, 5, 5])....}

enter image description here

The blue spots are the values between the ranges and rectangles are the ranges along with dashed line which is the reference line.

My current code looks like below:

from random import randint
from random import randrange, uniform
import numpy as np
from matplotlib.path import Path
import matplotlib.patches as patches
import matplotlib.pyplot as plt
 
def plotResults(d, referenceLine):
    ''' d is of type {(x, y, [values between x and y]), (x, y, [values between x and y])}'''
    #remap (x, y) points to start from 0 
    gap = 10
    start, end = 0, 0
    remap = []
    maxValue = 0
    for k, val in d.items():
        x, y = k
        start = end + gap
        end = start + (y - x)
        maxValue = end * 2
        remap.append((start, end, val))
 
    #draw the vertical rectangles
    vertices = []
    codes = []
    prev = 0
    for i in range(len(remap)):
        codes += [Path.MOVETO] + [Path.LINETO]*3
        vertices += [(remap[i][0]+prev, 0), (remap[i][0]+prev, 8), (remap[i][1]+prev, 8), (remap[i][1]+prev, 0)]
        prev = remap[i][1] + gap
    path = Path(vertices, codes)
    pathpatch = patches.PathPatch(path, facecolor='None', edgecolor='green')
    fig, ax = plt.subplots()
 
    #draw the points inside the rectangles
    for i in range(len(remap)):
        p = 0
        for j in range(remap[i][1]-remap[i][0]):
            mm = remap[i][2]
            circ = plt.Circle((remap[i][0]+j + prev, mm[p]), 0.1, color='b', alpha=0.3)
            ax.add_patch(circ)
            p += 1
        prev = remap[i][1] + gap
 
    #draw the referenceLine
    x = np.linspace(0, maxValue, 500)
    y = [referenceLine for i in range(len(x))]
    line1, = ax.plot(x, y, '--', linewidth=0.5)
    ax.add_patch(pathpatch)
    ax.autoscale_view()
    plt.show()
 
#generate the dataset of ranges with the values inside those ranges
referenceLine = 4
d = {}
start, end, gap = 0, 0, 10
for i in range(10):
    duration = randrange(40, 60)
    start = end + gap
    end = start + duration
    d[(start, end)] = [randint(0, 7) for j in range(end-start)]
    print(start, end)
plotResults(d, referenceLine)

Is there any better way to do this?

1
  • 1
    you don't need to build up that Patch object. use ax.plot and feed it arrays of x- and y-values Commented Jan 21, 2021 at 7:37

1 Answer 1

2

Some remarks:

  • The lines for the rectangle can be drawn directly using plot([x0,x0,x1,x1],[y0,y1,y1,y0]).
  • A circle in matplotlib has the same width as height, but measured in the coordinates of the axes. As the x-axis is much wider than the y-axis, the circle gets contracted to look like a line. Matplotlib's scatter() can draw dots that will look round on the image
  • There is a bug in the original code: prev doesn't get initialized in the third for-loop. This makes that the circles for the first rectangle are displaced towards the center of the x-axis.
  • The list remap looks very confusing and seems to contain the same information as the dictionary. The code can be simplified a lot leaving it out.

Here is how a small rewrite of the function could look like:

def plotResults(d, referenceLine):
    ''' d is of type {(x, y, [values between x and y]), (x, y, [values between x and y])}'''

    fig, ax = plt.subplots()
    # draw the vertical rectangles
    prev = 0
    for (start, end), value in d.items():
        ax.plot([start + prev, start + prev, end + prev, end + prev], [0, 8, 8, 0], color='green')
        prev = end + gap

    # draw the points inside the rectangles
    prev = 0
    for (start, end), values in d.items():
        for j in range(end - start):
            ax.scatter(start + j + prev, values[j], marker='o', color='blue') # marker='|' for a vertical line
        prev = end + gap

    # draw the referenceLine
    ax.axhline(referenceLine, linestyle='--', linewidth=0.5)
    plt.show()

The use of prev in the code of plotResults makes that the gaps are much wider than 10. Also, the start and end values are moved to about the double of their x-values. It is not clear whether that is intended. A still more simplified code, with the start and end keeping their position, could look like:

from random import randint, randrange
import matplotlib.pyplot as plt

def plotResults(d, referenceLine):
    ''' d is of type {(x, y, [values between x and y]), (x, y, [values between x and y])}'''
    fig, ax = plt.subplots()
    # draw the vertical rectangles
    for (start, end), value in d.items():
        ax.plot([start - 0.5, start - 0.5, end - 0.5, end - 0.5], [0, 7.1, 7.1, 0], color='green')

    # draw the points inside the rectangles
    for (start, end), values in d.items():
        ax.scatter(range(start, end), values, marker='|', color='blue')
    # draw the referenceLine
    ax.axhline(referenceLine, linestyle='--', linewidth=0.5)
    plt.show()

# generate the dataset of ranges with the values inside those ranges
referenceLine = 4
d = {}
start, end, gap = 0, 0, 10
# for i in range(10):
for i in range(10):
    duration = randrange(40, 60)
    start = end + gap
    end = start + duration
    d[(start, end)] = [randint(0, 7) for j in range(end - start)]
    print(start, end)
plotResults(d, referenceLine)

example plot

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.