python - 从一组点创建计数网格

时间:2017-10-08 12:12:48

标签: python matplotlib matplotlib-basemap

我有一个经度/纬度的数据集如下:

id,spp,lon,lat
1a,sp1,1,9
1b,sp1,3,11
1c,sp1,6,12
2a,sp2,1,9
2b,sp2,1,10
2c,sp2,3,10
2d,sp2,4,11
2e,sp2,5,12
2f,sp2,6,12
3a,sp3,4,13
3b,sp3,5,11
3c,sp3,8,8
4a,sp4,4,12
4b,sp4,6,11
4c,sp4,7,8
5a,sp5,8,8
5b,sp5,7,6
5c,sp5,8,2
6a,sp6,8,8
6b,sp6,7,5
6c,sp6,8,3

根据这些数据,我想生成一个这样的网格:

0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 1 0 0 0 0 0 0 
0 0 0 0 0 0 1 1 2 0 0 0 0 
0 0 0 0 0 1 1 1 1 0 0 0 0 
0 0 0 1 0 1 0 0 0 0 0 0 0 
0 0 0 2 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 1 3 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 1 0 0 0 
0 0 0 0 0 0 0 0 0 1 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 1 0 0 
0 0 0 0 0 0 0 0 0 0 1 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0

使用变量" spp"给出网格的每个单元格中的数据记录数。作为分类(分组)因素。

从这个网格中,我想创建一个叠加在地图上的热图,这样我最终得到的结果如下图所示。

enter image description here

我(终于)设法编写了一些能够满足我想要的代码。

这是:

import csv
import numpy as np
from mpl_toolkits.basemap import Basemap
from matplotlib import cm as cmap
import matplotlib.pyplot as plt

import warnings
warnings.filterwarnings("ignore")

#read input data
with open('testdata.csv', 'r') as csvfile:
    reader = csv.reader(csvfile, delimiter=',')
    headers = reader.next()
    input_data = list(reader)

#grid dimensions with one-degree resolution
lat_inf, lon_inf = 0, 0
lat_sup, lon_sup = 90, 360
resolution = 1

latitude, longitude = [], []
latitude = range(lat_inf, lat_sup, resolution)
longitude = range(lon_inf, lon_sup, resolution)

#create output grid
output_grid = []
for i in latitude:
   output_grid.append([])
   for j in longitude:
       output_grid[i].append(0)

#traverse the input_data evaluating the lat, lon coordinates
#summing +1 in the output_grid[latitude][longitude].
for row in input_data:
   lat = int(row[2]) 
   lon = int(row[3]) 
   #sp = row[1]

   #check its indexes
   i_lat = latitude.index(lat)
   i_lon = longitude.index(lon)

   #increase counter
   output_grid[i_lat][i_lon] += 1

output_grid = np.array(output_grid, np.int16)

#create map
m = Basemap()
m.drawcoastlines(linewidth=0.25)

#display image
im = m.imshow(output_grid.transpose(), cmap='summer', origin='lower', aspect='auto', interpolation='none')
m.colorbar(im)
plt.show()

它(大部分)起作用,但问题是网格图像没有正确显示:它在地图的左下角显得太小,如下图所示。)

enter image description here

此外,有没有办法改变图像网格的背景颜色,而不是摆弄Matplotlib色彩图?

任何提示,想法和建议都将不胜感激。

2 个答案:

答案 0 :(得分:1)

在底图上绘制图像可能是可能的,但您需要摆弄坐标和范围以使其位于正确的位置。

最好使用支持非等间距网格的pcolormesh,因此非常适合不同的投影。您所需要的不仅是您的数据,还有网格。此网格定义数据的每个点的位置。 在使用网格之前,您需要将其转换为底图坐标系,因此如果lons,lats = np.meshgrid(..., ...)是网格,m = Basemap(..),则转换后的网格是

X, Y = m(lons,lats)

然后可以将其提供给pcolormesh

m.pcolormesh(X,Y,data)

一个完整的例子,我们有一个形状(60x40)的数据数组,我们希望它们位于-10到50度的经度范围内,纬度范围在-20到20度之间:

import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.basemap import Basemap

data = np.random.rand(40,60)

lons,lats = np.meshgrid(np.linspace(-10,50,data.shape[1]+1),
                        np.linspace(-20,20,data.shape[0]+1))

fig, ax = plt.subplots()
m = Basemap(projection='merc', ax=ax, lat_0=0.,lon_0=0.,
            llcrnrlon=-179.,llcrnrlat=-80.,urcrnrlon=170.,urcrnrlat=80.)
m.drawcoastlines()
X, Y = m(lons,lats)
pc = m.pcolormesh(X,Y,data,cmap='RdBu_r')

m.drawparallels(np.arange(-60,61,20),labels=[1,1,0,1])
m.drawmeridians([-90,-10,50,90],labels=[1,1,0,1])
plt.show()

enter image description here

答案 1 :(得分:0)

doc for Basemap

  

使用参数lon,lat将调用Basemap类实例   将lon / lat(以度为单位)转换为x / y地图投影坐标(in   米)。如果是可选关键字,则完成逆变换   inverse设置为True。

因此,您需要将数据转换为lat和lon的度数,将这些数据传递给Basemap,并使用返回给您的数据绘制您的位置。

您获得的颜色是" cool"的默认颜色。 (参见情节右侧的颜色条)。我不确定如何更改它,但我认为应该有一些参数可以传递给MPL例程来告诉它使用哪种颜色"很酷"以及#34; warm"。