Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ab49a68

Browse files
Merge branch 'post-animated-polar-plot' of https://github.com/quai20/matplotblog into quai20-post-animated-polar-plot
2 parents 976cc68 + c9383ce commit ab49a68

File tree

4 files changed

+157
-0
lines changed

4 files changed

+157
-0
lines changed
Loading
59.2 KB
Loading
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
---
2+
title: "Animated polar plot with oceanographic data"
3+
date: 2020-06-12T09:56:36+02:00
4+
draft: false
5+
description: "This post describes how to animate some oceanographic measurements in a tweaked polar plot"
6+
categories: ["tutorials"]
7+
displayInList: true
8+
author: Kevin Balem
9+
resources:
10+
- name: featuredImage
11+
src: "thumbnail.png"
12+
params:
13+
showOnTop: false
14+
---
15+
The **ocean** is a key component of the Earth climate system. It thus needs a continuous real-time monitoring to help scientists better understand its dynamic and predict its evolution. All around the world, oceanographers have managed to join their efforts and set up a [Global Ocean Observing System](https://www.goosocean.org) among which [**Argo**](http://www.argo.ucsd.edu/) is a key component. Argo is a global network of nearly 4000 autonomous probes or floats measuring pressure, temperature and salinity from the surface to 2000m depth every 10 days. The localisation of these floats is nearly random between the 60th parallels (see live coverage [here](http://collab.umr-lops.fr/app/divaa/)). All data are collected by satellite in real-time, processed by several data centers and finally merged in a single dataset (collecting more than 2 millions of vertical profiles data) made freely available to anyone.
16+
17+
In this particular case, we want to plot temperature (surface and 1000m deep) data measured by those floats, for the period 2010-2020 and for the Mediterranean sea. We want this plot to be circular and animated, now you start to get the title of this post : **Animated polar plot**
18+
19+
First we need some data to work with. To retrieve our temperature values from argo float, we use [**Argopy**](https://argopy.readthedocs.io) wich is a python library that aims to ease Argo data access, manipulation and visualisation for standard users as well as Argo experts and operators. Argopy returns [xarray](http://xarray.pydata.org) dataset objects, wich make our analysis much easier.
20+
```python
21+
import pandas as pd
22+
import numpy as np
23+
from argopy import DataFetcher as ArgoDataFetcher
24+
argo_loader = ArgoDataFetcher(cache=True)
25+
#
26+
# Query surface and 1000m temp in Med sea with argopy
27+
df1 = argo_loader.region([-1.2,29.,28.,46.,0,10.,'2009-12','2020-01']).to_xarray()
28+
df2 = argo_loader.region([-1.2,29.,28.,46.,975.,1025.,'2009-12','2020-01']).to_xarray()
29+
#
30+
```
31+
32+
Here we create some arrays we'll use for plotting, we set up a date array and extract day of the year and year itself that will be usefull. Then to build our temperature array, we use xarray very usefull methods : `where()` and `mean()`. Then we build a pandas Dataframe, because it's prettier !
33+
```python
34+
# Weekly date array
35+
daterange=np.arange('2010-01-01','2020-01-03',dtype='datetime64[7D]'
36+
dayoftheyear=pd.DatetimeIndex(np.array(daterange,dtype='datetime64[D]')+3).dayofyear # middle of the week
37+
activeyear=pd.DatetimeIndex(np.array(daterange,dtype='datetime64[D]')+3).year # extract year
38+
39+
# Init final arrays
40+
tsurf=np.zeros(len(daterange))
41+
t1000=np.zeros(len(daterange))
42+
43+
# Filling arrays
44+
for i in range(len(daterange)):
45+
i1=(df1['TIME']>=daterange[i])&(df1['TIME']<daterange[i]+7)
46+
i2=(df2['TIME']>=daterange[i])&(df2['TIME']<daterange[i]+7)
47+
tsurf[i]=df1.where(i1,drop=True)['TEMP'].mean().values
48+
t1000[i]=df2.where(i2,drop=True)['TEMP'].mean().values
49+
50+
# Creating dataframe
51+
d = {'date': np.array(daterange,dtype='datetime64[D]'), 'tsurf': tsurf, 't1000': t1000}
52+
ndf = pd.DataFrame(data=d)
53+
ndf.head()
54+
55+
date tsurf t1000
56+
0 2009-12-31 15.725000 13.306133
57+
1 2010-01-07 15.530414 13.315658
58+
2 2010-01-14 15.307378 13.300347
59+
3 2010-01-21 14.954195 13.300647
60+
4 2010-01-28 14.708816 13.300274
61+
```
62+
63+
64+
Then it's time to plot, for that we first need to import what we need, and set some usefull variables.
65+
```python
66+
import matplotlib.pyplot as plt
67+
import matplotlib
68+
plt.rcParams['xtick.major.pad']='17'
69+
plt.rcParams["axes.axisbelow"] = False
70+
matplotlib.rc('axes',edgecolor='w')
71+
from matplotlib.lines import Line2D
72+
from matplotlib.animation import FuncAnimation
73+
from IPython.display import HTML
74+
75+
big_angle= 360/12 # How we split our polar space
76+
date_angle=((360/365)*dayoftheyear)*np.pi/180 # For a day, a corresponding angle
77+
# inner and outer ring limit values
78+
inner=10
79+
outer=30
80+
# setting our color values
81+
ocean_color = ["#ff7f50","#004752"]
82+
```
83+
84+
Then we want to make our axes like we want, for that we build a function `dress_axes` that will be called during the animation process. Here we plot some bars with an offset (combination of `bottom` and `ylim` after). Those bars are actually our background, and the offset allows us to plot a legend in the middle of the plot.
85+
```python
86+
def dress_axes(ax):
87+
ax.set_facecolor('w')
88+
ax.set_theta_zero_location("N")
89+
ax.set_theta_direction(-1)
90+
# Here is how we position the months labels
91+
middles=np.arange(big_angle/2 ,360, big_angle)*np.pi/180
92+
ax.set_xticks(middles)
93+
ax.set_xticklabels(['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August','September','October','November','December'])
94+
ax.set_yticks([15,20,25])
95+
ax.set_yticklabels(['15°C','20°C','25°C'])
96+
# Changing radial ticks angle
97+
ax.set_rlabel_position(359)
98+
ax.tick_params(axis='both',color='w')
99+
plt.grid(None,axis='x')
100+
plt.grid(axis='y',color='w', linestyle=':', linewidth=1)
101+
# Here is the bar plot that we use as background
102+
bars = ax.bar(middles, outer, width=big_angle*np.pi/180, bottom=inner, color='lightgray', edgecolor='w',zorder=0)
103+
plt.ylim([2,outer])
104+
# Custom legend
105+
legend_elements = [Line2D([0], [0], marker='o', color='w', label='Surface', markerfacecolor=ocean_color[0], markersize=15),
106+
Line2D([0], [0], marker='o', color='w', label='1000m', markerfacecolor=ocean_color[1], markersize=15),
107+
]
108+
ax.legend(handles=legend_elements, loc='center', fontsize=13, frameon=False)
109+
# Main title for the figure
110+
plt.suptitle('Mediterranean temperature from Argo profiles',fontsize=16,horizontalalignment='center')
111+
```
112+
From there we can plot the frame of our plot.
113+
```python
114+
fig = plt.figure(figsize=(10,10))
115+
ax = fig.add_subplot(111, polar=True)
116+
dress_axes(ax)
117+
plt.show()
118+
```
119+
![axesFrame](axes_empty.png)
120+
121+
122+
Then it's finally time to plot our data. Since we want to animated the plot, we'll build a function that will be called in `FuncAnimation` later on. Since the state of the plot changes on every time stamp, we have to redress the axes for each frame, easy with our `dress_axes` function. Then we plot our temperature data using basic `plot()` : Thin lines for historical measurements and a thicker ones for the current year.
123+
```python
124+
def draw_data(i):
125+
# Clear
126+
ax.cla()
127+
# Redressing axes
128+
dress_axes(ax)
129+
# Limit between thin lines and thick line, this is current date minus 51 weeks basically.
130+
# why 51 and not 52 ? That create a small gap before the current date, wich is prettier
131+
i0=np.max([i-51,0])
132+
133+
ax.plot(date_angle[i0:i+1], ndf['tsurf'][i0:i+1],'-',color=ocean_color[0],alpha=1.0,linewidth=5)
134+
ax.plot(date_angle[0:i+1], ndf['tsurf'][0:i+1],'-',color=ocean_color[0],linewidth=0.7)
135+
136+
ax.plot(date_angle[i0:i+1], ndf['t1000'][i0:i+1],'-',color=ocean_color[1],alpha=1.0,linewidth=5)
137+
ax.plot(date_angle[0:i+1], ndf['t1000'][0:i+1],'-',color=ocean_color[1],linewidth=0.7)
138+
139+
# Plotting a line to spot the current date easily
140+
ax.plot([date_angle[i],date_angle[i]],[inner,outer],'k-',linewidth=0.5)
141+
# Display the current year as a title, just beneath the suptitle
142+
plt.title(str(activeyear[i]),fontsize=16,horizontalalignment='center')
143+
144+
# Test it
145+
draw_data(322)
146+
plt.show()
147+
```
148+
![oneplot](thumbnail.png)
149+
150+
151+
Finally it's time to animate, using `FuncAnimation`. Then we save it as a mp4 file or we display it in our notebook with `HTML(anim.to_html5_video())`.
152+
```python
153+
anim = FuncAnimation(fig, draw_data, interval=40, frames=len(daterange)-1, repeat=False)
154+
#anim.save('ArgopyUseCase_MedTempAnimation.mp4')
155+
HTML(anim.to_html5_video())
156+
```
157+
![animation](animatedpolar.gif)
Loading

0 commit comments

Comments
 (0)