Geopandas plot shapefile on xarray with same legend - python

I'm trying to create some maps of precipitation data (xarray) with a shapefile of the region of interest on top. However, when Python plots the figures, I get two seperate figures:
When I open the data in QGIS they do appear on top of each other, so the coordinate systems do check out. Then I have an additional bonus question: I have to create multiple precipitation maps, on for a visual analysis it would be ideal if I could have the same legend (thus the same min/max for the colorbar) for each map. Anyone an idea how to proceed further?
My code so far:
def chirps_to_map(input1, input2, title):
projection = input1 + input2
plt.figure(figsize=(9, 9))
projection['pr'].plot()
watershed.plot()
plt.title(title)
plt.show()
plt.close()
projection.to_netcdf(str(path)+str(title)+".nc")
return projection

This is a case where it's simpler to use the Matplotlib object-oriented API.
A nice general workflow might be
fig, ax = plt.subplot()
gdf.plot(ax=ax) # Plot the vector data on the subplot
raster.plot(ax=ax) # Plot the raster data on the same subplot
Example
First, we get some sample raster+vector data
import xarray as xr
import geopandas as gpd
import matplotlib.pyplot as plt
da = xr.tutorial.load_dataset('ROMS_example').zeta.isel(ocean_time=0)
gdf = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
usa = gdf.loc[gdf['name'].eq('United States of America')]
Next, we plot both of the data on the same AxesSubplot
fig, ax = plt.subplots(figsize=(15, 10))
da.plot.pcolormesh(x='lon_rho', y='lat_rho', ax=ax)
usa.plot(ax=ax, edgecolor='red', color='none')
# Focus on the raster extent
ax.set_xlim(-95, -87)
ax.set_ylim(26, 32)
Bonus: hvPlot way
hvPlot provides a nice unified API for interactive plotting with pandas, xarray, and many other libraries, and might be of interest to people stumbling upon this answer.
Plotting both vector and raster data is rather easy, simply use the * operator.
import hvplot.pandas
import hvplot.xarray
usa.hvplot(geo=True) * da.hvplot.quadmesh(x='lon_rho', y='lat_rho', geo=True)

Related

Plotting coordinate data on top of a map in Python matplotlib

So, what I am having trouble with is how I am supposed to plot the data I have on top of a global map. I have an array of data, and two arrays of coordinates in latitude and longitude, where each datapoint was taken, but I am not sure of how to plot it on top of a global map. Creating the map itself is not too difficult, I just use:
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
fig = plt.figure(figsize=(10, 8))
m = Basemap(projection='cyl', resolution='c',
llcrnrlat=-90, urcrnrlat=90,
llcrnrlon=-180, urcrnrlon=180, )
m.shadedrelief(scale=0.5)
m.drawcoastlines(color='black')
But the next step is where I am having problems. I have tried doing both a colormesh plot and scatter plot, but they haven't worked so far. How should I go about it so that the data is plotted in the correct coordinate locations for the global map?
Thanks a lot for any help!
Maybe a bit late, but I have this piece of code I used to plot multiple linear plot over a map in Basemap that worked for me.
map = Basemap(projection='cyl', resolution='c',
llcrnrlat=mins[1], urcrnrlat=maxs[1],
llcrnrlon=mins[0], urcrnrlon=50, )
plt.figure(figsize=(15, 15))
for i in range(1259):
filepath = filename[i]
data = pd.read_csv(filepath, index_col=0)
map.plot(data.x,data.y,'k-', alpha=0.1) ### Calling the plot in a loop!!
map.drawcoastlines(linewidth=1)
map.drawcountries(linewidth=0.5, linestyle='solid', color='k' )
plt.show()
The loop calls data from different folders, and I just use the map.plot command to plot. By doing it like that, you can plot all data in the same map.

4D Density Plot in Python

I am looking to plot some density maps from some grid-like data:
X,Y,Z = np.mgrids[-5:5:50j, -5:5:50j, -5:5:50j]
rho = np.random.rand(50,50,50) #for the sake of argument
I am interested in producing an interpolated density plot as shown below, from Mathematica here, using Python.
Is there any solution in Matplotlib or another plotting suite for this sort of plot?
To be clear, I do not want a scatterplot of coloured points, which is not suitable the plot I am trying to make. I would like a 3D interpolated density plot, as shown below.
Plotly
Plotly Approach from https://plotly.com/python/3d-volume-plots/ uses np.mgrid
import plotly.graph_objects as go
import numpy as np
X, Y, Z = np.mgrid[-8:8:40j, -8:8:40j, -8:8:40j]
values = np.sin(X*Y*Z) / (X*Y*Z)
fig = go.Figure(data=go.Volume(
x=X.flatten(),
y=Y.flatten(),
z=Z.flatten(),
value=values.flatten(),
isomin=0.1,
isomax=0.8,
opacity=0.1, # needs to be small to see through all surfaces
surface_count=17, # needs to be a large number for good volume rendering
))
fig.show()
Pyvista
Volume Rendering example:
https://docs.pyvista.org/examples/02-plot/volume.html#sphx-glr-examples-02-plot-volume-py
3D-interpolation code you might need with pyvista:
interpolate 3D volume with numpy and or scipy

How to use a colored shape as yticks in matplotlib or seaborn?

I am working on a task called knowledge tracing which estimates the student mastery level over time. I would like to plot a similar figure as below using the Matplotlib or Seaborn.
It uses different colors to represent a knowledge concept, instead of a text. However, I have googled and found there is no article is talking about how we can do this.
I tried the following
# simulate a record of student mastery level
student_mastery = np.random.rand(5, 30)
df = pd.DataFrame(student_mastery)
# plot the heatmap using seaborn
marker = matplotlib.markers.MarkerStyle(marker='o', fillstyle='full')
sns_plot = sns.heatmap(df, cmap="RdYlGn", vmin=0.0, vmax=1.0)
y_limit = 5
y_labels = [marker for i in range(y_limit)]
plt.yticks(range(y_limit), y_labels)
Yet it simply returns the __repr__ of the marker, e.g., <matplotlib.markers.MarkerStyle at 0x1c5bb07860> on the yticks.
Thanks in advance!
While How can I make the xtick labels of a plot be simple drawings using matplotlib? gives you a general solution for arbitrary shapes, for the shapes shown here, it may make sense to use unicode symbols as text and colorize them according to your needs.
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(42)
fig, ax = plt.subplots()
ax.imshow(np.random.rand(3,10), cmap="Greys")
symbolsx = ["⚪", "⚪", "⚫", "⚫", "⚪", "⚫","⚪", "⚫", "⚫","⚪"]
colorsx = np.random.choice(["#3ba1ab", "#b43232", "#8ecc3a", "#893bab"], 10)
ax.set_xticks(range(len(symbolsx)))
ax.set_xticklabels(symbolsx, size=40)
for tick, color in zip(ax.get_xticklabels(), colorsx):
tick.set_color(color)
symbolsy = ["◾", "◾", "◾"]
ax.set_yticks(range(len(symbolsy)))
ax.set_yticklabels(symbolsy, size=40)
for tick, color in zip(ax.get_yticklabels(), ["crimson", "gold", "indigo"]):
tick.set_color(color)
plt.show()

Formatting style for matplotlib: scatterplot histogram hybrid

In an old standalone plotting package (sm) there was a style available for scatter plots which I found more appealing to the general style. It appears as each point looking almost like a histogram which stretches to the next point.
An example of a scatter plot using this style:
Matplotlib does have this style for histograms, but I'm wondering if there's a way to cheat the system to allow the style to work for scatter plots.
I think some of the confusion comes from the fact that the desired plot is not a scatter plot. It's a line plot with lines in form of a step-like function.
You may plot step functions with pyplot.step(x,y) or plot(x,y, drawstyle="steps").
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(42)
x = np.linspace(0,1)
y = np.random.rand(len(x))
fig, ax = plt.subplots()
ax.step(x,y)
# or
# ax.plot(x,y, drawstyle="steps")
plt.show()

Dynamic spectrum using plotly

I want to plot time vs frequency as x and y axis, but also a third parameter that is specified by the intensity of plot at (x, y) rather (time, frequency) point. [Actually, instead of going up with third axis in 3D visualisation, I want something like a 2D plot, with amplitude of third axis governed by the intensity(color) value at (x,y)].
Can someone please suggest me something similar that I am looking for? These plots are actually called dynamical spectrum.
PS: I am plotting in python offline. I have gone through https://plot.ly/python/, but still I am not sure which will serve my purpose.
Please suggest something that will help me accomplish the above :)
This is the code to compute and visualize the spectrogram with plotly, i tested the code with this audio file: vignesh.wav
The code was tested in Jupyter notebook using python 3.6
# Full example
import numpy as np
import matplotlib.pyplot as plt
# plotly offline
import plotly.offline as pyo
from plotly.offline import init_notebook_mode #to plot in jupyter notebook
import plotly.graph_objs as go
init_notebook_mode() # init plotly in jupyter notebook
from scipy.io import wavfile # scipy library to read wav files
AudioName = "vignesh.wav" # Audio File
fs, Audiodata = wavfile.read(AudioName)
Audiodata = Audiodata / (2.**15) # Normalized between [-1,1]
#Spectrogram
from scipy import signal
plt.figure()
N = 512 #Number of point in the fft
w = signal.blackman(N)
freqs, bins, Pxx = signal.spectrogram(Audiodata, fs,window = w,nfft=N)
# Plot with plotly
trace = [go.Heatmap(
x= bins,
y= freqs,
z= 10*np.log10(Pxx),
colorscale='Jet',
)]
layout = go.Layout(
title = 'Spectrogram with plotly',
yaxis = dict(title = 'Frequency'), # x-axis label
xaxis = dict(title = 'Time'), # y-axis label
)
fig = go.Figure(data=trace, layout=layout)
pyo.iplot(fig, filename='Spectrogram')
I'd suggest the pcolormesh plot
import matplotlib.pyplot as mp
import numpy as np
# meshgrid your timevector to get it in the desired format
X, Y = np.meshgrid(timevector, range(num_of_frequency_bins))
fig1, ax1 = mp.subplots()
Plothandle = mp.pcolormesh(X, Y, frequencies, cmap=mp.cm.jet, antialiased=True, linewidth=0)
Whereas num_of_frequency_bins the amount of frequencies to display on your y-axis. For example from 0Hz to 1000Hz with 10Hz resolution you'll have to do: range(0,1000,10)
Antialiased is just for the looks, same with linewidth.
Colormap jet is usually not recommended due to non-linear gray-scale, but in frequency-domains it is regularly used. Thus I used it here. But python has some nice linear gray-scale colormaps as well!
To the topic of using plotly: If you just want a static image, you don't have to use plotly. If you want to have an interactive image where you can drag around axes and stuff like this, you should take a look at plotly.

Categories

Resources