How to plot the following figure? - python

dears. I am a totally beginner to the python community and I would like to draw the below picture in python as a 3D.
I have made a try, but I could not reach any successful results.
This is my data
This is below my try:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
fig = plt.figure()
ax1 = fig.add_subplot(111, projection='3d')
# data
x = [86, 91, 97]
y = [82, 88, 94]
z = [80, 85, 89]
ax1.plot(x,y,z)
plt.show()

Shape of your data is not correct. You provide 3 flat lists, what you should provide are x, y, z coordinates for each bar.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import matplotlib.cm as cm
import numpy as np
# create data
data_2d = [[729, 575, 528],
[805, 768, 667],
[841, 773, 724],
[899, 857, 787]]
# Convert it into an numpy array.
data_array = np.array(data_2d)
# Create a figure for plotting the data as a 3D histogram.
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# Create an X-Y mesh of the same dimension as the 2D data. The floor of the plot.
x_data, y_data = np.meshgrid( np.arange(data_array.shape[1]),
np.arange(data_array.shape[0]) )
# Flatten out the arrays so that they may be passed to "ax.bar3d".
# ax.bar3d expects three one-dimensional arrays:
# x_data, y_data, z_data. The following call boils down to picking
# one entry from each array and plotting a bar to from
# (x_data[i], y_data[i], 0) to (x_data[i], y_data[i], z_data[i]).
x_data = x_data.flatten()
y_data = y_data.flatten()
z_data = data_array.flatten()
dz = z_data
offset = dz + np.abs(dz.min())
fracs = offset.astype(float)/offset.max()
norm = colors.Normalize(fracs.min(), fracs.max())
color_values = cm.jet(norm(fracs.tolist()))
ax.bar3d( x_data, y_data, np.zeros(len(z_data)), 0.6, 0.6, z_data, color=color_values)
# Labels
ax.set_xlabel("Grid Size")
ax.set_ylabel("Bézier")
ax.set_zlabel("Success Rate")
# Ticks
ax.set_zticks(range(0,1200,200))
# Shape of the 3D cube
ax.set_box_aspect(aspect=(8,8,10))
output:

Related

plt.imshow is not working as it should be

so i have this code in python
import numpy as np
import matplotlib.pyplot as plt
M = np.zeros((490,1900))
fig, ax = plt.subplots()
plt.imshow(M, aspect='auto')
ax.set_ylabel('f2')
ax.set_xlabel('f1')
plt.xlim((0, 490))
plt.ylim((0, 1900))
x_range = np.arange(0, 490, step=50)
x_strings = [str(x+190) for x in x_range]
plt.xticks(x_range, x_strings)
y_range = np.arange(0, 1900, step=200)
y_strings = [str(y+1710) for y in y_range]
plt.yticks(y_range, y_strings)
plt.colorbar()
plt.ioff()
plt.show()
when i'm compiling it supposed to cover the whole area with the grid of zeros (M) that I've created, but instead it covers part of it as seen in this
What i'm doing wrong here?
I think you're simply confusing yourself when creating the matrix.
You're creating a matrix with 490 rows and 1900 columns when you're probably aiming to create a matrix with 1900 rows and 490 columns.
Your problem can be solved simply by changing the order with which you define your matrix M:
import numpy as np
import matplotlib.pyplot as plt
M = np.zeros((1900, 490)) # change is here
fig, ax = plt.subplots()
plt.imshow(M, aspect='auto')
ax.set_ylabel('f2')
ax.set_xlabel('f1')
plt.xlim((0, 490))
plt.ylim((0, 1900))
x_range = np.arange(0, 490, step=50)
x_strings = [str(x+190) for x in x_range]
plt.xticks(x_range, x_strings)
y_range = np.arange(0, 1900, step=200)
y_strings = [str(y+1710) for y in y_range]
plt.yticks(y_range, y_strings)
plt.colorbar()
plt.ioff()
plt.show()
Which yields:
You could of course also be changing the plot limits by inverting xlim and ylim.

How could I plot an array with conditions over a contour map?

I have plotted a global map of GPP using the code below:
( 'lon' and 'lat' are both netCDF4 attributes and have a shape of (144, ) and (90, ) respectively, whilst 'gpp_avg' is a numpy array with a shape of (90, 144) )
import numpy as np
import netCDF4 as n4
import matplotlib.pyplot as plt
import cartopy as cart
import cartopy.crs as ccrs
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
from mpl_toolkits.basemap import Basemap
>> gpp_avg = n4.Dataset('decadal_gpp.nc', 'r')
>> lon = gpp_avg.variables['lon'] # 144 grid cells every 2.5 degrees (east-west)
>> lat = gpp_avg.variables['lat'] # 90 grid cells every 2 degrees (north-south)
>> # Plotting data on a map with Cartopy
>> plt.figure()
>> ax = plt.axes(projection=ccrs.PlateCarree())
>> ax.coastlines() # Adding coastlines
>> ax.add_feature(cart.feature.OCEAN, zorder=100, edgecolor='k')
>> cs = ax.contourf(lon[:], lat[:], gpp_avg[:], cmap = 'Spectral')
>> cbar = plt.colorbar(cs, ax=ax) # Additional necessary information
>> cbar.set_label('g[C]/m^2/day')
>> gridl = ax.gridlines(color="black", linestyle="dotted",
draw_labels=True) # Adding axis labels - latitude & longitude
>> gridl.xformatter=LONGITUDE_FORMATTER
>> gridl.yformatter=LATITUDE_FORMATTER
>> gridl.xlabels_top = False
>> gridl.ylabels_right = False
>> plt.show()
I have a numpy array 'ci_95_gpp' which has the shape (90, 144) which contains the p-values for each grid cell of the global map. I want to plot points on top of the global contour map where the p-values are greater than 2.
How would I go about doing this? Many thanks.
I generate a set of data for contour plot on a Cartopy map. The data points for contouring are separated into 2 groups, with negative and positive z-values. Numpy maskedarray is used in that operation. I hope that this is useful for the general readers, including the OP.
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.ticker as mticker
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
import pandas as pd
from numpy.random import uniform, seed
from matplotlib.mlab import griddata
# TODO, use newer scipy.interpolate() instead of `griddata`
import numpy.ma as ma
# make up some data around long,lat: (90, 18)
seed(0)
npts = 200
x0, y0 = 90, 18 # center of map in (long, lat), degrees
x = x0+uniform(-2, 2, npts)
y = y0+uniform(-2, 2, npts)
#z = x*np.exp(-x**2 - y**2)
z = (x-x0)*np.exp(-(x-x0)**2 - (y-y0)**2) # elevation in meters
# define grid, for points interpolation from the made-up data above
gridx, gridy = 50,50
xi = x0+np.linspace(-2.1, 2.1, gridx)
yi = y0+np.linspace(-2.1, 2.1, gridy)
# interpolate for gridded data of (gridx, gridy)
zi = griddata(x, y, z, xi, yi, interp='linear')
# xi.shape, yi.shape, zi.shape => ((50,), (50,), (50, 50))
xig,yig = np.meshgrid(xi, yi)
# projection
useproj = ccrs.PlateCarree()
fig = plt.figure(figsize = (9, 7))
rect = [0.05, 0.05, 0.95, 0.95] # for map extent
ax = fig.add_axes( rect, projection=useproj )
# contour the gridded data, plotting dots at the nonuniform data points.
CS = ax.contour(xig, yig, zi, 15, linewidths=0.5, colors='k')
CS = ax.contourf(xig, yig, zi, 15,
vmax=abs(zi).max(), vmin=-abs(zi).max())
plt.colorbar(CS) # draw colorbar
# prep points for scatterplot of the gridded points
# make 2 masked-arrays, based on `zi`
mag = ma.masked_greater(zi, 0) # mask points with +ve zi values
mal = ma.masked_less(zi, 0) # mask points with -ve zi values
# apply masking to xig,yig; borrowing mask from mag
xig_greater_masked = ma.MaskedArray(xig, mask=mag.mask) # must have compatible values
yig_greater_masked = ma.MaskedArray(yig, mask=mag.mask)
# apply masking to xig,yig; borrowing mask from mal
xig_less_masked = ma.MaskedArray(xig, mask=mal.mask)
yig_less_masked = ma.MaskedArray(yig, mask=mal.mask)
# for points with -ve z values (result of .masked_greater)
plt.scatter(xig_greater_masked, yig_greater_masked, s=3, color="w", \
alpha=1, zorder=15, label="masked_greater z")
# for points with +ve z values (result of .masked_less)
ax.scatter(xig_less_masked, yig_less_masked, s=3, color="r", alpha=1, \
zorder=15, label="masked_less z")
leg = ax.legend(title='Masked z', framealpha=1.0, facecolor="lightgray")
leg.set_zorder(20)
gl = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=True,
linewidth=2, color='gray', alpha=0.5, linestyle='--')
gl.xlabels_top = False
gl.ylabels_right = False
gl.xformatter = LONGITUDE_FORMATTER
gl.yformatter = LATITUDE_FORMATTER
gl.xlabel_style = {'size': 15, 'color': 'gray'}
#gl.xlabel_style = {'color': 'gray', 'weight': 'bold'}
plt.title('Masked data plot on contour')
plt.show()
The resulting plot:

ValueError: Invalid RGBA argument: What is causing this error?

I am trying to create a 3D colored bar chart using ideas from: this stackoverflow post.
First I create a 3D bar chart with the following code:
import numpy as np
import matplotlib.colors as colors
import matplotlib.cm as cm
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
samples = np.random.randint(91,size=(5000,2))
F = np.zeros([91,91])
for s in samples:
F[s[0],s[1]] += 1
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x_data, y_data = np.meshgrid( np.arange(F.shape[1]),
np.arange(F.shape[0]) )
x_data = x_data.flatten()
y_data = y_data.flatten()
z_data = F.flatten()
ax.bar3d(x_data,y_data,np.zeros(len(z_data)),1,1,z_data )
plt.show()
The following is the output:
Now I try to color the bars using code verbatim from: this stackoverflow post. Here is the code:
import numpy as np
import matplotlib.colors as colors
import matplotlib.cm as cm
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
samples = np.random.randint(91,size=(5000,2))
F = np.zeros([91,91])
for s in samples:
F[s[0],s[1]] += 1
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x_data, y_data = np.meshgrid( np.arange(F.shape[1]),
np.arange(F.shape[0]) )
x_data = x_data.flatten()
y_data = y_data.flatten()
z_data = F.flatten()
dz = F
offset = dz + np.abs(dz.min())
fracs = offset.astype(float)/offset.max()
norm = colors.Normalize(fracs.min(), fracs.max())
colors = cm.jet(norm(fracs))
# colors = np.random.rand(91,91,4)
ax.bar3d(x_data,y_data,np.zeros(len(z_data)),1,1,z_data,color=colors )
plt.show()
However I get: ValueError: Invalid RGBA argument:
Now I am unable to debug the Invalid RGBA argument because I don't understand what is causing the error. I even tried to use random colors instead with colors = np.random.rand(91,91,4) and still the error persists.
I have checked stackoverflow posts regarding Invalid RGBA argument (for example this,this,this and this) and none of that seems to answer my problem.
I want to know what could be causing this error. I am using the standard Anaconda distribution for python on Ubuntu Mate 16.
Could it be that due to recent updates in python, the solution as in the original stackoverflow post becomes obsolete?
The error message is misleading. You're getting a ValueError because the shape of colors is wrong, not because an RGBA value is invalid.
When coloring each bar a single color, color should be an array of length N, where N is the number of bars. Since there are 8281 bars,
In [121]: x_data.shape
Out[121]: (8281,)
colors should have shape (8281, 4). But instead, the posted code generates an array of shape (91, 91, 4):
In [123]: colors.shape
Out[123]: (91, 91, 4)
So to fix the problem, use color=colors.reshape(-1,4).
import numpy as np
import matplotlib.colors as colors
import matplotlib.cm as cm
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
samples = np.random.randint(91,size=(5000,2))
F = np.zeros([91,91])
for s in samples:
F[s[0],s[1]] += 1
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x_data, y_data = np.meshgrid( np.arange(F.shape[1]),
np.arange(F.shape[0]) )
x_data = x_data.flatten()
y_data = y_data.flatten()
z_data = F.flatten()
dz = F
offset = dz + np.abs(dz.min())
fracs = offset.astype(float)/offset.max()
norm = colors.Normalize(fracs.min(), fracs.max())
colors = cm.jet(norm(fracs))
ax.bar3d(x_data,y_data,np.zeros(len(z_data)),1,1,z_data,color=colors.reshape(-1,4) )
plt.show()
The color argument expects a 1D array, similar to all other arguments of bar3d.
Hence, you need to replace the line offset = dz + np.abs(dz.min())
by
offset = z_data + np.abs(z_data.min())
for your case. dz is not useful here (maybe it was in the linked example).
Note that color=np.random.rand(len(z_data),4) would equally work.
Then the result will be

Python How to set axes for a matplotlib plot

Hi for the matplotlib plot below I want to set the axes titles such that they show that the x-axis values run from
2**-5, 2**-4, 2**-3,..., 2**14, 2**15
and the y-axis values run from
2**-15, 2**-14,...., 2**4, 2**5
The graph I want to display them on is:
The code for the graph is below:
from matplotlib import pyplot
import matplotlib as mpl
import numpy as np
zvals = 100*np.random.randn(21, 21)
fig = pyplot.figure(2)
cmap2 = mpl.colors.LinearSegmentedColormap.from_list('my_colormap',
['blue','green','brown'],
256)
img2 = pyplot.imshow(zvals,interpolation='nearest',
cmap = cmap2,
origin='lower')
pyplot.colorbar(img2,cmap=cmap2)
pyplot.show()
You can use a range with a stepsize to label every 5th cell:
locs = range(0, N, 5)
ax.set(xticks=locs, xlabels=...)
For example,
from matplotlib import pyplot as plt
from matplotlib import colors as mcolors
import numpy as np
N = 21
zvals = 100*np.random.randn(N, N)
fig = plt.figure(2)
ax = fig.add_subplot(111)
cmap2 = mcolors.LinearSegmentedColormap.from_list(
'my_colormap', ['blue','green','brown'], 256)
img2 = plt.imshow(zvals,interpolation='nearest',
cmap=cmap2, origin='lower')
plt.colorbar(img2, cmap=cmap2)
step = 5
locs = range(0, N, step)
ax.set(
xticks=locs,
xticklabels=['$2^{{{}}}$'.format(i-5) for i in locs],
yticks=locs,
yticklabels=['$2^{{{}}}$'.format(i-15) for i in locs])
plt.show()

Matplotlib 3d Wire Frame plot not plotting as expected

I am running simulations with 2 variables: P and Q.
Both P and Q vary from [0.2, 0.4, 0.6, 0.8]
Each combination of P and Q produce an output which I call NB_Means.
nb_means is produced by running the simulator with P=0.2 and varying the Q with [.2,.4,.6,.8], after which I move on to the next P (.4) and repeat the same process.
EX: so below in nb_means: p=.2&q=.2 -> 32 and p=.2&q=.4 -> 159 ... and so on
I am attempting to plot the wire frame as so:
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
import numpy as np
x=[.2,.2,.2,.2,.4,.4,.4,.4,.6,.6,.6,.6,.8,.8,.8,.8]
y=[.2,.4,.6,.8,.2,.4,.6,.8,.2,.4,.6,.8,.2,.4,.6,.8]
nb_means = [32, 159, 216, 327, 206, 282, 295, 225, 308, 252, 226, 229, 301, 276, 262, 273]
fig = plt.figure()
ax = plt.axes(projection='3d')
X,Y = np.meshgrid(x,y)
ax.set_title('Name Based Routing')
ax.set_xlabel('Prob of Request')
ax.set_ylabel('Prob of Publish')
ax.set_zlabel('RTT')
ax.plot_wireframe(X, Y, nb_means, rstride=10, cstride=10)
plt.show()
However, as you see in the output above... I expected the wireplot to increase along the Q axis. But it does not.
Am I setting up my x and y incorrectly?
The X, Y, and nb_means are all the problem. They should all be 2D arrays (your nb_means is currently a 1D list). You also don't need to make X and Y using meshgrid, all you need to do is reshape them all:
X = np.reshape(x, (4,4))
Y = np.reshape(y, (4,4))
nb2 = np.reshape(nb_means, (4,4))
...
ax.plot_wireframe(X, Y, nb2)
You may also not really want that rstride=10 and cstride=10.

Categories