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()