Setting a colormap in ax.scatter - python

I am trying to create a scatter plot with the positions of particles from an sph simulation and I want to color the based on the density of each particle. I have created my colorbar for my colormap and I tried to do that but it only works if i set vmax to be 10**-4 of the larger value of my densities. If i try to go vmin=np.amin(density) and vmax=np.amax(density) I get only one color, representing the lowest value of my cmap. I attach the code so that you can see exactly what I did.
# Importing modules and libraries
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.colors import ListedColormap, LinearSegmentedColormap
import os
import matplotlib.ticker as ticker
# I load my files
path=os.path.abspath('BOXSINK.column.tmp')
file= np.loadtxt(path,skiprows=4)
position=file[::,0:3]
velocity=file[::,3:6]
density=file[::,8]
x=position[::,0]
y=position[::,1]
z=position[::,2]
# This is a function to format the ticks of the colorbar to scientific
def fmt(x, pos):
a, b = '{:.1e}'.format(x).split('e')
b = int(b)
return r'${} \times 10^{{{}}}$'.format(a, b)
# constract colorbar
n_lines=density[density!=0].size
cmap = mpl.cm.get_cmap('Wistia', n_lines)
c = density[density!=0]
norm = mpl.colors.Normalize(vmin=c.min(), vmax=c.max())
cmap = mpl.cm.ScalarMappable(norm=norm, cmap=mpl.cm.Wistia)
cmap.set_array([])
# plot
fig=plt.figure()
ax=fig.add_axes([0,0,1,1])
c=(density)
print(np.amax(density))
psm = ax.scatter(x, y, s=0.05, alpha=0.6, c=c,cmap='Wistia', vmin=0, vmax=6e-21)
ax.scatter(x[density==0],y[density==0], s=1, alpha=1, c='Black')
l=fig.colorbar(cmap, format=ticker.FuncFormatter(fmt), label='density [g cm-3]')
l.set_label('density [g cm-3]',labelpad=15, weight='bold')
ax.set_xlabel('x [pc]', weight='bold')
ax.set_ylabel('y [pc]', weight='bold')
fig.set_size_inches(8, 6)
plt.show()
First (almost correct) plot
The issue is that 6e-21 is close to the median of my values, with my maximum density value being 3.5e-16. But if I set vmax=np.amax(density) I get this plot
Plot with the values ranging for min and max of the density array
I would settle for the first plot but I need it to be publication ready and thus, accurate and I am feeling like I am not sampling the densities correctly with it.

Related

Python - setting arbitrary contour xy-ratio

I am reading the following discussion:
setting axis scale in matplotlib contour plot
From the discussion above, to get arbitrary ratio, we could use
plt.figure(figsize=(8,2))
# ...
plt.tight_layout()
However, this setting is for figure not for contourf.
I used the above codes in my codes
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
import pandas as pd
import math
rm = pd.read_excel("test_3d.xlsx", header = None)
# find min values of noise
rec = np.shape(rm)
# grid
X = np.arange(1,rec[1]+1,1)
Y = np.arange(1,rec[0]+1,1)
x , y = np.meshgrid(X,Y)
# plots
plt.clf()
con = plt.contourf(x,y,rm, cmap=cm.jet)
plt.figure(figsize=(8,2))
plt.tight_layout()
plt.title('2457MHz')
plt.show()
The result I got is
The ratio of bottom plot is what I want; however, I use plt.figure(figsize=(8,2)), which is not for contourf. Therefore, I did not get the correct result.
Is there any way that I can plot arbitrary ratio for contourf?
Instead of setting the figsize, use Axes.set_aspect to change the aspect ratio of the contour plot's Axes:
fig, ax = plt.subplots()
ax.contourf(x, y, rm, cmap='viridis')
ax.set_aspect(0.25)
If you prefer to stick with the plt syntax, access the Axes using plt.gca:
plt.contourf(x, y, rm, cmap='viridis')
plt.gca().set_aspect(0.25)

Customize Seaborn Pair Grid

I'm trying to use Seaborn Pair Grid to make a correlogram with scatterplots in one half, histograms on the diagonal and the pearson coefficient on the other half. I've managed to put together the following code which does what I need, but I'm really struggling with further customization
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import pearsonr
df = sns.load_dataset('iris')
def reg_coef(x,y,label=None,color=None,**kwargs):
ax = plt.gca()
r,p = pearsonr(x,y)
ax.annotate('{:.2f}'.format(r), xy=(0.5,0.5), xycoords='axes fraction', ha='center',fontsize=30,
bbox={'facecolor': 'red', 'alpha': 0.5, 'pad': 20})
ax.set_axis_off()
sns.set(font_scale=1.5)
sns.set_style("white")
g = sns.PairGrid(df)
g.map_diag(plt.hist)
g.map_lower(plt.scatter)
g.map_upper(reg_coef)
g.fig.subplots_adjust(top=0.9)
g.fig.suptitle('Iris Correlogram', fontsize=30)
plt.show()
This is the result
What I'd like to do:
Change the font used for the whole plot and assign my own defined rgb colour to the font and axes (same one)
Remove the X & Y tick labels
Change the colour of the scatter dots and histogram bars to my own defined rgb colour (same one)
Set a diverging colour map for the background of the pearson number to highlight the degree and type of correlation, again using my own defined rgb colours.
I know Im asking a lot but Ive spent hours going round in circles trying to figure this out!!
The color can be set as extra parameter in g.map_diag(plt.hist, color=...) and
g.map_lower(plt.scatter, color=...). The function reg_coef can be modified to take a colormap into account.
The font color and family can be set via the rcParams. The ticks can be removed via plt.setp(g.axes, xticks=[], yticks=[]). Instead of subplot_adjust, g.fig.tight_layout() usually fits all elements nicely into the plot. Here is an example:
import seaborn as sns
import matplotlib.pyplot as plt
from scipy.stats import pearsonr
def reg_coef(x, y, label=None, color=None, cmap=None, **kwargs):
ax = plt.gca()
r, p = pearsonr(x, y)
norm = plt.Normalize(-1, 1)
cmap = cmap if not cmap is None else plt.cm.coolwarm
ax.annotate(f'{r:.2f}', xy=(0.5, 0.5), xycoords='axes fraction', ha='center', fontsize=30,
bbox={'facecolor': cmap(norm(r)), 'alpha': 0.5, 'pad': 20})
ax.set_axis_off()
df = sns.load_dataset('iris')
sns.set(font_scale=1.5)
sns.set_style("white")
for param in ['text.color', 'axes.labelcolor', 'xtick.color', 'ytick.color']:
plt.rcParams[param] = 'cornflowerblue'
plt.rcParams['font.family'] = 'cursive'
g = sns.PairGrid(df, height=2)
g.map_diag(plt.hist, color='turquoise')
g.map_lower(plt.scatter, color='fuchsia')
g.map_upper(reg_coef, cmap=plt.get_cmap('PiYG'))
plt.setp(g.axes, xticks=[], yticks=[])
g.fig.suptitle('Iris Correlogram', fontsize=30)
g.fig.tight_layout()
plt.show()

No color when I make python scatter color plot using third variable to define color

I try to make colorful scatter plot using third variable to define color. It is simple to use the following code:
plt.scatter(mH, mA, s=1, c=mHc)
plt.colorbar()
plt.show()
But I do not have many choices to modify the frame of the plot. I am trying the following code to make colorful scatter plot, at the same time I try to optimize the frame of the plot:
import numpy as np
import math
from matplotlib import rcParams
import matplotlib.pyplot as plt
from matplotlib.ticker import AutoMinorLocator
fig, ax = plt.subplots()
cax = ax.scatter(mH,mA,s=0.5,c=mHc) ### mH, mA, mHC are the dataset
fig.colorbar(cax)
minor_locator1 = AutoMinorLocator(6)
minor_locator2 = AutoMinorLocator(6)
ax.xaxis.set_minor_locator(minor_locator1)
ax.yaxis.set_minor_locator(minor_locator2)
ax.tick_params('both', length=10, width=2, which='major')
ax.tick_params('both', length=5, width=2, which='minor')
ax.set_xlabel(r'$m_H$')
ax.set_ylabel(r'$m_A$')
ax.set_xticks([300,600,900,1200,1500])
ax.set_yticks([300,600,900,1200,1500])
plt.savefig('mH_mA.png',bbox_inches='tight')
plt.show()
But the plot I got is black-white. It looks like the problem lies in the marker size argument, but I do not have much idea how to correct it. I want to have smaller marker size. Anyone can offer me some idea to approach this issue. Thanks.
size=0.5 is extremely small - probably all you are seeing is the marker outlines. I would suggest you increase the size a bit, and perhaps pass edgecolors="none" to turn off the marker edge stroke:
import numpy as np
from matplotlib import pyplot as plt
n = 10000
x, y = np.random.randn(2, n)
z = -(x**2 + y**2)**0.5
fig, ax = plt.subplots(1, 1)
ax.scatter(x, y, s=5, c=z, cmap="jet", edgecolors="none")
You might also want to experiment with making the points semi-transparent using the alpha= parameter:
ax.scatter(x, y, s=20, c=z, alpha=0.1, cmap="jet", edgecolors="none")
It can be difficult to get scatter plots to look nice when you have such a massive number of overlapping points. I would be tempted to plot your data as a 2D histogram or contour plot instead, or perhaps even a combination of a scatter plot and a contour plot:
density, xe, ye = np.histogram2d(x, y, bins=20, normed=True)
ax.hold(True)
ax.scatter(x, y, s=5, c=z, cmap="jet", edgecolors="none")
ax.contour(0.5*(xe[:-1] + xe[1:]), 0.5*(ye[:-1] + ye[1:]), density,
colors='k')

Adding Radial Axis Label in matplotlib

I am making a polar scatter plot for a college project with matplotlib and I can't find out how to add a label to the radial axis. Here is my code ( I left out the data because it was read out of a csv)
import matplotlib.pyplot as plt
ax = plt.subplot(111, polar=True)
ax.set_rmax(1)
c = plt.scatter(theta, radii)
ax.set_title("Spread of Abell Cluster Supernova Events as a Function of Fractional Radius", va='bottom')
ax.legend(['Supernova'])
plt.show()
(My plot looks like this. I can't seem to find any straight forward method to do it. Has anyone dealt with this before and have any suggestions?
I don't know of a built in way to do it, but you could use ax.text to make your own. You can get the position of the radial tick labels using ax.get_rlabel_position(), and the mid point of the radial axis using ax.get_rmax()/2.
For example, here's your code (with some random data):
import matplotlib.pyplot as plt
import numpy as np
theta=np.random.rand(40)*np.pi*2.
radii=np.random.rand(40)
ax = plt.subplot(111, polar=True)
ax.set_rmax(1)
c = plt.scatter(theta, radii)
ax.set_title("Spread of Abell Cluster Supernova Events as a Function of Fractional Radius", va='bottom')
ax.legend(['Supernova'])
label_position=ax.get_rlabel_position()
ax.text(np.radians(label_position+10),ax.get_rmax()/2.,'My label',
rotation=label_position,ha='center',va='center')
plt.show()
And here's the output:
I'd be interested to see if there's a more elegant solution, but hopefully this helps you.
from pylab import *
N = 150
r = 2*rand(N)
theta = 2*pi*rand(N)
area = 200*r**2*rand(N)
colors = theta
ax = subplot(111, polar=True)
c = scatter(theta, r, c=colors, s=area, cmap=cm.hsv)
c.set_alpha(0.75)
ax.set_ylabel('Radius', rotation=45, size=11)
show()
A slightly different method from #tom. This uses directly the plt.legend option.
Example:
import matplotlib.pyplot as plt
import numpy as np
theta=np.random.rand(40)*np.pi*2.
radii=np.random.rand(40)
ax = plt.subplot(111, polar=True)
ax.set_rmax(1)
c = plt.scatter(theta, radii,label='Supernova')
ax.set_title("Spread of Abell Cluster Supernova Events as a Function of Fractional Radius", va='bottom')
ax.legend(loc='lower right', scatterpoints=1)
plt.show()
You can change lower right to upper right or even to best to leave the alignment of the legend to matplotlib.

Align color fields and labels in discrete colorbar

I am trying to create a filled contour plot with discrete contour levels which I need to control in order to compare values from different data sources. I thought that this should be easily accomplished with fig.colorbar(c, ax=ax, ticks=my_levels). However, as you can see from the example below, something goes wrong with the alignment of colors and values, and I haven't been able to figure out what is wrong with my code.
Here is the code:
# -*- coding: cp1252 -*-
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.ticker import ScalarFormatter
import numpy as np
def plot_discrete(x, y, data, cmax, nclevel=11):
"""Plot filled contour plot and add colorbar with discrete (linear) spacing"""
matplotlib.rcParams.update({'font.size' : 22})
# prepare plot
fig = plt.figure(figsize=(10,7), dpi=150)
fig.suptitle(unicode("Test ÄÖÜßäöü","latin-1"), fontsize=20, fontweight='bold')
ax = fig.add_subplot(1,1,1)
# determine contour levels and set color scale (norm)
clevel = np.linspace(0., cmax, nclevel)
print "clevel = ", clevel
print "cmax, max(data) = ", cmax, np.max(data)
norm = matplotlib.colors.BoundaryNorm(clevel, ncolors=256, clip=False)
# generate the contour plot
c = ax.contourf(x, y, data, level=clevel, norm=norm)
# prep up axis formatting and labelling
ax.set_xlabel('X',{'fontsize':20})
ax.set_ylabel('Y',{'fontsize':20})
ax.xaxis.set_major_formatter(ScalarFormatter())
ax.yaxis.set_major_formatter(ScalarFormatter())
# add the colorbar
fig.colorbar(c, ax=ax, norm=norm, ticks=clevel, boundaries=clevel)
plt.show()
if __name__ == "__main__":
x = np.linspace(0.,10.,20)
y = np.linspace(-10.,10.,21)
data = np.zeros((x.size, y.size))
for i,xx in enumerate(x):
for j,yy in enumerate(y):
data[i,j] = np.sqrt(xx)*yy**2
plot_discrete(y, x, data, 360.)
After some digging I believe I have discovered your issue:
The line
c = ax.contourf(x, y, data, level=clevel, norm=norm)
should have level as levels which means it now sees the proper argument and uses your user defined levels!

Categories