简体   繁体   中英

How to draw multiple triangles with different sizes and directions based on data using plotting tools like Matplotlib

I want to draw the graph like the picture below. Its x-axis is the order of the data points, eg from 1 to 7. The y-axis is the scale from 0 to 25. If I want to draw a triangle, for example, with its data (1,22,20), then '1' gives the order among all data points(different triangles), the triangle should be drew in most left; "22,20" gives the "bottom-tip" of the triangle along the y-axis.

Does anyone know how to draw such triangle with multiple number in a graph using matplotlib python package?

在此处输入图片说明

Read this post and this post about drawing polygons with matplotlib.

EDIT1: Just saw @Poolka's answer. This was also my way to go, but notice that in one of the above links, it is stated, that adding single polygons ( p = pat.Polygon([[x1, y1], [x2, y2], [x3, y3]); ax.add_patch(p) ) to the figure can become very slow, and therefore collections are preferred.

EDIT 2: Also see TheImportanceOfBeingErnest's answer for a more elaborated version of this concept. Together with this snippet of code, it should get you going:

import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.patches as pat  # Patches like pat.Polygon()
from matplotlib.collections import PolyCollection  # Collections of patches

test = ((1, 22, 20),
        (2, 21, 19.5),
        (3, 18, 20))  # Test data

triangles = []
fig, ax = plt.subplots(1, 1)

for t in test:
    xmid = t[0]  # Middle x-coord
    xleft = t[0] - 0.5
    xright = t[0] + 0.5  # Use fixed width of 0.5

    y1 = t[1]  # y-coords
    y2 = t[2]

    coordinates = [[xleft, y1], [xright, y1], [xmid, y2]]

    print(coordinates) 
    triangles.append(coordinates)  # Append to collection

z = np.random.random(len(triangles))
collec = PolyCollection(triangles, array=z, cmap=matplotlib.cm.viridis)

ax.add_collection(collec)  # Plot polygon collection
ax.autoscale_view()
plt.show()

在此处输入图片说明

Consider the following simple example:

import matplotlib.pyplot as plt

# data
data = [[1, 22, 20], [3, 20, 25]]

plt.figure()
for val in data:
    # coordinates
    dy = val[1] - val[2]
    dx = abs(dy) / 2
    x0 = val[0]
    y0 = val[1]
    # drawing
    triangle = plt.Polygon([[x0, y0], [x0 - dx, y0 + dy], [x0 + dx, y0 + dy]])
    plt.gca().add_patch(triangle)

# misc
plt.grid()
plt.axis('square')
# these 2 lines are needed because patches in matplotlib do not adjust axes limits automatically, another approach is to add some data to the figure with plot, scatter, etc.
plt.xlim([-20, 20])
plt.ylim([0, 40])

Result is: 在此处输入图片说明

Using a PolyCollection (as shown in @cripcate's answer ) is advantageous in this case. A more condensed version using a single numpy array could look like this:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import PolyCollection

def triangle_collection(d, ax=None, width=0.4, **kwargs):
    ax = ax or plt.gca()
    verts = np.c_[d[:,0]-width/2, d[:,1], d[:,0]+width/2, 
                  d[:,1], d[:,0], d[:,2]].reshape(len(d),3,2)
    c = PolyCollection(verts, **kwargs)
    ax.add_collection(c)
    ax.autoscale()
    return c


data = np.array([(1,22,20), (2,21,19.5), (3,18,20),
                 (4,17,19), (5,15,17), (6,11,8.5), (7,14,12)])

fig, ax = plt.subplots()
fig.subplots_adjust(left=0.3, right=0.7)

triangle_collection(data, facecolors=plt.cm.tab10(np.arange(len(data))))

plt.show() 

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM