简体   繁体   中英

Drawing a rectangle or bar between two points in a 3D scatter plot in Python and matplotlib

I have a 3D scatter plot which, on one of its planes, plots 2 points for each date. I've asked about how to draw a LINE between every pair of points , and received an answer for which I'm thankful. What I want now is to draw a BAR or a RECTANGLE to connect the points instead of just a line.

Here's what the plot looks like at the moment, but I want it to look a bit like the plot from the 3D bar demo from matplolib's docs except with the bars "floating" instead of anchored to the axis.

I've tried using Axes3D.bar (as explained on the matplotlib page) but it expects me to supply a "height" for each bar instead of two actual coordinates, and that height will be anchored to the axis.

This is the code, and any help is appreciated.

import matplotlib.pyplot
from mpl_toolkits.mplot3d import Axes3D

dates       = [20020514, 20020515, 20020516, 20020517, 20020520]
highs       = [1135, 1158, 1152, 1158, 1163]
lows        = [1257, 1253, 1259, 1264, 1252]
upperLimits = [1125.0, 1125.0, 1093.75, 1125.0, 1125.0]
lowerLimits = [1250.0, 1250.0, 1156.25, 1250.0, 1250.0]

zaxisvalues0= [0, 0, 0, 0, 0]
zaxisvalues1= [1, 1, 1, 1, 1]
zaxisvalues2= [2, 2, 2, 2, 2]

fig = matplotlib.pyplot.figure()
ax  = fig.add_subplot(111, projection = '3d')

ax.plot(dates, zaxisvalues1, lowerLimits, color = 'b')
ax.plot(dates, zaxisvalues2, upperLimits, color = 'r')

for i,j,k,h in zip(dates,zaxisvalues0,lows,highs):
    ax.plot([i,i],[j,j],[k,h],color = 'g')

ax.scatter(dates, zaxisvalues0, highs, color = 'g', marker = "o")
ax.scatter(dates, zaxisvalues0, lows, color = 'y', marker = "^")

matplotlib.pyplot.show()

I think it'll be easier to use a PolyCollection. Is this close to what you are after?

在此输入图像描述

import matplotlib.pyplot
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.collections import PolyCollection
import random

dates       = [20020514, 20020515, 20020516, 20020517, 20020520]
highs       = [1135, 1158, 1152, 1158, 1163]
lows        = [1257, 1253, 1259, 1264, 1252]
upperLimits = [1125.0, 1125.0, 1093.75, 1125.0, 1125.0]
lowerLimits = [1250.0, 1250.0, 1156.25, 1250.0, 1250.0]

zaxisvalues0= [0, 0, 0, 0, 0]
zaxisvalues1= [1, 1, 1, 1, 1]
zaxisvalues2= [2, 2, 2, 2, 2]

fig = matplotlib.pyplot.figure()
ax  = fig.add_subplot(111, projection = '3d')

ax.plot(dates, zaxisvalues1, lowerLimits, color = 'b')
ax.plot(dates, zaxisvalues2, upperLimits, color = 'r')

verts = []; fcs = []
for i in range(len(dates)-1):
   xs = [dates[i],dates[i+1],dates[i+1],dates[i],dates[i]] # each box has 4 vertices, give it 5 to close it, these are the x coordinates
   ys = [highs[i],highs[i+1],lows[i+1],lows[i], highs[i]]  # each box has 4 vertices, give it 5 to close it, these are the y coordinates
   verts.append(zip(xs,ys))
   fcs.append((random.random(),random.random(),random.random(),0.6))

poly = PolyCollection(verts, facecolors = fcs, closed = False)
ax.add_collection3d(poly, zs=[zaxisvalues0[0]] * len(verts), zdir='y') # in the "z" just use the same coordinate

ax.scatter(dates, zaxisvalues0, highs, color = 'g', marker = "o")
ax.scatter(dates, zaxisvalues0, lows, color = 'y', marker = "^")

matplotlib.pyplot.show()

You should be able to make a mix between:

3D bar demo from matplolib's docs

and

Bar stacked example

ie to draw bars in a 3D graph, but use the "bottom" parameter to set the starting heigh of your bars.

Alexis

Thanks for your help Alexis and Mark. I think it's sorted out now.

I've used Alexis' tip for using the 'zdir' property.

As for the wrong plane problem, you can fix it with parameter zdir, for instance ax.bar(dates, highs,zdir='y',bottom=highs, zs=0, color = 'b') – Alexis

At first it was generating bars that are twice as high as they should be, because it was measuring from the bottom (ie the 'lows' value) and then ADDING to it a height (from the 'highs' value).

So I ended up introducing a new list, 'displacements', which measures the distance between each high and each low (and in the process discovered that I had my lows and highs swapped around. Duh, sorry about that). So now I'm plotting 'displacements' rather than highs.

I added to Alexis' line a width, alignment, edgecolor and alpha (for transparency); then thickened the markers for the ax.scatter plots. Now the code works (well, almost, except for that arrow on the 4th bar being higher than it should be... hmmm)

import matplotlib.pyplot
from mpl_toolkits.mplot3d import Axes3D

dates       = [20020514, 20020515, 20020516, 20020517, 20020520]
lows        = [1135, 1158, 1152, 1158, 1163]
highs       = [1257, 1253, 1259, 1264, 1252]
upperLimits = [1125.0, 1125.0, 1093.75, 1125.0, 1125.0]
lowerLimits = [1250.0, 1250.0, 1156.25, 1250.0, 1250.0]

zaxisvalues0= [0, 0, 0, 0, 0]
zaxisvalues1= [1, 1, 1, 1, 1]
zaxisvalues2= [2, 2, 2, 2, 2]

fig = matplotlib.pyplot.figure()
ax  = fig.add_subplot(111, projection = '3d')

ax.plot(dates, zaxisvalues1, lowerLimits, color = 'b')
ax.plot(dates, zaxisvalues2, upperLimits, color = 'r')

ax.scatter(dates, zaxisvalues0, highs, color = 'g', marker = "^", linewidth=4)
ax.scatter(dates, zaxisvalues0, lows,  color = 'y', marker = "o", linewidth=4)

displacements = []
for i in lows:
    position = lows.index(i)
    disp = highs[position] - i
    displacements.append(disp)

ax.bar(dates, displacements, zdir='y', bottom=lows, zs=0, width=0.2, align='center', alpha=0.6, edgecolor='k')

matplotlib.pyplot.show()

This is the result:

情节

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