Remove white traces around contours from tricontourf - python

I'm trying to make a colormap in Python, and I have everything except this minor annoyance that is making the map look bad.
The code is straightforward. I am just using a matrix of values and plotting them using tricontourf. I am the looping over collections in my plot and changing the edgecolor and linewidth.
What I've noticed is the following. Say I want a thin white line around every contour, then I do.
CS = plt.tricontourf(X,Y,Z, 70, cmap=cm.seismic, antialiased=True)
print CS.collections
for c in CS.collections:
c.set_edgecolor('white')
c.set_linewidth(1)
plt.colorbar()
plt.show()
and get
Now obviously we look at this and say, well, the white lines around the contours look pretty bad, lets get rid of them. You could do this in a number of ways, perhaps by setting the linewidth=0 or the color to 'none'. I'll just do both in the following code. We have
CS = plt.tricontourf(X,Y,Z, 70, cmap=cm.seismic, antialiased=True)
print CS.collections
for c in CS.collections:
c.set_edgecolor('none')
c.set_linewidth(0)
plt.colorbar()
plt.show()
and get
Better, but do you still see the faint outlines of the contours? This is not just a shift in color from the colormap - this is clearly a very light line going through each contour.
Is there a way to somehow blend the colormap so that this doesn't happen? Thanks.

When you save a picture in pdf format, the problem becomes even more visible. With an increase in the number of contours, the picture is smoothed, but there are still problems with pdf.
For example:
import numpy as np
import matplotlib.pyplot as plt
from numpy.random import uniform
x = uniform(-2,2,200)
y = uniform(-2,2,200)
z = x*np.exp(-x**2-y**2)
plt.tricontourf(x,y,z, 300, cmap="seismic", antialiased=False)
plt.colorbar()
plt.savefig('stackoverflow/tricontourf.pdf')
plt.savefig('stackoverflow/tricontourf.png', dpi=300)
But very light lines running through each contour are still visible in pdf.
A partial solution is to use tricontour instead of tricontourf with some linewidths option:
import numpy as np
import matplotlib.pyplot as plt
from numpy.random import uniform
#Some Data
x = uniform(-2,2,200)
y = uniform(-2,2,200)
z = x*np.exp(-x**2-y**2)
plt.tricontour(x,y,z, 300, cmap="seismic", antialiased=False, linewidths=5)
plt.colorbar()
plt.savefig('stackoverflow/tricontour.pdf')
plt.savefig('stackoverflow/tricontour.png', dpi=300)
tricontourf does not support this option.

You can overlap a tricontour plot with the same colormap used in your tricontourf. This will effectively get rid of the white traces.

Related

Python: Delaunay triangle vertices don't "line up" when saved as .pdf or .png

I created a set of Delaunay triangles using scipy.spatial.
from matplotlib import pyplot as plt
import matplotlib
from skimage import io
from scipy.spatial import Delaunay
import numpy as np
from PIL import Image
from scipy.ndimage import rotate
h = 700
w = 700
npts = 500
pts = np.zeros((npts,2))
pts[:,0] = np.random.randint(0,w,npts)
pts[:,1] = np.random.randint(0,h,npts)
tri = Delaunay(pts)
centers = np.sum(pts[tri.simplices], axis=1, dtype='int')/3.0
#plt.figure()
fig, ax = plt.subplots()
plt.xlim(0, w)
plt.ylim(0, h)
for i in range(0,len(pts[tri.simplices])-1):
temp_tri = plt.Polygon(pts[tri.simplices][i], color = colors[i]/256) #colors variable is a numpy.ndarray variable that contains RGB values
plt.gca().add_patch(temp_tri)
plt.gca().set_aspect('equal')
plt.axis('off')
plt.savefig('test.pdf', bbox_inches = 'tight', dpi=fig.dpi)
plt.show()
On the screen, the output is as I intended. However, when I save it as pdf or png, the vertices of Delaunay triangles do not match (they do when I look at them through plt.show())
The picture below is part of the whole picture, just to highlight where the vertices don't match.
Few suggestions I found with issues regarding different images shown with plt.show() and fitsave() said I should match the dpi, which I have done.
Please advise on what I should try. Thank you so much in advance!
I can't tell the reason why it fails to produce the correct points for the triangles, but in general it's probably better to create a PolyCollection from the vertices, instead of individual triangles.
In that case the problem would not appear.
pc = PolyCollection(pts[tri.simplices],
facecolors=np.random.rand(len(pts[tri.simplices]),3),
edgecolor="face",linewidth=0.1)
plt.gca().add_collection(pc)
Here some remaining overlap is seen, which is due to the linewidth. Smaller linewidths would look better in the pdf, e.g. linewidth=0.01, but might result in "white" lines in the graphics on screen. You may play around with that parameter until you are happy with the result.

Shade the area between two axhline using matplotlib

What I'm trying to achieve: a plot with two axhline horizontal lines, with the area between them shaded.
The best so far:
ax.hline(y1, color=c)
ax.hline(y2, color=c)
ax.fill_between(ax.get_xlim(), y1, y2, color=c, alpha=0.5)
The problem is that this leaves a small amount of blank space to the left and right of the shaded area.
I understand that this is likely due to the plot creating a margin around the used/data area of the plot. So, how do I get the fill_between to actually cover the entire plot without matplotlib rescaling the x-axis after drawing? Is there an alternative to get_xlim that would give me appropriate limits of the plot, or an alternative to fill_between?
This is the current result:
Note that this is part of a larger grid layout with several plots, but they all leave a similar margin around these shaded areas.
Not strictly speaking an answer to the question of getting the outer limits, but it does solve the problem. Instead of using fill_between, I should have used:
ax.axhspan(y1, y2, facecolor=c, alpha=0.5)
Result:
ax.get_xlim() does return the limits of the axis, not that of the data:
Axes.get_xlim()
Returns the current x-axis limits as the tuple (left, right).
But Matplotlib simply rescales the x-axis after drawing the fill_between:
import matplotlib.pylab as pl
import numpy as np
pl.figure()
ax=pl.subplot(111)
pl.plot(np.random.random(10))
print(ax.get_xlim())
pl.fill_between(ax.get_xlim(), 0.5, 1)
print(ax.get_xlim())
This results in:
(-0.45000000000000001, 9.4499999999999993)
(-0.94499999999999995, 9.9449999999999985)
If you don't want to manually set the x-limits, you could use something like:
import matplotlib.pylab as pl
import numpy as np
pl.figure()
ax=pl.subplot(111)
pl.plot(np.random.random(10))
xlim = ax.get_xlim()
pl.fill_between(xlim, 0.5, 1)
ax.set_xlim(xlim)

How can I draw transparent lines where the color becomes stronger when they overlap?

When you draw a bunch of transparent lines in matplotlib like this, you get a nice effect; when they overlap they are a bit darker.
from pylab import *
for _ in xrange(1000) :
plot(np.random.randn(2),np.random.randn(2),alpha=0.1,color='k')
show()
It looks like this:
But if you draw one long line like this that overlaps with itself like this, the line doesn't "interact with itself." It looks like this:
I would like to draw a single curve that overlaps with itself, so that the more it overlaps with itself, the darker it becomes. If I use a loop to break up the curve and draw each line segment separately, I get what I want, but I also get ugly and unacceptable artifacts where the line segments meet, making the curve looked like a dotted or dashed line.. Like this:
Is there any nice way to draw a curve so that it becomes darker when it overlaps with itself, but you don't get artifacts like those just described?
When using a loop to break up the curve and draw each line segment separately, you can try to use the solid_capstyle argument to plot. The default is "projecting", but you could try using "butt" and see if it helps.
plt.plot(x,y, alpha=0.1, c="k", solid_capstyle="butt")
This might reduce the effect a little.
import matplotlib.pyplot as plt
import numpy as np
def squiggle_xy(a, b, c, d, i=np.arange(0.0, 2*np.pi, 0.05)):
return np.sin(i*a)*np.cos(i*b), np.sin(i*c)*np.cos(i*d)
x,y = squiggle_xy(2.5, 2, 1, 3)
fig, ax = plt.subplots(ncols=2, figsize=(6,3))
ax[0].set_title("solid_capstyle=\"projecting\"")
ax[1].set_title("solid_capstyle=\"butt\"")
for i in range(len(x)-1):
print x[i:i+2]
ax[0].plot(x[i:i+2], y[i:i+2], alpha=0.1, lw=10, solid_capstyle="projecting", c="b")
ax[1].plot(x[i:i+2], y[i:i+2], alpha=0.1, lw=10, solid_capstyle="butt", c="b")
plt.show()
See this question for a good explanation of solid_capstyle.

Removing wireframe without gaps in matplotlib plot_trisurf

I want to create a smooth cylinder using matplotlib/pyplot. I've adapted a tutorial online and produced the following minimal example:
from numpy import meshgrid,linspace,pi,sin,cos,shape
from matplotlib import pyplot
import matplotlib.tri as mtri
from mpl_toolkits.mplot3d import Axes3D
u,v = meshgrid(linspace(0,10,10),linspace(0,2*pi,20))
u = u.flatten()
v = v.flatten()
x = u
z = sin(v)
y = cos(v)
tri = mtri.Triangulation(u, v)
fig = pyplot.figure()
ax = fig.add_axes([0,0,1,1],projection='3d')
ax.plot_trisurf(x,y,z,triangles=tri.triangles,linewidth=0)
pyplot.show()
which produces a cylinder. I set linewidth=0 to remove the wireframe, however, there is now the "ghost" of the wireframe because the triangulation has (presumably) been spaced assuming the wireframe is there to fill in the gaps. This looks to be specific to plot_trisurf, because there are other 3d plotting examples (e.g., using plot_surface) which set linewidth=0 without these gaps showing up.
Doing an mtri.Triangulation?, it seems like it might not be possible to "perfectly" fill in the gaps, since it states
>Notes
> -----
> For a Triangulation to be valid it must not have duplicate points,
> triangles formed from colinear points, or overlapping triangles.
One partial solution is to just color the wireframe the same shade of blue, but after I've fixed this problem I also want to add a light source/shading on the surface, which would put me back at square one.
Is there a way to make this work? Or can someone suggest a different approach? Thanks for any help.
ax.plot_trisurf(x,y,z,triangles=tri.triangles,linewidth=0, antialiased=False)

Plot a (polar) color wheel based on a colormap using Python/Matplotlib

I am trying to create a color wheel in Python, preferably using Matplotlib. The following works OK:
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
xval = np.arange(0, 2*pi, 0.01)
yval = np.ones_like(xval)
colormap = plt.get_cmap('hsv')
norm = mpl.colors.Normalize(0.0, 2*np.pi)
ax = plt.subplot(1, 1, 1, polar=True)
ax.scatter(xval, yval, c=xval, s=300, cmap=colormap, norm=norm, linewidths=0)
ax.set_yticks([])
However, this attempt has two serious drawbacks.
First, when saving the resulting figure as a vector (figure_1.svg), the color wheel consists (as expected) of 621 different shapes, corresponding to the different (x,y) values being plotted. Although the result looks like a circle, it isn't really. I would greatly prefer to use an actual circle, defined by a few path points and Bezier curves between them, as in e.g. matplotlib.patches.Circle. This seems to me the 'proper' way of doing it, and the result would look nicer (no banding, better gradient, better anti-aliasing).
Second (relatedly), the final plotted markers (the last few before 2*pi) overlap the first few. It's very hard to see in the pixel rendering, but if you zoom in on the vector-based rendering you can clearly see the last disc overlap the first few.
I tried using different markers (. or |), but none of them go around the second issue.
Bottom line: can I draw a circle in Python/Matplotlib which is defined in the proper vector/Bezier curve way, and which has an edge color defined according to a colormap (or, failing that, an arbitrary color gradient)?
One way I have found is to produce a colormap and then project it onto a polar axis. Here is a working example - it includes a nasty hack, though (clearly commented). I'm sure there's a way to either adjust limits or (harder) write your own Transform to get around it, but I haven't quite managed that yet. I thought the bounds on the call to Normalize would do that, but apparently not.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import cm
import matplotlib as mpl
fig = plt.figure()
display_axes = fig.add_axes([0.1,0.1,0.8,0.8], projection='polar')
display_axes._direction = 2*np.pi ## This is a nasty hack - using the hidden field to
## multiply the values such that 1 become 2*pi
## this field is supposed to take values 1 or -1 only!!
norm = mpl.colors.Normalize(0.0, 2*np.pi)
# Plot the colorbar onto the polar axis
# note - use orientation horizontal so that the gradient goes around
# the wheel rather than centre out
quant_steps = 2056
cb = mpl.colorbar.ColorbarBase(display_axes, cmap=cm.get_cmap('hsv',quant_steps),
norm=norm,
orientation='horizontal')
# aesthetics - get rid of border and axis labels
cb.outline.set_visible(False)
display_axes.set_axis_off()
plt.show() # Replace with plt.savefig if you want to save a file
This produces
If you want a ring rather than a wheel, use this before plt.show() or plt.savefig
display_axes.set_rlim([-1,1])
This gives
As per #EelkeSpaak in comments - if you save the graphic as an SVG as per the OP, here is a tip for working with the resulting graphic: The little elements of the resulting SVG image are touching and non-overlapping. This leads to faint grey lines in some renderers (Inkscape, Adobe Reader, probably not in print). A simple solution to this is to apply a small (e.g. 120%) scaling to each of the individual gradient elements, using e.g. Inkscape or Illustrator. Note you'll have to apply the transform to each element separately (the mentioned software provides functionality to do this automatically), rather than to the whole drawing, otherwise it has no effect.
I just needed to make a color wheel and decided to update rsnape's solution to be compatible with matplotlib 2.1. Rather than place a colorbar object on an axis, you can instead plot a polar colored mesh on a polar plot.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import cm
import matplotlib as mpl
# If displaying in a Jupyter notebook:
# %matplotlib inline
# Generate a figure with a polar projection
fg = plt.figure(figsize=(8,8))
ax = fg.add_axes([0.1,0.1,0.8,0.8], projection='polar')
# Define colormap normalization for 0 to 2*pi
norm = mpl.colors.Normalize(0, 2*np.pi)
# Plot a color mesh on the polar plot
# with the color set by the angle
n = 200 #the number of secants for the mesh
t = np.linspace(0,2*np.pi,n) #theta values
r = np.linspace(.6,1,2) #radius values change 0.6 to 0 for full circle
rg, tg = np.meshgrid(r,t) #create a r,theta meshgrid
c = tg #define color values as theta value
im = ax.pcolormesh(t, r, c.T,norm=norm) #plot the colormesh on axis with colormap
ax.set_yticklabels([]) #turn of radial tick labels (yticks)
ax.tick_params(pad=15,labelsize=24) #cosmetic changes to tick labels
ax.spines['polar'].set_visible(False) #turn off the axis spine.
It gives this:

Categories

Resources