Heat map on Basemap\RuntimeError matplotlib-basemap - python

I made heat map on basemap like this and evrything work well, but i want to add else label with number of occurrences, and i get: RuntimeError: No mappable was found to use for colorbar creation. First define a mappable such as an image (with imshow) or a contour set (with contourf).
How to do it correctly ?
import numpy as np
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
import pandas as pd
df = pd.read_csv(r"C:\Users\Piotr\Desktop\Meteorite_Landings1.csv")
df = df.dropna()
lat = df['reclat'].values
lon = df['reclong'].values
m = Basemap(projection = 'mill',
llcrnrlat = -90,
urcrnrlat = 90,
llcrnrlon = -180,
urcrnrlon = 180,
resolution = 'c')
m.drawparallels(np.arange(-90, 90,10), labels=[True, False, False, False])
m.drawmeridians(np.arange(-180, 180, 30), labels = [0,0,0,1])
m.drawcoastlines()
x,y = m(lon, lat)
m.plot(x, y, 'o', c= range(amount), markersize=4,zorder=8, markerfacecolor='#424FA4',markeredgecolor="none", alpha=0.33)
plt.colorbar(label="Amount")
plt.clim(0, 6000)
plt.show()

You need to do some statistic computing that produces mappable values for the colorbar. For example, replace the line:
m.plot(...)
with
m.hexbin(x, y, bins='log', gridsize=30, alpha=0.5, edgecolors='gray')
It will show the number of occurrences better than using heatmap.
If you want to take the mass into consideration, you can use:
m.hexbin(x,y, bins='log', C=df['mass_g'].values, gridsize=30, alpha=0.5, edgecolors='gray')
You will get an output plot like this:
Hope this helps.

Related

Python matplotlib polar coordinate is not plotting as it is supposed to be

I am plotting from a CSV file that contains Cartesian coordinates and I want to change it to Polar coordinates, then plot using the Polar coordinates.
Here is the code
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import seaborn as sns
df = pd.read_csv('test_for_plotting.csv',index_col = 0)
x_temp = df['x'].values
y_temp = df['y'].values
df['radius'] = np.sqrt( np.power(x_temp,2) + np.power(y_temp,2) )
df['theta'] = np.arctan2(y_temp,x_temp)
df['degrees'] = np.degrees(df['theta'].values)
df['radians'] = np.radians(df['degrees'].values)
ax = plt.axes(polar = True)
ax.set_aspect('equal')
ax.axis("off")
sns.set(rc={'axes.facecolor':'white', 'figure.facecolor':'white','figure.figsize':(10,10)})
# sns.scatterplot(data = df, x = 'x',y = 'y', s= 1,alpha = 0.1, color = 'black',ax = ax)
sns.scatterplot(data = df, x = 'radians',y = 'radius', s= 1,alpha = 0.1, color = 'black',ax = ax)
plt.tight_layout()
plt.show()
Here is the dataset
If you run this command using polar = False and use this line to plot sns.scatterplot(data = df, x = 'x',y = 'y', s= 1,alpha = 0.1, color = 'black',ax = ax) it will result in this picture
now after setting polar = True and run this line to plot sns.scatterplot(data = df, x = 'radians',y = 'radius', s= 1,alpha = 0.1, color = 'black',ax = ax) It is supposed to give you this
But it is not working as if you run the actual code the shape in the Polar format is the same as Cartesian which does not make sense and it does not match the picture I showed you for polar (If you are wondering where did I get the second picture from, I plotted it using R)
I would appreciate your help and insights and thanks in advance!
For a polar plot, the "x-axis" represents the angle in radians. So, you need to switch x and y, and convert the angles to radians (I also added ax=ax, as the axes was created explicitly):
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import seaborn as sns
data = {'radius': [0, 0.5, 1, 1.5, 2, 2.5], 'degrees': [0, 25, 75, 155, 245, 335]}
df_temp = pd.DataFrame(data)
ax = plt.axes(polar=True)
sns.scatterplot(x=np.radians(df_temp['degrees']), y=df_temp['radius'].to_numpy(),
s=100, alpha=1, color='black', ax=ax)
for deg, y in zip(df_temp['degrees'], df_temp['radius']):
x = np.radians(deg)
ax.axvline(x, color='skyblue', ls=':')
ax.text(x, y, f' {deg}', color='crimson')
ax.set_rlabel_position(-15) # Move radial labels away from plotted dots
plt.tight_layout()
plt.show()
About your new question: if you have an xy plot, and you convert these xy values to polar coordinates, and then plot these on a polar plot, you'll get again the same plot.
After some more testing with the data, I decided to create the plot directly with matplotlib, as seaborn makes some changes that don't have exactly equal effects across seaborn and matplotlib versions.
What seems to be happening in R:
The angles (given by "x") are spread out to fill the range (0,2 pi). This either requires a rescaling of x, or change how the x-values are mapped to angles. One way to get this, is subtracting the minimum. And with that result divide by the new maximum and multiply by 2 pi.
The 0 of the angles it at the top, and the angles go clockwise.
The following code should create the plot with Python. You might want to experiment with alpha and with s in the scatter plot options. (Default the scatter dots get an outline, which often isn't desired when working with very small dots, and can be removed by lw=0.)
ax = plt.axes(polar=True)
ax.set_aspect('equal')
ax.axis('off')
x_temp = df['x'].to_numpy()
y_temp = df['y'].to_numpy()
x_temp -= x_temp.min()
x_temp = x_temp / x_temp.max() * 2 * np.pi
ax.scatter(x=x_temp, y=y_temp, s=0.05, alpha=1, color='black', lw=0)
ax.set_rlim(y_temp.min(), y_temp.max())
ax.set_theta_zero_location("N") # set zero at the north (top)
ax.set_theta_direction(-1) # go clockwise
plt.show()
At the left the resulting image, at the right using the y-values for coloring (ax.scatter(..., c=y_temp, s=0.05, alpha=1, cmap='plasma_r', lw=0)):

How to plot pcolormesh for specific coordinate points instead of a meshgrid from 1D lat lon values

I have a dataframe which contains three columns: Latitude, Longitude and Variable. There are roughly 100K rows in it. I need to plot a colormesh of this data which is showing weird lines and areas.
I have seen posts everywhere to convert 1D lat lon arrays using meshgrid but that won't work in this case because these are specific coordinates themselves. I tried to sort them in ascending lon and/or lat and/or both. Then I tried shifting the coordinates, reshaping them into factors of their total lengths but in vain.
Data:
Longitude = [-10, -40, 34, 12, 67, ...] # 138627 elements
Latitude = [ 23, -89, 67, -25, 92, ...] # same
Variable = [ 1, 2, 3, 4, 5, ...] # same
Code:
import cartopy, glob, warnings, os, matplotlib.pyplot as plt
import numpy as np, cartopy.crs as ccrs
from netCDF4 import Dataset as netcdf_dataset
from cartopy import config
from mpl_toolkits.basemap import Basemap
warnings.simplefilter('ignore')
# CARTOPY
ax = plt.axes(projection=ccrs.PlateCarree())
plt.pcolormesh(Longitude, Latitude, Variable)
ax.coastlines()
plt.show()
# BASEMAP
m = Basemap(projection='cyl', llcrnrlat=-90, llcrnrlon=-180, rcrnrlat=90, urcrnrlon=180)
#TRY 1
new_coor = sorted([(i,j) for i,j in zip(Longitude, Latitude)], key=lambda x: x[0])
shape = (3,46209) #len(Latitude) = 138627
Longitude = np.asarray([i[0] for i in new_coor]).reshape(shape)
Latitude = np.asarray([i[1] for i in new_coor]).reshape(shape)
xi, yi = m(Longitude, Latitude)
cs = m.pcolor(xi, yi, np.squeeze(Variable)) #tried *pcolormesh* also
# tried *m.shifting()* and *lat_lon = True* also
m.drawcoastlines()
m.drawcountries()
cbar = m.colorbar(cs, location='bottom', pad="10%")
plt.show()
You are dealing with unstructured data. You may want to define a grid and to interpolate the data onto this grid, but in my opinion, a neater way is to use tricontourf. This function makes use of triangulation so that your original data is not modified before being plotted. Useful keywords are, for example, antialiased, levels, extend, cmap.
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
from matplotlib.tri import Triangulation
triMesh = Triangulation(Longitude, Latitude)
fig, ax = plt.subplots(nrows=1, ncols=1, num=0,
subplot_kw={'projection': ccrs.PlateCarree()},
figsize=(16, 8))
ctrf = ax.tricontourf(triMesh, Variable)
cbar = fig.colorbar(ctrf)

Bigger marker size for plot using pcolormesh

I am trying to create a color mesh plot but the data points and their corresponding colors appear too small.
My script is:
import pandas as pd
import numpy as np
from mpl_toolkits.basemap import Basemap, cm
import matplotlib.pyplot as plt
df = pd.read_csv('data.csv', usecols=[1,2,4])
df = df.apply(pd.to_numeric)
val_pivot_df = df.pivot(index='Latitude', columns='Longitude', values='Bin 1')
lons = val_pivot_df.columns.astype(float)
lats = val_pivot_df.index.astype(float)
fig, ax = plt.subplots(1, figsize=(8,8))
m = Basemap(projection='merc',
llcrnrlat=df.dropna().min().Latitude-5
, urcrnrlat=df.dropna().max().Latitude+5
, llcrnrlon=df.dropna().min().Longitude-5
, urcrnrlon=df.dropna().max().Longitude+5
, resolution='i', area_thresh=10000
)
m.drawcoastlines()
m.drawstates()
m.drawcountries()
m.fillcontinents(color='gray', lake_color='white')
m.drawmapboundary(fill_color='0.3')
x, y = np.meshgrid(lons,lats)
px,py = m(x,y)
data_values = val_pivot_df.values
masked_data = np.ma.masked_invalid(data_values)
cmap = plt.cm.viridis
m.pcolormesh(px, py, masked_data, vmin=0, vmax=8000)
m.colorbar()
plt.show()
I'm looking to get the markersize larger of each data point but I can't seem to find any documentation on how to do this for pcolormesh
There is no marker in a pcolormesh. The size of the colored areas in a pcolor plot is determined by the underlying grid. As an example, if the grid in x direction was [0,1,5,105], the last column would be 100 times larger in size than the first.
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(1)
x = [0,1,5,25,27,100]
y = [0,10,20,64,66,100]
X,Y = np.meshgrid(x,y)
Z = np.random.rand(len(y)-1, len(x)-1)
plt.pcolormesh(X,Y,Z)
plt.show()

Changing point color on matplolib and basemap not working

I am having some issue with getting my data onto a map with Basemap and having those points change in color. I have read many different things online about how to do this, but I still get a map with no points. Here is my code:
import pandas as pd
import numpy as np
import pickle
from IPython.display import SVG, display_svg
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import matplotlib.colors as co
d3data = pickle.load( open( "9_28_2015to10_04_2015.pickle", "rb" ) )
lons = d3data['longitude'].tolist()
lats = d3data['latitude'].tolist()
normcts = co.Normalize(d3data['GrossCounts'])
plt.figure(figsize=(20,10))
m = Basemap(projection='cass', lat_0 = 40.108004, lon_0 = -88.228878,
resolution = 'h', area_thresh = 0.1,
llcrnrlon=-88.238399, llcrnrlat=40.097942,
urcrnrlon=-88.219345, urcrnrlat=40.116158)
m.drawcountries()
m.fillcontinents(color='white')
m.drawmapboundary()
m.readshapefile('mhj_shapes/lines', 'lines')
cmap = plt.cm.RdYlBu_r
norm = co.Normalize(vmin=d3data['GrossCounts'].min(),
vmax=d3data['GrossCounts'].max())
pointcolors = plt.cm.ScalarMappable(norm, cmap)
for i in range(0, len(d3data)):
col = pointcolors.to_rgba(d3data['GrossCounts'][i])
x,y = m(d3data['longitude'][i],d3data['latitude'][i])
m.scatter(x, y, marker = 'o', s=10, color=col, cmap=cmap)
plt.show()
My problem is that my shape file generates a map just fine, but I don't get any points on top of it. I want to plot the dataframe columns d3data['GrossCounts'] on top of the map and have the color scale with the (integer) value of d3data['GrossCounts'].
Any suggestions would be greatly appreciated!
It is hard to test without your data/shape file etc, but the problem could be your for loop. Maybe try without the loop:
col = pointcolors.to_rgba(d3data['GrossCounts'])
x, y = m(d3data['longitude'], d3data['latitude'])
m.scatter(x, y, marker='o', s=10, color=col, cmap=cmap)
i.e. if I do:
plt.figure(figsize=(8,5))
m = Basemap(projection='cass', lat_0 = 0, lon_0 = 0,
resolution = 'l', area_thresh = 0.1,
llcrnrlon=-10, llcrnrlat=-10,
urcrnrlon=10, urcrnrlat=10)
m.drawcountries()
m.fillcontinents(color='white')
m.drawmapboundary()
#m.readshapefile('mhj_shapes/lines', 'lines')
cmap = plt.cm.RdYlBu_r
x, y = m([0, 5], [0, 5])
df = pd.DataFrame({'a': [1,2]})
norm = co.Normalize(vmin=df.a.min(), vmax=df.a.max())
pointcolors = plt.cm.ScalarMappable(norm, cmap)
col = pointcolors.to_rgba(df.a)
m.scatter(x, y, s=10, color=col)
plt.show()
I get:
Is that what you're after?
Problem solved!
It turns out that this is described in `map.scatter` on basemap not displaying markers (although I was not searching for the right terms when I googled in here). Here is the change that finally worked:
m.drawcountries()
m.fillcontinents(color='white', zorder=0) # <--zorder!!!
m.drawmapboundary()
m.readshapefile('mhj_shapes/lines', 'lines')
cmap = plt.cm.jet
x, y = m(lons, lats)
norm = co.Normalize(vmin=d3data.GrossCounts.min(), vmax=250)
pointcolors = plt.cm.ScalarMappable(norm, cmap)
col = pointcolors.to_rgba(d3data.GrossCounts)
m.scatter(x, y, s=10, c=col, cmap=plt.cm.jet)
I don't know why I never saw zorder in any of the online examples that I had found, but adding this makes sure that the map itself is sent to the back so the points are brought to the front. Thank you, all for your help!

How to project a 2d array on Basemap using python?

I'm trying to display data on a map using python's basemap library. The data I have is in a 2D array (72 X 144). I have been able to get a map to show but when I display the data on the map, it looks as like this:
http://i.stack.imgur.com/Zsw9Y.png
My question is how can I show the data so it expands the entire map? I don't know how to get change the size of the map.
My code is shown below:
import numpy as np
from matplotlib import pylab as plt
from pylab import *
from matplotlib import colors
from mpl_toolkits.basemap import Basemap
A = np.fromfile('1983/yyyymmddhh.83100100', dtype='int32')
B = np.reshape(A, (72, 144))
for i, n in enumerate(B):
for j, m in enumerate(n):
if (B[i][j] == -999):
B[i][j] = 13
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
m = Basemap(projection='cyl', lat_0 = 50, lon_0 = -100,
ax = ax, resolution = 'l',
llcrnrlat=-90, urcrnrlat = 90, llcrnrlon = -180, urcrnrlon = 180)
border_color = 'black'
m.drawcoastlines()
plt.imshow(B)
plt.colorbar()
plt.show()
There are a couple ways to do this, as you can see here; http://matplotlib.org/basemap/api/basemap_api.html
The first, listed in the comments above, is to use m.imshow.
Next, you can plot a pseudocolor image with m.pcolor, or m.pcolormesh, by specifying the lat/lon coordinates for each point; this will color squares.
Lastly, you can display a contour map using contour, or filled countour map, using m.contour or m.countourf.

Categories

Resources