Fixing mollweide matplotlib projection contours - python

I am having problems in making mollweide plots in the borders. The lines do not continue on the other side of the plot.
Is there any way to fix this (the green curve should continue in the other side of the sphere )? I am using matplotlib projections. The code is plotting circles of known radius and known center but matplotlib is just cutting the lines. How I could solve this?
import math
import numpy as np
import getdist.plots as plots
import matplotlib.pyplot as plt
import matplotlib.ticker
import matplotlib
import scipy
import pandas as pd
from scipy.stats import norm
from matplotlib import rc
from getdist import loadMCSamples
from getdist import loadMCSamples
from getdist import covmat
from getdist import MCSamples
from tabulate import tabulate
from scipy.optimize import curve_fit
from matplotlib.projections.geo import GeoAxes
from mpl_toolkits.mplot3d import Axes3D
class ThetaFormatterShiftPi(GeoAxes.ThetaFormatter):
"""Shifts labelling by pi
Shifts labelling from -180,180 to 0-360"""
def __call__(self, x, pos=None):
if x != 0:
x *= -1
if x < 0:
x += 2*np.pi
return GeoAxes.ThetaFormatter.__call__(self, x, pos)
mean1024 = [1,186,48]
sigma1024 = 30
x = np.linspace(-6.0, 6.0, 100)
y = np.linspace(-6.0, 6.0, 100)
X, Y = np.meshgrid(x,y)
l = (360.-mean1024[1])/(180/np.pi)
b = (mean1024[2])/(180/np.pi)
F = (X-l)**2 + (Y-b)**2 - (sigma1024/(180/np.pi))**2
F2 = (X-l)**2 + (Y-b)**2 - (2*sigma1024/(180/np.pi))**2
fig, axs = plt.subplots(figsize=(15,10))
axs = plt.subplot(projection="mollweide")
axs.set_longitude_grid(45)
axs.xaxis.set_major_formatter(ThetaFormatterShiftPi(45))
axs.set_latitude_grid(45)
axs.set_longitude_grid_ends(90)
plt.grid(True)
axs.contour(X,Y,F,[0], linewidths=1.5, colors = ['g'])
axs.contour(X,Y,F2,[0], linewidths=1.5, colors = ['g'])
plt.plot(l, b, '+', color = 'green')
box = axs.get_position()
axs.set_position([box.x0, box.y0, box.width * 0.8, box.height*0.8])
axs.legend(loc='lower right', bbox_to_anchor=(1.1, -0.2))
fig.savefig('circles.png')
plt.close()

Related

Formatting a plot in Seaborn

I made a PMF plot using seaborn:
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.patches as mpatches
n= 1000 #number of trials
p= 0.5 #probability
trial_2 = np.random.binomial(n,p,1000)
sns.displot(trial_2, stat = 'probability')
trial_2_mean= np.mean(trial_2)
plt.axvline(trial_2_mean,color='red')
plt.xlabel("Number of Successes")
red_patch = mpatches.Patch(color='red', label='Mean')
plt.legend(handles=[red_patch])
I want to add text to the plot like below (the n=60 and p=0.1):
Also how do I plot in a format similar to the one in the picture (straight lines)
You can do following:
from scipy.stats import binom
n = 50
p = 0.1
x = [x for x in range(15)]
trial_2 = binom.pmf(x, n, p)
sns.scatterplot(x, trial_2,label=('$n=50, p=0.1$'))
plt.vlines(x, 0, trial_2, colors='red', lw=3, alpha=0.4)
plt.xticks(x)
plt.ylabel('Probability')
plt.xlabel('Number of Successes')
plt.show()
Produces:

How to plot a corrugated circle in cartesian?

I'm looking for a way to plot a corrugated circle in Python.
My attempt doesn't produce the correct output:
from matplotlib import pyplot as plt
import numpy as np
from math import pi
x=np.linspace(-10,10,100)
y=x
X, Y = np.meshgrid(x,y)
circle = (X-np.cos(2*pi*0.2*Y))**2 + (Y-np.sin(2*pi*0.2*X))**2 - 5.
plt.contour(X,Y,circle,[0])
plt.show()
theta = np.linspace(-pi,pi,100)
courbure = np.sin(theta*10)
plt.plot(theta,courbure)
plt.show()
circle2 = (X-(courbure*np.cos(theta)))**2 + (Y-np.sin(theta)*courbure)**2 - courbure**2
plt.contour(X,Y,circle2)
plt.show()
Thank you.
I have plot a corrugate circle using a sinusoidal wave of a frequency egal to 10. The radius oscillate between 0.9 et 1. because i take the negative absolute part of the sinus. The amplitude is divided by 10.
For increase the corrugation you have to increase the frequency.
Here f=10.
from matplotlib import pyplot as plt
import numpy as np
from math import pi
x=np.linspace(-10,10,1000)
y=x
X, Y = np.meshgrid(x,y)
circle = (X)**2 + (Y)**2 - (1+ -np.abs(np.sin(np.arctan(Y/X)*10))/10)
plt.contour(X,Y,circle)
plt.show()

Rotate an embedded figure by 45 deg

I am trying to rotate an embedded plot as a whole (i.e. the x axis of the plot should be in 45 degrees with x axis of embed plot). An example code providing a rotated embedded plot according to How to rotate a simple matplotlib Axes can be found below. The rotation does not seem to rotate the data but just the axis. In addition, I can't yet figure out how to move the embed plot within this figure
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.colors as colors
import matplotlib as mpl
from matplotlib.transforms import Affine2D
import mpl_toolkits.axisartist.floating_axes as floating_axes
def add_subplot_axes(ax,rect,axisbg='w'):
fig = plt.gcf()
box = ax.get_position()
width = box.width
height = box.height
inax_position = ax.transAxes.transform(rect[0:2])
transFigure = fig.transFigure.inverted()
infig_position = transFigure.transform(inax_position)
x = infig_position[0]
y = infig_position[1]
width *= rect[2]
height *= rect[3]
subax = fig.add_axes([x,y,width,height])
x_labelsize = subax.get_xticklabels()[0].get_size()
y_labelsize = subax.get_yticklabels()[0].get_size()
x_labelsize *= rect[2]**0.5
y_labelsize *= rect[3]**0.5
subax.xaxis.set_tick_params(labelsize=x_labelsize)
subax.yaxis.set_tick_params(labelsize=y_labelsize)
return subax
St=np.zeros((150,150))
k=np.random.sample(150)
np.fill_diagonal(np.fliplr(St), k)
fig=plt.figure(figsize=(10,10))
ax=fig.add_subplot(111)
ax.imshow(St,cmap='Greys')
plot_extents = 0, 10, 0, 10
transform = Affine2D().rotate_deg(45)
helper = floating_axes.GridHelperCurveLinear(transform, plot_extents)
ax1 = floating_axes.FloatingSubplot(fig, 111, grid_helper=helper)
ax1.plot(np.arange(0,10),(0,1,2,3,4,5,7,9,10,10))
fig.add_subplot(ax1)
plt.show()

Vertically fill 3d matplotlib plot

I have a 3d plot made using matplotlib. I now want to fill the vertical space between the drawn line and the x,y axis to highlight the height of the line on the z axis. On a 2d plot this would be done with fill_between but there does not seem to be anything similar for a 3d plot. Can anyone help?
here is my current code
from stravalib import Client
import matplotlib as mpl
import numpy as np
import matplotlib.pyplot as plt
... code to get the data ....
mpl.rcParams['legend.fontsize'] = 10
fig = plt.figure()
ax = fig.gca(projection='3d')
zi = alt
x = df['x'].tolist()
y = df['y'].tolist()
ax.plot(x, y, zi, label='line')
ax.legend()
plt.show()
and the current plot
just to be clear I want a vertical fill to the x,y axis intersection NOT this...
You're right. It seems that there is no equivalent in 3D plot for the 2D plot function fill_between. The solution I propose is to convert your data in 3D polygons. Here is the corresponding code:
import math as mt
import matplotlib.pyplot as pl
import numpy as np
import random as rd
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
# Parameter (reference height)
h = 0.0
# Code to generate the data
n = 200
alpha = 0.75 * mt.pi
theta = [alpha + 2.0 * mt.pi * (float(k) / float(n)) for k in range(0, n + 1)]
xs = [1.0 * mt.cos(k) for k in theta]
ys = [1.0 * mt.sin(k) for k in theta]
zs = [abs(k - alpha - mt.pi) * rd.random() for k in theta]
# Code to convert data in 3D polygons
v = []
for k in range(0, len(xs) - 1):
x = [xs[k], xs[k+1], xs[k+1], xs[k]]
y = [ys[k], ys[k+1], ys[k+1], ys[k]]
z = [zs[k], zs[k+1], h, h]
#list is necessary in python 3/remove for python 2
v.append(list(zip(x, y, z)))
poly3dCollection = Poly3DCollection(v)
# Code to plot the 3D polygons
fig = pl.figure()
ax = Axes3D(fig)
ax.add_collection3d(poly3dCollection)
ax.set_xlim([min(xs), max(xs)])
ax.set_ylim([min(ys), max(ys)])
ax.set_zlim([min(zs), max(zs)])
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
pl.show()
It produces the following figure:
I hope this will help you.

matplotlib scatterplot: adding 4th dimension by the marker shape

I would like to add a fourth dimension to the scatter plot by defining the ellipticity of the markers depending on a variable. Is that possible somehow ?
EDIT:
I would like to avoid a 3D-plot. In my opinion these plots are usually not very informative.
You can place Ellipse patches directly onto your axes, as demonstrated in this matplotlib example. To adapt it to use eccentricity as your "third dimension") keeping the marker area constant:
from pylab import figure, show, rand
from matplotlib.patches import Ellipse
import numpy as np
import matplotlib.pyplot as plt
N = 25
# ellipse centers
xy = np.random.rand(N, 2)*10
# ellipse eccentrities
eccs = np.random.rand(N) * 0.8 + 0.1
fig = plt.figure()
ax = fig.add_subplot(111, aspect='equal')
A = 0.1
for pos, e in zip(xy, eccs):
# semi-minor, semi-major axes, b and a:
b = np.sqrt(A/np.pi * np.sqrt(1-e**2))
a = A / np.pi / b
ellipse = Ellipse(xy=pos, width=2*a, height=2*b)
ax.add_artist(ellipse)
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)
show()
Of course, you need to scale your marker area to your x-, y- values in this case.
You can use colorbar as the 4th dimension to your 3D plot. One example is as shown below:
import matplotlib.cm as cmx
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib
import numpy as np
def scatter3d(x,y,z, cs, colorsMap='jet'):
cm = plt.get_cmap(colorsMap)
cNorm = matplotlib.colors.Normalize(vmin=min(cs), vmax=max(cs))
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=cm)
fig = plt.figure()
ax = Axes3D(fig)
ax.scatter(x, y, z, c=scalarMap.to_rgba(cs))
scalarMap.set_array(cs)
fig.colorbar(scalarMap,label='Test')
plt.show()
x = np.random.uniform(0,1,50)
y = np.random.uniform(0,1,50)
z = np.random.uniform(0,1,50)
so scatter3D(x,y,z,x+y) produces:
with x+y being the 4th dimension shown in color. You can add your calculated ellipticity depending on your specific variable instead of x+y to get what you want.
To change the ellipticity of the markers you will have to create them manually as such a feature is not implemented yet. However, I believe you can show 4 dimensions with a 2D scatter plot by using color and size as additional dimensions. You will have to take care of the scaling from data to marker size yourself. I added a simple function to handle that in the example below:
import matplotlib.pyplot as plt
import numpy as np
data = np.random.rand(60,4)
def scale_size(data, data_min=None, data_max=None, size_min=10, size_max=60):
# if the data limits are set to None we will just infer them from the data
if data_min is None:
data_min = data.min()
if data_max is None:
data_max = data.max()
size_range = size_max - size_min
data_range = data_max - data_min
return ((data - data_min) * size_range / data_range) + size_min
plt.scatter(data[:,0], data[:,1], c=data[:,2], s=scale_size(data[:,3]))
plt.colorbar()
plt.show()
Result:

Categories