How can I make a simple 3D line with Matplotlib? - python

I want to generate the lines, which I get from an array in 3D.
Here is the code:
VecStart_x = [0,1,3,5]
VecStart_y = [2,2,5,5]
VecStart_z = [0,1,1,5]
VecEnd_x = [1,2,-1,6]
VecEnd_y = [3,1,-2,7]
VecEnd_z =[1,0,4,9]
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot([VecStart_x ,VecEnd_x],[VecStart_y,VecEnd_y],[VecStart_z,VecEnd_z])
plt.show()
Axes3D.plot()
I get that error:
ValueError: third arg must be a format string

I guess, you want to plot 4 lines. Then you can try
for i in range(4):
ax.plot([VecStart_x[i], VecEnd_x[i]], [VecStart_y[i],VecEnd_y[i]],zs=[VecStart_z[i],VecEnd_z[i]])
As Nicolas has suggested, do have a look at the matplotlib gallery.

The gallery is a great starting point to find out examples:
http://matplotlib.org/gallery.html
There is an example of 3d line plot here:
http://matplotlib.org/examples/mplot3d/lines3d_demo.html
You see that you need to pass to the ax.plot function 3 vectors.
You are actually passing list of lists.
I don't know what you mean by the Start and End sublist, but the following line should work :
ax.plot(VecStart_x + VecEnd_x, VecStart_y + VecEnd_y, VecStart_z +VecEnd_z)
Here I sum the sublist (concatenation) in order to have only one list by axis.

Related

Set output range of matplotlib - imshow

For a project I'm working on I have created two sets of data which is made from a function that takes two input and returns a 3rd. I am currently using a matplotlib imshow graph to show the data. As one of the data sets contains far higher values than the other data set so I was hoping to set a range for both meaning the colours would represent the same value across the two charts. Is there a good way to do this? thankyou
Here is the code I am currently using:
import matplotlib.pyplot as plt
import json
import numpy as np
with open("multi_testing\out_put\\bit_shift.txt","r") as f:
n = json.loads(f.read())
n = n[0]
inp = np.array(n)
fig, ax = plt.subplots()
im = ax.imshow(inp)
ax.invert_yaxis()
ax.set_title("bit shifting")
fig.tight_layout()
plt.show()
and here are the two data sets:
[[[7,7,7,7,7,7,7,7,7,7,7],[11,11,11,11,11,11,11,11,11,11,11],[15,15,15,15,15,15,15,15,15,15,15],[19,19,19,19,19,19,19,19,19,19,19],[23,23,23,23,23,23,23,23,23,23,23],[27,27,27,27,27,27,27,27,27,27,27],[31,31,31,31,31,31,31,31,31,31,31],[35,35,35,35,35,35,35,35,35,35,35],[39,39,39,39,39,39,39,39,39,39,39],[43,43,43,43,43,43,43,43,43,43,43],[47,47,47,47,47,47,47,47,47,47,47]]]
and
[[[10,19,26,28,33,35,35,37,40,42,42],[10,19,26,28,33,35,35,37,40,42,42],[10,19,26,28,33,35,35,37,40,42,42],[10,19,26,28,33,35,35,37,40,42,42],[10,19,26,28,33,35,35,37,40,42,42],[10,19,26,28,33,35,35,37,40,42,42],[10,19,26,28,33,35,35,37,40,42,42],[10,19,26,28,33,35,35,37,40,42,42],[10,19,26,28,33,35,35,37,40,42,42],[10,19,26,28,33,35,35,37,40,42,42],[10,19,26,28,33,35,35,37,40,42,42]]]
You can use vmin and vmax for this while using ax.imshow(). For example:
im = ax.imshow(inp, vmin=0, vmax=50)

Plots not visible when using a line plot

I am new to python and I am trying to plot x and y (both have a large number of data) but when I use a plt.plot there is not plot visible on the output.
The code I have been using is
for i in range(len(a)):
plt.plot(a[i],b[i])
plt.figure()
plt.show()
when I tried a scatter plot
for i in range(len(a)):
plt.scatter(a[i],b[i])
plt.figure()
plt.show()
I am not able to understand the reason for missing the line plot and even when I try seaborn it showing me an error ValueError: If using all scalar values, you must pass an index
import numpy as np
import matplotlib.pyplot as plt
a = np.linspace(0,5,100)
b = np.linspace(0,10,100)
plt.plot(a,b)
plt.show()
I think this answers your question. I have taken sample values of a and b. The matplotlib line plots are not required to run in loops
A line is created between two points. If you are plotting single values, a line can't be constructed.
Well, you might say "but I am plotting many points," which already contains part of the answer (points). Actually, matplotlib.plot() plots line-objects. So every time, you call plot, it creates a new one (no matter if you are calling it on the same or on a new axis). The reason why you don't get lines is that only single points are plotted. The reason why you're not even seeing the these points is that plot() does not indicate the points with markers per default. If you add marker='o' to plot(), you will end up with the same figure as with scatter.
A scatter-plot on the other hand is an unordered collection of points. There characteristic is that there are no lines between these points because they are usually not a sequence. Nonetheless, because there are no lines between them, you can plot them all at once. Per default, they have all the same color but you can even specify a color vector so that you can encode a third information in it.
import matplotlib.pyplot as plt
import numpy as np
# create random data
a = np.random.rand(10)
b = np.random.rand(10)
# open figure + axes
fig,axs = plt.subplots(1,2)
# standard scatter-plot
axs[0].scatter(a,b)
axs[0].set_title("scatter plot")
# standard line-plot
axs[1].plot(a,b)
axs[1].set_title("line plot")

Matplotlib alternative to fill_betweenx()

I'm trying to get the functionality of fill_betweenx() without having to use the function itself, because it doesn't accept the interpolate parameter. I need the interpolate functionality that is supported by fill_between(), but for the filling to happen relative to the x axis. It sounds like the interpolate parameter will be supported for fill_betweenx() in matplotlib 2.1, but it would be great to have access to the functionality via a workaround in the meantime.
This is the line of code in question:
ax4.fill_betweenx(x,300,p, where=p>=150, interpolate=True, facecolor='White', lw=1, zorder=2)
Unfortunately this gives me AttributeError: Unknown property interpolate.
One lazy way to do it is to use the fill_between() function with inverted coordinates on a figure that you don't show (i.e. close the figure before using plt.show()), and then re-use the vertices of the PolyCollection that fill_between() returns on your actual plot. It's not perfect, but it works as a quick fix. Here an example of what I'm talking about:
from matplotlib import pyplot as plt
from matplotlib.collections import PolyCollection
import numpy as np
fig, axes = plt.subplots(nrows = 2, ncols =2, figsize=(8,8))
#the data
x = np.linspace(0,np.pi/2,3)
y = np.sin(x)
#fill_between without interpolation
ax = axes[0,0]
ax.plot(x,y,'k')
ax.fill_between(x,0.5,y,where=y>0.25)
#fill_between with interpolation, keep the PolyCollection
ax = axes[0,1]
ax.plot(x,y,'k')
poly_col = ax.fill_between(x,0.5,y,where=y>0.25,interpolate=True)
#fill_betweenx -- no interpolation possible
ax = axes[1,0]
ax.plot(y,x,'k')
ax.fill_betweenx(x,0.5,y,where=y>0.25)
#faked fill_betweenx:
ax = axes[1,1]
ax.plot(y,x,'k')
#get the vertices from the saved PolyCollection, swap x- and y-values
v=poly_col.get_paths()[0].vertices
#convert to correct format
v2=list(zip(v[:,1],v[:,0]))
#and add to axes
ax.add_collection(PolyCollection([v2]))
#voila
plt.show()
The result of the code looks like this:

matplotlib plotting multiple lines in 3D

I am trying to plot multiple lines in a 3D plot using matplotlib. I have 6 datasets with x and y values. What I've tried so far was, to give each point in the data sets a z-value. So all points in data set 1 have z=1 all points of data set 2 have z=2 and so on.
Then I exported them into three files. "X.txt" containing all x-values, "Y.txt" containing all y-values, same for "Z.txt".
Here's the code so far:
#!/usr/bin/python
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
import pylab
xdata = '/X.txt'
ydata = '/Y.txt'
zdata = '/Z.txt'
X = np.loadtxt(xdata)
Y = np.loadtxt(ydata)
Z = np.loadtxt(zdata)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_wireframe(X,Y,Z)
plt.show()
What I get looks pretty close to what I need. But when using wireframe, the first point and the last point of each dataset are connected. How can I change the colour of the line for each data set and how can I remove the connecting lines between the datasets?
Is there a better plotting style then wireframe?
Load the data sets individually, and then plot each one individually.
I don't know what formats you have, but you want something like this
from mpl_toolkits.mplot3d.axes3d import Axes3D
import matplotlib.pyplot as plt
fig, ax = plt.subplots(subplot_kw={'projection': '3d'})
datasets = [{"x":[1,2,3], "y":[1,4,9], "z":[0,0,0], "colour": "red"} for _ in range(6)]
for dataset in datasets:
ax.plot(dataset["x"], dataset["y"], dataset["z"], color=dataset["colour"])
plt.show()
Each time you call plot (or plot_wireframe but i don't know what you need that) on an axes object, it will add the data as a new series. If you leave out the color argument matplotlib will choose them for you, but it's not too smart and after you add too many series' it will loop around and start using the same colours again.
n.b. i haven't tested this - can't remember if color is the correct argument. Pretty sure it is though.

Waterfall plot python?

Is there a python module that will do a waterfall plot like MATLAB does? I googled 'numpy waterfall', 'scipy waterfall', and 'matplotlib waterfall', but did not find anything.
You can do a waterfall in matplotlib using the PolyCollection class. See this specific example to have more details on how to do a waterfall using this class.
Also, you might find this blog post useful, since the author shows that you might obtain some 'visual bug' in some specific situation (depending on the view angle chosen).
Below is an example of a waterfall made with matplotlib (image from the blog post):
(source: austringer.net)
Have a look at mplot3d:
# copied from
# http://matplotlib.sourceforge.net/mpl_examples/mplot3d/wire3d_demo.py
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
X, Y, Z = axes3d.get_test_data(0.05)
ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10)
plt.show()
I don't know how to get results as nice as Matlab does.
If you want more, you may also have a look at MayaVi: http://mayavi.sourceforge.net/
The Wikipedia type of Waterfall chart one can obtain also like this:
import numpy as np
import pandas as pd
def waterfall(series):
df = pd.DataFrame({'pos':np.maximum(series,0),'neg':np.minimum(series,0)})
blank = series.cumsum().shift(1).fillna(0)
df.plot(kind='bar', stacked=True, bottom=blank, color=['r','b'])
step = blank.reset_index(drop=True).repeat(3).shift(-1)
step[1::3] = np.nan
plt.plot(step.index, step.values,'k')
test = pd.Series(-1 + 2 * np.random.rand(10), index=list('abcdefghij'))
waterfall(test)
I have generated a function that replicates the matlab waterfall behaviour in matplotlib. That is:
It generates the 3D shape as many independent and parallel 2D curves
Its color comes from a colormap in the z values
I started from two examples in matplotlib documentation: multicolor lines and multiple lines in 3d plot. From these examples, I only saw possible to draw lines whose color varies following a given colormap according to its z value following the example, which is reshaping the input array to draw the line by segments of 2 points and setting the color of the segment to the z mean value between these 2 points.
Thus, given the input matrixes n,m matrixes X,Y and Z, the function loops over the smallest dimension between n,m to plot each of the waterfall plot independent lines as a line collection of the 2 points segments as explained above.
def waterfall_plot(fig,ax,X,Y,Z,**kwargs):
'''
Make a waterfall plot
Input:
fig,ax : matplotlib figure and axes to populate
Z : n,m numpy array. Must be a 2d array even if only one line should be plotted
X,Y : n,m array
kwargs : kwargs are directly passed to the LineCollection object
'''
# Set normalization to the same values for all plots
norm = plt.Normalize(Z.min().min(), Z.max().max())
# Check sizes to loop always over the smallest dimension
n,m = Z.shape
if n>m:
X=X.T; Y=Y.T; Z=Z.T
m,n = n,m
for j in range(n):
# reshape the X,Z into pairs
points = np.array([X[j,:], Z[j,:]]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
# The values used by the colormap are the input to the array parameter
lc = LineCollection(segments, cmap='plasma', norm=norm, array=(Z[j,1:]+Z[j,:-1])/2, **kwargs)
line = ax.add_collection3d(lc,zs=(Y[j,1:]+Y[j,:-1])/2, zdir='y') # add line to axes
fig.colorbar(lc) # add colorbar, as the normalization is the same for all
# it doesent matter which of the lc objects we use
ax.auto_scale_xyz(X,Y,Z) # set axis limits
Therefore, plots looking like matlab waterfall can be easily generated with the same input matrixes as a matplotlib surface plot:
import numpy as np; import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from mpl_toolkits.mplot3d import Axes3D
# Generate data
x = np.linspace(-2,2, 500)
y = np.linspace(-2,2, 60)
X,Y = np.meshgrid(x,y)
Z = np.sin(X**2+Y**2)-.2*X
# Generate waterfall plot
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
waterfall_plot(fig,ax,X,Y,Z,linewidth=1.5,alpha=0.5)
ax.set_xlabel('X'); ax.set_ylabel('Y'); ax.set_zlabel('Z')
fig.tight_layout()
The function assumes that when generating the meshgrid, the x array is the longest, and by default the lines have fixed y, and its the x coordinate what varies. However, if the size of the y array is longer, the matrixes are transposed, generating the lines with fixed x. Thus, generating the meshgrid with the sizes inverted (len(x)=60 and len(y)=500) yields:
To see what are the possibilities of the **kwargs argument, refer to the LineCollection class documantation and to its set_ methods.

Categories