Turning 2D graphics into 3D in python - 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.

Related

Python matplotlib 3D bar plot with error bars

I am trying to get a 3D barplot with error bars.
I am open to use matplotlib, seaborn or any other python library or tool
Searching in SO I found 3D bar graphs can be done by drawing several 2D plots (here for example). This is my code:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
dades01 = [54,43,24,104,32,63,57,14,32,12]
dades02 = [35,23,14,54,24,33,43,55,23,11]
dades03 = [12,65,24,32,13,54,23,32,12,43]
df_3d = pd.DataFrame([dades01, dades02, dades03]).transpose()
colors = ['r','b','g','y','b','p']
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
z= list(df_3d)
for n, i in enumerate(df_3d):
print 'n',n
xs = np.arange(len(df_3d[i]))
ys = [i for i in df_3d[i]]
zs = z[n]
cs = colors[n]
print ' xs:', xs,'ys:', ys, 'zs',zs, ' cs: ',cs
ax.bar(xs, ys, zs, zdir='y', color=cs, alpha=0.8)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()
I get the 3D 'ish' plot.
My question is: How do I add error bars?
To make it easy, lets try to add the same error bars to all the plots:
yerr=[10,10,10,10,10,10,10,10,10,10]
If I add my error bars in each '2D' plot:
ax.bar(xs, ys, zs, zdir='y', color=cs,yerr=[10,10,10,10,10,10,10,10,10,10], alpha=0.8)
Doesn't work:
AttributeError: 'LineCollection' object has no attribute 'do_3d_projection'
I have also tried to add:
#ax.errorbar(xs, ys, zs, yerr=[10,10,10,10,10,10,10,10,10,10], ls = 'none')
But again an error:
TypeError: errorbar() got multiple values for keyword argument 'yerr'
Any idea how I could get 3D plot bars with error bars?
There is no direct way to the best of my knowledge to do it in 3d. However, you can create a workaround solution as shown below. The solution is inspired from here. The trick here is to pass two points lying vertically and then use _ as the marker to act as the error bar cap.
yerr=np.array([10,10,10,10,10,10,10,10,10,10])
for n, i in enumerate(df_3d):
xs = np.arange(len(df_3d[i]))
ys = [i for i in df_3d[i]]
zs = z[n]
cs = colors[n]
ax.bar(xs, ys, zs, zdir='y', color=cs, alpha=0.8)
for i, j in enumerate(ys):
ax.plot([xs[i], xs[i]], [zs, zs], [j+yerr[i], j-yerr[i]], marker="_", color=cs)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
First of all, don't use a 3D plot when a 2D plot would suffice, which in this case, it would. Using 3D plots for 2D data unnecessarily obfuscates things.
Second, you can use a combination of a MultiIndex pandas dataframe to get your desired result:
df = pd.DataFrame({
'a': list(range(5))*3,
'b': [1, 2, 3]*5,
'c': np.random.randint(low=0, high=10, size=15)
}).set_index(['a', 'b'])
fig, ax = plt.subplots(figsize=(10,6))
y_errs = np.random.random(size=(3, 5))
df.unstack().plot.bar(ax=ax, yerr=y_errs)
This produces a plot like the following:
I'm using the 'bmh' style here (i.e., I called plt.style.use('bmh') earlier in my notebook that I had opened), which is why it looks the way it does.

Matplotlib: categorical plot without strings and inversion of axes

Let's take this snippet of Python:
import matplotlib.pyplot as plt
x = [5,4,3,2,1,0]
x_strings = ['5','4','3','2','1','0']
y = [0,1,2,3,4,5]
plt.figure()
plt.subplot(311)
plt.plot(x, y, marker='o')
plt.subplot(312)
plt.plot(x_strings, y, marker='^', color='red')
plt.subplot(313)
plt.plot(x, y, marker='^', color='red')
plt.gca().invert_xaxis()
plt.show()
Which produces these three subplots:
In the top subplot the x values are automatically sorted increasingly despite their order in the given list. If I want to plot x vs. y exactly in the given order of x, then I have two possibilities:
1) Convert x values to strings and have a categorical plot -- that's the middle subplot.
2) Invert the x-axis -- that's the bottom subplot.
Question: is there any other way to do a sort of categorical plot, but without conversion of numbers into strings and without the inversion of the x-axis?
ADD-ON:
If I use set_xticklabels(list), then for some unclear reason the first element in the list is skipped (no matter if I refer to the x or to the x_strings list), and the resulting plot is also totally strange:
import matplotlib.pyplot as plt
x = [5,4,3,2,1,0]
x_strings = ['5','4','3','2','1','0']
y = [0,1,2,3,4,5]
fig, ax = plt.subplots()
ax.set_xticklabels(x)
ax.plot(x, y, marker='^', color='red')
plt.show()
Both attempted solutions seem possible. Alternatively, you can always mimic categorical plots by plotting integer numbers and setting the ticklabels to your liking.
import matplotlib.pyplot as plt
x = [5,4,3,2,1,0]
y = [0,1,2,3,4,5]
fig, ax = plt.subplots()
ax.plot(range(len(y)), y, marker='^', color='red')
ax.set_xticks(range(len(y)))
ax.set_xticklabels(x)
plt.show()
I have found another way to do it, without being anyhow categorical and without x-axis inversion!
ax = plt.subplot()
ax.set_xlim(x[0],x[-1], auto=True) # this line plays the trick
plt.plot(x, y, marker='^', color='red')

Align projected 2d plot on 3dscatter grid in matplotlib

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

Plotting streamlines in a Matplotlib 3Dplot?

I'm trying to plot streamlines on a plane in a 3D plot using Matplotlib. For the streamlines, I'd like to use the function streamplot(), because of its simplicity. Here's a MWE that I modified from the gallery (to attempt the streamplot() call):
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from matplotlib import cm
X, Y, Z = axes3d.get_test_data(0.05)
fig = plt.figure()
ax = fig.gca()
plt.streamplot(X, Y, X, Y)
plt.show()
fig = plt.figure()
ax = fig.gca(projection='3d')
cset = ax.contour(X, Y, Z, zdir='z', offset=-10, cmap=cm.coolwarm)
ax.set_zlim(-100, 100)
plt.show()
fig = plt.figure()
ax = fig.gca(projection='3d')
cset = ax.streamplot(X, Y, X, Y, zdir='z', offset=-10, cmap=cm.coolwarm)
ax.set_zlim(-100, 100)
plt.show()
The first and second examples work as intended, however, the third example gives me this error:
TypeError: streamplot() got an unexpected keyword argument 'zdir'
which hints to the possibility of this not being implemented. If I check Axes3D, the function streamplot() I get:
In [28]: Axes3D.streamplot
Out[28]: <function matplotlib.axes._axes.Axes.streamplot>
From which we get that streamplot() is the 2D version, and hence can't be directly used in 3D plots.
Is there a way to circumvent this and get a streamlines plane in a 3D plot?

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