#!/usr/bin/env python # coding: utf-8 TO DO NEXT: Try last effort(described below) at making quartile legend. If that fails put the ugly-code matplotlib legend solution back in (it's hiding in the "copy". To do: Map Tiles Scale ?side by side maps point people towards the folium examples on github Clean up code and what not Write an intro (that includes the fact i'm looking for a job outside of teaching ASAP (but in portland):p) Write prose to explain any complicated bit Write conclusion about limitations ?Figure out how to deal with massive file sizes Figure out how to get it to the web for sharing # In[96]: #Collin Reinking #collin.reinking@berkeley.edu import folium get_ipython().run_line_magic('matplotlib', 'inline') import pandas as pd import re import os import branca.colormap as cm import re import numpy as np #import matplotlib as mpl #import matplotlib.pyplot as plt #from matplotlib import colors #from scipy import stats # # Simple Dot Plot On Map # In[2]: data = pd.read_pickle('data/listings_cleaned.pkl') data = pd.DataFrame(data, columns = ['latitude','longitude']) #[:50] <-this kind of slice is useful for developing a map data.head() # In[3]: #create a map this_map = folium.Map(prefer_canvas=True) def plotDot(point): '''input: series that contains a numeric named latitude and a numeric named longitude this function creates a CircleMarker and adds it to your this_map''' folium.CircleMarker(location=[point.latitude, point.longitude], radius=2, weight=0).add_to(this_map) #use df.apply(,axis=1) to iterate through every row in your dataframe data.apply(plotDot, axis = 1) #Set the zoom to the maximum possible this_map.fit_bounds(this_map.get_bounds()) #Save the map to an HTML file #this_map.save(os.path.join('html_map_output/simple_dot_plot.html')) this_map # # Dots with Popup Info # In[5]: data = pd.read_pickle('data/listings_cleaned.pkl') data = pd.DataFrame(data, columns = ['latitude','longitude','listing_name']) #[:50] <-this kind of slice is useful for developing a map data.head() # In[10]: #create a map this_map = folium.Map(prefer_canvas=True) def plotDot(point): '''input: series that contains a numeric named latitude and a numeric named longitude this function creates a CircleMarker and adds it to your this_map''' folium.CircleMarker(location=[point.latitude, point.longitude], radius=2, weight=0,#remove outline popup = point.listing_name, fill_color='#000000').add_to(this_map) #use df.apply(,axis=1) to iterate through every row in your dataframe data.apply(plotDot, axis = 1) #Set the zoom to the maximum possible this_map.fit_bounds(this_map.get_bounds()) #Save the map to an HTML file #this_map.save(os.path.join('html_map_output/basic_pop_up.html')) this_map # # Popups with Links # In[10]: data = pd.read_pickle('data/listings_cleaned.pkl') data = pd.DataFrame(data, columns = ['latitude','longitude','listing_name','listing_url','host_name','host_url'])[:50]# <-this kind of slice is useful for developing a map data.head() # In[12]: #create a map this_map = folium.Map(prefer_canvas=True) def makeHref(url,link_text = None): if link_text == None: link_text = str(url) return '' + re.sub(r"[']+", "\\\\'", link_text[:45]) +'' def popopHTMLString(point): '''input: a series that contains a url somewhere in it and generate html''' html = 'Listing: ' + makeHref(point.listing_url, point.listing_name) + '
' html += 'Host: ' + makeHref(point.host_url, point.host_name) return html def plotDot(point): '''input: series that contains a numeric named latitude and a numeric named longitude this function creates a CircleMarker and adds it to your this_map''' htmlString = folium.Html(popopHTMLString(point), script=True) folium.CircleMarker(location=[point.latitude, point.longitude], radius=2, weight=0,#remove outline popup = folium.Popup(htmlString), fill_color='#000000').add_to(this_map) #use df.apply(,axis=1) to iterate through every row in your dataframe data.apply(plotDot, axis = 1) #Set the zoom to the maximum possible this_map.fit_bounds(this_map.get_bounds()) #Save the map to an HTML file #this_map.save(os.path.join('html_map_output/html_pop_up.html')) this_map # # Color by value using a Colormap # In[20]: data = pd.read_pickle('data/listings_cleaned.pkl') data = pd.DataFrame(data, columns = ['latitude','longitude','listed_price','accommodates'])[:50]# <-this kind of slice is useful for developing a map data['price_per_person'] = data.listed_price / data.accommodates data.drop(['listed_price','accommodates'], axis='columns', inplace=True) data.head() # In[16]: blRd = cm.LinearColormap(['blue', 'red'], vmin=3, vmax=10) blRd # In[97]: #create a map this_map = folium.Map(prefer_canvas=True) #create a color map color_var = 'price_per_person' #what variable will determine the color cmap = cm.LinearColormap(['blue', 'red'], vmin=data[color_var].quantile(0.05), vmax=data[color_var].quantile(0.95), caption = color_var) #Add the color map legend to your map this_map.add_child(cmap) def plotDot(point): '''input: series that contains a numeric named latitude and a numeric named longitude this function creates a CircleMarker and adds it to your this_map''' folium.CircleMarker(location=[point.latitude, point.longitude], fill_color=cmap(point[color_var]), radius=2, weight=0).add_to(this_map) #use df.apply(,axis=1) to iterate through every row in your dataframe data.apply(plotDot, axis = 1) #Set the zoom to the maximum possible this_map.fit_bounds(this_map.get_bounds()) #Save the map to an HTML file #this_map.save(os.path.join('html_map_output/color_by_value.html')) this_map # # Color Points by quartile groupings # In[98]: data = pd.read_pickle('data/listings_cleaned.pkl') data = pd.DataFrame(data, columns = ['latitude','longitude','listed_price','listing_name','listing_url','host_name','host_url']) data.head() # In[107]: color_var = 'listed_price' cmap = cm.LinearColormap(['cornflowerblue', 'limegreen', 'orange', 'red'] ) cmap = cmap.to_step( n=4, data=data[color_var], method='quantiles') cmap.caption = color_var + " grouped by quartile" this_map = folium.Map(prefer_canvas=True) this_map.add_child(cmap) this_map #As you can see there is a major limitation with this map: #The data data in the color_var is so skewed that the 4th quartile #completely dominates the legend. # In[135]: cmap_linear = cm.LinearColormap(['cornflowerblue', 'limegreen', 'orange', 'red']) cmap = cmap_linear.to_step( n=4, data=data[color_var], method='quantiles') cmap_legend = cmap_linear.to_step(4) cmap_legend.caption = color_var + " grouped by quartile" this_map = folium.Map(prefer_canvas=True) this_map.add_child(cmap_legend) this_map #this Makes the a well proportioned colorbar for the legend #and you can still use cmap for the color map BUT... #there is no way to change the labels on top so the next solution to try to attempt is to create a proxy variable to reprsent the quartile that the color_var quartile and use that for color create the color bar AND color the point # In[102]: import pdir pdir(cmap) # In[25]: #create a map #this_map = folium.Map(prefer_canvas=True) from matplotlib import colors #create a color map my_colors = [(.4, .4, 1), (0, .9, 0) ,(1, .6, 0) , (1, 0, 0)] cm = colors.LinearSegmentedColormap.from_list("MyMap", my_colors, N=4) #cm is the color map #norm = colors.Normalize(0, 1) #color_dict = {data.ix[p][color_var]:colors.to_hex(cm(data.ix[p].color_group-.0001)) for p in data.T} cm # In[32]: fig = plt.figure(figsize=(4, .1)) ax = fig.add_axes([1, 1, 1, 1]) leg = mpl.colorbar.ColorbarBase(ax, cmap=cm, norm=norm, orientation='horizontal') leg.set_ticks([q/4 for q in range(5)]) leg.set_ticklabels([data[color_var].quantile(q/4) for q in range(5)]) fig.text(1.5, 2, color_var, horizontalalignment='center', verticalalignment='bottom') fig.savefig(str('legends/legend_'+color_var+'_quartiles.png'), bbox_inches='tight',transparent=True) #plt.close(fig) #To DO: there has to be a smarter way to do this (dealing with the "URL" issue) FloatImage(str(server_url + 'files/legends/legend_'+color_var+'_quartiles.png'),bottom=90, left=65).add_to(this_map) # In[33]: def popopHTMLString(point): '''input: a series that contains a url somewhere in it and generate html''' html = 'Listing: ' html += '' html += re.sub(r"[']+", "\\\\'", point.listing_name) #to put a \ in front of any ' in the name html += ' ' html += '
' html += 'Host: ' html += '' html += re.sub(r"[']+", "\\\\'", point.host_name) html += ' ' html += '
' html += 'listed_price = ' html += str('${:,.2f}'.format(point.listed_price)) return html # In[34]: def plotDot(point): '''input: series that contains a numeric named latitude and a numeric named longitude this function creates a CircleMarker and adds it to your this_map''' htmlString = folium.Html(popopHTMLString(point), script=True) folium.CircleMarker(location=[point.latitude, point.longitude], fill_color=color_dict[point[color_var]], ####NOTE THE CHANGE IN THE COLOR radius=2, popup = folium.Popup(htmlString), weight=0).add_to(this_map) #use df.apply(,axis=1) to iterate through every row in your dataframe data.apply(plotDot, axis = 1) #Set the zoom to the maximum possible this_map.fit_bounds(this_map.get_bounds()) #Save the map to an HTML file #this_map.save(os.path.join('html_map_output/color_by_value_quartile.html')) this_map