Matplotlib Contourf Plots Unwanted Outlines when Alpha < 1 - python

I am using matplotlib in Python 2.7 to plot a filled contour plot. I want to overlay this over an image, so I am using the alpha keyword to make the plot semi-transparent. When I do this, the body of the contours are the correct transparency, but contourf() plots unwanted lines on the boundaries between different levels. I have attempted to eliminate them with the keyword argument linecolor='none', but this has not helped.
Code:
CS = map.contourf(xi, yi, zi, 25, alpha=0.3, linecolor='none')
A link to an image example of the problem; I would like the filled contours to meet without the bright boundary lines:
Any help or insight into this problem is appreciated.

Try turn on antialiased=True:
x, y = np.mgrid[-1:1:100j, -1:1:100j]
contourf(x, y, x**2+y**2 + np.random.rand(100, 100)*0.1, 10, alpha=0.3, antialiased=True)
here is my result:

Related

Matplotlib contourf plots with streamline numbers

I am trying to generate a contour plot with line numbers inside! I used plt.contourf to draw the contour plot and plt.clabel to draw the lines on my contour plot! The numbers in my plot are incorrect as shown in the figure!
Contour plot with lines and wrong numbers
Contour lines with correct numbers
X = Data3.iloc[:,0].drop_duplicates()
Y = Data3.iloc[:,1].drop_duplicates()
Z = Data3.pivot('Battery capacity (kWh)','Solar capacity (kW)', 'Diesel electricity generation
(kWh)')
plt.figure(figsize=(7, 5))
contours = plt.contourf(X, Y, Z, 10, cmap='viridis', alpha=0.8 )
plt.colorbar();
plt.clabel(contours, inline = True, fontsize=8, fmt='%d', colors = 'black')
plt.xlabel('Solar Capacity (kW)',fontsize = 13) # x-axis label with fontsize 12
plt.ylabel('Battery Capacity (kWh)',fontsize = 12) # y-axis label with fontsize 12
plt.title('Diesel Electricity Generation (% of total generation)',fontsize = 15)
plt.scatter(x=(233*1.83*0.16), y=250, color = 'r', marker='o')
I also used plt.contour and plt.clabel, the numbers were placed correctly! How can I draw lines on plt.contourf without mixing the line numbers?
this question is almost a MRE. It would be helpful if it was because then I'd be able to copy and paste this code and run it on my computer. The only thing that's missing is the definitions for X,Y and Z, so I made a version of the question that is reproducible where it tries to graph a simpler contour plot:
import numpy as np
import itertools
from matplotlib import pyplot as plt
# Initialize the contour map data as a multiplication table
X= np.arange(10)
Y= np.arange(10)
Z= np.zeros((10, 10))
for i in range(10):
for j in range(10):
Z[i][j] = i*j
# Rest of example:
contours = plt.contour(X, Y, Z, 10, cmap='viridis', alpha=0.8 )
plt.colorbar();
plt.clabel(contours, inline = True, fontsize=8, fmt='%d', colors = 'black')
plt.xlabel('Solar Capacity (kW)',fontsize = 13) # x-axis label with fontsize 12
plt.ylabel('Battery Capacity (kWh)',fontsize = 12) # y-axis label with fontsize 12
plt.title('Diesel Electricity Generation (% of total generation)',fontsize = 15)
# Change location of single red point:
plt.scatter(x=5, y=7, color = 'r', marker='o')
When I run my example though, the contour labels show up correctly.
I'm wondering if this problem has to do with the input data in the Data3 variable.
Edit: I tried plotting this data with contourf to match the original question with contours = plt.contourf(X, Y, Z, 10, cmap='viridis', alpha=0.8 )
and I now see the problems with the contour labels:
I tried playing with all of the options to clabels and couldn't come up with something that outputs something suitable.
I suspect this is a bug with contourf. I couldn't find a bug report for this, so would you be comfortable with opening a bug ticket here in matplotlib?
In the short term, I suppose you could work around this by using contour() to plot. Then, if the plot really needs filled contours, my best idea is to fill them in manually with MS Paint or something -- but that's not a very good idea at all.

How to make an inset plot with mollweide projection?

I want to make a skymap using the Mollweide projection for a main set of axes and for an inset axes. This is easy for the main axes but not for the inset. I've tried a few different things but it doesn't work for the inset. Please help!
Here you can find the latitude and longitude data, and here you can find the sky location probability density data.
First, I make the main plot:
xmin = min(l)
xmax = max(l)
ymin = min(b)
ymax = max(b)
X, Y = np.meshgrid(np.linspace(xmin, xmax, 100), np.linspace(ymin, ymax, 100))
mpl.rcParams["text.usetex"] = True
fig = plt.figure(1)
fig.set_figheight(8)
fig.set_figwidth(8)
ax = plt.axes(projection='mollweide')
ax.grid()
# skypost is the sky location probability-density data accessible above
plt.contour(X, Y, skypost, colors='blue', levels=[5, 50, 95])
which works fine. Next, I define the inset axes and plot the contours, however there seems to be no way that completely works for this. What I want is for the inset to zoom-in on the contours while keeping the mollweide projection. I've tried to do as the example on ligo.skymaps, i.e.,
axesinset = plt.axes(
[0.0, 0.2, 0.25, 0.25],
projection='astro degrees zoom',
center='110d +20d',
radius='10 deg' )
plt.sca(axesinset)
axesinset.contour(X, Y, skypost, colors='blue', levels=[5, 50, 95])
axesinset.grid()
but this doesn't work since the contours don't even appear! I don't understand why they don't appear. I also do not understand why the x-axis of the inset is backwards?
Instead, I've tried just plotting a new mollweide projection in the inset, and restricting the xlim and ylim, but it says these options are not supported for the mollweide projection. Is there a way around this to restrict the axes limits?
Lastly, I've tried just doing a regular inset without the mollweide, which works, however the shape of the contours are distorted relative to the contours on the main mollweide plot which is physically relevant for my case. So this is very sub-optimal.
Any suggestions and advice are greatly appreciated.
To have the axis in the correct way, you can rotate the subplot by using rotate.
Concerning the fact that your contour are not shown, it is probably because you have to add the transform keyword. If you don't specify it, it is plotted in pixel coordinates by default (https://docs.astropy.org/en/stable/visualization/wcsaxes/overlays.html).
The example below shows that the desired point (in blue) is obtained by adding ax.get_transform("world").
The blue and green points are in the lower right corner because of the rotate.
I guess that it should be the same for contour.
ax = plt.subplot(111, projection='geo degrees zoom',
center="0d - 0d", radius='10 deg', rotate='180 deg')
ax.grid()
ax.set_xlabel(r"$\phi \, [deg]$")
ax.set_ylabel(r"$\theta \, [deg]$")
ax.scatter(0,0, color = "blue")
ax.scatter(100,0, color = "green")
ax.scatter(0,0, color = "red", transform = ax.get_transform("world"))
I'm a bit late to the party, but I thought its worth mentioning that I've created a nice inset-map functionality for EOmaps...
It lets you create inset-maps in arbitrary projections and you can add whatever features you want!
from eomaps import Maps
m = Maps(Maps.CRS.Mollweide())
m.add_feature.preset.coastline()
# create a rectangular inset-map that shows a 5 degree rectangle
# centered around a given point
inset = m.new_inset_map(xy=(6, 43), xy_crs=4326,
radius=5, radius_crs=4326,
inset_crs=Maps.CRS.Mollweide(),
shape="rectangles")
inset.add_feature.preset.coastline()
inset.add_feature.preset.ocean()
inset.add_feature.cultural_10m.urban_areas(fc="r", ec="none")
m.apply_layout(
{'0_map': [0.01, 0.17333, 0.98, 0.65333],
'1_map': [0.05, 0.11667, 0.43341, 0.76667]})

Contour on color map: not continuous (Python)

I made some color maps in python. On top of them I wanted to add some continental contours, using the land-sea mask provided in the model I run. It consists of just 1 or 0, 1 for land and 0 for no-land.
There is some strange characters written into the contour plot. Does anyone here knows how I could get the contour to connect to itself so it's smooth instead of being broken with those small strange characters in between the ends of each line?
Here is what the figure looks like:
And here is a piece of the code (note this map was part of a plot containing other maps, so this is the map of index 9).
lsmfile = netcdf.netcdf_file("/Volumes/LaCie/Plasim/Earth2/high/1367/SOL1367earth050.nc","r")
lat = lsmfile.variables["lat"].data
lon = lsmfile.variables["lon"].data
mask = lsmfile.variables["lsm"].data
mask = mask[0]
cmap = plt.get_cmap('bwr')
fig, ax = plt.subplots(nrows=5,ncols=2,figsize=(16,14))
im9 = ax.flat[9].pcolormesh(lon, lat, surfalbearth, cmap=cmap,norm=norm)
fig.colorbar(im9, ax=ax.flat[9])
ax.flat[9].set_xlim(xmin=0, xmax=355)
ax.flat[9].set_ylim(ymin=-86, ymax=86)
CS = plt.contour(lon,lat,mask, 1,colors='k')
plt.clabel(CS, fontsize=3, inline=1)
fig.tight_layout()
plt.savefig('Maps')
plt.show()
It seems you have asked for having those contour labels (clabel) in your plot by using the line
plt.clabel(CS, fontsize=3, inline=1)
So if you remove that line, the contour labels should disappear.

How to change marker border width and hatch width?

In this example of a marker from my scatter plot I have set the color to green, and edge color to black, and hatch to "|". For the hatch pattern to show up at all I must set the edgecolor, however when I do, I get a very thick border around the marker. Two questions:
1) How can I to set the size of this border (preferably to 0)?
2) How can I increase the thickness of the hatch lines?
You just need to set the linewidth to control the marker border thickness.
You can increase the density of hatching, by repeating symbols (in the example below, the '|' is repeated in the R/H pane; note that to obtain NW->SE diagonal lines the symbol must be escaped so needs twice as many characters to really double it -- '\\\\' is density 2 while '||||' is density 4). However, I don't think the thickness of individual lines within hatching is controllable.
See the code example below to produce scatter plots such as these:
import matplotlib.pyplot as plt
# generate some data
x = [1,2,3,4,5,8]
y= [i**2 for i in x]
y2= [60-i**2+3*i for i in x]
# plot markers with thick borders
plt.subplot(121)
plt.scatter(x,y, s=500, marker='s', edgecolor='black', linewidth=3, facecolor='green', hatch='|')
# compare with no borders, and denser hatch.
plt.subplot(122)
plt.scatter(x,y2, s=500, marker='s', edgecolor='black', linewidth=0, facecolor='green', hatch='||||')
plt.show()
matplotlib documentation on collections
and scatter.
This is several years after you asked the question, but the only way I've found to do it is to change the matplotlib.rc.
You can do this either in the actual .rc file or within your python script, e.g.
import matplotlib as mpl
mpl.rc('hatch', color='k', linewidth=1.5)
This will make all of the hatch lines in your script black and thickness 1.5 rather than the default 1.0.
If you're using plt.plot, you'll want to pass argument markeredgewidth.

How to do a scatter plot with empty circles in Python?

In Python, with Matplotlib, how can a scatter plot with empty circles be plotted? The goal is to draw empty circles around some of the colored disks already plotted by scatter(), so as to highlight them, ideally without having to redraw the colored circles.
I tried facecolors=None, to no avail.
From the documentation for scatter:
Optional kwargs control the Collection properties; in particular:
edgecolors:
The string ‘none’ to plot faces with no outlines
facecolors:
The string ‘none’ to plot unfilled outlines
Try the following:
import matplotlib.pyplot as plt
import numpy as np
x = np.random.randn(60)
y = np.random.randn(60)
plt.scatter(x, y, s=80, facecolors='none', edgecolors='r')
plt.show()
Note: For other types of plots see this post on the use of markeredgecolor and markerfacecolor.
Would these work?
plt.scatter(np.random.randn(100), np.random.randn(100), facecolors='none')
or using plot()
plt.plot(np.random.randn(100), np.random.randn(100), 'o', mfc='none')
Here's another way: this adds a circle to the current axes, plot or image or whatever :
from matplotlib.patches import Circle # $matplotlib/patches.py
def circle( xy, radius, color="lightsteelblue", facecolor="none", alpha=1, ax=None ):
""" add a circle to ax= or current axes
"""
# from .../pylab_examples/ellipse_demo.py
e = Circle( xy=xy, radius=radius )
if ax is None:
ax = pl.gca() # ax = subplot( 1,1,1 )
ax.add_artist(e)
e.set_clip_box(ax.bbox)
e.set_edgecolor( color )
e.set_facecolor( facecolor ) # "none" not None
e.set_alpha( alpha )
(The circles in the picture get squashed to ellipses because imshow aspect="auto" ).
In matplotlib 2.0 there is a parameter called fillstyle
which allows better control on the way markers are filled.
In my case I have used it with errorbars but it works for markers in general
http://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.errorbar.html
fillstyle accepts the following values: [‘full’ | ‘left’ | ‘right’ | ‘bottom’ | ‘top’ | ‘none’]
There are two important things to keep in mind when using fillstyle,
1) If mfc is set to any kind of value it will take priority, hence, if you did set fillstyle to 'none' it would not take effect.
So avoid using mfc in conjuntion with fillstyle
2) You might want to control the marker edge width (using markeredgewidth or mew) because if the marker is relatively small and the edge width is thick, the markers will look like filled even though they are not.
Following is an example using errorbars:
myplot.errorbar(x=myXval, y=myYval, yerr=myYerrVal, fmt='o', fillstyle='none', ecolor='blue', mec='blue')
Basend on the example of Gary Kerr and as proposed here one may create empty circles related to specified values with following code:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.markers import MarkerStyle
x = np.random.randn(60)
y = np.random.randn(60)
z = np.random.randn(60)
g=plt.scatter(x, y, s=80, c=z)
g.set_facecolor('none')
plt.colorbar()
plt.show()
So I assume you want to highlight some points that fit a certain criteria. You can use Prelude's command to do a second scatter plot of the hightlighted points with an empty circle and a first call to plot all the points. Make sure the s paramter is sufficiently small for the larger empty circles to enclose the smaller filled ones.
The other option is to not use scatter and draw the patches individually using the circle/ellipse command. These are in matplotlib.patches, here is some sample code on how to draw circles rectangles etc.

Categories