Are there libraries or methods in python that are capable of creating plots that look like this? (preferably based around MatPlotLib for the sake of embedding the plots in HTML pages)
My goal is to create 3D renderings of data that is read from a Neo4J database and model them as the cylinders above.
The code below attempts to create a similar 3D plot (not cylindrical but rectangular) with legends from a dataframe. The plot is interactive. Resources: 1, 2, 3, 4 (Jupyter Notebook 5.0.0, Python 3.6.6)
Import libraries
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from mpl_toolkits.mplot3d import axes3d
import matplotlib.patches as mpatches # for legends
%matplotlib notebook
Create a sample dataframe
# Create two sets of identical xpos and ypos
# So taht the z-values are plotted at same location for stacking
xtemp = np.random.randint(1, 10, size=5)
ytemp = np.random.randint(1, 10, size=5)
df = pd.DataFrame({
# category
'season': ['S1']*5 + ['S2']*5 + ['S3']*5,
#'wins': np.random.randint(1, 10, size=15),
# define pos
'xpos' : list(xtemp)+list(xtemp)+list(xtemp),
'ypos' : list(ytemp)+list(ytemp)+list(ytemp),
'zpos' : np.zeros(15),
# define delta
'dx': 0.8*np.ones(15),
'dy': 0.8*np.ones(15),
'dz': np.random.randint(1, 5, size=15), #np.ones(15)
})
df.head(5)
Plot the figure
Note: Figure are in two parts: (1) 2D plot for the N-S, E-W lines and (2) 3D bar plot
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# ..................
# Line-1 on x-y plane
x = [4, 4]
y = [-3, 12]
ax.plot(x, y, zs=0, zdir='z', color='orange', alpha=0.8)
# Line-2 on x-y plane
y = [4, 4]
x = [-3, 12]
ax.plot(x, y, zs=0, zdir='z', color='blue', alpha=0.5)
# Creat multiple overlap plots within a loop
color = ['#6495ED', '#6E8B3D', '#FFB90F']
slist = ['S1', 'S2', 'S3']
stack_zpos = pd.Series(np.zeros(5))
for i in range(0,3):
q = df[df['season']==slist[i]].reset_index(inplace=False)
ax.bar3d(q.xpos, q.ypos, stack_zpos, q.dx, q.dy, q.dz, color=color[i], alpha=1)
stack_zpos += q.dz # values added here for stacking
Annotate lines and remove z-axis panes and grid lines
# Remove the z-axis panes, grids and lines
alpha = 0
ax.w_xaxis.set_pane_color((1.0, 1.0, 1.0, alpha))
ax.w_yaxis.set_pane_color((1.0, 1.0, 1.0, alpha))
#
ax.zaxis._axinfo["grid"]['color'] = (1.0, 1.0, 1.0, alpha)
ax.w_yaxis._axinfo["grid"]['linewidth'] = 0
ax.w_xaxis._axinfo["grid"]['linewidth'] = 0
#
ax.w_zaxis.line.set_lw(0.)
ax.set_zticks([])
#
ax.set_zlabel("") # remove z-axis label 'z'
# ..........
# Annotate the N, S, E, W lines on the x-y plane
zdirs = (None, 'x', 'y', 'z', (1, 1, 0), (1, 1, 1))
xs = (4, 4, -3, 12)
ys = (-3,12, 4, 4)
zs = (0, 0, 0, 0)
i=0 # Counter
nsew = ['N', 'S', 'E', 'W'] # list of labels
for zdir, x, y, z in zip(zdirs, xs, ys, zs):
label = '{0}'.format(nsew[i])
#label = 'N, S, E, W' #% (x, y, z, zdir)
ax.text(x, y, z, label, zdir)
i +=1
Create and add legends to the plot
# Add legend
patch1 = mpatches.Patch(color=color[0], label=slist[0])
patch2 = mpatches.Patch(color=color[1], label=slist[1])
patch3 = mpatches.Patch(color=color[2], label=slist[2])
plt.legend(handles=[patch1, patch2,patch3])
Visualize plot
plt.show()
Related
I am trying to create a 3D barplot using matplotlib in python, and apply a colormap which is tied some data (4th dimension) which is not explicitly plotted. I think what makes this even more complicated is that I want this 4th dimension to be a range of values as opposed to a single value.
So far I have managed to create the 3D bar plot with a colormap tied to the z-dimension thanks primarily to this post how to plot gradient fill on the 3d bars in matplotlib. The code can be found below.
import numpy as np
import glob,os
from matplotlib import pyplot as plt
import matplotlib.colors as cl
import matplotlib.cm as cm
from mpl_toolkits.mplot3d import Axes3D
os.chdir('./')
# axis details for the bar plot
x = ['1', '2', '3', '4', '5'] # labels
x_tick_locks = np.arange(0.1, len(x) + 0.1, 1)
x_axis = np.arange(len(x))
y = ['A', 'B']
y_tick_locks = np.arange(-0.1, len(y) - 0.1, 1)
y_axis = np.arange(len(y))
x_axis, y_axis = np.meshgrid(x_axis, y_axis)
x_axis = x_axis.flatten()
y_axis = y_axis.flatten()
x_data_final = np.ones(len(x) * len(y)) * 0.5
y_data_final = np.ones(len(x) * len(y)) * 0.5
z_axis = np.zeros(len(x)*len(y))
z_data_final = [[30, 10, 15, 20, 25], [10, 15, 15, 28, 40]]
values_min = [[5, 1, 6, 8, 3], [2, 1, 3, 9, 4]]
values_max = [[20, 45, 11, 60, 30], [11, 28, 6, 30, 40]]
cmap_max = max(values_max)
cmap_min = min(values_min)
############################### FOR 3D SCALED GRADIENT BARS ###############################
def make_bar(ax, x0=0, y0=0, width = 0.5, height=1 , cmap="plasma",
norm=cl.Normalize(vmin=0, vmax=1), **kwargs ):
# Make data
u = np.linspace(0, 2*np.pi, 4+1)+np.pi/4.
v_ = np.linspace(np.pi/4., 3./4*np.pi, 100)
v = np.linspace(0, np.pi, len(v_)+2 )
v[0] = 0 ; v[-1] = np.pi; v[1:-1] = v_
#print(u)
x = np.outer(np.cos(u), np.sin(v))
y = np.outer(np.sin(u), np.sin(v))
z = np.outer(np.ones(np.size(u)), np.cos(v))
xthr = np.sin(np.pi/4.)**2 ; zthr = np.sin(np.pi/4.)
x[x > xthr] = xthr; x[x < -xthr] = -xthr
y[y > xthr] = xthr; y[y < -xthr] = -xthr
z[z > zthr] = zthr ; z[z < -zthr] = -zthr
x *= 1./xthr*width; y *= 1./xthr*width
z += zthr
z *= height/(2.*zthr)
#translate
x += x0; y += y0
#plot
ax.plot_surface(x, y, z, cmap=cmap, norm=norm, **kwargs)
def make_bars(ax, x, y, height, width=1):
widths = np.array(width)*np.ones_like(x)
x = np.array(x).flatten()
y = np.array(y).flatten()
h = np.array(height).flatten()
w = np.array(widths).flatten()
norm = cl.Normalize(vmin=0, vmax=h.max())
for i in range(len(x.flatten())):
make_bar(ax, x0=x[i], y0=y[i], width = w[i] , height=h[i], norm=norm)
############################### FOR 3D SCALED GRADIENT BARS ###############################
# Creating graph surface
fig = plt.figure(figsize=(9,6))
ax = fig.add_subplot(111, projection= Axes3D.name)
ax.azim = 50
ax.dist = 10
ax.elev = 30
ax.invert_xaxis()
ax.set_box_aspect((1, 0.5, 1))
ax.zaxis.labelpad=7
ax.text(0.9, 2.2, 0, 'Group', 'x')
ax.text(-2, 0.7, 0, 'Class', 'y')
ax.set_xticks(x_tick_locks)
ax.set_xticklabels(x, ha='left')
ax.tick_params(axis='x', which='major', pad=-2)
ax.set_yticks(y_tick_locks)
ax.set_yticklabels(y, ha='right', rotation=30)
ax.tick_params(axis='y', which='major', pad=-5)
ax.set_zlabel('Number')
make_bars(ax, x_axis, y_axis, z_data_final, width=0.2, )
fig.colorbar(plt.cm.ScalarMappable(cmap = 'plasma'), ax = ax, shrink=0.8)
#plt.tight_layout() # doesn't seem to work properly for 3d plots?
plt.show()
As I mentioned, I don't want the colormap to be tied to the z-axis but rather a 4th dimension, which is a range. In other words, I want the colours of the colormap to range from cmap_min to cmap_max (so min is 1 and max is 60), then for the bar plot with a z_data_final entry of 30 for example, its colours should correspond with the range of 5 to 20.
Some other posts seem to provide a solution for a single 4th dimensional value, i.e. (python) plot 3d surface with colormap as 4th dimension, function of x,y,z or How to make a 4d plot using Python with matplotlib however I wasn't able to find anything specific to bar plots with a range of values as your 4th dimensional data.
I would appreciate any guidance in this matter, thanks in advance.
This is the 3D bar plot with colormap tied to the z-dimension
I have 9 temperature points. 1 in the center and 8 on the circle. I need to create a heatmap in a circle. I set the points at which to perform calculations, and use the scipy.interpolate.griddata, but the full circle is not drawn, program draws an octagon. How can i fill in the missing parts?
import scipy.interpolate
import numpy
import matplotlib
import matplotlib.pyplot as plt
import math
# close old plots
plt.close("all")
# some parameters
xy_center = [2,2] # center of the plot
radius = 2 # radius
# mostly original code
meanR = [33.9, 34.2, 33.1, 33.5, 33., 32.7, 32.3, 31.8, 35.]
x = numpy.array([2, 2, 2+math.sqrt(2), 4, 2+math.sqrt(2), 2, 2+(-math.sqrt(2)), 0, 2+(-math.sqrt(2))])
y = numpy.array([2, 4, 2+math.sqrt(2), 2, 2+(-math.sqrt(2)), 0, 2+(-math.sqrt(2)), 2, 2+math.sqrt(2)])
z = meanR
xi, yi = numpy.mgrid[x.min():x.max():500j, y.min():y.max():500j]
zi = scipy.interpolate.griddata((x, y), z, (xi, yi), method='cubic')
# make figure
fig = plt.figure(figsize=(10, 10))
# set aspect = 1 to make it a circle
ax = fig.add_subplot(111, aspect = 1)
# use different number of levels for the fill and the lines
CS = ax.contourf(xi, yi, zi, 300, cmap=plt.cm.viridis, zorder=1)
# make a color bar
cbar = fig.colorbar(CS, ax=ax)
# add the data points
ax.scatter(x, y, marker = 'o', c = 'b', s = 15, zorder = 3)
for i in range(9):
ax.annotate(str(z[i]), (x[i],y[i]))
# draw a circle
circle = matplotlib.patches.Circle(xy = xy_center, radius = radius, edgecolor = "k", facecolor = "none")
ax.add_patch(circle)
# remove the ticks
ax.set_xticks([])
ax.set_yticks([])
# set axes limits
ax.set_xlim(-0.5, 4.5)
ax.set_ylim(-0.5, 4.5)
plt.show()
Radial basis functions (Rbf) can be used to interpolate/extrapolate your data.
scipy.interpolation Here is a modified code that produces the plot you need.
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import math
from scipy.interpolate import Rbf
# some parameters
xy_center = [2,2] # center of the plot
radius = 2 # radius
# Data part
# ---------
# mostly original code
meanR = [33.9, 34.2, 33.1, 33.5, 33., 32.7, 32.3, 31.8, 35.] #9 points data
x = np.array([2, 2, 2+math.sqrt(2), 4, 2+math.sqrt(2), 2, 2+(-math.sqrt(2)), 0, 2+(-math.sqrt(2))])
y = np.array([2, 4, 2+math.sqrt(2), 2, 2+(-math.sqrt(2)), 0, 2+(-math.sqrt(2)), 2, 2+math.sqrt(2)])
z = meanR
# use RBF (Radial basis functions) that allows extrapolation
rbf = Rbf(x, y, z, epsilon=radius+1) #epsilon is based on some parameters of the data
# Interpolation/extrapolation
# ---------------------------
xi, yi = np.mgrid[x.min():x.max():500j, y.min():y.max():500j]
# applies and get inter/extra-polated values
zi = rbf(xi, yi)
# make zi outside circle --> np.none
midr,midc = zi.shape[0]/2, zi.shape[1]/2
for er in range(zi.shape[0]):
for ec in range(zi.shape[1]):
if np.abs(math.sqrt((er-midr)**2 + (ec-midc)**2))>zi.shape[0]/2:
# outside the circle, dont plot this pixel
zi[er][ec] = np.nan
pass
pass
# make figure
fig = plt.figure(figsize=(8, 8))
# set aspect = 1 to make it a circle
ax = fig.add_subplot(111, aspect = 1)
# add the data points
ax.scatter(x, y, marker = 'o', c = 'b', s = 15, zorder = 3)
for i in range(9):
ax.annotate(str(z[i]), (x[i],y[i]))
# draw a circle
circle = matplotlib.patches.Circle(xy = xy_center, radius = radius, edgecolor = "k", facecolor = "none")
ax.add_patch(circle)
CS = ax.contourf(xi, yi, zi, 300, cmap=plt.cm.viridis, zorder=1)
cbar = fig.colorbar(CS, ax=ax, shrink=0.7) # make a color bar
# remove the ticks
ax.set_xticks([])
ax.set_yticks([])
# set axes limits
ax.set_xlim(-0.5, 4.5)
ax.set_ylim(-0.5, 4.5)
plt.show()
The result:
I'm trying to plot some trendlines for some data that I have. Problem is that it looks fuzzy and it seems to overlap on itself if I use dotted or dashed styles.
Not sure why, but the lines also look like there's no anti-aliasing done -- they look jagged. Trying to produce the exact same graph in Excel gives clean lines.
Using other line styles doesn't help; nor does increasing the spacing by adding dashes=(1,5) or similar. Even if you increase plot size or change linewidth - it still overlaps.
Here's the code:
from matplotlib import pyplot as plt
import matplotlib.ticker as mtick
import numpy
from scipy import stats
radon = [49.6,61.7,58.7,64.1,59.4,64.6,65.4,65.3,65.5,66.0,50.5,64.8,71.9,
71.9,60.4,54.4,50.9,58.1,52.6,55.6,56.6,41.6,43.0,33.0,41.5,53.5,52.2,45.1,
46.8,63.0,73.8,61.3,44.4,39.3,38.2,45.4,39.9,36.3,41.0,38.5,35.4,40.2,11.3,
34.7,24.2,24.5,32.1,26.3,23.6,32.1,27.5,39.0,24.9,22.0,18.2,23.0,21.1,15.4,
13.9,10.2,26.4,18.2,16.6]
tout = [-2.57,-3.31,-0.63,-0.60,0.39,-1.64,-7.62,-1.90,-0.35,-4.88,-1.27,
-0.23,-6.99,-2.87,-12.27,-11.90,-9.42,-4.10,-3.15,0.81,3.87,-11.41,-9.47,
0.25,-6.81,-13.70,-16.41,-14.14,-9.70,-10.32,-21.83,-26.55,-16.88,-6.85,
4.03,-7.89,-6.53,-3.96,-6.09,-3.15,-0.51,-2.62,8.13,2.08,0.58,1.99,-6.64,
-12.13,-4.95,-2.99,-4.81,-0.88,0.28,-1.44,1.92,3.73,0.21,1.11,6.83,13.31,
7.04,1.46,1.78]
# start and end index for data
a = [0,21,42]
b = [20,41,62]
n = 0 # just a counter
# set font family
hfont = {'family':'Arial'}
plt.rcParams.update({'font.family': 'Arial', 'font.size':12})
# set axis minor tick marks
plt.axes().yaxis.set_minor_locator(mtick.MultipleLocator(4))
plt.axes().xaxis.set_minor_locator(mtick.MultipleLocator(2))
plt.axes().yaxis.set_major_formatter(mtick.FormatStrFormatter('%.0f%%'))
# config axis labels
plt.xlabel("Outdoor Temperature", **hfont)
plt.ylabel("Radiator on %", **hfont)
# set line and marker types and colors
marker = ['o', 's', 'x']
marker_facecolor = ['None', 'k', 'None']
names = ['Manual Control', 'Enforced Schedule', 'Occupancy-based']
lines = [':','--','-']
line_spacing = [[1, 5], [5, 5], [0, 0]]
transparency = [1, 0.75, 1]
for i,j in zip(a,b):
# get x and y
x = numpy.array(tout[i:j])
y = numpy.array(radon[i:j])
# set axis ranges
plt.ylim(0, 100)
plt.xlim(-30, 20)
# plot data
plt.plot(x, y, marker[n],markeredgewidth=0.75
,markeredgecolor='k',markerfacecolor=marker_facecolor[n],
alpha=transparency[n],label=names[n])
# perform regressions
z = numpy.polyfit(x, y, 1)
p = numpy.poly1d(z)
# plot trendline
plt.plot(x,p(x),'k%s' % lines[n], linewidth=0.85)
# increment counter
n+=1
plt.legend(loc='upper right')
leg = plt.legend()
leg.get_frame().set_edgecolor('k')
plt.savefig('tout_vs_radon.png', dpi=300)
plt.show()
Here's the result of the above code:
https://i.imgur.com/K6jIHBM.png
(don't have enough reputation to post image, sorry)
You can see the trendline overlapping itself in the center for dotted or dashed styles. I'm using matplotlib v3.0.3 and Python v3.6.5 on Windows 10.
The key would be to plot the array in a sorted fashion. Else, the line will jump back and forth and overlap itself.
order = np.argsort(x)
plt.plot(x[order],p(x[order]), ..)
Complete code:
from matplotlib import pyplot as plt
import matplotlib.ticker as mtick
import numpy as np
radon = [49.6,61.7,58.7,64.1,59.4,64.6,65.4,65.3,65.5,66.0,50.5,64.8,71.9,
71.9,60.4,54.4,50.9,58.1,52.6,55.6,56.6,41.6,43.0,33.0,41.5,53.5,52.2,45.1,
46.8,63.0,73.8,61.3,44.4,39.3,38.2,45.4,39.9,36.3,41.0,38.5,35.4,40.2,11.3,
34.7,24.2,24.5,32.1,26.3,23.6,32.1,27.5,39.0,24.9,22.0,18.2,23.0,21.1,15.4,
13.9,10.2,26.4,18.2,16.6]
tout = [-2.57,-3.31,-0.63,-0.60,0.39,-1.64,-7.62,-1.90,-0.35,-4.88,-1.27,
-0.23,-6.99,-2.87,-12.27,-11.90,-9.42,-4.10,-3.15,0.81,3.87,-11.41,-9.47,
0.25,-6.81,-13.70,-16.41,-14.14,-9.70,-10.32,-21.83,-26.55,-16.88,-6.85,
4.03,-7.89,-6.53,-3.96,-6.09,-3.15,-0.51,-2.62,8.13,2.08,0.58,1.99,-6.64,
-12.13,-4.95,-2.99,-4.81,-0.88,0.28,-1.44,1.92,3.73,0.21,1.11,6.83,13.31,
7.04,1.46,1.78]
# start and end index for data
a = [0,21,42]
b = [20,41,62]
n = 0 # just a counter
# set font family
hfont = {'family':'Arial'}
plt.rcParams.update({'font.family': 'Arial', 'font.size':12})
# set axis minor tick marks
plt.gca().yaxis.set_minor_locator(mtick.MultipleLocator(4))
plt.gca().xaxis.set_minor_locator(mtick.MultipleLocator(2))
plt.gca().yaxis.set_major_formatter(mtick.FormatStrFormatter('%.0f%%'))
# config axis labels
plt.xlabel("Outdoor Temperature", **hfont)
plt.ylabel("Radiator on %", **hfont)
# set line and marker types and colors
marker = ['o', 's', 'x']
marker_facecolor = ['None', 'k', 'None']
names = ['Manual Control', 'Enforced Schedule', 'Occupancy-based']
lines = [':','--','-']
line_spacing = [[1, 5], [5, 5], [0, 0]]
transparency = [1, 0.75, 1]
for i,j in zip(a,b):
# get x and y
x = np.array(tout[i:j])
y = np.array(radon[i:j])
# set axis ranges
plt.ylim(0, 100)
plt.xlim(-30, 20)
# plot data
plt.plot(x, y, marker[n],markeredgewidth=0.75,
markeredgecolor='k',markerfacecolor=marker_facecolor[n],
alpha=transparency[n],label=names[n])
# perform regressions
z = np.polyfit(x, y, 1)
p = np.poly1d(z)
# plot trendline
order = np.argsort(x)
plt.plot(x[order],p(x[order]),'k%s' % lines[n], linewidth=0.85)
# increment counter
n+=1
plt.show()
I am scatter ploting data points with a very small marker (see screengrab below). When I use the very small marker ',' the legend is very hard to read (example code taken from here).
(Python 3, Jupyter lab)
How can I increase the size of the marker in the legend. The two versions shown on the above mentioned site do not work:
legend = ax.legend(frameon=True)
for legend_handle in legend.legendHandles:
legend_handle._legmarker.set_markersize(9)
and
ax.legend(markerscale=6)
The two solutions do however work when the marker is set to '.'.
How can I show bigger makers in the legend?
Sample Code from intoli.com:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(12)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
for i in range(5):
mean = [np.random.random()*10, np.random.random()*10]
covariance = [ [1 + np.random.random(), np.random.random() - 1], [0, 1 + np.random.random()], ]
covariance[1][0] = covariance[0][1] # must be symmetric
x, y = np.random.multivariate_normal(mean, covariance, 3000).T
plt.plot(x, y, ',', label=f'Cluster {i + 1}')
ax.legend(markerscale=12)
fig.tight_layout()
plt.show()
You can get 1 pixel sized markers for a plot by setting the markersize to 1 pixel. This would look like
plt.plot(x, y, marker='s', markersize=72./fig.dpi, mec="None", ls="None")
What the above does is set the marker to a square, set the markersize to the ppi (points per inch) divided by dpi (dots per inch) == dots == pixels, and removes lines and edges.
Then the solution you tried using markerscale in the legend works nicely.
Complete example:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(12)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
for i in range(5):
mean = [np.random.random()*10, np.random.random()*10]
covariance = [ [1 + np.random.random(), np.random.random() - 1], [0, 1 + np.random.random()], ]
covariance[1][0] = covariance[0][1] # must be symmetric
x, y = np.random.multivariate_normal(mean, covariance, 3000).T
plt.plot(x, y, marker='s', markersize=72./fig.dpi, mec="None", ls="None",
label=f'Cluster {i + 1}')
ax.legend(markerscale=12)
fig.tight_layout()
plt.show()
According to this discussion, the markersize has no effect when using pixels (,) as marker. How about generating a custom legend instead? For example, by adapting the first example in this tutorial, one can get a pretty decent legend:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
np.random.seed(12)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
for i in range(5):
mean = [np.random.random()*10, np.random.random()*10]
covariance = [ [1 + np.random.random(), np.random.random() - 1], [0, 1 + np.random.random()], ]
covariance[1][0] = covariance[0][1] # must be symmetric
x, y = np.random.multivariate_normal(mean, covariance, 3000).T
plt.plot(x, y, ',', label=f'Cluster {i + 1}')
##generating custom legend
handles, labels = ax.get_legend_handles_labels()
patches = []
for handle, label in zip(handles, labels):
patches.append(mpatches.Patch(color=handle.get_color(), label=label))
legend = ax.legend(handles=patches)
fig.tight_layout()
plt.show()
The output would look like this:
I have a data set which looks like this:
Intensity = ( [1, 2, 3, 4], [6, 7, 9, 10] )
Xposition = (0.1, 0.2, 0.3, 0.4)
Yposition = (1E^-9, 1.2E^-9)
So, for each Yposition, we have an 1D array stored in Intensity, corresponding to each Xposition.
Now I want to plot Xposition (X-axis), Yposition (Y-axis) and Intensity along Z to generate a 3D plot. How can I do this using matplotlib?
There are nice tutorials on matplotlib page. Looking at two examples and slightly tweaking the code:
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = (0.1,0.2,0.3,0.4)
y = (10**-9, 1.2*10**-9)
x,y = np.meshgrid(x,y)
z = ( [1,2,3,4], [6,7,9,10] )
ax.scatter(x, y, z)
plt.show()