Geopandas & Mapplotlib, how do I plot without an outline around any shape? - matplotlib

When I run the code below in a Jupyter Notebook,
I get a map of the world, colored in red.
There are fine white-ish lines between the countries.
Is there a way to plot the world so that all countries
are solid and there's no line in between?
I'm asking, because my real world usecase is a fine grid that
behaves just like the world map: Each grid shape has a fine outline
which I do not want to have in the plot. (Update, since this was asked: The grid shapes will not have the same fill color.
)
import geopandas as gpd
import geoplot as gplt
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
world['total'] = 1
world.plot(column='total', cmap='Set1')
For the grid example, the grid files are at https://opendata-esri-de.opendata.arcgis.com/datasets/3c1f46241cbb4b669e18b002e4893711_0
A simplified example that shows the problem.
sf = 'Hexagone_125_km/Hexagone_125_km.shp'
shp = gpd.read_file(sf)
shp.crs = {'init': 'epsg:4326'}
shp['sum'] = 1 # for example, fill sum with something
shp.plot(figsize=(20,20), column='sum', cmap='gnuplot', alpha=1, legend=True)

The white lines are due to antialiasing. This usually makes the visual more smooth, but leads to white lines in between different shapes. You can turn off anialiasing via
antialiased=False
That has the inevitable drawback of the plot looking pixelated.
An alternative is to give the patches an edge with a certain linewidth. The edges should probably have the same color as the faces, so
edgecolor="face", linewidth=0.4
would be an option. This removes the white lines, but introduces a slight "searing" effect (You'll notice mainly looking at islands like Indonesia or Japan). This will be the more noticable, the smaller the features, so it may be irrelevant for showing a hexbin plot. Still, playing a bit with the linewidth might improve the result further.
Code for reproduction:
import numpy as np; np.random.seed(42)
import geopandas as gpd
import matplotlib.pyplot as plt
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
world['total'] = np.random.randint(0,10, size=len(world))
fig, (ax1, ax2, ax3) = plt.subplots(nrows=3, figsize=(7,10))
world.plot(column='total', cmap='Set1', ax=ax1)
world.plot(column='total', cmap='Set1', ax=ax2, antialiased=False)
world.plot(column='total', cmap='Set1', ax=ax3, edgecolor="face", linewidth=0.4)
ax1.set_title("original")
ax2.set_title("antialiased=False")
ax3.set_title("edgecolor='face', linewidth=0.4")
plt.tight_layout()
plt.savefig("world.png")
plt.show()

Related

matplotlib find_peaks I need to ignore spureous peaks

I generate a graph like this, it measures the content of a transparent tube but the edges of the tube appear like that, you can see the peaks on the edges of the tube... Any suggestion on how to avoid this "extra" peaks? Any suggestion is welcome.
The image of the tube is this one
This is in fact not a matplotlib-specific question. From my understanding of your question, you would like to keep red peaks while remove blue ones. This task can be done by scipy.signal.find_peaks, you can specifiy a height value to control the peaks the algorithm finds. Here are some minimum code (adapted from scipy docs):
import matplotlib.pyplot as plt
from scipy.misc import electrocardiogram
from scipy.signal import find_peaks
x = electrocardiogram()[2000:4000]
peaks, _ = find_peaks(x, height=0.5)
fig, ax = plt.subplots(1, 1, figsize=(7.2, 7.2))
ax.plot(x)
ax.plot(peaks, x[peaks], "x")
ax.axhline(y=0.5, ls="--", color="gray")

layout problem of multiple heatmaps in one figure with matplotlib

I put multiple heatmaps in one figure with matplotlib. I cannot layout it well. Here is my code.
import matplotlib; matplotlib.use('agg')
import matplotlib.pyplot as plt
import numpy as np
x = np.random.rand(6,240,240)
y = np.random.rand(6,240,240)
t = np.random.rand(6,240,240)
plt.subplots_adjust(wspace=0.2, hspace=0.3)
c=1
for i in range(6):
ax=plt.subplot(6,3,c)
plt.imshow(x[i])
ax.set_title("x"+str(i))
c+=1
ax=plt.subplot(6,3,c)
plt.imshow(y[i])
ax.set_title("y"+str(i))
c+=1
ax=plt.subplot(6,3,c)
plt.imshow(t[i])
ax.set_title("t"+str(i))
c+=1
plt.tight_layout()
plt.savefig("test.png")
test.png looks like this.
I want to
make each heatmap bigger
reduce the margin between each heatmaps in row.
I tried to adjust by "subplots_adjust", but it doesn't work.
Additional information
According to ImportanceOfBeingErnest's comment, I removed tight_layout(). It generated this.
It makes bigger each heatmap, but titles overlappes on subplots. And I still want to make each heatmap more bigger, and I want to reduce the margin in row.

Overlay two seaborn barplots of different size

Say there are two datasets: a big "background" set, and much smaller "foreground" set. The foreground set comes from the background, but might be much smaller.
I am interested in showing the entire background distribution in an ordered sns.barplot, and have the foreground set a brighter contrasting color to draw attention to these samples.
The best solution I could find is to display one graph on top of the other, but what happens is the graph shrinks down to the smaller domain. Here's what I mean:
import matplotlib.pyplot as plt
import seaborn
# Load the example car crash dataset
crashes = sns.load_dataset("car_crashes").sort_values("total", ascending=False)
# states of interest
txcahi = crashes[crashes['abbrev'].isin(['TX','CA','HI'])]
# Plot the total crashes
f, ax = plt.subplots(figsize=(10, 5))
plt.xticks(rotation=90, fontsize=10)
sns.barplot(y="total", x="abbrev", data=crashes, label="Total", color="lightgray")
# overlay special states onto gray plot as red bars
sns.barplot(y="total", x="abbrev", data=txcahi, label="Total", color="red")
sns.despine(left=True, bottom=True)
This data produces:
But it should look like this (ignore stylistic differences):
Why doesn't this approach work, and what would be a better way to accomplish this?
A seaborn barplot just plots the its n data along the values of 0 to n-1. If instead you'd use a matplotlib bar plot, which is unit aware (from matplotlib 2.2 onwards), it'll work as expected.
import matplotlib.pyplot as plt
import seaborn as sns
# Load the example car crash dataset
crashes = sns.load_dataset("car_crashes").sort_values("total", ascending=False)
# states of interest
txcahi = crashes[crashes['abbrev'].isin(['TX','CA','HI'])]
# Plot the total crashes
f, ax = plt.subplots(figsize=(10, 5))
plt.xticks(rotation=90, fontsize=10)
plt.bar(height="total", x="abbrev", data=crashes, label="Total", color="lightgray")
plt.bar(height="total", x="abbrev", data=txcahi, label="Total", color="red")
sns.despine(left=True, bottom=True)

How do I use colourmaps with variable alpha in a Seaborn kdeplot without seeing the contour lines?

Python version: 3.6.4 (Anaconda on Windows)
Seaborn: 0.8.1
Matplotlib: 2.1.2
I'm trying to create a 2D Kernel Density plot using Seaborn but I want each step in the colourmap to have a different alpha value. I had a look at this question to create a matplotlib colourmap with alpha values: Add alpha to an existing matplotlib colormap.
I have a problem in that the lines between contours are visible. The result I get is here:
I thought that I had found the answer when I found this question: Hide contour linestroke on pyplot.contourf to get only fills. I tried the method outlined in the answer (using set_edgecolor("face") but it did not work in this case. That question also seemed to be related to vector graphics formats and I am just writing out a PNG.
Here is my script:
import numpy as np
import seaborn as sns
import matplotlib.colors as cols
import matplotlib.pyplot as plt
def alpha_cmap(cmap):
my_cmap = cmap(np.arange(cmap.N))
# Set a square root alpha.
x = np.linspace(0, 1, cmap.N)
my_cmap[:,-1] = x ** (0.5)
my_cmap = cols.ListedColormap(my_cmap)
return my_cmap
xs = np.random.uniform(size=100)
ys = np.random.uniform(size=100)
kplot = sns.kdeplot(data=xs, data2=ys,
cmap=alpha_cmap(plt.cm.viridis),
shade=True,
shade_lowest=False,
n_levels=30)
plt.savefig("example_plot.png")
Guided by some comments on this question I have tried some other methods that have been successful when this problem has come up. Based on this question (Matplotlib Contourf Plots Unwanted Outlines when Alpha < 1) I have tried altering the plot call to:
sns.kdeplot(data=xs, data2=ys,
cmap=alpha_cmap(plt.cm.viridis),
shade=True,
shade_lowest=False,
n_levels=30,
antialiased=True)
With antialiased=True the lines between contours are replaced by a narrow white line:
I have also tried an approach similar to this question - Pyplot pcolormesh confused when alpha not 1. This approach is based on looping over the PathCollections in kplot.collections and tuning the parameters of the edges so that they become invisible. I have tried adding this code and tweaking the linewidth -
for thing in kplot.collections:
thing.set_edgecolor("face")
thing.set_linewidth(0.01)
fig.canvas.draw()
This results in a mix of white and dark lines - .
I believe that I will not be able to tune the line width to make the lines disappear because of the variable width of the contour bands.
Using both methods (antialiasing + linewidth) makes this version, which looks cool but isn't quite what I want:
I also found this question - Changing Transparency of/Remove Contour Lines in Matplotlib
This one suggests overplotting a second plot with a different number of contour levels on the same axis, like:
kplot = sns.kdeplot(data=xs, data2=ys,
ax=ax,
cmap=alpha_cmap(plt.cm.viridis),
shade=True,
shade_lowest=False,
n_levels=30,
antialiased=True)
kplot = sns.kdeplot(data=xs, data2=ys,
ax=ax,
cmap=alpha_cmap(plt.cm.viridis),
shade=True,
shade_lowest=False,
n_levels=35,
antialiased=True)
This results in:
This is better, and almost works. The problem here is I need variable (and non-linear) alpha throughout the colourmap. The variable banding and lines seem to be a result of the combinations of alpha when contours are plotted over each other. I also still see some clear/white lines in the result.

Seaborn stripplot set edgecolor based on hue/palette

I'm trying to create a figure like this one from the seaborn documentation but with the edgecolor of the stripplot determined by the hue. This is my attempt:
import seaborn as sns
df = sns.load_dataset("tips")
ax = sns.stripplot(x="sex", y="tip", hue="day", data=df, jitter=True,
edgecolor=sns.color_palette("hls", 4),
facecolors="none", split=False, alpha=0.7)
But the color palettes for male and female appear to be different. How do I use the same color palette for both categories?
I'm using seaborn 0.6.dev
The edgecolor parameter is just passed straight through to plt.scatter. Currently you're giving it a list of 4 colors. I'm not exactly sure what I would expect it to do in that case (and I am not exactly sure why you end up with what you're seeing here), but I would not have expected it to "work".
The ideal way to have this work would be to have a "hollow circle" marker glyph that colors the edges based on the color (or facecolor) attribute rather than the edges. While it would be nice to have this as an option in core matplotlib, there are some inconsistencies that might make that unworkable. However, it's possible to hack together a custom glyph that will do the trick:
import numpy as np
import matplotlib as mpl
import seaborn as sns
sns.set_style("whitegrid")
df = sns.load_dataset("tips")
pnts = np.linspace(0, np.pi * 2, 24)
circ = np.c_[np.sin(pts) / 2, -np.cos(pts) / 2]
vert = np.r_[circ, circ[::-1] * .7]
open_circle = mpl.path.Path(vert)
sns.stripplot(x="sex", y="tip", hue="day", data=df,
jitter=True, split=False,
palette="hls", marker=open_circle, linewidth=0)
FWIW I should also mention that it's important to be careful when using this approach because the colors become much harder to distinguish. The hls palette exacerbates the problem as the lime green and cyan middle colors end up quite similar. I can imagine situations where this would work nicely, though, for instance a hue variable with two levels represented by gray and a bright color, where you want to emphasize the latter.