Plot multiple data on 3d scatter plot - python

I'm having trouble plotting multiple sets of data onto a single 3D scatter plot. What I'm doing is I have a system of three equations and I'm calculating the zeros of the equations using linalg. I'm then plotting each set of zeros I get onto a 3D plot. For one of my parameters, I'm changing it's value and observing how the zeros change from that. I'd like to plot all of the data sets on one 3D scatter plot so it'd be easy to compare how they differ but I keep getting one graph plotted for each data set. Can any of you figure out what I need to fix?
import numpy as np
from numpy import linalg
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
plt.close('all')
#Will be solving the following system of equations:
#sx-(b/r)z=0
#-x+ry+(s-b)z=0
#(1/r)x+y-z=0
r=50.0
b=17.0/4.0
s=[10.0,20.0,7.0,r/b]
color=['r','b','g','y']
markers=['s','o','^','d']
def system(s,b,r,color,m):
#first creates the matrix as an array so the parameters can be changed from outside
#and then coverts array into a matrix
u_arr=np.array([[s,0,-b/r],[-1,r,s-b],[1/r,1,-1]])
u_mat=np.matrix(u_arr)
U_mat=linalg.inv(u_mat)
#converts matrix into an array and then into a list to manipulate
x_zeros=np.array(U_mat[0]).reshape(-1).tolist()
y_zeros=np.array(U_mat[1]).reshape(-1).tolist()
z_zeros=np.array(U_mat[2]).reshape(-1).tolist()
zeros=[x_zeros,y_zeros,z_zeros]
coordinates=['x','y','z']
print('+'*70)
print('For s=%1.1f:' % s)
print('\n')
for i in range(3):
print('For the %s direction, the roots are: ' % coordinates[i])
for j in range(3):
print(zeros[i][j])
print('-'*50)
fig3d=plt.figure()
ax=Axes3D(fig3d)
ax.scatter(x_zeros,y_zeros,z_zeros,c=color,marker=m)
plt.title('Zeros for a Given System of Equations for s=%1.1f' % (s))
ax.set_xlabel('Zeros in x Direction')
ax.set_ylabel('Zeros in y Direction')
ax.set_zlabel('Zeros in z Direction')
plt.show()
for k in range(len(s)):
system(s[k],b,r,color[k],markers[k])
Thanks in advance for any help.

You are creating a new axes instance each time system() is called. Instead pass ax as an argument to system
def system(s,b,r,color,m, ax):
# ...
ax.scatter(x_zeros,y_zeros,z_zeros,c=color,marker=m)
Then create the axes instance before looping
fig3d=plt.figure()
ax=Axes3D(fig3d)
for k in range(len(s)):
system(s[k],b,r,color[k],markers[k], ax)
plt.show()
This was all plots are added to ax. You may then want to think about setting the axes labels etc outside of the system() function. Splitting it into two functions, one which sets the plot up and one which creates the required data and plots it.

Related

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 3D Waterfall Plot with Colored Heights

I'm trying to visualise a dataset in 3D which consists of a time series (along y) of x-z data, using Python and Matplotlib.
I'd like to create a plot like the one below (which was made in Python: http://austringer.net/wp/index.php/2011/05/20/plotting-a-dolphin-biosonar-click-train/), but where the colour varies with Z - i.e. so the intensity is shown by a colormap as well as the peak height, for clarity.
An example showing the colormap in Z is (apparently made using MATLAB):
This effect can be created using the waterfall plot option in MATLAB, but I understand there is no direct equivalent of this in Python.
I have also tried using the plot_surface option in Python (below), which works ok, but I'd like to 'force' the lines running over the surface to only be in the x direction (i.e. making it look more like a stacked time series than a surface). Is this possible?
Any help or advice greatly welcomed. Thanks.
I have generated a function that replicates the matlab waterfall behaviour in matplotlib, but I don't think it is the best solution when it comes to performance.
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 the 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 line like in the example, by 2 points segments, where the reshaping to plot by segments is done reshaping the array with the same code as the example.
def waterfall_plot(fig,ax,X,Y,Z):
'''
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
'''
# 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)
lc = LineCollection(segments, cmap='plasma', norm=norm)
# Set the values used for colormapping
lc.set_array((Z[j,1:]+Z[j,:-1])/2)
lc.set_linewidth(2) # set linewidth a little larger to see properly the colormap variation
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
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, 40)
X,Y = np.meshgrid(x,y)
Z = np.sin(X**2+Y**2)
# Generate waterfall plot
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
waterfall_plot(fig,ax,X,Y,Z)
ax.set_xlabel('X') ; ax.set_xlim3d(-2,2)
ax.set_ylabel('Y') ; ax.set_ylim3d(-2,2)
ax.set_zlabel('Z') ; ax.set_zlim3d(-1,1)
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 dimension is larger, the matrixes are transposed, generating the lines with fixed x. Thus, generating the meshgrid with the sizes inverted (len(x)=40 and len(y)=500) yields:
with a pandas dataframe with the x axis as the first column and each spectra as another column
offset=0
for c in s.columns[1:]:
plt.plot(s.wavelength,s[c]+offset)
offset+=.25
plt.xlim([1325,1375])

How to extract x,y data from kdensity plot from matplotlib for python

I am trying to figure out how to make a 3d figure of uni-variate kdensity plots as they change over time (since they pull from a sliding time window of data over time).
Since I can't figure out how to do that directly, I am first trying to get the x,y plotting data for kdensity plots of matplotlib in python. I hope after I extract them I can use them along with a time variable to make a three dimensional plot.
I see several posts telling how to do this in Matlab. All reference getting Xdata and Ydata from the underlying figure:
x=get(h,'Xdata')
y=get(h,'Ydata')
How about in python?
The answer was already contained in another thread (How to create a density plot in matplotlib?). It is pretty easy to get a set of kdensity x's and y's from a set of data.
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import gaussian_kde
data = [1.5]*7 + [2.5]*2 + [3.5]*8 + [4.5]*3 + [5.5]*1 + [6.5]*8 # data is a set of univariate data
xs = np.linspace(0,max(data),200) # This 200 sets the # of x (and so also y) points of the kdensity plot
density = gaussian_kde(data)
density.covariance_factor = lambda : .25
density._compute_covariance()
ys = density(xs)
plt.plot(xs,ys)
And there you have it. Both the kdensity plot and it's underlying x,y data.
Not sure how kdensity plots work, but note that matplotlib.pyplot.plot returns a list of the added Line2D objects, which are, in fact, where the X and Y data are stored. I suspect they did that to make it work similarly to MATLAB.
import matplotlib.pyplot as plt
h = plt.plot([1,2,3],[2,4,6]) # [<matplotlib.lines.Line2D object at 0x021DA9F0>]
x = h[0].get_xdata() # [1,2,3]
y = h[0].get_ydata() # [2,4,6]

Update marker sizes of a scatter plot

A scatter plot object has a method called .set_array to update the colours of the markers and .set_offsets to update their position but how can I update the marker sizes?
I need this for fast real time plotting.
Yes it is doable, with using a magic method (_size). Use it with caution, as it may become broken in future releases:
from matplotlib import pyplot as plt
import numpy as np
x, y=range(10), range(10)
sca=plt.scatter(x,y)
raw_input()
sca._sizes=(5+np.arange(10))*10 #you can set you markers to different sizes
plt.draw()
The method to update the sizes of the scatter points is called .set_sizes()
scat = plt.scatter(x,y)
scat.set_sizes(sizes)
where sizes must be an array or list of same length as x and y.

How to plot a data cube in python

I was wondering if there's a way to plot a data cube in Python. I mean I have three coordinate for every point
x=part.points[:,0]
y=part.points[:,1]
z=part.points[:,2]
And for every point I have a scalar field t(x,y,z)
I would like to plot a 3D data cube showing the position of the point and for every point a color which is proportional to the scalar field t in that point.
I tried with histogramdd but it didn't work.
You can use matplotlib.
Here you have a working example (that moves!):
import random
from matplotlib import pyplot
from mpl_toolkits.mplot3d import Axes3D
mypoints = []
for _ in range(100):
mypoints.append([random.random(), #x
random.random(), #y
random.random(), #z
random.randint(10,100)]) #scalar
data = zip(*mypoints) # use list(zip(*mypoints)) with py3k
fig = pyplot.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(data[0], data[1], data[2], c=data[3])
pyplot.show()
You probably have to customize the relation of your scalar values with the corresponding colors.
Matplotlib has a very nice look but it can be slow drawing and moving these 3D drawings when you have many points. In these cases I used to use Gnuplot controlled by gnuplot.py. Gnuplot can also be used directly as a subprocess as shown here and here.
Another option is Dots plot, produced by MathGL. It is GPL plotting library. Add it don't need many memory if you save in bitmap format (PNG, JPEG, GIF and so on).

Categories