Matplotlib hatching with circles: How to suppress display of "truncated" circles? - python

By default, Matplotlib "clips" or "truncates" circles (or other symbols) displayed as hatch overlay, as illustrated in the example figure created with this code.
import numpy as np
from matplotlib import pyplot as plt
n = 20
sig = np.ma.masked_greater(np.random.rand(n,n), 0.25)
f, ax1 = plt.subplots(1,1, figsize=(4,4))
ax1.pcolor(sig, hatch="o", alpha=0)
I understand why this is so, but in some of my applications, I would like to solely display "untruncated" symbols. In the below example, I tried ax1.pcolor(sig, hatch="o", alpha=0, clip_on=False), but it has no effect.
How is this possible?
And: I there any option to control that "truncated" symbols are either (a) not shown at all or (b) as complete symbols?

Related

Rightmost part of axes disappears in Matplotlib PostScript figure

I'm creating a Matplotlib figure, which I need to be quite wide (174 mm) and in .eps format. I also need it to be created with LaTeX for consistency with other figures. The problem is that the rightmost parts of the axes do not appear in the output figure, and the legend's box and handles also disappear.
The problem appears only if the figure if very wide, when I use LaTeX to produce it, and when I save it in .eps. The figure is as expected if it is thinner, if I save it in .pdf or .png, or if I just replace plt.savefig(...) with plt.show() and use Matplotlib's default viewer.
To be clearer, consider the following code.
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
x = np.linspace(-1, 1, 100)
y = np.exp(x)
mpl.rcParams['text.usetex'] = True
mm = 1/25.4
fig = plt.figure(figsize=(174*mm, 44*mm))
plt.plot(x, y, label='exponential')
plt.legend(loc='lower right')
plt.tight_layout()
plt.savefig('test.eps')
This outputs the following figure, where the legend handle and the rightmost part of the axes do not appear.
If it can help, the .eps file output by the above code is available here.

Changing the default seaborn heatmap plots

I have exported a large Matrix from Matlab to a data.dat file, which is tab delimited. I am importing this data into a iPython script to use seaborn to create a heatmap of the matrix using the following MWE:
import numpy as np
import seaborn as sns
import matplotlib.pylab as plt
uniform_data = np.loadtxt("data.dat", delimiter="\t")
ax = sns.heatmap(uniform_data, linewidth=0.0)
plt.show()
This code runs fine and outputs a correct heatmap, generating the following output:
How can I change the style of this output? Specifically, I would like to change the colour scheme and also have the fonts in LaTeX form. This is since I would like to export this output as a .pdf file and import into a LaTeX document.
You can control the color scheme with the cmap key of sns.heatmap(). See here for what color maps are available.
Generally to make all fonts in a plot look like latex fonts you can do
sns.set(rc={'text.usetex': True})
What it does is adding a $ around each text object in the plot to allow the underlying tex environment to "tex-ify" it. This works fine for the colorbar but, as you can see here, there seems to be a (to me still unresolved bug) making it not working for axes ticklabels. As a workaround you can manually add $ around all tick labels to allow the tex interpreter recognize it as tex again à la
# Collecting all xtick locations and labels and wrapping the labels in two '$'
plt.xticks(plt.xticks()[0], ['$' + label._text + '$' for label in plt.xticks()[1]])
So for demonstrating your example
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
# Use tex for all labels globally in the plot
sns.set(rc={'text.usetex': True})
uniform_data = np.random.rand(30, 30)
# Adjust colormap with the cmap key (here 'cubehelix')
ax = sns.heatmap(uniform_data, linewidth=0.0, cmap='cubehelix')
# workaround wrap '$' around tick labels for x and y axis
# commenting the following two lines makes only the colorbar in latex font
plt.xticks(plt.xticks()[0], ['$' + label._text + '$' for label in plt.xticks()[1]])
plt.yticks(plt.yticks()[0], ['$' + label._text + '$' for label in plt.yticks()[1]])
plt.show()
leads to

Hatching frequency on plots

Suppose I have the following script that produces a plot (as shown below) where some datapoints have hatching. At DPI = 200, the hatching frequency (space between dots) is good, but if I want to increase the resolution of the plot (DPI = 600 for example), the dots become very fine. Is there a way to set the gap between dots? Thanks in advance.
import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.basemap import Basemap
Sig = np.random.rand(50,50)
Sig = np.ma.masked_greater(Sig, 0.25)
f, ax1 = plt.subplots(1,1)
ax1.pcolor(np.linspace(0,90,50),np.linspace(0,50,50),Sig, hatch=".",alpha=0)
fig = plt.gcf()
fig.set_size_inches(8, 8)
fig.savefig('Trial.png',bbox_inches='tight', dpi=200)
There is no way to accurately control the spacing between hatch patterns. You do have the option to increase the hatch density though. Instead of hatch = "." you can add the symbol more often, hatch="..."; this will produce a denser pattern.
The above figure has been produced with standard dpi of 100.
Changing the dpi to 300 gives the following image:
As can be seen the issue of a changed hatch density for different dpi is not there anymore; it has been brought up in this issue and afterwards been fixed. The solution is thus to update to the newest matplotlib version.

Is it possible to control matplotlib in predictable way?

The following code behaves absolutely ununderstandable for me:
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
img=mpimg.imread('stinkbug.png')
imgplot = plt.imshow(img)
circle = plt.Circle((0, 0), radius=100, fc='y')
plt.figure(0)
#plt.show(imgplot)
plt.show(circle)
It displays two figures, although no only one show() function called.
It displays stinkbug in figure, although imgplot was never shown.
It does not display circle, although circle was shown.
You are telling matplotlib to do the following:
Load an image (... so far so good)
Create a figure displaying the image (Figure 1 by default) (... so far so good)
Create a patch object that represents a circle. This is not associated with any axes or anything where it could be drawn.
Create an empty Figure 0. Why? We may never know.
Call plt.show() with a patch as an argument. Because matplotlib is being nice, it ignores this argument and just displays the two figures as predicted.
Some Notes
Patch objects are just representations of a shape. You have to plot them somewhere for them to work.
plt.show() just displays all the figures if you are not in interactive mode.
A Solution
Given all that, here is what I think you were trying to do:
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
img = mpimg.imread('stinkbug.png')
circle = plt.Circle((0, 0), radius=100, fc='y')
fig, ax = plt.subplots()
ax.imshow(img)
ax.add_artist(circle)
fig.show()
subplots creates both a figure and axes for you. You can then use ax.imshow to display the image and ax.add_artist to display the circle. fig.show() and plt.show() are identical in this case.

Zigzag or wavy lines in matplotlib

Is there an easy way to draw a zigzag or wavy line in matplotlib?
I'm aware of the different line styles (http://matplotlib.org/examples/lines_bars_and_markers/line_styles_reference.html), and I'm of course aware that instead of plotting
plt.figure(); plt.plot(n.linspace(0.7,1.42,100),[0.7]*100)
I could plot
plt.figure(); plt.plot(n.linspace(0.7,1.42,100),[0.69,0.71]*50)
for a zigzag-line, but I was wondering whether there was a more straightforward way?
Yes there is, but it comes with a little bit of fallout. The easiest way is to use the xkcd mode in matplotlib.
import numpy as np
import matplotlib.pyplot as plt
plt.xkcd()
plt.figure()
plt.plot(np.linspace(0.7,1.42,100),[0.7]*100)
plt.show()
Which gives you the following:
If you take a look at the code used to achieve this you will find that the xkcd function makes some changes to the rcParams dictionary. Most notably the entry rcParams['path.sketch'] = (scale, length, randomness) which is a path effect that is able to simulate a hand drawn look. The default parameters used by xkcd style are:
# explanation from the docstring of the xkcd function
scale = 1 # amplitude of the wiggle
length = 100 # length of the wiggle along the line
randomness = 2 # scale factor for shrinking and expanding the length
You can change the entries in the rcParams dictionary if you import it from the matplotlib package. In the following example I increased the randomness value from 2 to 100:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rcParams
rcParams['path.sketch'] = (1, 100, 100)
plt.plot(np.linspace(0.7,1.42,100),[0.7]*100)
plt.show()
Which will result in the following plot:
As you can see, more jiggling and the font used for the ticks is still 'normal'. However, the style is also used to draw the axes and so far I have not found a way around that.
Two workarounds could be:
Work without drawn borders/ spines.
Plot spines and line independently (hard and annoying to automize).
Dig through the documentation of matplotlib and path styles and find out if there is a way to set path styles only for a subset of drawn lines.
Option 1 can be achieved like this:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rcParams
rcParams['path.sketch'] = (10, 10, 100)
fig = plt.plot(np.linspace(0.7,1.42,100),[0.7]*100)
for pos, spine in fig[0].axes.spines.items():
spine.set_visible(False)
plt.show()
Which, in my opinion look quite ok. borders around plots are highly overrated anyways.
Edit: Less Chaos
To get an evenly waved line, set the randomness parameter to 1 and pick small values for amplitude and length:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rcParams
rcParams['path.sketch'] = (3, 10, 1)
fig = plt.plot(np.linspace(0.7,1.42,100),[0.7]*100)
for pos, spine in fig[0].axes.spines.items():
spine.set_visible(False)
plt.show()
Bonus image: More Chaos
rcParams['path.sketch'] = (100, 1, 100)
You can apply the change in rcParams['path.sketch'] dictionary only to selected curves using with.
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
# prepare some fancy data
x = np.linspace(0,5,200)
y_0 = 10*x**0.2-x**1.5
y_1 = 20*np.sin(x)
y_2 = x**2
# prepare figure and axis
fig, ax = plt.subplots(nrows=1, ncols = 1, figsize = (5,3), dpi = 128)
# plot with some normal style
ax.plot(x, y_0, color = 'gray', ls='-.', lw = 2, label = 'normal style')
# now plot the wavy-like style!!!!
with mpl.rc_context({'path.sketch': (5, 15, 1)}):
ax.plot(x, y_1, color = 'blue', label = 'wavy style!')
# again plot with some different normal style
ax.plot(x, y_2, color = 'orange', ls = '-', lw = 3, label = 'again normal style')
ax.legend(loc='best') # turn on legend with automatic best location
plt.show()

Categories

Resources