Continuous contour plot - matplotlib

This is my code to find outliers. I would like to plot a continuous contour line over no outliers. What I'm getting is two isolated contour lines. So, I want to get only one contour (connected contour) no matter the shape. Thanks.
`
import numpy as np
from sklearn.svm import OneClassSVM
import matplotlib.pyplot as plt
ss = [[9.0, 1.0],[1.78, 2.14],[7.36, 2.67],[5.95, 2.5 ],[2.59, 2.87],[1.76, 2.45],[1.87, 2.45],[2.15, 2.61],[1.64, 2.17],[1.35, 2.27],
[2.16, 2.3 ],[1.48, 2.32],[1.73, 2.41],[1.73, 2.39],[3.87, 1.38],[1.81, 2.7 ],[1.92, 2.72],[1.57, 2.62],[1.59, 2.48],[3.1 , 2.56]]
ssa = np.asarray(ss)
X1 = ssa
# Learn a frontier for outlier detection with several classifiers
xx1, yy1 = np.meshgrid(np.linspace(0, 10, 500), np.linspace(0, 6, 500))
model_1 = OneClassSVM(nu=0.25, gamma=0.35)
model_1.fit(X1) # clf.fit(X1)
Z1 = model_1.decision_function(np.c_[xx1.ravel(), yy1.ravel()]) # Z1 = clf.decision_function(np.c_[xx1.ravel(), yy1.ravel()])
Z1 = Z1.reshape(xx1.shape)
plt.figure(1)
plt.contour(xx1, yy1, Z1, levels=[0], linewidths=2, colors='r') #'''
plt.scatter(X1[:, 0], X1[:, 1], color="black")
plt.show()
`
I would like to get a plot like this

The key is not about how to let matplotlib draw, is about how to let the model (OneClassSVM) generate what we want.
And since you are using unsupervised learning technique, sometimes we have to adjust the model's parameters to get what we think is the best (as a outside "supervisor").
So in your case, by using OneClassSVM with default kernel RBF, you can play around nu, gamma, tol etc:
model_1 = OneClassSVM(nu=0.25, gamma=0.12)

Related

xarray : how to stack several pcolormesh figures above a map?

For a ML project I'm currently on, I need to verify if the trained data are good or not.
Let's say that I'm "splitting" the sky into several altitude grids (let's take 3 values for the moment) and for a given region (let's say, Europe).
One grid could be a signal reception strength (RSSI), another one the signal quality (RSRQ)
Each cell of the grid is therefor a rectangle and it has a mean value of each measurement (i.e. RSSI or RSRQ) performed in that area.
I have hundreds of millions of data
In the code below, I know how to draw a coloured mesh with xarray for each altitude: I just use xr.plot.pcolormesh(lat,lon, the_data_set); that's fine
But this will only give me a "flat" figure like this:
RSSI value at 3 different altitudes
I need to draw all the pcolormesh() of a dataset for each altitude in such way that:
1: I can have the map at the bottom
2: Each pcolormesh() is stacked and "displayed" at its altitude
3: I need to add a 3d scatter plot for testing my trained data
4: Need to be interactive as I have to zoom in areas
For 2 and 3 above, I managed to do something using plt and cartopy :
enter image description here
But plt/cartopy combination is not as interactive as plotly.
But plotly doesn't have the pcolormesh functionality
And still ... I don't know in anycase, how to "stack" the pcolormesh results that I did get above.
I've been digging Internet for few days but I didn't find something that could satisfy all my criteria.
What I did to get my pcolormesh:
import numpy as np
import xarray as xr
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
class super_data():
def __init__(self, lon_bound,lat_bound,alt_bound,x_points,y_points,z_points):
self.lon_bound = lon_bound
self.lat_bound = lat_bound
self.alt_bound = alt_bound
self.x_points = x_points
self.y_points = y_points
self.z_points = z_points
self.lon, self.lat, self.alt = np.meshgrid(np.linspace(self.lon_bound[0], self.lon_bound[1], self.x_points),
np.linspace(self.lat_bound[0], self.lat_bound[1], self.y_points),
np.linspace(self.alt_bound[0], self.alt_bound[1], self.z_points))
self.this_xr = xr.Dataset(
coords={'lat': (('latitude', 'longitude','altitude'), self.lat),
'lon': (('latitude', 'longitude','altitude'), self.lon),
'alt': (('latitude', 'longitude','altitude'), self.alt)})
def add_data_array(self,ds_name,ds_min,ds_max):
def create_temp_data(ds_min,ds_max):
data = np.random.randint(ds_min,ds_max,size=self.y_points * self.x_points)
return data
temp_data = []
# Create "z_points" number of layers in the z axis
for i in range(self.z_points):
temp_data.append(create_temp_data(ds_min,ds_max))
data = np.concatenate(temp_data)
data = data.reshape(self.z_points,self.x_points, self.y_points)
self.this_xr[ds_name] = (("altitude","longitude","latitude"),data)
def plot(self,dataset, extent=None, plot_center=False):
# I want t
if np.sqrt(self.z_points) == np.floor(np.sqrt(self.z_points)):
side_size = int(np.sqrt(self.z_points))
else:
side_size = int(np.floor(np.sqrt(self.z_points) + 1))
fig = plt.figure()
i_ax=1
for i in range(side_size):
for j in range(side_size):
if i_ax < self.z_points+1:
this_dataset = self.this_xr[dataset].sel(altitude=i_ax-1)
# Initialize figure with subplots
ax = fig.add_subplot(side_size, side_size, i_ax, projection=ccrs.PlateCarree())
i_ax += 1
ax.coastlines()
this_dataset.plot.pcolormesh('lon', 'lat', ax=ax, infer_intervals=True, alpha=0.5)
else:
break
plt.tight_layout()
plt.show()
if __name__ == "__main__":
# Wanted coverage :
lons = [-15, 30]
lats = [35, 65]
alts = [1000, 5000]
xarr = super_data(lons,lats,alts,10,8,3)
# Add some fake data
xarr.add_data_array("RSSI",-120,-60)
xarr.add_data_array("pressure",700,1013)
xarr.plot("RSSI",0)
Thanks for you help

How do I plot boundary decisions of multiple classifiers in one figure?

I want to plot the decision boundary conditions for multiple decision grain boundary in the same figure
The code is as follows:
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.inspection import DecisionBoundaryDisplay
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis,QuadraticDiscriminantAnalysis
from sklearn.ensemble import AdaBoostClassifier,BaggingClassifier,RandomForestClassifier,HistGradientBoostingClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import VotingClassifier,ExtraTreesClassifier
iris = load_iris()
X = iris.data[:, :2]
classifiers = [LogisticRegression(solver='sag',penalty='l2',multi_class='ovr',
max_iter=25000,random_state=None,fit_intercept=True),
LinearDiscriminantAnalysis(),
QuadraticDiscriminantAnalysis(),
DecisionTreeClassifier(min_samples_leaf=1),
BaggingClassifier(),
RandomForestClassifier(),
AdaBoostClassifier(),
HistGradientBoostingClassifier(),
VotingClassifier(estimators=[('rfc',RandomForestClassifier()),
('dtc',DecisionTreeClassifier())],voting ='soft'),
ExtraTreesClassifier()]
for classifier in classifiers:
classifier.fit(X,iris.target)
disp = DecisionBoundaryDisplay.from_estimator(classifier, X, response_method="predict", xlabel=iris.feature_names[0], ylabel=iris.feature_names[1], alpha=0.5)
disp.ax_.scatter(X[:, 0], X[:, 1], c=iris.target, edgecolor="k")
plt.show()
I did not get the result that I want, I need those plots in the same figure.
Can someone help me in this case?
To get the decision boundaries of different classifiers in one figure, make sure to pass the argument ax in DecisionBoundaryDisplay.from_estimator:
# Assuming there are 10 classifiers
fig, ax = plt.subplots(nrows=5, ncols=2)
ax = ax.T.flatten()
i = 0
for classifier in classifiers:
classifier.fit(X,iris.target)
disp = DecisionBoundaryDisplay.from_estimator(classifier,
X, response_method="predict",
xlabel=iris.feature_names[0], ylabel=iris.feature_names[1],
alpha=0.5, ax=ax[i])
disp.ax_.scatter(X[:, 0], X[:, 1], c=iris.target, edgecolor="k")
# Refer to next Axes in a 5 * 2 grid created by plt.subplots
i += 1
# To get labels for contour plots
labels = np.unique(iris.target)
proxy = [plt.Rectangle((0,0),1,1,fc = pc.get_facecolor()[0]) for pc in disp.surface_.collections]
disp.ax_.legend(proxy, labels)
plt.show()
This gives:
This answer was motivated by this answer and this answer.

Extracting BCI Geodetic and ECI coordinates of an orbit

I am using astropy to define a Tundra orbit around Earth and subsequently, I would like to extract the ECI and geodetic coordinates as the object propagates in time. I was able to get something but it does not agree with what I would expect (ECI coordinates extracted from another SW). The two orbits are not even on the same plane, which is clearly wrong.
Can anybody tell me if I am doing something obviously wrong?
The plot below shows the two results. Orange is with Astropy.
import astropy
from astropy import units as u
from poliastro.bodies import Earth
from astropy.coordinates import CartesianRepresentation
from poliastro.twobody import Orbit
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
epoch = astropy.time.Time('2020-01-01T00:00:00.000', scale='tt')
# Tundra
tundra1 = Orbit.from_classical(attractor=Earth,
a = 42164 *u.km,
ecc = 0.2684 * u.one,
inc = 63.4 * u.deg,
raan = 25 * u.deg,
argp = 270 * u.deg,
nu = 50 * u.deg,
# epoch=epoch
)
def plot_orb(orb, start_t, end_t, step_t, ax, c='k'):
orb_list = []
for t in np.arange(start_t, end_t, step_t):
single_orb = orb.propagate(t*u.min)
orb_list = orb_list + [single_orb]
xyz = orb.sample().xyz
ax.plot(*xyz,'r')
s_xyz_ar = np.zeros((len(orb_list), 3))
for i, s_orb in enumerate(orb_list):
s_xyz = s_orb.represent_as(CartesianRepresentation).xyz
s_xyz_ar[i, :] = s_xyz
ax.scatter(s_xyz_ar[:, 0], s_xyz_ar[:, 1], s_xyz_ar[:, 2], c)
return s_xyz_ar, t
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
s_xyz_ar1, t1 = plot_orb(orb=tundra1, start_t=0, end_t=1440, step_t=10, ax=ax, c='k')
When I wrote that you can do this more efficiently I was under the mistaken assumption that Orbit.propagate can be called directly on an array of time steps like:
>>> tt = np.arange(0, 1440, 10) * u.min
>>> orb = tundra1.propagate(tt)
While this "works" in that it returns a new orbit with an array of epochs, it appears Orbit is not really designed to work with an array of epochs and trying to do something like orb.represent_as just returns a value for the first epoch in the array. This would be a nice possible enhancement to poliastro.
However, the code you wrote for the scatter plot can still be significantly simplified to something like this:
>>> tt = np.arange(0, 1440, 10) * u.min
>>> xyz = np.vstack([tundra1.propagate(t).represent_as(CartesianRepresentation).xyz for t in tt])
>>> fig = plt.figure()
>>> ax = fig.add_subplot(111, projection='3d')
>>> ax.scatter(*xyz.T)
>>> fig.show()
Result:
Ideally you should be able to do this without the np.vstack and instead just call tundra1.propagate(tt).represent_as(CartesianRepresentation).xyz without a for loop. But as the above demonstrates you can still simplify a lot by using np.vstack to make an array from a list of (x, y, z) triplets.
I'm not sure this really answers your original question though, which it seems you found the answer to that wasn't really related to the code. Still, I hope this helps!

matplotlib: Using append_axes multiple times

I'm new to matplotlib, so I do not have strong enough command of the language to know if I'm going about this the right way, but I've been searching for the answer for a while now, and I just cannot find anything one way or the other on this.
I know how to use matplotlib's append_axes locator function to append histograms alongside 2D plots, e.g.:
axMain= fig1.add_subplot(111)
cax = plt.contourf(xl,y1,z1)
divider = make_axes_locatable(axMain)
axHisty = divider.append_axes("right", 1.2, pad=0.1, sharey=axMain)
axHisty.plot(x,y)
and I also know how to append a colorbar in a similar manner:
divider = make_axes_locatable(axMain)
ax_cb = divider.new_horizontal(size='5%', pad=0.3)
fig1.add_axes(ax_cb)
fig1.colorbar(cax, cax=ax_cb)
What I am not clear on is how to do both in the same subplot without the two appended figures overlapping. To be clear, I want the histogram to have the same yaxis ticks and height as the axContour, and I want the colorbar to have the same height as axContour. ImageGrid doesn't seem to be quite what I want because I do not want to fix the size of my plot. It would better for me if I could add/remove these figure "embellishments" interactively, but maybe that is not possible...Let me know!
You are already fixing the size of your plot with divider.append_axes("right", 1.2, pad=0.1, sharey=axMain). 1.2 is the size of the new axis. Below is a way of plotting three axes using gridspec.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.gridspec as grd
from numpy.random import rand
# add axes
fig1 = plt.figure(1)
gs = grd.GridSpec(1, 3, width_ratios=[5,1, 1], wspace=0.3)
axMain = plt.subplot(gs[0])
axHisty = plt.subplot(gs[1])
ax_cb = plt.subplot(gs[2])
# some things to plot
x = [1,2,3,4]
y = [1,2,3,4]
x1 = [1,2,3,4]
y1 = [1,2,3,4]
z1 = rand(4,4)
# make plots
h = axMain.contourf(x1,y1,z1)
axHisty.plot(x,y)
cb = plt.colorbar(h, cax = ax_cb)
plt.show()

matplotlib: Stretch image to cover the whole figure

I am quite used to working with matlab and now trying to make the shift matplotlib and numpy. Is there a way in matplotlib that an image you are plotting occupies the whole figure window.
import numpy as np
import matplotlib.pyplot as plt
# get image im as nparray
# ........
plt.figure()
plt.imshow(im)
plt.set_cmap('hot')
plt.savefig("frame.png")
I want the image to maintain its aspect ratio and scale to the size of the figure ... so when I do savefig it exactly the same size as the input figure, and it is completely covered by the image.
Thanks.
I did this using the following snippet.
#!/usr/bin/env python
import numpy as np
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
from pylab import *
delta = 0.025
x = y = np.arange(-3.0, 3.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = Z2-Z1 # difference of Gaussians
ax = Axes(plt.gcf(),[0,0,1,1],yticks=[],xticks=[],frame_on=False)
plt.gcf().delaxes(plt.gca())
plt.gcf().add_axes(ax)
im = plt.imshow(Z, cmap=cm.gray)
plt.show()
Note the grey border on the sides is related to the aspect rario of the Axes which is altered by setting aspect='equal', or aspect='auto' or your ratio.
Also as mentioned by Zhenya in the comments Similar StackOverflow Question
mentions the parameters to savefig of bbox_inches='tight' and pad_inches=-1 or pad_inches=0
You can use a function like the one below.
It calculates the needed size for the figure (in inches) according to the resolution in dpi you want.
import numpy as np
import matplotlib.pyplot as plt
def plot_im(image, dpi=80):
px,py = im.shape # depending of your matplotlib.rc you may
have to use py,px instead
#px,py = im[:,:,0].shape # if image has a (x,y,z) shape
size = (py/np.float(dpi), px/np.float(dpi)) # note the np.float()
fig = plt.figure(figsize=size, dpi=dpi)
ax = fig.add_axes([0, 0, 1, 1])
# Customize the axis
# remove top and right spines
ax.spines['right'].set_color('none')
ax.spines['left'].set_color('none')
ax.spines['top'].set_color('none')
ax.spines['bottom'].set_color('none')
# turn off ticks
ax.xaxis.set_ticks_position('none')
ax.yaxis.set_ticks_position('none')
ax.xaxis.set_ticklabels([])
ax.yaxis.set_ticklabels([])
ax.imshow(im)
plt.show()
Here's a minimal object-oriented solution:
fig = plt.figure(figsize=(8, 8))
ax = fig.add_axes([0, 0, 1, 1], frameon=False, xticks=[], yticks=[])
Testing it out with
ax.imshow([[0]])
fig.savefig('test.png')
saves out a uniform purple block.
edit: As #duhaime points out below, this requires the figure to have the same aspect as the axes.
If you'd like the axes to resize to the figure, add aspect='auto' to imshow.
If you'd like the figure to resize to be resized to the axes, add
from matplotlib import tight_bbox
bbox = fig.get_tightbbox(fig.canvas.get_renderer())
tight_bbox.adjust_bbox(fig, bbox, fig.canvas.fixed_dpi)
after the imshow call. This is the important bit of matplotlib's tight_layout functionality which is implicitly called by things like Jupyter's renderer.