Align projected 2d plot on 3dscatter grid in matplotlib - python

I'm don't have much experience with python, only a basic course. I'm trying to create a scatter plot with 3 dimensions and 2d plot proyected on the faces of the plot area.
This is the dataset
So far, I have this:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from mpl_toolkits.mplot3d import Axes3D
dataset = pd.read_csv("dummy.csv")
x = dataset.das_avg
y = dataset.thick_mm
z = dataset.cool_rate_avg
color = dataset.temp_mold
fig= plt.figure(figsize=(10,5))
ax = Axes3D(fig)
ax.plot(x, z, 'r+', zdir='y', zs=0 ,marker= 'o', alpha=0.05)
ax.plot(y, z, 'g+', zdir='x', zs=0, marker= 'o', alpha=0.05)
ax.plot(x, y, 'y+', zdir='z', zs=0, marker= 'o', alpha=0.05)
ax.scatter(x, y, z, s=20, c=color, cmap=plt.cm.jet)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
sm = plt.cm.ScalarMappable(cmap=plt.cm.jet, norm=plt.Normalize(vmin=color.min(), vmax=color.max()))
sm._A = []
plt.colorbar(sm)
plt.show()
And so far, so good; but I'm having trouble aligning the projected plot on the grid on the chart area:
Example
The yellow dots should be on the value 2 on the grid, for example.
Playing around with the zs value on the plots, I can align them, but when I change the values, the grid's scale moves and the plots are not aligned again.
Also, I can't for the life of me push the red dots to the back of the chart.
Is there a way to achieve this?

Just modify the following lines in your code and it makes it look like you want. You can adjust the ylim as per your desired aesthetics
ax.plot(x, z, 'r+', zdir='y', zs=2.5 ,marker= 'o', alpha=0.05)
ax.plot(x, y, 'y+', zdir='z', zs=0, marker= 'o', alpha=0.05)
ax.set_ylim(0, 2.5)
ax.set_zlim(0, 600)
Output

Related

3D wireframe plot with 2D projections: Spatial organiszation & frequency of projection

I'm working on a 3D plot displayed by a wireframe, where 2D plots are projected on the x, y, and z surface, respectively. Below you can find a minimum example.
I have 2 questions:
With contourf, the 2D plots for every x=10, x=20,... or y=10, y=20,... are displayed on the plot walls. Is there a possibility to define for which x or y, respectively, the contour plots are displayed? For example, in case I only want to have the xz contour plot for y = 0.5 mirrored on the wall?
ADDITION: To display what I mean with "2D plots", I changed "contourf" in the code to "contour" and added the resulting plot to this question. Here you can see now the xz lines for different y values, all offset to y=90. What if I do not want to have all the lines, but only two of them for defined y values?
3D_plot_with_2D_contours
As you can see in the minimum example, the 2D contour plot optically covers the wireframe 3D plot. With increasing the transparency with alpha=0.5 I can increase the transparency of the 2D contours to at least see the wireframe, but it is still optically wrong. Is it possible to sort the objects correctly?
import matplotlib.pyplot as plt,numpy as np
import pylab as pl
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt,numpy as np
plt.clf()
fig = plt.figure(1,figsize=(35,17),dpi=600,facecolor='w',edgecolor='k')
fig.set_size_inches(10.5,8)
ax = fig.gca(projection='3d')
X, Y, Z = axes3d.get_test_data(0.05)
Xnew = X + 50
Ynew = Y + 50
cset = ax.contourf(Xnew, Ynew, Z, zdir='z', offset=-100, cmap=plt.cm.coolwarm, alpha=0.5)
cset = ax.contourf(Xnew, Ynew, Z, zdir='x', offset=10, cmap=plt.cm.coolwarm, alpha=0.5)
cset = ax.contourf(Xnew, Ynew, Z, zdir='y', offset=90, cmap=plt.cm.coolwarm, alpha = 0.5)
ax.plot_wireframe(Xnew, Ynew, Z, rstride=5, cstride=5, color='black')
Z=Z-Z.min()
Z=Z/Z.max()
from scipy.ndimage.interpolation import zoom
Xall=zoom(Xnew,5)
Yall=zoom(Ynew,5)
Z=zoom(Z,5)
ax.set_xlim(10, 90)
ax.set_ylim(10, 90)
ax.set_zlim(-100, 100)
ax.tick_params(axis='z', which='major', pad=10)
ax.set_xlabel('X',labelpad=10)
ax.set_ylabel('Y',labelpad=10)
ax.set_zlabel('Z',labelpad=17)
ax.view_init(elev=35., azim=-70)
fig.tight_layout()
plt.show()
ADDITION 2: Here is the actual code I'm working with. However, the original data are hidden in the csv files which are too big to be included in the minimal example. That's why was initially replacing them by the test data. However, maybe the actual code helps nevertheless.
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt,numpy as np
import pylab as pl
from matplotlib.markers import MarkerStyle
import csv
with open("X.csv", 'r') as f:
X = list(csv.reader(f, delimiter=";"))
import numpy as np
X = np.array(X[1:], dtype=np.float)
import csv
with open("Z.csv", 'r') as f:
Z = list(csv.reader(f, delimiter=";"))
import numpy as np
Z = np.array(Z[1:], dtype=np.float)
Y = [[7,7.1,7.2,7.3,7.4,7.5,7.6,7.7,7.8,7.9,8,8.1,8.2,8.3,8.4,8.5,8.6,8.7,8.8,8.9,9]]
Xall = np.repeat(X[:],21,axis=1)
Yall = np.repeat(Y[:],30,axis=0)
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt,numpy as np
plt.clf()
fig = plt.figure(1,figsize=(35,17),dpi=600,facecolor='w',edgecolor='k')
fig.set_size_inches(10.5,8)
ax = fig.gca(projection='3d')
cset = ax.contourf(Xall, Yall, Z, 2, zdir='x', offset=0, cmap=plt.cm.coolwarm, shade = False, edgecolor='none', alpha=0.5)
cset = ax.contourf(Xall, Yall, Z, 2, zdir='y', offset=9, cmap=plt.cm.coolwarm, shade = False, edgecolor='none', alpha=0.5)
ax.plot_wireframe(Xall, Yall, Z, rstride=1, cstride=1, color='black')
Z=Z-Z.min()
Z=Z/Z.max()
from scipy.ndimage.interpolation import zoom
Xall=zoom(Xall,5)
Yall=zoom(Yall,5)
Z=zoom(Z,5)
cset = ax.plot_surface(Xall, Yall, np.zeros_like(Z)-0,facecolors=plt.cm.coolwarm(Z),shade=False,alpha=0.5,linewidth=False)
ax.set_xlim(-0.5, 31)
ax.set_ylim(6.9, 9.1)
ax.set_zlim(0, 500)
labelsx = [item.get_text() for item in ax.get_xticklabels()]
empty_string_labelsx = ['']*len(labelsx)
ax.set_xticklabels(empty_string_labelsx)
labelsy = [item.get_text() for item in ax.get_yticklabels()]
empty_string_labelsy = ['']*len(labelsy)
ax.set_yticklabels(empty_string_labelsy)
labelsz = [item.get_text() for item in ax.get_zticklabels()]
empty_string_labelsz = ['']*len(labelsz)
ax.set_zticklabels(empty_string_labelsz)
import matplotlib.ticker as ticker
ax.xaxis.set_major_locator(ticker.MultipleLocator(5))
ax.xaxis.set_minor_locator(ticker.MultipleLocator(1))
ax.yaxis.set_major_locator(ticker.MultipleLocator(0.5))
ax.yaxis.set_minor_locator(ticker.MultipleLocator(0.25))
ax.zaxis.set_major_locator(ticker.MultipleLocator(100))
ax.zaxis.set_minor_locator(ticker.MultipleLocator(50))
ax.tick_params(axis='z', which='major', pad=10)
ax.set_xlabel('X',labelpad=5,fontsize=15)
ax.set_ylabel('Y',labelpad=5,fontsize=15)
ax.set_zlabel('Z',labelpad=5,fontsize=15)
ax.view_init(elev=35., azim=-70)
fig.tight_layout()
plt.show()
Alternate possible answer.
This code demonstrates
A plot of a surface and its correponding wireframe
The creation of data and its plot of 3d lines (draped on the surface in 1) at specified values of x and y
Projections of the 3d lines (in 2) on to the frame walls
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from scipy import interpolate
import numpy as np
# use the test data for plotting
fig = plt.figure(1, figsize=(6,6), facecolor='w', edgecolor='gray')
ax = fig.gca(projection='3d')
X, Y, Z = axes3d.get_test_data(0.1) #get 3d data at appropriate density
# create an interpolating function
# can take a long time if data is too large
f1 = interpolate.interp2d(X, Y, Z, kind='linear')
# in general, one can use a set of other X,Y,Z that cover a surface
# preferably, (X,Y) are in grid arrangement
# make up a new set of 3d data to plot
# ranges of x1, and y1 will be inside (X,Y) of the data obtained above
# related grid, x1g,y1g,z1g will be obtained from meshgrid and the interpolated function
x1 = np.linspace(-15,15,10)
y1 = np.linspace(-15,15,10)
x1g, y1g = np.meshgrid(x1, y1)
z1g = f1(x1, y1) #dont use (x1g, y1g)
# prep data for 3d line on the surface (X,Y,Z) at x=7.5
n = 12
x_pf = 7.5
x5 = x_pf*np.ones(n)
y5 = np.linspace(-15, 15, n)
z5 = f1(x_pf, y5)
# x5,y5,z5 can be used to plot 3d line on the surface (X,Y,Z)
# prep data for 3d line on the surface (X,Y,Z) at y=6
y_pf = 6
x6 = np.linspace(-15, 15, n)
y6 = x_pf*np.ones(n)
z6 = f1(x6, y_pf)
# x6,y6,z6 can be used to plot 3d line on the surface (X,Y,Z)
ax = fig.gca(projection='3d')
ax.plot_surface(x1g, y1g, z1g, alpha=0.25)
ax.plot_wireframe(x1g, y1g, z1g, rstride=2, cstride=2, color='black', zorder=10, alpha=1, lw=0.8)
# 3D lines that follow the surface
ax.plot(x5,y5,z5.flatten(), color='red', lw=4)
ax.plot(x6,y6,z6.flatten(), color='green', lw=4)
# projections of 3d curves
# project red and green lines to the walls
ax.plot(-15*np.ones(len(y5)), y5, z5.flatten(), color='red', lw=4, linestyle=':', alpha=0.6)
ax.plot(x6, 15*np.ones(len(x6)), z6.flatten(), color='green', lw=4, linestyle=':', alpha=0.6)
# projections on other sides (become vertical lines)
# change to if True, to plot these
if False:
ax.plot(x5, 15*np.ones(len(x5)), z5.flatten(), color='red', lw=4, alpha=0.3)
ax.plot(-15*np.ones(len(x6)), y6, z6.flatten(), color='green', lw=4, alpha=0.3)
ax.set_title("Projections of 3D lines")
# set limits
ax.set_xlim(-15, 15.5)
ax.set_ylim(-15.5, 15)
plt.show();
(Answer to question 1) To plot the intersections between the surface and the specified planes (y=-20, and y=20), one need to find what Y[?]=-20 and 20. By inspection, I found that Y[100]=20, Y[20]=-20.
The relevant code to plot the lines of intersection:
# By inspection, Y[100]=20, Y[20]=-20
ax.plot3D(X[100], Y[100], Z[100], color='red', lw=6) # line-1 at y=20
ax.plot3D(X[20], Y[20], Z[20], color='green', lw=6) # line-2 at y=-20
# Project them on Z=-100 plane
ax.plot3D(X[100], Y[100], -100, color='red', lw=3) # projection of Line-1
ax.plot3D(X[20], Y[20], -100, color='green', lw=3) # projection of Line-2
The output plot:
(Answer to question 2) To get better plot with the wireframe standout from the surface plot. The surface plot must be partially transparent, which is achieved by setting option alpha=0.6. The relevant code follows.
Z1 = Z-Z.min()
Z1 = Z1/Z.max()
Xall = zoom(X,3)
Yall = zoom(Y,3)
Zz = zoom(Z1, 3)
surf = ax.plot_surface(Xall, Yall, Zz, rstride=10, cstride=10,
facecolors = cm.jet(Zz/np.amax(Zz)),
linewidth=0, antialiased=True,
alpha= 0.6)
# Wireframe
ax.plot_wireframe(X, Y, Z, rstride=5, cstride=5, color='black', alpha=1, lw=0.8)
The plot is:

Turning 2D graphics into 3D in python

In 2D I have my x that gets the value of the x and y coordinates:
x = [[0.72,0.82]]
And at some point in the code I use this:
plt.plot(x[i][0], x[i][1], 'go', markersize=15, alpha=.5)
Now I have an x that gets the value of the x, y, and z coordinates:
x = [[0.72,0.82,-0.77]]
And I want to reproduce the same effect of 2D only now in 3D, I tried to do something like:
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)
ax.set_zlim(-1, 1)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
ax.scatter(x[i][0], x[i][1], x[i][2], 'go', markersize=15, alpha=.5)
But I get the following error:
AttributeError: Unknown property markersize
P.S.: I'm using:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
I'd like to know how can I plot them correctly.
Check matplotlib reference for ax.scatter arguments, markerzise and alpha are not there, to change points' size you should use sargument, something like:
ax.scatter(xs, ys, zs, s=10, c=c, marker=m)
Notice s can also be an array of the same length as xs if you want points' size to be proportional to it's xsvalue.

No color when I make python scatter color plot using third variable to define color

I try to make colorful scatter plot using third variable to define color. It is simple to use the following code:
plt.scatter(mH, mA, s=1, c=mHc)
plt.colorbar()
plt.show()
But I do not have many choices to modify the frame of the plot. I am trying the following code to make colorful scatter plot, at the same time I try to optimize the frame of the plot:
import numpy as np
import math
from matplotlib import rcParams
import matplotlib.pyplot as plt
from matplotlib.ticker import AutoMinorLocator
fig, ax = plt.subplots()
cax = ax.scatter(mH,mA,s=0.5,c=mHc) ### mH, mA, mHC are the dataset
fig.colorbar(cax)
minor_locator1 = AutoMinorLocator(6)
minor_locator2 = AutoMinorLocator(6)
ax.xaxis.set_minor_locator(minor_locator1)
ax.yaxis.set_minor_locator(minor_locator2)
ax.tick_params('both', length=10, width=2, which='major')
ax.tick_params('both', length=5, width=2, which='minor')
ax.set_xlabel(r'$m_H$')
ax.set_ylabel(r'$m_A$')
ax.set_xticks([300,600,900,1200,1500])
ax.set_yticks([300,600,900,1200,1500])
plt.savefig('mH_mA.png',bbox_inches='tight')
plt.show()
But the plot I got is black-white. It looks like the problem lies in the marker size argument, but I do not have much idea how to correct it. I want to have smaller marker size. Anyone can offer me some idea to approach this issue. Thanks.
size=0.5 is extremely small - probably all you are seeing is the marker outlines. I would suggest you increase the size a bit, and perhaps pass edgecolors="none" to turn off the marker edge stroke:
import numpy as np
from matplotlib import pyplot as plt
n = 10000
x, y = np.random.randn(2, n)
z = -(x**2 + y**2)**0.5
fig, ax = plt.subplots(1, 1)
ax.scatter(x, y, s=5, c=z, cmap="jet", edgecolors="none")
You might also want to experiment with making the points semi-transparent using the alpha= parameter:
ax.scatter(x, y, s=20, c=z, alpha=0.1, cmap="jet", edgecolors="none")
It can be difficult to get scatter plots to look nice when you have such a massive number of overlapping points. I would be tempted to plot your data as a 2D histogram or contour plot instead, or perhaps even a combination of a scatter plot and a contour plot:
density, xe, ye = np.histogram2d(x, y, bins=20, normed=True)
ax.hold(True)
ax.scatter(x, y, s=5, c=z, cmap="jet", edgecolors="none")
ax.contour(0.5*(xe[:-1] + xe[1:]), 0.5*(ye[:-1] + ye[1:]), density,
colors='k')

How to plot individual points without curve in python?

I want to plot individual data points with error bars on a plot, but I don't want to have the curve. How can I do this? Are there some 'invisible' line style or can I set the line style colourless (but the marker still has to be visible)?
So this is the graph I have right now:
plt.errorbar(x5,y5,yerr=error5, fmt='o')
plt.errorbar(x3,y3,yerr=error3, fmt='o')
plt.plot(x3_true,y3_true, 'r--', label=(r'$\lambda = 0.3$'))
plot(x5_true, y5_true, 'b--', label=(r'$\lambda = 0.5$'))
plt.plot(x5,y5, linestyle=':', marker='o', color='red') #this is the 'ideal' curve that I want to add
plt.plot(x3,y3, linestyle=':', marker='o', color='red')
I want to keep the two dashed curve but I don't want the two dotted curve. How can I do this? And how can I change the color of the markers so I can have red points for the red curve, blue points for the blue curve?
You can use scatter:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 2*np.pi, 10)
y = np.sin(x)
plt.scatter(x, y)
plt.show()
Alternatively:
plt.plot(x, y, 's')
EDIT: If you want error bars you can do:
plt.errorbar(x, y, yerr=err, fmt='o')

Matplotlib 2d Plot on Faces of 3d Plot

I am producing plots of a spacecraft's trajectory at a specific point in its orbit.
I have a piece of code which produces a 3d line plot in 3dMatplotlib (a part of mycode and figure is shown here (I have drastically reduced the number of points within X,Y,Z to ~20 per array to make it easier to simply copy and paste as the principle is the same):
#
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.axes3d import Axes3D
from numpy import *
XdS=[14.54156005, 14.53922242, 14.53688586, 14.53454823, 14.5322106 , 14.52987297, 14.52753426, 14.52519555, 14.52285792, 14.52051922, 14.51818051, 14.51584073, 14.51350095, 14.51116117, 14.5088214 , 14.50648162, 14.50414076, 14.50179991, 14.49945906, 14.49711821]
YdS=[31.13035144, 31.12920087, 31.12805245, 31.12690188, 31.12575131, 31.12460073, 31.12345016, 31.12229745, 31.12114473, 31.11999201, 31.1188393 , 31.11768443, 31.11652957, 31.11537471, 31.11421984, 31.11306283, 31.11190582, 31.11074882, 31.10959181, 31.1084348]
ZdS=[3.94109446, 3.94060316, 3.94011186, 3.93962083, 3.93912926, 3.93863796, 3.93814639, 3.93765482, 3.93716325, 3.93667169, 3.93617985, 3.93568828, 3.93519618, 3.93470434, 3.9342125 , 3.9337204 , 3.93322829, 3.93273592, 3.93224382, 3.93175144]
fig=plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot(XdS,YdS,ZdS,c='black',linewidth=2)
ax.set_xlabel('XKSM (Saturn Radii)')
ax.set_ylabel('YKSM (Saturn Radii)')
ax.set_zlabel('ZKSM (Saturn Radii)')
plt.show()
#
What I want to do is be able to plot the 2d plots X vs Y, X vs Z, and Y vs Z on the edges/planes of this plot i.e. show what the 3d trajectory looks like looking at it in the 3 2d planes and display them at each axis of the current plot. (It isn’t actually as complicated as it might sound, as I already have the X,Y,Z, values for the trajectory). Here I found a similar example which achieves this, however utilising all 3d plot functions, available at: http://matplotlib.org/1.3.1/examples/mplot3d/contour3d_demo3.html : If you check out check out the link it will show the type of image i am trying to achieve.
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from matplotlib import cm
fig = plt.figure()
ax = fig.gca(projection='3d')
X, Y, Z = axes3d.get_test_data(0.05)
ax.plot_surface(X, Y, Z, rstride=8, cstride=8, alpha=0.3)
cset = ax.contour(X, Y, Z, zdir='z', offset=-100, cmap=cm.coolwarm)
cset = ax.contour(X, Y, Z, zdir='x', offset=-40, cmap=cm.coolwarm)
cset = ax.contour(X, Y, Z, zdir='y', offset=40, cmap=cm.coolwarm)
ax.set_xlabel('X')
ax.set_xlim(-40, 40)
ax.set_ylabel('Y')
ax.set_ylim(-40, 40)
ax.set_zlabel('Z')
ax.set_zlim(-100, 100)
plt.show()
This is in theory exactly what I need, in the way it takes sort of a planar view of the 3d situation. However I cannot implement a 2d line plot on a 3d axis nor can I use the offset command in a 2d plot (getting the error: TypeError: There is no line property "offset").
Is there a 2d equivalent to the 3d “offset” command and Is it possible to plot the 2d values on the planes of the 3d plot as I desire? Also is there a way to plot 2d lines having initialised a 3d projection? Can anyone offer any ideas/point me in any direction in general to help me achieve this?
My sincere thanks in advance and apologies if any part of this post is out of order, this is my first one!
Try this:
xmin = min(XdS)
ymax = max(YdS)
zmin = min(ZdS)
length_of_array = len(XdS)
xmin_array = [xmin] * length_of_array
ymax_array = [ymax] * length_of_array
zmin_array = [zmin] * length_of_array
fig=plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot(XdS,YdS,ZdS,zdir='z', c='r')
ax.plot(XdS,YdS,zmin_array, zdir='z', c='g')
ax.plot(xmin_array, YdS, ZdS, 'y')
ax.plot(XdS,ymax_array,ZdS,'b')
ax.set_xlabel('XKSM (Saturn Radii)')
ax.set_ylabel('YKSM (Saturn Radii)')
ax.set_zlabel('ZKSM (Saturn Radii)')
plt.show()

Categories