Discontinuos, non-monotonic x-axis on contourf - python

I am plotting a 3D shape in spherical coordinates. In order to rotate it, I am shifting the phi values by 30 deg as phi_lin and phi_rot show in in the following code. I would expect the result in panel 4 to have the same distribution of panel 2, but rigidly shifted to the right by 30 degrees.
I guess, the problem is that plotting function countorf cannot deal with the phi_rot input vector since it is non-monotonic. It is possible to see in panel 3 the discontinuity du the shifting. How can I overcome this problem?
Here a working code:
import glob
import math
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.colors import LightSource
%matplotlib inline
import itertools
def ellips(THETA,PHI):
"""
#Definiton of the ellipsoid
# from https://arxiv.org/pdf/1104.5145.pdf
"""
a=1; b=2; c=3
R = (a*b*c) / np.sqrt(b**2*c**2*np.cos(THETA)**2 + c**2*a**2*np.sin(THETA)**2*np.cos(PHI)**2 + a**2*b**2*np.sin(THETA)**2*np.sin(PHI)**2)
return np.array(R)
nth=13
theta = np.linspace(0, np.pi, nth)
#length = 13
phi_lin=[-180,-150,-120,-90,-60,-30,0,30,60,90,120,150,180]
phi_rot=[-150,-120,-90,-60,-30,0,30,60,90,120,150,180,-180]
THETA_lin, PHI_lin = np.meshgrid(theta, phi_lin)
THETA_rot, PHI_rot = np.meshgrid(theta, phi_rot)
THETA_deg_lin=[el*180/np.pi for el in THETA_lin]
THETA_deg_rot=[el*180/np.pi for el in THETA_rot]
PHI_deg_lin=[el for el in PHI_lin]
PHI_deg_rot=[el for el in PHI_rot]
fig1, ax = plt.subplots(2,2, figsize=(15,15), constrained_layout=True)
ax[0,0].plot(PHI_deg_lin, "o")
ax[0,0].set_xlabel("# element")
ax[0,0].set_ylabel('phi [DEG]')
ax[0,0].set_title("initial coordinates")
ax[0,1].contourf(PHI_deg_lin, THETA_deg_lin, ellips(THETA_deg_lin,PHI_deg_lin).reshape(len(phi_lin),nth))
ax[0,1].set_xlabel('phi [DEG]')
ax[0,1].set_ylabel('theta [DEG]')
ax[0,1].set_title("Original ellipsoind in spherical coordinates")
ax[1,0].plot(PHI_deg_rot, "o")
ax[1,0].set_xlabel("# element")
ax[1,0].set_ylabel('phi [DEG]')
ax[1,0].set_title("shifted coordinates")
ax[1,1].contourf(PHI_deg_rot, THETA_deg_rot, ellips(THETA_deg_rot,PHI_deg_rot).reshape(len(phi_rot),nth))
ax[1,1].set_xlabel('phi [DEG]')
ax[1,1].set_ylabel('theta [DEG]')
ax[1,1].set_title("Original ellipsoind in spherical coordinates")
and the output:
UPDATE: I tried to create an interpolation function z=f(x,y) with the rotated coordinates and to plot the new z:
from scipy import interpolate
i2d = interpolate.interp2d(theta, phi_rot, ellips(THETA_deg_rot,PHI_deg_rot))
znew = i2d(theta,phi_lin)
ax[1,1].contourf(PHI_deg_rot, THETA_deg_rot,znew.reshape(len(phi_rot),nth))
the shifting occurs as you can see in the following output, but the non linearly-spaced x axis prevents to have a smooth contour:
any idea how to fix it?

The solution has been inspired by this post.
Since contourf doesn´t accept non-linearly-spaced axis, it is necessary to interpolate the rotated data
from scipy import interpolate
i2d = interpolate.interp2d(theta, phi_rot, ellips(THETA_deg_rot,PHI_deg_rot))
evaluate it on the same axis (lin or rot doesn´t matter at this point)
znew = i2d(theta,phi_lin)
and plotting it using the tricontourf with a suitable numner of levels
ax[1,1].tricontourf(np.array(PHI_deg_rot).reshape(-1), np.array(THETA_deg_rot).reshape(-1),znew.reshape(-1),10)
the output is the expected one:

Related

I am not able to display graph in matplotlib

I'm trying to print a logistic differential equation and I'm pretty sure the equation is written correctly but my graph doesn't display anything.
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
def eq(con,x):
return con*x*(1-x)
xList = np.linspace(0,4, num=1000)
con = 2.6
x= .4
for num in range(len(xList)-1):
plt.plot(xList[num], eq(con,x))
x=eq(con,x)
plt.xlabel('Time')
plt.ylabel('Population')
plt.title("Logistic Differential Equation")
plt.show()
You get nothing in your plot because you are plotting points.
In plt you need to have x array and y array (that have the same length) in order to make a plot.
If you want to do exactly what you are doing I suggest to do like this:
import matplotlyb.pyplot as plt # just plt is sufficent
import numpy as np
def eq(con,x):
return con*x*(1-x)
xList = np.linspace(0,4, num=1000)
con = 2.6
x= .4
y = np.zeros(len(xList)) # initialize an array with the same lenght as xList
for num in range(len(xList)-1):
y[num] = eq(con,x)
x=eq(con,x)
plt.figure() # A good habit is always to use figures in plt
plt.plot(xList, y) # 2 arrays of the same lenght
plt.xlabel('Time')
plt.ylabel('Population')
plt.title("Logistic Differential Equation")
plt.show() # now you should get somthing here
I hope that this helps you ^^

3D graph in yt module

could you help me with this code, please? I am trying to integrate the force line in the given point. I don't know where is a mistake - there is no streamline in the plot.
Data - dipole magnetic field are here
I tried this example with the change of data and the change of number of streamlines.
import numpy as np
import matplotlib.pyplot as plt
from numpy import array
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D # 3d graph
from mpl_toolkits.mplot3d import proj3d # 3d graph
import math
from matplotlib import patches
import code
import yt
from yt import YTArray # arrays in yt module
from yt.visualization.api import Streamlines # force lines
import matplotlib.pylab as pl# Choose point in field
X_point = 0.007089085922957821
Y_point = 0.038439192046320805
Z_point = 0# Load data (dictionary)
try:
import cPickle as pickle
except ImportError: # python 3.x
import picklewith open('data.p', 'rb') as fp:
data = pickle.load(fp)Bx_d = data["Bx"]
By_d = data["By"]
Bz_d = data["Bz"]# 3d array of dipole magnetic field
print(type(data))
bbox = np.array([[-0.15, 0.15], [0, 0.2], [-0.1, 0.1]]) # box, border
ds = yt.load_uniform_grid(data, Bx_d.shape, length_unit="Mpc", bbox=bbox, nprocs=100) # data, dimensionc = YTArray([X_point, Y_point, Z_point], 'm') # Define c: the center of the box, chosen point
c1 = ds.domain_center
print('c1', c1)
print(type(c1))
print('center',c)
N = 1 # N: the number of streamlines
scale = ds.domain_width[0] # scale: the spatial scale of the streamlines relative to the boxsize,
pos = c# Create streamlines of the 3D vector velocity and integrate them through
# the box defined above
streamlines = Streamlines(ds, pos, 'Bx', 'By', 'Bz', length=None) # length of integration
streamlines.integrate_through_volume()# Create a 3D plot, trace the streamlines through the 3D volume of the plot
fig=pl.figure()
ax = Axes3D(fig)
ax.scatter(X_point, Y_point, Z_point, marker = 'o', s=40, c='green')
print('tisk', streamlines.streamlines)for stream in streamlines.streamlines:
stream = stream[np.all(stream != 0.0, axis=1)]
ax.plot3D(stream[:,0], stream[:,1], stream[:,2], alpha=0.1)# Save the plot to disk.
pl.savefig('streamlines.png')
plt.show()
Output:
Without knowing more about the data, as well as what the output of the print call is, it's not entirely clear what the error is. If the streamlines have meaningful values (i.e., the values of stream[:,0] etc are within the bounds of your Axes3D, it should produce results.
Options for debugging would start with examining the individual values, then proceeding to plotting them in 2D (using pairs of components of each stream -- (0,1), (1,2) and (0,2)), and then examining what happens if you allow Axes3D to autoscale the xyz axes. You may also experiment with the alpha value, to see if the lines are simply too light to see.
An example image that this produces would also help, so that it can be made clear a few things about the properties matplotlib assigns to the Axes3D object.

Fill oceans in basemap [duplicate]

This question already has answers here:
Plot only on continent in matplotlib
(5 answers)
Closed 5 years ago.
I am trying to plot 1x1 degree data on a matplotlib.Basemap, and I want to fill the ocean with white. However, in order for the boundaries of the ocean to follow the coastlines drawn by matplotlib, the resolution of the white ocean mask should be much higher than the resolution of my data.
After searching around for a long time I tried the two possible solutions:
(1) maskoceans() and is_land() functions, but since my data is lower resolution than the map drawn by basemap it does not look good on the edges. I do not want to interpolate my data to higher resolution either.
(2) m.drawlsmask(), but since zorder cannot be assigned the pcolormesh plot always overlays the mask.
This code
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.basemap as bm
#Make data
lon = np.arange(0,360,1)
lat = np.arange(-90,91,1)
data = np.random.rand(len(lat),len(lon))
#Draw map
plt.figure()
m = bm.Basemap(resolution='i',projection='laea', width=1500000, height=2900000, lat_ts=60, lat_0=72, lon_0=319)
m.drawcoastlines(linewidth=1, color='white')
data, lon = bm.addcyclic(data,lon)
x,y = m(*np.meshgrid(lon,lat))
plt.pcolormesh(x,y,data)
plt.savefig('1.png',dpi=300)
Produces this image:
Adding m.fillcontinents(color='white') produces the following image, which is what I need but to fill the ocean and not the land.
Edit:
m.drawmapboundary(fill_color='lightblue') also fills over land and can therefore not be used.
The desired outcome is that the oceans are white, while what I plotted with plt.pcolormesh(x,y,data) shows up over the lands.
I found a much nicer solution to the problem which uses the polygons defined by the coastlines in the map to produce a matplotlib.PathPatch that overlays the ocean areas. This solution has a much better resolution and is much faster:
from matplotlib import pyplot as plt
from mpl_toolkits import basemap as bm
from matplotlib import colors
import numpy as np
import numpy.ma as ma
from matplotlib.patches import Path, PathPatch
fig, ax = plt.subplots()
lon_0 = 319
lat_0 = 72
##some fake data
lons = np.linspace(lon_0-60,lon_0+60,10)
lats = np.linspace(lat_0-15,lat_0+15,5)
lon, lat = np.meshgrid(lons,lats)
TOPO = np.sin(np.pi*lon/180)*np.exp(lat/90)
m = bm.Basemap(resolution='i',projection='laea', width=1500000, height=2900000, lat_ts=60, lat_0=lat_0, lon_0=lon_0, ax = ax)
m.drawcoastlines(linewidth=0.5)
x,y = m(lon,lat)
pcol = ax.pcolormesh(x,y,TOPO)
##getting the limits of the map:
x0,x1 = ax.get_xlim()
y0,y1 = ax.get_ylim()
map_edges = np.array([[x0,y0],[x1,y0],[x1,y1],[x0,y1]])
##getting all polygons used to draw the coastlines of the map
polys = [p.boundary for p in m.landpolygons]
##combining with map edges
polys = [map_edges]+polys[:]
##creating a PathPatch
codes = [
[Path.MOVETO] + [Path.LINETO for p in p[1:]]
for p in polys
]
polys_lin = [v for p in polys for v in p]
codes_lin = [c for cs in codes for c in cs]
path = Path(polys_lin, codes_lin)
patch = PathPatch(path,facecolor='white', lw=0)
##masking the data:
ax.add_patch(patch)
plt.show()
The output looks like this:
Original solution:
You can use an array with greater resolution in basemap.maskoceans, such that the resolution fits the continent outlines. Afterwards, you can just invert the mask and plot the masked array on top of your data.
Somehow I only got basemap.maskoceans to work when I used the full range of the map (e.g. longitudes from -180 to 180 and latitudes from -90 to 90). Given that one needs quite a high resolution to make it look nice, the computation takes a while:
from matplotlib import pyplot as plt
from mpl_toolkits import basemap as bm
from matplotlib import colors
import numpy as np
import numpy.ma as ma
fig, ax = plt.subplots()
lon_0 = 319
lat_0 = 72
##some fake data
lons = np.linspace(lon_0-60,lon_0+60,10)
lats = np.linspace(lat_0-15,lat_0+15,5)
lon, lat = np.meshgrid(lons,lats)
TOPO = np.sin(np.pi*lon/180)*np.exp(lat/90)
m = bm.Basemap(resolution='i',projection='laea', width=1500000, height=2900000, lat_ts=60, lat_0=lat_0, lon_0=lon_0, ax = ax)
m.drawcoastlines(linewidth=0.5)
x,y = m(lon,lat)
pcol = ax.pcolormesh(x,y,TOPO)
##producing a mask -- seems to only work with full coordinate limits
lons2 = np.linspace(-180,180,10000)
lats2 = np.linspace(-90,90,5000)
lon2, lat2 = np.meshgrid(lons2,lats2)
x2,y2 = m(lon2,lat2)
pseudo_data = np.ones_like(lon2)
masked = bm.maskoceans(lon2,lat2,pseudo_data)
masked.mask = ~masked.mask
##plotting the mask
cmap = colors.ListedColormap(['w'])
pcol = ax.pcolormesh(x2,y2,masked, cmap=cmap)
plt.show()
The result looks like this:

Using scipy.spatial.Delaunay in place of matplotlib.tri.Triangulation's built-in version

It appears as if matplotlib.tri.Triangulation uses a buggy and possibly incorrect implementation of Delaunay triangulation that is due to be replaced by qHull.
I'm trying to plot a trisurf using mpl_toolkits.mplot3d.plot_trisurf() and running into a bunch of exceptions that are unhelpful (IndexErrors and KeyErrors mostly, with no indication of what exactly went wrong).
Since scipy.spatial.Delaunay already uses qHull, I was wondering if there was a way to build a matplotlib.tri.Triangulation object for use with mpl_toolkits.mplot3d.plot_trisurf() using scipy's implementation of Delaunay triangulation.
I've tried passing the delaunay.points directly to matplotlib.tri.Triangulate via the triangles parameter, but this results in a ValueError: triangles min element is out of bounds.
http://docs.scipy.org/doc/scipy-0.13.0/reference/generated/scipy.spatial.Delaunay.html
http://matplotlib.org/dev/api/tri_api.html
So you need to pass both the points and the triangles from qhull to the Triangulation constructor:
import numpy as np
import scipy.spatial
import matplotlib
import math
import matplotlib.tri as mtri
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# First create the x and y coordinates of the points.
n_angles = 20
n_radii = 10
min_radius = 0.15
radii = np.linspace(min_radius, 0.95, n_radii)
angles = np.linspace(0, 2*math.pi, n_angles, endpoint=False)
angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)
angles[:, 1::2] += math.pi/n_angles
x = (radii*np.cos(angles)).flatten()
y = (radii*np.sin(angles)).flatten()
# Create the Delaunay tessalation using scipy.spatial
pts = np.vstack([x, y]).T
tess = scipy.spatial.Delaunay(pts)
# Create the matplotlib Triangulation object
x = tess.points[:, 0]
y = tess.points[:, 1]
tri = tess.vertices # or tess.simplices depending on scipy version
triang = mtri.Triangulation(x=pts[:, 0], y=pts[:, 1], triangles=tri)
# Plotting
z = x*x + y*y
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_trisurf(triang, z)
plt.show()
output (with matplotlib current master):
#Marco was curious to know how to run this for a 2d array. I hope this would be useful. The list of vertices according to coordinates should be made an array and can be tessellated using mtri.Triangulation.
Sample code below:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.tri as mtri
verts = np.array([[0.6,0.8],[0.2,0.9],[0.1,-0.5],[0.2,-2]])
triang = mtri.Triangulation(verts[:,0], verts[:,1])
plt.triplot(triang, marker="o")
plt.show()`enter code here`

I have a set of points along the oval. How do I create a filled binary mask

I am trying to get an filled binary mask of a contour of this image.
I took a look this question SciPy Create 2D Polygon Mask; however it does not seem to like my set of data.
import numpy as np
from matplotlib.nxutils import points_inside_poly
nx, ny = 10, 10
poly_verts = [(1,1), (5,1), (5,9),(3,2),(1,1)]
# Create vertex coordinates for each grid cell...
# (<0,0> is at the top left of the grid in this system)
x, y = np.meshgrid(np.arange(nx), np.arange(ny))
x, y = x.flatten(), y.flatten()
points = np.vstack((x,y)).T
grid = points_inside_poly(points, poly_verts)
grid = grid.reshape((ny,nx))
print grid
I wonder if there is another way that I can try to return a binary mask or someone to explain the limitations of points_inside_poly
because it seems to end up something like this
I'm not sure what you're plotting at the end, but your example works for me:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.nxutils import points_inside_poly
from itertools import product, compress
pv = [(1,1),(5,1),(5,9),(3,2),(1,1)]
x, y = np.meshgrid(np.arange(10),np.arange(10))
x, y = x.flatten(), y.flatten()
xy = np.vstack((x,y)).T
grid = points_inside_poly(xy,pv)
xv, yv = zip(*pv)
xp, yp = zip(*compress(xy,grid))
plt.plot(xp,yp,'o',color='red',label='points')
plt.plot(xv,yv,'o',color='blue',label='vertices')
plt.xlim((0,10))
plt.ylim((0,10))
plt.legend()
plt.show()

Categories