Basemap subplots with pandas groupby data issue - python

Problem: struggling to get subplots with Basemap and pandas group-by function. I've worked around several SO similar questions, the closest being: Trouble with basemap subplots and How to make grouper and axis the same length?. Nothing wrong when I plot individually, or manually set the axis (i.e ax = ax[0], ax [1] etc). I could set axis on a counter, but surely within the loop i, ax = ax[i] should work. Greatly appreciate any help or suggestions. Just can't seem to see it...
Desired outcome: Use the pandas group-by function to plot one basemap for each day, for the selected df column. In this sample, plot 4 basemaps (4 days) for T1.
Here is the code so far (apologies, tried to cut excess off but leave enough to follow + txt):
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.mlab import griddata
import pandas as pd
from matplotlib.path import Path
from matplotlib.patches import PathPatch
"""grab data"""
colnames = ['Date','Day','Lat','Lon','Location','T1','T2']
data = pd.read_table('sampledata.txt',sep = '\,',header=0,names=colnames)
"""set up plot"""
ncol = 2
nrow = 2
fig, ax = plt.subplots(nrows=nrow, ncols=ncol)
"""define map extent"""
lonmin=31.034
latmin=-29.87226
lonmax=31.06757
latmax=-29.76993
"""Contour plot area"""
lon_l = 31.034458
lon_r = 31.063841
lat_top = -29.772105
lat_bot = -29.866729
"""transform lon / lat coordinates to map projection"""
data['projected_lon'], data['projected_lat'] = map(*(data.Lon.values, data.Lat.values))
"""grid data"""
numcols, numrows = 100, 100
xi = np.linspace(data['projected_lon'].min(), data['projected_lon'].max(), numcols)
yi = np.linspace(data['projected_lat'].min(), data['projected_lat'].max(), numrows)
xi, yi = np.meshgrid(xi, yi)
"""Set Boundary for clip"""
verts = [
(3413.68264053, 12562.754945193),(2529.90577973, 10518.39901756),( 1836.04976419, 8720.5262622),
( 1511.02714665, 7828.73600079),(1164.54391838, 5708.6428411),(1262.72899238, 3495.89719006),
(1504.13306445, 2376.68833083),(2119.70788849, 1417.91132045),(2828.7976018 , 1093.77254193),
(2687.91369608, 812.19595702), (1553.61478351, 738.21055305),(870.98945027, 2237.5816522 ),
(515.94421668, 3930.99046256),(580.43724377, 6163.91186676),(682.62533323, 6802.055938 ),
(771.02525829, 7354.17422723),(743.67131922, 7644.57787453), (107.19185881, 7843.3455092 ),
(142.88541346, 8263.93842556),(1078.47908569, 8510.110874 ), (1503.57709008, 9693.10793354),
(2152.06559691, 11221.41133494),(2926.64909117, 12784.761329246),(3413.68264053, 12562.754945193),
]
codes = [Path.MOVETO, Path.LINETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.LINETO, Path.LINETO,Path.LINETO,Path.LINETO, Path.LINETO, Path.LINETO,
Path.LINETO, Path.LINETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.LINETO, Path.LINETO,Path.LINETO,Path.LINETO,Path.LINETO, Path.CLOSEPOLY,
]
path = Path(verts, codes)
clip = Path(verts, codes)
clip = PathPatch(clip, transform=ax.transData)
""" Setup Plots """
for i, group in data.groupby(['Day']):
plt.figure()
group[i].plot(title=str(i),ax=ax[i])
map = Basemap(projection='merc', lat_0=-29, lon_0=31,
resolution = 'l', area_thresh = 0.1,
llcrnrlon = lonmin, llcrnrlat = latmin ,
urcrnrlon = lonmax, urcrnrlat =latmax ,
ax=ax[i])
map.drawcoastlines()
map.fillcontinents(color='burlywood')
"""interpolate on defined grid"""
x, y, z = data['projected_lon'].values, data['projected_lat'].values, data['T1'].values
zi = griddata(x, y, z, xi, yi,interp='linear')
"""contour plot + clip"""
con = map.contourf(xi, yi, zi)
clip = PathPatch(clip, transform=ax[i].transData)
for contour in con.collections:
contour.set_clip_path(clip)
"""Map Extras"""
map.drawmapboundary(fill_color = 'white')
map.scatter(
data['projected_lon'],
data['projected_lat'],
color='#545454',
edgecolor='#ffffff',
vmin=zi.min(), vmax=zi.max(), zorder=4)
"""Finally Plot all figures generated"""
plt.show()
Sample text file:
Date,Day,Lat,Lon,Location,T1,T2
27-Feb-15,1,-29.86198,31.04568,A1,7,4
27-Feb-15,1,-29.86151,31.0472,A2,4,22
27-Feb-15,1,-29.86098,31.04907,A3,9,24
27-Feb-15,1,-29.85482,31.04243,A4,2,17
27-Feb-15,1,-29.8547,31.04394,A5,2,29
27-Feb-15,1,-29.8544,31.04598,A6,10,27
27-Feb-15,1,-29.8416,31.03864,A7,8,11
27-Feb-15,1,-29.84162,31.04041,A8,1,13
27-Feb-15,1,-29.84161,31.04219,A9,1,14
27-Feb-15,1,-29.83229,31.03868,A10,14,53
27-Feb-15,1,-29.83246,31.04055,A11,8,10
27-Feb-15,1,-29.83256,31.04251,A12,1,13
27-Feb-15,1,-29.82418,31.03922,A13,46,71
27-Feb-15,1,-29.82448,31.04116,A14,4,53
27-Feb-15,1,-29.82461,31.04331,A15,7,5
27-Feb-15,1,-29.81028,31.04301,A16,39,29
27-Feb-15,1,-29.81083,31.0449,A17,12,15
27-Feb-15,1,-29.81114,31.0467,A18,2,9
27-Feb-15,1,-29.79983,31.04648,A19,30,15
27-Feb-15,1,-29.80046,31.04805,A20,37,38
27-Feb-15,1,-29.80078,31.04983,A21,4,23
27-Feb-15,1,-29.78945,31.05083,A22,11,63
27-Feb-15,1,-29.79008,31.05287,A23,5,47
27-Feb-15,1,-29.79047,31.05408,A24,4,15
27-Feb-15,1,-29.78094,31.05577,A25,6,37
27-Feb-15,1,-29.78131,31.05677,A26,13,41
27-Feb-15,1,-29.78178,31.05827,A27,12,46
27-Feb-15,1,-29.77251,31.06032,A28,14,64
27-Feb-15,1,-29.77316,31.0618,A29,5,36
27-Feb-15,1,-29.77348,31.06291,A30,12,45
27-Feb-15,1,-29.86373,31.05944,A31,0,0
27-Feb-15,1,-29.865902,31.058159,A32,0,0
27-Feb-15,1,-29.810559,31.035082,A33,0,0
27-Feb-15,1,-29.866335,31.049232,A34,7,4
28-Feb-15,2,-29.86198,31.04568,A1,3,2
28-Feb-15,2,-29.86151,31.0472,A2,1,2
28-Feb-15,2,-29.86098,31.04907,A3,2,2
28-Feb-15,2,-29.85482,31.04243,A4,62,490
28-Feb-15,2,-29.8547,31.04394,A5,1,1
28-Feb-15,2,-29.8544,31.04598,A6,5,28
28-Feb-15,2,-29.8416,31.03864,A7,1,23
28-Feb-15,2,-29.84162,31.04041,A8,1,46
28-Feb-15,2,-29.84161,31.04219,A9,1,6
28-Feb-15,2,-29.83229,31.03868,A10,12,44
28-Feb-15,2,-29.83246,31.04055,A11,1,18
28-Feb-15,2,-29.83256,31.04251,A12,1,18
28-Feb-15,2,-29.82418,31.03922,A13,6,25
28-Feb-15,2,-29.82448,31.04116,A14,1,19
28-Feb-15,2,-29.82461,31.04331,A15,4,34
28-Feb-15,2,-29.81028,31.04301,A16,6,55
28-Feb-15,2,-29.81083,31.0449,A17,9,52
28-Feb-15,2,-29.81114,31.0467,A18,9,28
28-Feb-15,2,-29.79983,31.04648,A19,33,20
28-Feb-15,2,-29.80046,31.04805,A20,29,20
28-Feb-15,2,-29.80078,31.04983,A21,21,20
28-Feb-15,2,-29.78945,31.05083,A22,9,20
28-Feb-15,2,-29.79008,31.05287,A23,1,11
28-Feb-15,2,-29.79047,31.05408,A24,1,14
28-Feb-15,2,-29.78094,31.05577,A25,4,6
28-Feb-15,2,-29.78131,31.05677,A26,1,42
28-Feb-15,2,-29.78178,31.05827,A27,1,10
28-Feb-15,2,-29.77251,31.06032,A28,22,82
28-Feb-15,2,-29.77316,31.0618,A29,65,30
28-Feb-15,2,-29.77348,31.06291,A30,1,8
28-Feb-15,2,-29.86373,31.05944,A31,24,39
28-Feb-15,2,-29.865902,31.058159,A32,24,39
28-Feb-15,2,-29.810559,31.035082,A33,70,17000
28-Feb-15,2,-29.866335,31.049232,A34,3,2
01-Mar-15,3,-29.86198,31.04568,A1,1,1
01-Mar-15,3,-29.86151,31.0472,A2,1,1
01-Mar-15,3,-29.86098,31.04907,A3,1,1
01-Mar-15,3,-29.85482,31.04243,A4,1,2
01-Mar-15,3,-29.8547,31.04394,A5,1,5
01-Mar-15,3,-29.8544,31.04598,A6,1,1
01-Mar-15,3,-29.8416,31.03864,A7,1,1
01-Mar-15,3,-29.84162,31.04041,A8,1,1
01-Mar-15,3,-29.84161,31.04219,A9,1,1
01-Mar-15,3,-29.83229,31.03868,A10,4,10
01-Mar-15,3,-29.83246,31.04055,A11,1,5
01-Mar-15,3,-29.83256,31.04251,A12,1,2
01-Mar-15,3,-29.82418,31.03922,A13,6,26
01-Mar-15,3,-29.82448,31.04116,A14,3,7
01-Mar-15,3,-29.82461,31.04331,A15,1,1
01-Mar-15,3,-29.81028,31.04301,A16,70,70
01-Mar-15,3,-29.81083,31.0449,A17,70,70
01-Mar-15,3,-29.81114,31.0467,A18,90,70
01-Mar-15,3,-29.79983,31.04648,A19,27,210
01-Mar-15,3,-29.80046,31.04805,A20,54,600
01-Mar-15,3,-29.80078,31.04983,A21,90,70
01-Mar-15,3,-29.78945,31.05083,A22,27,160
01-Mar-15,3,-29.79008,31.05287,A23,45,250
01-Mar-15,3,-29.79047,31.05408,A24,53,580
01-Mar-15,3,-29.78094,31.05577,A25,19,70
01-Mar-15,3,-29.78131,31.05677,A26,37,180
01-Mar-15,3,-29.78178,31.05827,A27,60,400
01-Mar-15,3,-29.77251,31.06032,A28,6,28
01-Mar-15,3,-29.77316,31.0618,A29,1,32
01-Mar-15,3,-29.77348,31.06291,A30,6,38
01-Mar-15,3,-29.86373,31.05944,A31,3,6
01-Mar-15,3,-29.865902,31.058159,A32,3,6
01-Mar-15,3,-29.810559,31.035082,A33,120,30
01-Mar-15,3,-29.866335,31.049232,A34,1,1
02-Mar-15,4,-29.86198,31.04568,A1,12,11
02-Mar-15,4,-29.86151,31.0472,A2,8,5
02-Mar-15,4,-29.86098,31.04907,A3,6,3
02-Mar-15,4,-29.85482,31.04243,A4,14,14
02-Mar-15,4,-29.8547,31.04394,A5,16,13
02-Mar-15,4,-29.8544,31.04598,A6,3,4
02-Mar-15,4,-29.8416,31.03864,A7,37,27
02-Mar-15,4,-29.84162,31.04041,A8,10,7
02-Mar-15,4,-29.84161,31.04219,A9,9,7
02-Mar-15,4,-29.83229,31.03868,A10,200,30
02-Mar-15,4,-29.83246,31.04055,A11,25,11
02-Mar-15,4,-29.83256,31.04251,A12,52,23
02-Mar-15,4,-29.82418,31.03922,A13,400,43
02-Mar-15,4,-29.82448,31.04116,A14,360,70
02-Mar-15,4,-29.82461,31.04331,A15,420,62
02-Mar-15,4,-29.81028,31.04301,A16,1000,110
02-Mar-15,4,-29.81083,31.0449,A17,1100,100
02-Mar-15,4,-29.81114,31.0467,A18,900,120
02-Mar-15,4,-29.79983,31.04648,A19,6300,170
02-Mar-15,4,-29.80046,31.04805,A20,4800,190
02-Mar-15,4,-29.80078,31.04983,A21,4700,110
02-Mar-15,4,-29.78945,31.05083,A22,1000,68
02-Mar-15,4,-29.79008,31.05287,A23,18,7
02-Mar-15,4,-29.79047,31.05408,A24,21,6
02-Mar-15,4,-29.78094,31.05577,A25,,
02-Mar-15,4,-29.78131,31.05677,A26,20,2
02-Mar-15,4,-29.78178,31.05827,A27,15,10
02-Mar-15,4,-29.77251,31.06032,A28,27,14
02-Mar-15,4,-29.77316,31.0618,A29,34,18
02-Mar-15,4,-29.77348,31.06291,A30,22,6
02-Mar-15,4,-29.86373,31.05944,A31,800,200
02-Mar-15,4,-29.865902,31.058159,A32,800,200
02-Mar-15,4,-29.810559,31.035082,A33,2400,600
02-Mar-15,4,-29.866335,31.049232,A34,12,11

Related

Plot 4D data heatmap in Python

hey how can I plot a 2D heatmap in 3D? Now I create a python script to make a 2D Heatmap Plot with data from CSV (CSV format: x,y,z,v).
For example:
First csv
0,000;-110,000;110,000;0,101
Second csv
0,000;-66,000;110,000;0,104
Third csv
0,000;-22,000;110,000;0,119
....
In this example, it is a heatmap in xz-plane and I create e.g. five more plots, so that I can insert six xz-plane Plots in a 3D room.
In 4D heatmap plot with matplotlib there is a very nice example for doing it. But I don't know how to use it in my case.
import numpy as np
import os
import matplotlib.pyplot as plt
from scipy.interpolate import griddata
'Create a list for every parameter'
x = []
y = []
z = []
v = []
file_path = "path/."
'Insert data from csv into lists'
for root, dirs, files in os.walk(file_path, topdown=False):
for name in files:
if name[-4:] != '.csv': continue
with open(os.path.join(root, name)) as data:
data = np.genfromtxt((line.replace(',', '.') for line in data), delimiter=";")
if data[1] == 22:
x.append(data[0])
y.append(data[1])
z.append(data[2])
v.append(data[3])
'Create axis data'
xi = np.linspace(min(x), max(x), 1000)
zi = np.linspace(min(z), max(z), 1000)
vi = griddata((x, z), v, (xi[None,:], zi[:,None]), method='cubic')
'Create the contour plot'
CS = plt.contourf(xi, zi, vi, 20, cmap=plt.cm.rainbow)
plt.title("Heatmap xz-plane", y=1.05,
fontweight="bold")
plt.xlabel("length x in cm")
plt.xticks(np.arange(0, 201, step=40))
plt.ylabel("height z in cm")
plt.yticks(np.arange(110, 251, step=20))
cbar = plt.colorbar()
cbar.set_label("velocity v in m/s", labelpad=10)
plt.savefig('testplot.png', dpi=400)
plt.show()
Satisfying the request of #keepAlive wishing to see the result of his untested answer... :
it actually works great :-)
Disclaimer: I am the author of the cited example, so I think that copying/pasting myself is not really a problem.
Note that your dataset does not look (at least) 3-dimensional. But I will assume there is an unwilling selection bias.
You first need to aggregate your "points" per level of altitude, which I assume is the third component of your vectors. They will be constitutive of your planes once gathered.
# libraries
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import scipy.interpolate as si
from matplotlib import cm
import collections as co # <------------------
import pandas as pd
import numpy as np
planes = co.defaultdict(list)
for root, dirs, files in os.walk(file_path, topdown=False):
# [...]
# [...]
# [...]
# [...]
# [...]
level = data[2] # <------ third component.
planes[level].append(data)
Now, at that stage, we have a list of arrays per level. Let's define our grids_maker function
def grids_maker(arrays_list, colnames=list('xyzg')):
# 0- The idea behind `list('xyzg')` is only to change the order
# of names, not the names as such. In case for example you
# want to use another component than the third to organize
# your planes.
# 1- Instantiate a dataframe so as to minimize the modification
# of the function copied/pasted pasted from
# https://stackoverflow.com/a/54075350/4194079
# 2- Pandas is also going to do some other jobs for us, such as
# stacking arrays, etc....
df = pd.DataFrame(arrays_list, columns=colnames)
# Make things more legible
xy = df.loc[:, ['x', 'y']]
x = xy.x
y = xy.y
z = df.z
g = df.g
reso_x = reso_y = 50
interp = 'cubic' # or 'nearest' or 'linear'
# Convert the 4d-space's dimensions into grids
grid_x, grid_y = np.mgrid[
x.min():x.max():1j*reso_x,
y.min():y.max():1j*reso_y
]
grid_z = si.griddata(
xy, z.values,
(grid_x, grid_y),
method=interp
)
grid_g = si.griddata(
xy, g.values,
(grid_x, grid_y),
method=interp
)
return {
'x' : grid_x,
'y' : grid_y,
'z' : grid_z,
'g' : grid_g,
}
Let's use grids_maker over our list of arrays and get the extrema of each z-level's 4th dimension.
g_mins = []
g_maxs = []
lgrids = {}
for level, arrays_list in planes.items():
lgrids[level] = grids = grids_maker(arrays_list)
g_mins.append(grids['g'].min())
g_maxs.append(grids['g'].max())
Let's create our (all-file unifying) color-scale and show the plot.
# Create the 4th color-rendered dimension
scam = plt.cm.ScalarMappable(
norm=cm.colors.Normalize(min(g_mins), max(g_maxs)),
cmap='jet' # see https://matplotlib.org/examples/color/colormaps_reference.html
)
fig = plt.figure()
ax = fig.gca(projection='3d')
for grids in lgrids.values():
scam.set_array([])
ax.plot_surface(
grids['x'], grids['y'], grids['z'],
facecolors = scam.to_rgba(grids['g']),
antialiased = True,
rstride=1, cstride=1, alpha=None
)
plt.show()
I would be glad to see the result.

How to plot a list of figures in a single subplot?

I have 2 lists of figures and their axes.
I need to plot each figure in a single subplot so that the figures become in one big subplot. How can I do that?
I tried for loop but it didn't work.
Here's what I have tried:
import ruptures as rpt
import matplotlib.pyplot as plt
# make random data with 100 samples and 9 columns
n_samples, n_dims, sigma = 100, 9, 2
n_bkps = 4
signal, bkps = rpt.pw_constant(n_samples, n_dims, n_bkps, noise_std=sigma)
figs, axs = [], []
for i in range(signal.shape[1]):
points = signal[:,i]
# detection of change points
algo = rpt.Dynp(model='l2').fit(points)
result = algo.predict(n_bkps=2)
fig, ax = rpt.display(points, bkps, result, figsize=(15,3))
figs.append(fig)
axs.append(ax)
plt.show()
I had a look at the source code of ruptures.display(), and it accepts **kwargs that are passed on to matplotlib. This allows us to redirect the output to a single figure, and with gridspec, we can position individual subplots within this figure:
import ruptures as rpt
import matplotlib.pyplot as plt
n_samples, n_dims, sigma = 100, 9, 2
n_bkps = 4
signal, bkps = rpt.pw_constant(n_samples, n_dims, n_bkps, noise_std=sigma)
#number of subplots
n_subpl = signal.shape[1]
#give figure a name to refer to it later
fig = plt.figure(num = "ruptures_figure", figsize=(8, 15))
#define grid of nrows x ncols
gs = fig.add_gridspec(n_subpl, 1)
for i in range(n_subpl):
points = signal[:,i]
algo = rpt.Dynp(model='l2').fit(points)
result = algo.predict(n_bkps=2)
#rpt.display(points, bkps, result)
#plot into predefined figure
_, curr_ax = rpt.display(points, bkps, result, num="ruptures_figure")
#position current subplot within grid
curr_ax[0].set_position(gs[i].get_position(fig))
curr_ax[0].set_subplotspec(gs[i])
plt.show()
Sample output:

How to use geopandas to plot latitude and longitude on a more detailed map with by using basemaps?

I am trying to plot some latitude and longitudes on the map of delhi which I am able to do by using a shape file in python3.8 using geopandas
Here is the link for the shape file:
https://drive.google.com/file/d/1CEScjlcsKFCgdlME21buexHxjCbkb3WE/view?usp=sharing
Following is my code to plot points on the map:
lo=[list of longitudes]
la=[list of latitudes]
delhi_map = gpd.read_file(r'C:\Users\Desktop\Delhi_Wards.shp')
fig,ax = plt.subplots(figsize = (15,15))
delhi_map.plot(ax = ax)
geometry = [Point(xy) for xy in zip(lo,la)]
geo_df = gpd.GeoDataFrame(geometry = geometry)
print(geo_df)
g = geo_df.plot(ax = ax, markersize = 20, color = 'red',marker = '*',label = 'Delhi')
plt.show()
Following is the result:
Now this map is not very clear and anyone will not be able to recognise the places marked so i tried to use basemap for a more detailed map through the following code:
df = gpd.read_file(r'C:\Users\Jojo\Desktop\Delhi_Wards.shp')
new_df = df.to_crs(epsg=3857)
print(df.crs)
print(new_df.crs)
ax = new_df.plot()
ctx.add_basemap(ax)
plt.show()
And following is the result:
I am getting the basemap but my shapefile is overlapping it. Can i get a map to plot my latitudes and longitudes where the map is much more detailed with names of places or roads or anything similar to it like in google maps or even something like the map which is being overlapped by the blue shapefile map?
Is it possible to plot on a map like this??
https://www.researchgate.net/profile/P_Jops/publication/324715366/figure/fig3/AS:618748771835906#1524532611545/Map-of-Delhi-reproduced-from-Google-Maps-12.png
use zorder parameter to adjust the layers' orders (lower zorder means lower layer), and alpha to the polygon. anyway, I guess, you're plotting df twice, that's why it's overlapping.
here's my script and the result
import geopandas as gpd
import matplotlib.pyplot as plt
import contextily as ctx
from shapely.geometry import Point
long =[77.2885437011719, 77.231931, 77.198767, 77.2750396728516]
lat = [28.6877899169922, 28.663863, 28.648287, 28.5429172515869]
geometry = [Point(xy) for xy in zip(long,lat)]
wardlink = "New Folder/wards delimited.shp"
ward = gpd.read_file(wardlink, bbox=None, mask=None, rows=None)
geo_df = gpd.GeoDataFrame(geometry = geometry)
ward.crs = {'init':"epsg:4326"}
geo_df.crs = {'init':"epsg:4326"}
# plot the polygon
ax = ward.plot(alpha=0.35, color='#d66058', zorder=1)
# plot the boundary only (without fill), just uncomment
#ax = gpd.GeoSeries(ward.to_crs(epsg=3857)['geometry'].unary_union).boundary.plot(ax=ax, alpha=0.5, color="#ed2518",zorder=2)
ax = gpd.GeoSeries(ward['geometry'].unary_union).boundary.plot(ax=ax, alpha=0.5, color="#ed2518",zorder=2)
# plot the marker
ax = geo_df.plot(ax = ax, markersize = 20, color = 'red',marker = '*',label = 'Delhi', zorder=3)
ctx.add_basemap(ax, crs=geo_df.crs.to_string(), source=ctx.providers.OpenStreetMap.Mapnik)
plt.show()
I don't know about google maps being in the contextily, I don't think it's available. alternatively, you can use OpenStreetMap base map which shows quite the same toponym, or any other basemap you can explore. use `source` keyword in the argument, for example, `ctx.add_basemap(ax, source=ctx.providers.OpenStreetMap.Mapnik)` . here's how to check the available providers and the map each providers provides:
>>> ctx.providers.keys()
dict_keys(['OpenStreetMap', 'OpenSeaMap', 'OpenPtMap', 'OpenTopoMap', 'OpenRailwayMap', 'OpenFireMap', 'SafeCast', 'Thunderforest', 'OpenMapSurfer', 'Hydda', 'MapBox', 'Stamen', 'Esri', 'OpenWeatherMap', 'HERE', 'FreeMapSK', 'MtbMap', 'CartoDB', 'HikeBike', 'BasemapAT', 'nlmaps', 'NASAGIBS', 'NLS', 'JusticeMap', 'Wikimedia', 'GeoportailFrance', 'OneMapSG'])
>>> ctx.providers.OpenStreetMap.keys()
dict_keys(['Mapnik', 'DE', 'CH', 'France', 'HOT', 'BZH'])
I don't know geopandas. The idea I'm suggesting uses only basic python and matplotlib. I hope you can adapt it to your needs.
The background is the following map. I figured out the GPS coordinates of its corners using google-maps.
The code follows the three points of my remark. Note that the use of imread and imshow reverses the y coordinate. This is why the function coordinatesOnFigur looks non-symmetrical in x and y.
Running the code yields the map with a red bullet near Montijo (there is a small test at the end).
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib import patches
from matplotlib.widgets import Button
NE = (-8.9551, 38.8799)
SE = (-8.9551, 38.6149)
SW = (-9.4068, 38.6149)
NW = (-9.4068, 38.8799)
fig = plt.figure(figsize=(8, 6))
axes = fig.add_subplot(1,1,1, aspect='equal')
img_array = plt.imread("lisbon_2.jpg")
axes.imshow(img_array)
xmax = axes.get_xlim()[1]
ymin = axes.get_ylim()[0] # the y coordinates are reversed, ymax=0
# print(axes.get_xlim(), xmax)
# print(axes.get_ylim(), ymin)
def coordinatesOnFigure(long, lat, SW=SW, NE=NE, xmax=xmax, ymin=ymin):
px = xmax/(NE[0]-SW[0])
qx = -SW[0]*xmax/(NE[0]-SW[0])
py = -ymin/(NE[1]-SW[1])
qy = NE[1]*ymin/(NE[1]-SW[1])
return px*long + qx, py*lat + qy
# plotting a red bullet that corresponds to a GPS location on the map
x, y = coordinatesOnFigure(-9, 38.7)
print("test: on -9, 38.7 we get", x, y)
axes.scatter(x, y, s=40, c='red', alpha=0.9)
plt.show()

Heatmap with circles indicating size of population

I would like to produce a heatmap in Python, similar to the one shown, where the size of the circle indicates the size of the sample in that cell. I looked in seaborn's gallery and couldn't find anything, and I don't think I can do this with matplotlib.
It's the inverse. While matplotlib can do pretty much everything, seaborn only provides a small subset of options.
So using matplotlib, you can plot a PatchCollection of circles as shown below.
Note: You could equally use a scatter plot, but since scatter dot sizes are in absolute units it would be rather hard to scale them into the grid.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import PatchCollection
N = 10
M = 11
ylabels = ["".join(np.random.choice(list("PQRSTUVXYZ"), size=7)) for _ in range(N)]
xlabels = ["".join(np.random.choice(list("ABCDE"), size=3)) for _ in range(M)]
x, y = np.meshgrid(np.arange(M), np.arange(N))
s = np.random.randint(0, 180, size=(N,M))
c = np.random.rand(N, M)-0.5
fig, ax = plt.subplots()
R = s/s.max()/2
circles = [plt.Circle((j,i), radius=r) for r, j, i in zip(R.flat, x.flat, y.flat)]
col = PatchCollection(circles, array=c.flatten(), cmap="RdYlGn")
ax.add_collection(col)
ax.set(xticks=np.arange(M), yticks=np.arange(N),
xticklabels=xlabels, yticklabels=ylabels)
ax.set_xticks(np.arange(M+1)-0.5, minor=True)
ax.set_yticks(np.arange(N+1)-0.5, minor=True)
ax.grid(which='minor')
fig.colorbar(col)
plt.show()
Here's a possible solution using Bokeh Plots:
import pandas as pd
from bokeh.palettes import RdBu
from bokeh.models import LinearColorMapper, ColumnDataSource, ColorBar
from bokeh.models.ranges import FactorRange
from bokeh.plotting import figure, show
from bokeh.io import output_notebook
import numpy as np
output_notebook()
d = dict(x = ['A','A','A', 'B','B','B','C','C','C','D','D','D'],
y = ['B','C','D', 'A','C','D','B','D','A','A','B','C'],
corr = np.random.uniform(low=-1, high=1, size=(12,)).tolist())
df = pd.DataFrame(d)
df['size'] = np.where(df['corr']<0, np.abs(df['corr']), df['corr'])*50
#added a new column to make the plot size
colors = list(reversed(RdBu[9]))
exp_cmap = LinearColorMapper(palette=colors,
low = -1,
high = 1)
p = figure(x_range = FactorRange(), y_range = FactorRange(), plot_width=700,
plot_height=450, title="Correlation",
toolbar_location=None, tools="hover")
p.scatter("x","y",source=df, fill_alpha=1, line_width=0, size="size",
fill_color={"field":"corr", "transform":exp_cmap})
p.x_range.factors = sorted(df['x'].unique().tolist())
p.y_range.factors = sorted(df['y'].unique().tolist(), reverse = True)
p.xaxis.axis_label = 'Values'
p.yaxis.axis_label = 'Values'
bar = ColorBar(color_mapper=exp_cmap, location=(0,0))
p.add_layout(bar, "right")
show(p)
One option is to use matplotlib's scatter plots with legends and grid. You can specify size of those circles with specifying the scales. You can also change the color of each circle. You should somehow specify X,Y values so that the circles sit straight on lines. This is an example I got from here:
volume = np.random.rayleigh(27, size=40)
amount = np.random.poisson(10, size=40)
ranking = np.random.normal(size=40)
price = np.random.uniform(1, 10, size=40)
fig, ax = plt.subplots()
# Because the price is much too small when being provided as size for ``s``,
# we normalize it to some useful point sizes, s=0.3*(price*3)**2
scatter = ax.scatter(volume, amount, c=ranking, s=0.3*(price*3)**2,
vmin=-3, vmax=3, cmap="Spectral")
# Produce a legend for the ranking (colors). Even though there are 40 different
# rankings, we only want to show 5 of them in the legend.
legend1 = ax.legend(*scatter.legend_elements(num=5),
loc="upper left", title="Ranking")
ax.add_artist(legend1)
# Produce a legend for the price (sizes). Because we want to show the prices
# in dollars, we use the *func* argument to supply the inverse of the function
# used to calculate the sizes from above. The *fmt* ensures to show the price
# in dollars. Note how we target at 5 elements here, but obtain only 4 in the
# created legend due to the automatic round prices that are chosen for us.
kw = dict(prop="sizes", num=5, color=scatter.cmap(0.7), fmt="$ {x:.2f}",
func=lambda s: np.sqrt(s/.3)/3)
legend2 = ax.legend(*scatter.legend_elements(**kw),
loc="lower right", title="Price")
plt.show()
Output:
I don't have enough reputation to comment on Delenges' excellent answer, so I'll leave my comment as an answer instead:
R.flat doesn't order the way we need it to, so the circles assignment should be:
circles = [plt.Circle((j,i), radius=R[j][i]) for j, i in zip(x.flat, y.flat)]
Here is an easy example to plot circle_heatmap.
from matplotlib import pyplot as plt
import pandas as pd
from sklearn.datasets import load_wine as load_data
from psynlig import plot_correlation_heatmap
plt.style.use('seaborn-talk')
data_set = load_data()
data = pd.DataFrame(data_set['data'], columns=data_set['feature_names'])
#data = df_corr_selected
kwargs = {
'heatmap': {
'vmin': -1,
'vmax': 1,
'cmap': 'viridis',
},
'figure': {
'figsize': (14, 10),
},
}
plot_correlation_heatmap(data, bubble=True, annotate=False, **kwargs)
plt.show()

Lines not plotting on graph using Python/Basemap

Sorry if this question is simple I'm a newb to using Python and Basemap. Anyway I'm trying to plot the path of 20 hurricanes on a map (graph). The map itself and the legend show up perfectly but the paths of the hurricanes do not. Also I'm not getting any traceback messages but I think I have an idea of where my problem may be. Could someone please tell me where I went wrong.
Here's a sample of the csv file:
Year, Name, Type, Latitude, Longitude
1957,AUDREY,HU, 21.6, 93.3
1957,AUDREY,HU,22.0, 93.4
1957,AUDREY,HU,22.6, 93.5
1969,AUDREY,HU,28.2,99.6
1957,AUDREY,HU,26.5,93.8
1957,AUDREY,HU,27.9,93.8
1957,AUDREY,HU,29.3,95
1957,AUDREY,HU,27.9,93.8
1957,AUDREY,HU,29.3,93.8
1957,AUDREY,HU,30.7,93.5
1969,CAMILLE,HU, 21.6,99.3
1969,CAMILLE,HU,22.0,98.4
1969,CAMILLE,HU,22.6,90.5
1969,CAMILLE,HU,23.2,93.6
Here's the code I have so far:
import numpy as np
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import csv, os, scipy
import pandas
from PIL import *
data = np.loadtxt('louisianastormb.csv',dtype=np.str,delimiter=',',skiprows=1)
'''print data'''
fig = plt.figure(figsize=(12,12))
ax = fig.add_axes([0.1,0.1,0.8,0.8])
m = Basemap(llcrnrlon=-100.,llcrnrlat=0.,urcrnrlon=-20.,urcrnrlat=57.,
projection='lcc',lat_1=20.,lat_2=40.,lon_0=-60.,
resolution ='l',area_thresh=1000.)
m.bluemarble()
m.drawcoastlines(linewidth=0.5)
m.drawcountries(linewidth=0.5)
m.drawstates(linewidth=0.5)
# Creates parallels and meridians
m.drawparallels(np.arange(10.,35.,5.),labels=[1,0,0,1])
m.drawmeridians(np.arange(-120.,-80.,5.),labels=[1,0,0,1])
m.drawmapboundary(fill_color='aqua')
color_dict = {'AUDREY': 'red', 'ETHEL': 'white', 'BETSY': 'yellow','CAMILLE': 'blue', 'CARMEN': 'green','BABE': 'purple', }
colnames = ['Year','Name','Type','Latitude','Longitude']
data = pandas.read_csv('louisianastormb.csv', names=colnames)
names = list(data.Name)
lat = list(data.Latitude)
long = list(data.Longitude)
colorName = list(data.Name)
#print lat
#print long
lat.pop(0)
long.pop(0)
colorName.pop(0)
latitude= map(float, lat)
longitude = map(float, long)
x, y = m(latitude,longitude)
#Plots points on map
for colorName in color_dict.keys():
plt.plot(x,y,linestyle ='-',label=colorName,color=color_dict[colorName], linewidth=5 )
lg = plt.legend()
lg.get_frame().set_facecolor('grey')
plt.show()
two (okay I lied, should be there) problems with your code
i, your input longitude should be negative to be within the boundary you defined for your basemap, so add this after before converting to x and y
longitude = [-i for i in longitude]
ii, your coordinate conversion line is wrong, you should swap lon and lat in the argument list
x, y = m(longitude, latitude)
instead of
x, y = m(latitude,longitude)
EDIT:
okay, the second question that OP posted in the comments, please check the complete code below and please pay attention to the changes I've made compared to yours
# Last-modified: 21 Oct 2013 05:35:16 PM
import numpy as np
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import csv, os, scipy
import pandas
from PIL import *
data = np.loadtxt('louisianastormb.csv',dtype=np.str,delimiter=',',skiprows=1)
'''print data'''
fig = plt.figure(figsize=(12,12))
ax = fig.add_axes([0.1,0.1,0.8,0.8])
m = Basemap(llcrnrlon=-100.,llcrnrlat=0.,urcrnrlon=-20.,urcrnrlat=57.,
projection='lcc',lat_1=20.,lat_2=40.,lon_0=-60.,
resolution ='l',area_thresh=1000.)
m.drawcoastlines(linewidth=0.5)
m.drawcountries(linewidth=0.5)
m.drawstates(linewidth=0.5)
# m.bluemarble(ax=ax)
# Creates parallels and meridians
m.drawparallels(np.arange(10.,35.,5.),labels=[1,0,0,1])
m.drawmeridians(np.arange(-120.,-80.,5.),labels=[1,0,0,1])
m.drawmapboundary(fill_color='aqua')
color_dict = {'AUDREY': 'red', 'ETHEL': 'white', 'BETSY': 'yellow','CAMILLE': 'blue', 'CARMEN': 'green','BABE': 'purple', }
colnames = ['Year','Name','Type','Latitude','Longitude']
data = pandas.read_csv('louisianastormb.csv', names=colnames)
names = list(data.Name)
lat = list(data.Latitude)
long = list(data.Longitude)
colorNames = list(data.Name)
#print lat
#print long
lat.pop(0)
long.pop(0)
colorNames.pop(0)
latitude= map(float, lat)
longitude = map(float, long)
# added by nye17
longitude = [-i for i in longitude]
# x, y = m(latitude,longitude)
x, y = m(longitude,latitude)
# convert to numpy arrays
x = np.atleast_1d(x)
y = np.atleast_1d(y)
colorNames = np.atleast_1d(colorNames)
#Plots points on map
for colorName in color_dict.keys():
plt.plot(x[colorName == colorNames],y[colorName == colorNames],linestyle ='-',label=colorName,color=color_dict[colorName], linewidth=5 )
lg = plt.legend()
lg.get_frame().set_facecolor('grey')
plt.show()
I think your difficulty is not so much in Basemap as in the plotting. Instead of plotting the entire x/y data set you need to find the x/y points corresponding on hurricane Z. Then plot only those points in a certain color c. Then find the points corresponding to the next hurricane etc...
The below, while not using the Basemap data structure should provide a starting point for plotting subsets of points based on some selector vector.
#given a list of x,y coordinates with a label we'll plot each line individually
#first construct some points to plot
x1 = [1,1.1,1.2,1.3, 2.0,2.2,2.3, 4,3.9,3.8,3.7]
y1 = [5,5.1,5.2,5.3, 6.0,6.2,6.3, 2,2.1,2.2,2.3]
pointNames = []
#generate some labels
pointNames.extend(['a']*4)
pointNames.extend(['b']*3)
pointNames.extend(['c']*4)
#make things easy by casting to numpy arrays to allow for easier indexing
x1 = numpy.array(x1)
y1 = numpy.array(y1)
pointNames = numpy.array(pointNames)
for elem in ['a','b','c']:
selector = pointNames==elem
subsetX = x1[selector]
subsetY = y1[selector]
#now plot subsetX vs subsetY in color Z
plot(subsetX,subsetY,'*-')
show()

Categories

Resources