With SciPy dendrogram, can I change the linewidth? - matplotlib

I'm making a big dendrogram using SciPy and in the resulting dendrogram the line thickness makes it hard to see detail. I want to decrease the line thickness to make it easier to see and more MatLab like. Any suggestions?
I'm doing:
import scipy.cluster.hierarchy as hicl
from pylab import savefig
distance = #distance matrix
links = hicl.linkage(distance,method='average')
pden = hicl.dendrogram(links,color_threshold=optcutoff[0], ...
count_sort=True,no_labels=True)
savefig('foo.pdf')
And getting a result like this.

Matplotlib has a context manager now, which allows you to only override the default values temporarily, for that one plot:
import matplotlib.pyplot as plt
from scipy.cluster import hierarchy
distance = #distance matrix
links = hierarchy.linkage(distance, method='average')
# Temporarily override the default line width:
with plt.rc_context({'lines.linewidth': 0.5}):
pden = hierarchy.dendrogram(links, color_threshold=optcutoff[0], ...
count_sort=True, no_labels=True)
# linewidth is back to its default here...!
plt.savefig('foo.pdf')
See the Matplotlib configuration API for more details.

Set the default linewidth before calling dendrogram. For example:
import scipy.cluster.hierarchy as hicl
from pylab import savefig
import matplotlib
# Override the default linewidth.
matplotlib.rcParams['lines.linewidth'] = 0.5
distance = #distance matrix
links = hicl.linkage(distance,method='average')
pden = hicl.dendrogram(links,color_threshold=optcutoff[0], ...
count_sort=True,no_labels=True)
savefig('foo.pdf')
See Customizing matplotlib for more information.

set dendrogram on existing axes than change its artists using setp. It allow changing all parameters, that won't work if dendrogram is sent to axes or won't work with dendrogram at all like linestyle.
import matplotlib.pyplot as plt
import scipy.cluster.hierarchy as hicl
links = #linkage
fig,ax = plt.subplots()
hicl.dendrogram(links,ax=ax)
plt.setp(ax.collections,linewidth=3,linestyle=":", ...other line parameters...)

Related

How do I subplot each contour with a legenda?

I want to subplot correctly and get two legends. I think that if you open the added image you get what I am trying to achieve because it sucks right now. I am learning to code so I don't understand everyone else's code on here so I am probably asking something that has been asked tons but I don't understand anyone else's code. My current code exists of the following
from IPython import get_ipython
get_ipython().magic('reset -f')
# Set up your graphics environment
get_ipython().magic('matplotlib')
# Import the modules you always need
import numpy as np
import matplotlib.pyplot as plt
# Import the modules for 3D plotting
from mpl_toolkits.mplot3d.axes3d import Axes3D
from matplotlib import cm
plt.close('all')
slopeangle = np.arange(5, 45, 1)
intangle = np.arange(20, 45, 1)
slopeangle_m, intangle_m = np.meshgrid(slopeangle, intangle)
#F = np.zeros(np.shape(slopeangle_m)
F = (((15.2-(9.81*0.5))*2.0*((np.cos(slopeangle_m*np.pi/180))**2)*np.tan(((np.pi*intangle_m)/180)))/(15.2*2.0*np.sin(slopeangle_m*np.pi/180)*(np.cos(slopeangle_m*np.pi/180))))
M0 = (((15.2-(9.81*0))*2.0*((np.cos(slopeangle_m*np.pi/180))**2)*np.tan(((np.pi*intangle_m)/180)))/(15.2*2.0*np.sin(slopeangle_m*np.pi/180)*(np.cos(slopeangle_m*np.pi/180))))
M75 = (((15.2-(9.81*0.75))*2.0*((np.cos(slopeangle_m*np.pi/180))**2)*np.tan(((np.pi*intangle_m)/180)))/(15.2*2.0*np.sin(slopeangle_m*np.pi/180)*(np.cos(slopeangle_m*np.pi/180))))
fig2 = plt.figure()
ax = fig2.add_subplot(211)
plt.contourf(slopeangle, intangle, M0, levels=[np.min(M0),1 ,np.max(M0)], cmap=plt.cm.seismic)
ax.legend
ax=plt.gca()
ax.set_title("Factor m as value 0")
ax.set_xlabel('Slope angle (°)')
ax.set_ylabel('Internal angle (°)')
ax2 = fig2.add_subplot(212)
plt.contourf(slopeangle, intangle, M75, levels=[np.min(M75),1 ,np.max(M75)], cmap=plt.cm.seismic)
ax2=plt.gca()
ax2.set_title("Factor m as value 0.75")
ax2.set_xlabel('Slope angle (°)')
ax2.set_ylabel('Internal angle (°)')
I get the following

set_xlim() does not work with text labels

I am trying to zoom in on geopandas map with labels using set_xlim() in with matplotlib. I basically adapted this SO question to add labels to a map.
However, set_xlim() does not seem to work and did not zoom in on the given extent. (By the way, I've also tried to use text() instead of annotate(), to no avail.)
What I did was the following:
I used the same US county data as in the question linked above, extracted the files, and then executed the following in Jupyter notebook:
import geopandas as gpd
import matplotlib.pyplot as plt
%matplotlib inline
shpfile='shp/cb_2015_us_county_20m.shp'
gdf=gpd.read_file(shpfile)
gdf.plot()
, which gives a map of all US counties as expected:
Adding labels as with one of the answers also works:
ax = gdf.plot()
gdf.apply(lambda x: ax.annotate(s=x.NAME, xy=x.geometry.centroid.coords[0], ha='center'),axis=1);
However, when trying to zoom in to a particular geographic extent with set_xlim() and set_ylim() as follows:
ax = gdf.plot()
gdf.apply(lambda x: ax.annotate(s=x.NAME, xy=x.geometry.centroid.coords[0], ha='center'),axis=1);
ax.set_xlim(-84.2, -83.4)
ax.set_ylim(42, 42.55)
, the two functions do not seem to work. Instead of zooming in, they just trimmed everything outside of the given extent.
If the labeling code is dropped out (gdf.apply(lambda x: ax.annotate(s=x.NAME, xy=x.geometry.centroid.coords[0], ha='center'),axis=1);, the set_xlim() works as expected:
My question is:
What is the correct way to zoom in to an area when labels are present in a plot?
You need some coordinate transformation.
import cartopy.crs as ccrs
# relevant code follows
# set numbers in degrees of longitude
ax.set_xlim(-84.2, -83.4, ccrs.PlateCarree())
# set numbers in degrees of latitude
ax.set_ylim(42, 42.55, ccrs.PlateCarree())
plt.show()
with the option ccrs.PlateCarree(), the input values are transformed to proper data coordinates.
When I try it, I can't draw on matplotlib with the axes restricted. So it's possible to extract the data.
import geopandas as gpd
import matplotlib.pyplot as plt
%matplotlib inline
fig,ax = plt.subplots(1,1, figsize=(4,4), dpi=144)
shpfile = './cb_2015_us_county_20m/cb_2015_us_county_20m.shp'
gdf = gpd.read_file(shpfile)
# gdf = gdf.loc[gdf['STATEFP'] == '27']
gdf['coords'] = gdf['geometry'].apply(lambda x: x.representative_point().coords[:])
gdf['coords'] = [coords[0] for coords in gdf['coords']]
gdf = (gdf[(gdf['coords'].str[0] >= -84.2) & (gdf['coords'].str[0] <= -83.4)
& (gdf['coords'].str[1] >= 42) & (gdf['coords'].str[1] <= 42.55)])
gdf.plot(ax=ax)
gdf.apply(lambda x: ax.annotate(text=x.NAME, xy=x.geometry.centroid.coords[0], ha='center'),axis=1)

Plotting a rasterio raster on a Cartopy GeoAxes

I've seen a few other questions on this topic, but the library has changed enough that the answers to those no longer seem to apply.
Rasterio used to include an example for plotting a rasterio raster on a Cartopy GeoAxes. The example went roughly like this:
import matplotlib.pyplot as plt
import rasterio
from rasterio import plot
import cartopy
import cartopy.crs as ccrs
world = rasterio.open(r"../tests/data/world.rgb.tif")
fig = plt.figure(figsize=(20, 12))
ax = plt.axes(projection=ccrs.InterruptedGoodeHomolosine())
ax.set_global()
plot.show(world, origin='upper', transform=ccrs.PlateCarree(), interpolation=None, ax=ax)
ax.coastlines()
ax.add_feature(cartopy.feature.BORDERS)
However, this code no longer draws the raster. Instead, I get something like this:
It should look like this:
When I asked about this in the rasterio issues tracker, they told me the example was deprecated (and deleted the example). Still, I wonder if there's some way to do what I'm trying to do. Can anyone point me in the right direction?
I think you may want to read the data to a numpy.ndarray and plot it using ax.imshow, where ax is your cartopy.GeoAxes (as you have it already). I offer an example of what I mean, below.
I clipped a small chunk of Landsat surface temperature and some agricultural fields for this example. Get them on this drive link.
Note fields are in WGS 84 (epsg 4326), Landsat image is in UTM Zone 12 (epsg 32612), and I want my map in Lambert Conformal Conic. Cartopy makes this easy.
import numpy as np
import cartopy.crs as ccrs
from cartopy.io.shapereader import Reader
from cartopy.feature import ShapelyFeature
import rasterio
import matplotlib.pyplot as plt
def cartopy_example(raster, shapefile):
with rasterio.open(raster, 'r') as src:
raster_crs = src.crs
left, bottom, right, top = src.bounds
landsat = src.read()[0, :, :]
landsat = np.ma.masked_where(landsat <= 0,
landsat,
copy=True)
landsat = (landsat - np.min(landsat)) / (np.max(landsat) - np.min(landsat))
proj = ccrs.LambertConformal(central_latitude=40,
central_longitude=-110)
fig = plt.figure(figsize=(20, 16))
ax = plt.axes(projection=proj)
ax.set_extent([-110.8, -110.4, 45.3, 45.6], crs=ccrs.PlateCarree())
shape_feature = ShapelyFeature(Reader(shapefile).geometries(),
ccrs.PlateCarree(), edgecolor='blue')
ax.add_feature(shape_feature, facecolor='none')
ax.imshow(landsat, transform=ccrs.UTM(raster_crs['zone']),
cmap='inferno',
extent=(left, right, bottom, top))
plt.savefig('surface_temp.png')
feature_source = 'fields.shp'
raster_source = 'surface_temperature_32612.tif'
cartopy_example(raster_source, feature_source)
The trick with Cartopy is to remember to use the projection keyword for your axes object, as this renders the map in a nice projection of your choice (LCC in my case). Use transform keyword to indicate what projection system your data is in, so Cartopy knows how to render it.
No need of rasterio. Get a bluemarble image, then plot it.
Here is the working code:
import cartopy
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
fig = plt.figure(figsize=(10, 5))
ax = plt.axes(projection=ccrs.InterruptedGoodeHomolosine())
# source of the image:
# https://eoimages.gsfc.nasa.gov/images/imagerecords/73000/73909/world.topo.bathy.200412.3x5400x2700.jpg
fname = "./world.topo.bathy.200412.3x5400x2700.jpg"
img_origin = 'lower'
img = plt.imread(fname)
img = img[::-1]
ax.imshow(img, origin=img_origin, transform=ccrs.PlateCarree(), extent=[-180, 180, -90, 90])
ax.coastlines()
ax.add_feature(cartopy.feature.BORDERS)
ax.set_global()
plt.show()
The output plot:

Matplotlib colorbar and WCS projection

I'm trying to write a function to display astronomical images with a colorbar on the top (automaticly with the same length of the x-axis).
I'm having problem because when I try to put the tick on the top it doesn't do anything...it keeps the tick on the bottom of the colorbar (and also the tick on the y-axis of the colobar).
I think that could be a problem with the WCS coordinate of the x-axis, because when i try to do it without the projection it work well!
import numpy as np
import matplotlib.pyplot as plt
from astropy import wcs
from matplotlib.colors import PowerNorm
from mpl_toolkits.axes_grid1 import make_axes_locatable
from matplotlib import cm
#WCS coordinate system
w = wcs.WCS(naxis=2)
w.wcs.crpix = [23.5, 23.5]
w.wcs.cdelt = np.array([-0.0035, 0.0035])
w.wcs.crval = [266.8451, -28.151658]
w.wcs.ctype = ["RA---TAN", "DEC--TAN"]
w.wcs.set_pv([(2, 1, 45.0)])
#generate an array as image test
data = (np.arange(10000).reshape((100,100)))
#display image
fig = plt.figure()
ax = plt.gca(projection=w)
graf = ax.imshow(data, origin='lower', cmap=cm.viridis, norm=PowerNorm(1))
#colorbar
divider = make_axes_locatable(ax)
cax = divider.append_axes("top", size="5%")
cbar = fig.colorbar(graf, cax=cax, orientation='horizontal')
cax.xaxis.set_ticks_position('top')
fig.show()
Thanks!
You can fix this issue using matplotlib's axes class.
...
import matplotlib.axes as maxes
cax = divider.append_axes("top", size="5%", axes_class=maxes.Axes)
...
You need to use the internal machinery of the WCSAxes to handle the ticks in the WCS projection. It looks like WCSAxes handles the colorbar ticks through a coordinate map container (you can find it in cbar.ax.coords) instead of the xaxis/yaxis attributes (that don't seem to be used much).
So, after running your code, the following trick worked for me and the xticks moved up:
c_x = cbar.ax.coords['x']
c_x.set_ticklabel_position('t')
cbar.update_normal(cax)
To get something like this to work, I needed a few additional parameters:
from mpl_toolkits.axes_grid1 import make_axes_locatable
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)
cax.coords[0].grid(False)
cax.coords[1].grid(False)
cax.tick_params(direction='in')
cax.coords[0].set_ticks(alpha=0, color='w', size=0, values=[]*u.dimensionless_unscaled)
cax.coords[1].set_ticklabel_position('r')
cax.coords[1].set_axislabel_position('r')
because the default axis gad the grid on, the labels to the left, and x-axis labels enabled. I'm not sure why the original post didn't have issues with this.

Matplotlib Color Palette

Is it possible to change what colors Matplotlib cycles through when it is generating its own colors for a graph's lines? I'm using the pylab module.
from pylab import *
import matplotlib.cm as cm
x=[1,2,3,4]
y=[5,6,7,8]
fig1 = Figure()
plot1 = fig1.add_subplot(311)
plot1.plot(x,y)
plot2 = fig1.add_subplot(312)
plot2.plot(x,y)
plot3 = fig1.add_subplot(313)
plot3.plot(x,y)
Yes, of course. Since it accept many kinds of color definition. It's easy to define your own color map. Here I just get colors from the colormap hot
import pylab as py
import numpy as np
import matplotlib.cm as cm
a = np.arange(0,10)
py.figure()
for i in np.arange(10):
c = cm.hot(i/10.,1)
py.plot(a,i*a,color=c)
py.show()
The colors are extracted from color maps. You can use one of the predefined colormaps, or define your own.
Unfortunately there is no way to use multiple colormaps per figure, you have to do it manually:
import pylab as pl
import matplotlib.cm as cm
xval = pl.arange(0, 20, 0.2)
pl.subplot(311)
pl.plot(xval, pl.sin(xval), c=cm.summer(0))
pl.subplot(312)
pl.plot(xval, pl.cos(xval), c=cm.spring(0))
pl.subplot(313)
pl.plot(xval, pl.arctan(xval), xval, pl.fabs(xval))
pl.show()