In this example, rental prices of 2 bedroom apartments in the bay area are used to generate a price heat map overlayed on top of a Google map. The first step is to load the image's meta data which is stored externally in a json file. Python's json module is used.
In [ ]:
import json
with open('./map_info.json', 'r') as f:
map_info = json.loads(f.read())
coords = map_info
image_size = [coords.pop('width'), coords.pop('height')]
Pandas has a built in module for loading json files directly. Here it's used to load the markers data.
In [ ]:
import pandas as pd
markers = pd.io.json.read_json('./markers.json')
In [ ]:
markers[:5]
Each marker has details associated with it. Those details are called a "listing". The same Pandas loading method is used for the listings data.
In [ ]:
listings = pd.io.json.read_json('./listings.json')
In [ ]:
listings[:5]
As separate data frames, the data isn’t as useful to us. Here the data is merged into one data frame by listing id.
In [ ]:
df = markers.merge(listings, 'outer', 'id')
df[:5]
Take a quick look at all of the columns to see what data is available.
In [ ]:
df.columns.tolist()
Make a smaller frame containing the X and Y coordinates and the price.
In [ ]:
bd2_prices = df[['lat_x', 'lng_x', 'price']]
bd2_prices[:5]
When working with numerical data, I tend to use numpy. Converting from a Pandas data frame to a numpy array is easy.
In [ ]:
array = bd2_prices.as_matrix()
array
Since the map image size and the longitude and latitude window is known, the longitudes and latitudes can be converted to pixel coordinates, which is necessary for plotting on top of the map image.
In [ ]:
long2px = lambda x: (x - coords['westLong']) / (coords['eastLong'] - coords['westLong']) * image_size[0]
lat2px = lambda y: (1 - (y - coords['southLat']) / (coords['northLat'] - coords['southLat'])) * image_size[1]
array[:, 0] = lat2px(array[:, 0])
array[:, 1] = long2px(array[:, 1])
array
Import matplotlib and set the necessary config options.
In [ ]:
import matplotlib.pyplot as plt
%matplotlib inline
%config InlineBackend.figure_format = 'retina'
Load the map image a plot it.
In [ ]:
cropped_map = plt.imread('./map.png')
plt.imshow(cropped_map)
Plot the markers above the map image.
In [ ]:
plt.imshow(cropped_map)
plt.scatter(array[:, 1], array[:, 0], s=4., c='r', linewidths=0)
plt.show()
Use scipy's griddata function to interpolate the scattered markers into a heat map image. Plot that heat map above the Google map.
In [ ]:
import numpy as np
from scipy.interpolate import griddata
# define grid.
xi = np.arange(0,image_size[0])
yi = np.arange(0,image_size[1]-1)
# grid the data.
zi = griddata((array[:, 1], array[:, 0]),
array[:, 2], (xi[None,:], yi[:,None]),
method='linear', fill_value=array[:, 2].min())
overlay = plt.imread('./map_overlay.png')
zi = np.ma.masked_where(overlay[:,:,2]>0,zi)
plt.figure(figsize = (8,8))
plt.imshow(cropped_map)
plt.imshow(zi, alpha=0.5, cmap='nipy_spectral')
plt.colorbar()
plt.title('2bd Apartment Monthly Rent')