This lesson is being piloted (Beta version)

Visualize and publish with Python

Overview

Teaching: 0 min
Exercises: 0 min
Questions
  • How to generate interactive plots?

  • How to generate animations?

  • How to create publication ready jupyter notebooks?

  • How to publish jupyter notebooks?

Objectives

Map plotting

Hexbin map example

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
from mpl_toolkits.basemap import Basemap
%matplotlib inline
import matplotlib.colors as colors
from numpy import array
from numpy import max
import numpy as np

plt.figure()
filename = "data/Justin/icepmag_20190118.csv"
frame = pd.read_csv(filename)

frame.head()
# replace -9999 by NaN (missing values)
frame.replace(-9999.0,np.NaN, inplace=True)
# remove rows where lon or lat are missing
frame.dropna(subset=['lon', 'lat'], inplace=True)
# drop duplicates
latlons = frame[['lat', 'lon']].drop_duplicates()

map = Basemap(llcrnrlon=335,llcrnrlat=63.0,urcrnrlon=348.,urcrnrlat=67.,
             resolution='i', projection='tmerc', lat_0 = 64.5, lon_0 = 340)


x,y = map(latlons['lon'].values,latlons['lat'].values)
map.hexbin(x,y, gridsize=20, mincnt=1, cmap='summer', norm=colors.LogNorm())


map.drawcoastlines()
plt.show()

Interactive plot in the Jupyter notebooks

Add interactive button to select image

You can allow users to select themselves which plots to show:

import matplotlib
import matplotlib.pyplot as plt
from matplotlib import colors as c
%matplotlib inline
from mpl_toolkits.basemap import Basemap, shiftgrid
import numpy as np
import netCDF4
from ipywidgets import interact

mpl.rcParams['figure.figsize'] = [20., 10.]
def drawmap(data, title):
    ax = plt.axes(projection=ccrs.PlateCarree(central_longitude=180))
    data.z.plot.pcolormesh(
                   transform=ccrs.PlateCarree())
    ax.set_title(data.title)
    ax.coastlines()
    ax.gridlines()
    
def myanimate(i, filenames):
    dset = xr.open_dataset(filenames[i])
    new_contour = drawmap(dset, 'data %03d'%(i) ) 
    dset.close()
   

@interact(depth=(0,20))
def finteract(depth):
     ca = myanimate(depth, filenames)

We used @interact function to automatically creates user interface (UI) for selecting the data frame. When you pass this function as the first argument to interact along with a keyword argument (here depth=(0,20)), a slider is generated and bound to the function parameter (depth).

The name of the function finteract is chosen by you and can be anything.

More information and examples here.

Create a mp4 movie

We can use a similar approach to generate mp4 movies.

import matplotlib.animation as animation

def drawmap(ax, data, title):
    data.z.plot.pcolormesh(add_colorbar=False,
                   transform=ccrs.PlateCarree())
    ax.set_title(data.title)
    ax.coastlines()
    ax.gridlines()
    
def myanimate(i, ax, filenames):
    ax.clear()
    dset = xr.open_dataset(filenames[i])
    new_contour = drawmap(ax, dset, 'data %03d'%(i) ) 
    dset.close()

FFMpegWriter = animation.writers['ffmpeg']
metadata = dict(title='Grace data', artist='CEED workshop',
                comment='Movie for sequence of images')
writer = FFMpegWriter(fps=20, metadata=metadata)

# Create the figure

fig = plt.figure(figsize=[20,10])  # a new figure window
ax = fig.add_subplot(1, 1, 1)  # specify (nrows, ncols, axnum)
ax = plt.axes(projection=ccrs.PlateCarree(central_longitude=180))
    
ani = animation.FuncAnimation(fig, myanimate, frames=np.arange(21), 
    fargs=(ax, filenames), interval=100)
ani.save("grace_movie.mp4"))

Embedded animations within your jupyter notebook

The main goal here is to create animations embedded within your jupyter notebook. This is fairly simple to plot your animation within your jupyter notebook.

Let’s continue our previous example, and add the following:

from IPython.display import HTML
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import numpy as np
mpl.rcParams["animation.embed_limit"]=100

import matplotlib.animation as animation

import glob
def getint(name):
    _, num = name.split('P_')
    num, _ = num.split('.')
    return int(num)

filenames = glob.glob("data/Grace/grd_files_to_interp/GypsumP_*.grd")

filenames = sorted(filenames, key=getint)

def drawmap(ax, data, title):
    data.z.plot.pcolormesh(add_colorbar=False,
                   transform=ccrs.PlateCarree())
    ax.set_title(data.title)
    ax.coastlines()
    ax.gridlines()
    
def myanimate(i, ax, filenames):
    ax.clear()
    dset = xr.open_dataset(filenames[i])
    new_contour = drawmap(ax, dset, 'data %03d'%(i) ) 
    dset.close()

FFMpegWriter = animation.writers['ffmpeg']
metadata = dict(title='Grace data', artist='CEED workshop',
                comment='Movie for sequence of images')
writer = FFMpegWriter(fps=20, metadata=metadata)

# Create the figure
fig = plt.figure(figsize=[20,10])  # a new figure window
ax = fig.add_subplot(1, 1, 1)  # specify (nrows, ncols, axnum)
ax = plt.axes(projection=ccrs.PlateCarree(central_longitude=180))

ani = animation.FuncAnimation(fig, myanimate, frames=np.arange(21), 
    fargs=(ax, filenames), interval=100)

HTML(ani.to_html5_video())

or generate HTML representation of the animation:

HTML(ani.to_jshtml())

Create an interactive map with folium

import folium
map = folium.Map(location=[65., 335.0], zoom_start=5 , tiles='Stamen Terrain')
map

Custom tileset

map = folium.Map(location=[65., 335.0], zoom_start=5 , 
                 tiles='https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}.png',
                 attr = "IGN")
map

Read csv file with pandas

import pandas as pd
import numpy as np
filename = "/opt/uio/deep_python/data/Justin/icepmag_20190118.csv"
frame = pd.read_csv(filename)

frame.head()
UID ref_id external_database_id region_id area_id location_id site_name lat lon height ... aniso_t aniso_l aniso_f aniso_ll aniso_ff aniso_vg aniso_fl aniso_test aniso_ftest12 aniso_ftest23
0 1 1 1 1 1 1 WB-1 64.355 338.7277 -9999.0 ... -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999
1 2 1 1 1 1 1 WB-2 64.355 338.7277 -9999.0 ... -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999
2 3 1 1 1 1 1 WB-3 64.355 338.7277 -9999.0 ... -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999
3 4 1 1 1 1 1 WB-4 64.355 338.7277 -9999.0 ... -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999
4 5 1 1 1 1 1 WB-5 64.355 338.7277 -9999.0 ... -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999

5 rows × 104 columns

Prepare dataset

# replace -9999 by NaN (missing values)
frame.replace(-9999.0,np.NaN, inplace=True)
# remove rows where lon or lat are missing
frame.dropna(subset=['lon', 'lat'], inplace=True)
# drop duplicates
latlons = frame[['lat', 'lon']].drop_duplicates()
latlons.head()
lat lon
0 64.35500 338.7277
18 64.25003 338.6219
43 64.25138 338.5982
57 64.25783 338.6408
63 64.26000 338.6318
map = folium.Map(location=[65., 335.0], zoom_start=5 , tiles='Stamen Terrain')
# loop over each point and add a marker to our map
for lat, lon in zip(latlons['lat'],latlons['lon']):
    folium.Marker([lat, lon]).add_to(map)
map
# Same as before but add a popup showing lat, lon values when clicking on a marker
# For popup, you can use HTML syntax
map = folium.Map(location=[65., 335.0], zoom_start=5 , tiles='Stamen Terrain')
for lat, lon in zip(latlons['lat'],latlons['lon']):
    folium.Marker([lat, lon], popup='<b>'+str(lat)+','+str(lon)+'</b>').add_to(map)
map