Python Code for Climate Science

In [1]:
####################################################################
#
#This Python Code for Climate Science
#is written for the book entitled
#"Climate Mathematics: Theory and Applications"
#A Cambridge University Press book authored by SSP Shen and RCJ Somerville
#July 2019
#The Python codes were based on the R codes written by Samuel Shen
#Distinguished Professor, San Diego State University, USA
#and were translated from R by
#Louis Selstad, Stephen Shen, Gregori Clarke, and Dakota Newmann
#and edited by Samuel Shen, 
#Email: sshen@sdsu.edu
#www.climatemathematics.org
#March 2019, San Diego
#
####################################################################
#

Chapter 2: Basics of R Programming

#
In [2]:
import numpy as np 
import matplotlib.pyplot as plt
from matplotlib.colors import rgb2hex
from matplotlib.patches import Polygon
import matplotlib.mlab as mlab
import pandas as pd
from mpl_toolkits.basemap import Basemap
from mpl_toolkits import mplot3d
from netCDF4 import Dataset as ds
from urllib import request
import scipy as sp
from scipy import stats as stt
from sklearn import datasets, linear_model
from sklearn.neighbors import KernelDensity as kd
from sklearn.linear_model import LinearRegression
from random import randint
from scipy.stats import norm
import statsmodels
import sklearn
import math
import statistics
import sympy as sy
from sympy import symbols, diff
import statsmodels.api as sm
from datetime import date

Python As a Smart Calculator

In [3]:
1+4 #The result 5 is shown as an Out[5] below. 
Out[3]:
5
In [4]:
2 + np.pi/4 - 0.8 #\pi=3.14...... in Python is given by np.pi
Out[4]:
1.9853981633974482
In [5]:
x=1
y=2
z=4
t=2*x**y-z  #x**y means x^y: x to the power of y
t
Out[5]:
-2
In [6]:
u=2
v=3
u+v
Out[6]:
5
In [7]:
np.sin(u*v)  #np.sin is sine function in Python
Out[7]:
-0.27941549819892586
In [8]:
tmax=[77, 72,75,73,66,64,59] #Enter the temperature data
tmax #Show the temperarure data
Out[8]:
[77, 72, 75, 73, 66, 64, 59]

Define a Sequence in Python

In [9]:
np.arange(9) #A Python sequence starts from 0 while R from 1
#A sequence is called an array in Python
Out[9]:
array([0, 1, 2, 3, 4, 5, 6, 7, 8])
In [10]:
# np.arange(x,y) creates an array of numbers incremented by 1 
# between integer x and integer y-1 
np.arange(1, 9) 
Out[10]:
array([1, 2, 3, 4, 5, 6, 7, 8])
In [11]:
np.arange(-5.5, 2) #np.arange(x,y) can take negative and real numbers with increment 1
Out[11]:
array([-5.5, -4.5, -3.5, -2.5, -1.5, -0.5,  0.5,  1.5])
In [12]:
# np.arange(x,y,b) creates an array of numbers incremented by b
# between x and y-1 
#x,y, and b can be real numbers although they are integers in most applications
np.arange(1, 9, 2) 
Out[12]:
array([1, 3, 5, 7])
In [13]:
# np.linspace(x, y, n) creates an array of n numbers evenly distributed between x and y
np.linspace(0, 4.5, 5) #linspace means linearly/uniformly distributed space division
Out[13]:
array([0.   , 1.125, 2.25 , 3.375, 4.5  ])

Define a Function in R

In [14]:
# Define the x^2 function and call it samfctn(x)
def samfctn(x):
    return x * x;
samfctn(4)
Out[14]:
16
In [15]:
# Define a multivaraite function "x+y-z/2" 
def fctn2(x, y, z):
    return x + y - z / 2;
fctn2(1, 2, 3)
Out[15]:
1.5

Plot with Python

In [16]:
import math
import numpy
import scipy
import sympy
#After import these packages, the following three return the same pi=3.141592...
math.pi 
np.pi
scipy.pi
Out[16]:
3.141592653589793

Plot the curve of y=sin(x) from -pi to 2 pi

In [17]:
t = np.arange(-math.pi, 2 *math.pi, 0.1) #math.pi is the same as np.pi and scipy.pi, just using different packages
plt.plot(t, np.sin(t))
plt.show()

Plot x-y data based on a formula

In [18]:
t = np.linspace(-3, 2, 20) #uniform division using 20 points
plt.plot(t, t ** 2) 
plt.show()
In [19]:
np.size(t) #length of the data array
Out[19]:
20
In [20]:
import matplotlib.pyplot as plt
t = np.linspace(-3, 2, 300) #uniform division using 200 points
plt.plot(t, (np.sin(t ** 2))**2, 'ro', color='blue',linewidth=5) #linestyle ='ro'
plt.xlabel('Time')
plt.ylabel('Function Value')
plt.title('Plot a function')
plt.show()

Plot a 3D surface, yields a 3D surface with a perspective angle

In [21]:
x = np.linspace(-1, 1, 100)
y = np.linspace(-1, 1, 100)

# defines a formula for Z
def f(x, y):
    return 1 - x**2 - y**2
 
X, Y = np.meshgrid(x, y)
Z = f(X, Y) # renders z function on the x, y grid

# creates the 3D plot
fig = plt.figure()
ax = plt.axes(projection = '3d')
ax.contour3D(X, Y, Z, 30, cmap = 'binary')
plt.show()
In [22]:
x = np.linspace(-1, 1, 100)
y = np.linspace(-1, 1, 100)

# defines a formula for Z
def f(x, y):
    return 1 - x**2 - y**2
 
X, Y = np.meshgrid(x, y)
Z = f(X, Y) # renders z function on the x, y grid

# creates the 3D plot
fig = plt.figure()
ax = plt.axes(projection = '3d')
ax.plot_surface(X, Y, Z, rstride=1, cstride=1,
                cmap='viridis', edgecolor='none') #cmap = 'color'

# labels the plot
ax.set_title('Contour surface')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z');
ax.view_init(30)
plt.show()

Contour plot

In [23]:
x = np.linspace(-1, 1, 30)
y = np.linspace(-1, 1, 30)
 
X, Y = np.meshgrid(x, y)
Z = f(X, Y)

# creates and colors the contour plot
plt.contourf(X, Y, Z, 20, cmap = 'bwr') #cmap='color-scheme'

# adds colorbar for color map of contours
plt.colorbar()
plt.show()
In [24]:
## Symbolic calculations by Python
In [25]:
# Makes 'x' and 'y' symbols used for derivatives
x = symbols('x')
y = symbols('y')
# 'diff' command takes the derivative of x^2 w.r.t. x
diff(x ** 2, x)
Out[25]:
$\displaystyle 2 x$
In [26]:
# creates a function of x^2 
fx = x ** 2
# takes the derivative of function w.r.t x
rslt = diff(fx, x)
print('The derivative of the function fx w.r.t x is: ', rslt)
The derivative of the function fx w.r.t x is:  2*x
In [27]:
# creates a function of x^2*sin(x)
fx = (x ** 2) * sy.sin(x)
# Take the derivative of function w.r.t x
rslt = diff(fx, x)
print('The derivative of the function w.r.t x is: ', rslt)
The derivative of the function w.r.t x is:  x**2*cos(x) + 2*x*sin(x)
In [28]:
# Create a multivariate function: x^2+y^2 
fxy = x ** 2 + y ** 2
# Take partial derivative of function w.r.t x
rslt = diff(fxy, x)
print('The partial derivative of the function w.r.t x is: ', rslt)
The partial derivative of the function w.r.t x is:  2*x
In [29]:
# Take partial derivative of function w.r.t y
rslt = diff(fxy, y)
print('The partial derivative of the function w.r.t y is: ', rslt)
The partial derivative of the function w.r.t y is:  2*y
In [30]:
# Integrates x^2 from 0 to 1
sy.integrate(x ** 2, (x, 0, 1))
Out[30]:
$\displaystyle \frac{1}{3}$
In [31]:
# integrates cos(x) from 0 to pi/2
sy.integrate(sy.cos(x), (x, 0, math.pi / 2))
Out[31]:
$\displaystyle 1.0$

Vectors, Matrices and Solve Linear Equations

In [32]:
# creates a 5X1 matrix
np.mat([[1], [6], [3], [np.pi], [-3]])
Out[32]:
matrix([[ 1.        ],
        [ 6.        ],
        [ 3.        ],
        [ 3.14159265],
        [-3.        ]])
In [33]:
# creates a sequence from 2 to 6
np.arange(2, 7)
Out[33]:
array([2, 3, 4, 5, 6])
In [34]:
# creates a sequence incremented by 2 from 1 to 10 
np.arange(1, 10, 2)
Out[34]:
array([1, 3, 5, 7, 9])
In [35]:
# creates a 4X1 matrix
x = np.mat([[1], [-1], [1], [-1]])
print(x)
[[ 1]
 [-1]
 [ 1]
 [-1]]
In [36]:
# adds number 1 to each element of x
print(x + 1)
[[2]
 [0]
 [2]
 [0]]
In [37]:
# multiplies number 2 to each element of x
print(x * 2)
[[ 2]
 [-2]
 [ 2]
 [-2]]
In [38]:
#x/2, divides each element of x by 2
print(x / 2)
[[ 0.5]
 [-0.5]
 [ 0.5]
 [-0.5]]
In [39]:
# creates a 1X4 vector with a sequence incremented by 1 from 1 to 4
y = np.mat(np.arange(1, 5))

# this multiplication multiples each pair of elements and results in 4X1 vector
np.multiply(x, y.T)
Out[39]:
matrix([[ 1],
        [-2],
        [ 3],
        [-4]])
In [40]:
print(x / 2)
print(x)
print(y)
[[ 0.5]
 [-0.5]
 [ 0.5]
 [-0.5]]
[[ 1]
 [-1]
 [ 1]
 [-1]]
[[1 2 3 4]]
In [41]:
# the dot product of two vectors  
np.dot(y, x)
Out[41]:
matrix([[-2]])
In [42]:
x.shape
Out[42]:
(4, 1)
In [43]:
# transforms x into a row 1X4 vector
x.T.shape
Out[43]:
(1, 4)
In [44]:
# dot product and results in 1X1 matrix
np.dot(x.T, y.T)
Out[44]:
matrix([[-2]])
In [45]:
# this column times row yields a 4X4 matrix
np.matmul(x, y)
Out[45]:
matrix([[ 1,  2,  3,  4],
        [-1, -2, -3, -4],
        [ 1,  2,  3,  4],
        [-1, -2, -3, -4]])
In [46]:
# convert a vector into a matrix of the same number of elements
# the matrix elements go by column, first column, second, etc
my = y.reshape(2, 2).transpose()
print(my)
[[1 3]
 [2 4]]
In [47]:
# finds dimensions of a matrix
my.shape
Out[47]:
(2, 2)
In [48]:
# converts a matrix to a vector, again via columns
my.flatten()
Out[48]:
matrix([[1, 3, 2, 4]])
In [49]:
# reshapes x to a 2X2 matrix
mx = x.reshape(2, -1).transpose()
# multiplication between each pair of elements
np.multiply(mx, my)
Out[49]:
matrix([[ 1,  3],
        [-2, -4]])
In [50]:
# division between each pair of elements
np.divide(mx, my)
Out[50]:
matrix([[ 1.        ,  0.33333333],
        [-0.5       , -0.25      ]])
In [51]:
np.subtract(mx, 2 * my)
Out[51]:
matrix([[-1, -5],
        [-5, -9]])
In [52]:
# this is matrix multiplication in linear algebra
np.matmul(mx, my)
Out[52]:
matrix([[ 3,  7],
        [-3, -7]])
In [53]:
# determinant of a matrix
x = np.linalg.det(my)
print(x)
-2.0
In [54]:
# inverse of a matrix
z = np.linalg.inv(my)
print(z)
[[-2.   1.5]
 [ 1.  -0.5]]
In [55]:
# verifies that the inverse of the matrix is correct by multiplying 
# the original matrix and the inverse matrix
a = np.matmul(z, my)
print(a)
[[1. 0.]
 [0. 1.]]
In [56]:
# returns a left to right diagonal vector of a matrix
np.diagonal(my)
Out[56]:
array([1, 4])
In [57]:
# returns the eigenvalues of the "my" matrix
myeigval = np.linalg.eigvals(my)
print(myeigval) 
[-0.37228132  5.37228132]
In [58]:
# returns the eigenvalues and eigenvectors of the "my" matrix
myeigvect = np.linalg.eig(my)
print(myeigvect)
(array([-0.37228132,  5.37228132]), matrix([[-0.90937671, -0.56576746],
        [ 0.41597356, -0.82456484]]))
In [59]:
# returns the singular value decomposition(svd) of the "my" matrix
mysvd = np.linalg.svd(my)
print(mysvd)
(matrix([[-0.57604844, -0.81741556],
        [-0.81741556,  0.57604844]]), array([5.4649857 , 0.36596619]), matrix([[-0.40455358, -0.9145143 ],
        [ 0.9145143 , -0.40455358]]))
In [60]:
# returns a solved linear matrix equation of the "my" matrix and array [1,3]
ysolve = np.linalg.solve(my, [1,3])
print(ysolve)
[ 2.5 -0.5]
In [61]:
# verifies the solution is correct by multiplying the "my" matrix with "ysolve" 
n = np.matmul(my, ysolve)
print(n)
[[1. 3.]]

Simple Statistics by Python

In [62]:
# generates 10 normally distributed numbers
random = np.random.normal(0, 1, 10)
print(random)
[-0.56944224  0.92318125  0.00635913 -0.19910735 -1.05111789  1.22739994
  0.96547908  0.58251679 -0.34498744  0.29586004]
In [63]:
# returns the mean of that random sample
mean = np.mean(random)
print(mean)
0.1836141304857005
In [64]:
# returns the variance of that random sample
var = np.var(random)
print(var)
0.49684553535901477
In [65]:
# returns the standard deviation of that random sample
std = np.std(random)
print(std)
0.7048727086212196
In [66]:
# returns the median of that random sample
median = np.median(random)
print(median)
0.15110958317541215
In [67]:
# returns quantile values in [0,.25,.5,.75,1]
quantile = pd.DataFrame(random).quantile([0, 0.25, 0.5, 0.75, 1])
print(quantile)
             0
0.00 -1.051118
0.25 -0.308517
0.50  0.151110
0.75  0.838015
1.00  1.227400
In [68]:
# returns range of values (max - min) in random sample
rang = np.ptp(random)
print(rang)
2.278517829232926
In [69]:
# returns the min value in random
minimum = np.min(random)
print(minimum)
-1.051117888472511
In [70]:
# returns the max value in random
maximum = np.max(random)
print(maximum)
1.2273999407604146
In [71]:
# yields the box plot of random values
fig = plt.figure(1, figsize=(8, 10))
ax = fig.add_subplot(111)
bp = ax.boxplot(random)
plt.show()
In [72]:
# statistical summary of the data sequence
random = np.random.normal(0, 1, 12)
summ = pd.DataFrame(random).describe()
print(summ)
               0
count  12.000000
mean    0.369812
std     1.388178
min    -2.561957
25%    -0.207109
50%     0.653065
75%     1.316669
max     2.230248
In [73]:
# yields the histogram of 1000 random numbers with a normal distribution
w = np.random.normal(0, 1, 1000)
hist = plt.hist(w, facecolor='black')
plt.show()

Read data from files, process and plot

In [74]:
# read the 5 deg gridded NOAAGlobalTemp .asc data
file = open('NOAAGlobalTemp.gridded.v4.0.1.201701.asc', "r")
# read float numbers from the file
da1 = []
for line in file:
    x = line.strip().split()
    for f in x:
        da1.append(float(f))

len(da1)
Out[74]:
4267130
In [75]:
da1[0 : 3]
Out[75]:
[1.0, 1880.0, -999.9]
In [76]:
tm1 = np.arange(0, 4267129, 2594)
tm2 = np.arange(1, 4267130, 2594)
len(tm1)
Out[76]:
1645
In [77]:
len(tm2)
Out[77]:
1645
In [78]:
# extract months
mm1 = []   
for i in range(len(tm1)):
    mm1.append(da1[tm1[i]])
    
# extract years
yy1 = []
for i in range(len(tm2)):
    yy1.append(da1[tm2[i]])
    
# combine YYYY with MM
rw1 = []
for i in range(len(mm1)):
    rw1.append(str(int(yy1[i])) + "-" + str(int(mm1[i])))    
In [79]:
mm1[0 : 6]
Out[79]:
[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
In [80]:
yy1[0 : 6]
Out[80]:
[1880.0, 1880.0, 1880.0, 1880.0, 1880.0, 1880.0]
In [81]:
len(mm1)
Out[81]:
1645
In [82]:
len(yy1)
Out[82]:
1645
In [83]:
tm1[0 : 6]
Out[83]:
array([    0,  2594,  5188,  7782, 10376, 12970])
In [84]:
tm2[0 : 6]
Out[84]:
array([    1,  2595,  5189,  7783, 10377, 12971])
In [85]:
tm3 = pd.concat([pd.DataFrame(tm1), pd.DataFrame(tm2)], axis = 1)

# remove the months and years data from the scanned data
tm4 = [] 
for r in range(len(tm1)):
    tm4.append(tm1[r])
    tm4.append(tm2[r])

tm4.reverse()
da2 = da1.copy() 
for f in tm4:
    del da2[f]

len(da2)/(36*72) # months, 137 yrs 1 mon: Jan 1880-Jan 2017
Out[85]:
1645.0
In [86]:
# generates the space-time data
# 2592 (=36*72) rows and 1645 months (=137 yrs 1 mon)
var = [[0 for x in range(1645)] for y in range(2592)]
i = 0
for c in range(1645):
    for r in range(2592):
        var[r][c] = da2[i]
        i += 1

da3 = pd.DataFrame(var, columns = rw1)

lat1=np.linspace(-87.5, 87.5, 36).tolist()
lon1=np.linspace(2.5, 357.5, 72).tolist()

Lat = sorted(lat1 * 72)
Lon = lon1 * 36

rw1.insert(0, "LAT")
rw1.insert(1, "LON")
gpcpst = pd.concat([pd.DataFrame(Lat), pd.DataFrame(Lon), 
                    pd.DataFrame(da3)], axis = 1)
gpcpst.columns = rw1
In [87]:
gpcpst['2015-12'].values
Out[87]:
array([-999.9, -999.9, -999.9, ..., -999.9, -999.9, -999.9])
In [88]:
gpcpst.columns
Out[88]:
Index(['LAT', 'LON', '1880-1', '1880-2', '1880-3', '1880-4', '1880-5',
       '1880-6', '1880-7', '1880-8',
       ...
       '2016-4', '2016-5', '2016-6', '2016-7', '2016-8', '2016-9', '2016-10',
       '2016-11', '2016-12', '2017-1'],
      dtype='object', length=1647)
In [89]:
gpcpst.shape
Out[89]:
(2592, 1647)
In [90]:
# the first two columns are Lat and Lon
# -87.5 to 87.5 and then 2.5 to 375.5
# the first row for time is header, not counted as data. 
gpcpst.to_csv("NOAAGlobalT1.csv") #Output the data as a csv file

Plot the climate data map for a given time

In [91]:
# column '2015-12' corresponding to Dec 2015
# convert the vector into a lon-lat matrix for contour map plotting
# compresses numbers to [-6, 6] range
mapmat = np.reshape(gpcpst['2015-12'].values, (-1, 72)) #36 X 72
# this command compresses numbers to the range of -6 to 6
mapmat = np.maximum(np.minimum(mapmat, 6), -6)  

dpi = 100
fig = plt.figure(figsize = (1100/dpi, 1100/dpi), dpi = dpi)
ax  = fig.add_axes([0.1, 0.1, 0.8, 0.9])

# create map
dmap = Basemap(projection = 'cyl', llcrnrlat = -87.5, urcrnrlat = 87.5,
              resolution = 'c',  llcrnrlon = 2.5, urcrnrlon = 357.5)

# draw coastlines, state and country boundaries, edge of map
dmap.drawcoastlines()
dmap.drawstates()
dmap.drawcountries()

# create and draw meridians and parallels grid lines
dmap.drawparallels(np.arange( -50, 100, 50.), labels = [1, 0, 0, 0], 
                  fontsize = 10)
dmap.drawmeridians(np.arange(50, 350, 50.), labels = [0, 0, 0, 1], 
                  fontsize = 10)

# convert latitude/longitude values to plot x/y values
# need to reverse sequence of lat1
#lat1_rev = lat1[::-1] 
lat1_rev = lat1
x, y = dmap(*np.meshgrid(lon1, lat1_rev))

# contour levels
clevs = np.arange(-7, 7, 0.5)
print(x.shape,y.shape, mapmat.shape)
# draw filled contours
cnplot = dmap.contourf(x, y, mapmat, clevs, cmap = plt.cm.jet)

# add colorbar
# pad: distance between map and colorbar
cbar = dmap.colorbar(cnplot, location = 'right', pad = "2%")      
cbar.set_label('[oC]') # add colorbar title string

# add plot title
plt.title('NOAAGlobalTemp Anomalies Dec 2015 [deg C]')

# label x and y
plt.xlabel('Longitude', labelpad = 20)
plt.ylabel('Latitude', labelpad = 40)

# display on screen
plt.show()
(36, 72) (36, 72) (36, 72)

Extract the data for a specific region

In [92]:
# keep only the data for the Pacific region for years from 1951-2000 

# get data for region with -20 < Latitude < 20 and 160 < Longitude < 260
n2 = gpcpst[(gpcpst['LAT'] > -20) & (gpcpst['LAT'] < 20) & 
            (gpcpst['LON'] > 160) & (gpcpst['LON'] < 260)]
print(gpcpst.shape)
print(n2.size)

# from 1951-2000 
# note Jan 1951 data starts from column 854 (note python is zero based)
pacificdat = n2.iloc[:, 854 : 1454]
(2592, 1647)
263520

Plot a climate map for a specific region

In [93]:
Lat = np.arange(-17.5, 22.5, 5)
Lon = np.arange(162.5, 262.5, 5)

plt.close()

# get Dec 1997 data from pacificdat (tropical region)
mapmat = np.reshape(pacificdat['1997-12'].values, (8, -1)) # 8 * 20

fig = plt.figure(figsize=(1100/dpi, 1100/dpi), dpi = dpi)
ax  = fig.add_axes([0.1, 0.1, 0.8, 0.9])

# create map
smap = Basemap(projection = 'cyl', llcrnrlat = -40, urcrnrlat = 40,
              resolution = 'c', llcrnrlon = 120, urcrnrlon = 300)

# draw coastlines, state and country boundaries, edge of map
smap.drawcoastlines()
smap.drawstates()
smap.drawcountries()

# create and draw meridians and parallels grid lines
smap.drawparallels(np.arange( -40, 60, 20.),labels=[1, 0, 0, 0],fontsize = 10)
smap.drawmeridians(np.arange(50, 350, 50.),labels=[0, 0, 0, 1],fontsize = 10)

# convert latitude/longitude values to plot x/y values
x, y = smap(*np.meshgrid(Lon, Lat))

# contour levels
clevs = np.arange(-4, 4, 0.1)

# draw filled contours
cnplot = smap.contourf(x, y, mapmat, clevs, cmap = plt.cm.jet)

# add colorbar
# pad: distance between map and colorbar
cbar = smap.colorbar(cnplot, location = 'right', pad = "2%") 
# add colorbar title string
cbar.set_label('[oC]', verticalalignment = 'top') 

# add plot title
plt.title('Tropic Pacific SAT Anomalies Dec 1997 [deg C]')

# label x and y
plt.xlabel('Longitude', labelpad = 20)
plt.ylabel('Latitude', labelpad = 40)

# display on screen
plt.show()

Extract and plot data for a specified box with given lat and lon

In [94]:
# get region with Latitude = 32.5 and Longitude = 242.5
n2 = gpcpst[(gpcpst['LAT'] == 32.5) & (gpcpst['LON'] == 242.5)]

SanDiegoData = n2.iloc[:, 854 : 1454] #from 1951 - 2000

# note that using plt.subplots below is equivalent to using
fig, ax = plt.subplots()
ax.plot(np.linspace(1951, 2000, 
                    len(SanDiegoData.values.T)), SanDiegoData.values[0, :])

ax.set(xlabel = 'Year', ylabel = 'Temp [oC]',
       title = 'San Diego temperature anomalies history 1951-2000')
ax.grid()

plt.show()

Fill the few missing data

In [95]:
temp = gpcpst.copy()
areaw = np.zeros((2592, 1647))
areaw.shape 
areaw[:, 0] = temp['LAT'].values
areaw[:, 1] = temp['LON'].values
# convert degrees to radians 
veca=np.cos(temp['LAT'].values * np.pi / 180).tolist()
tempvalues = temp.values # get the underlying ndarray from the temp DataFrame
# area-weight matrix equal to cosine lat of the boxes with data 
# and to zero for the boxes of missing data -999.9
for j in range(2, 1647):
    for i in range(0, 2592):
        if tempvalues[i, j] > -290.0:
            areaw[i][j] = veca[i]

Compute an area-weighted temperature data matrix and its average

In [96]:
# area-weight data matrix first two columns as lat-lon
# create monthly global average vector for 1645 months (Jan 1880- Jan 2017)
tempw = np.multiply(areaw, tempvalues)
tempw[:, 0] = temp['LAT'].values
tempw[:, 1] = temp['LON'].values
avev= np.divide(tempw.sum(axis = 0)[2 : 1647], areaw.sum(axis = 0)[2 : 1647])

Plot time series of the global ave temp

In [97]:
#from sklearn.linear_model import LinearRegression
plt.close()
timemo = np.linspace(1880, 2017, 1645)

plt.plot(timemo, avev, '-')
plt.title('Area-weighted global average of monthly SAT anomalies: Jan 1880-Jan 2017')
plt.xlabel('Year')
plt.ylabel('Temperature anomaly [oC]')

# plot linear regression
lm = LinearRegression()
lm.fit(timemo.reshape(-1, 1), avev)
predictions = lm.predict(timemo.reshape(-1, 1))

plt.plot(timemo, predictions, '-')
font = {'family': 'serif',
        'color':  'blue',
        'weight': 'normal',
        'size': 12,
        }
plt.text(1885, .5, 'Linear trend: 0.69 [oC] per century', fontdict = font)

plt.show()  

Plot the data coverage time series

In [98]:
# plot this time series
motime = np.linspace(1880, 2017, 1645)
rcover = 100 * areaw.sum(axis = 0)[2 : 1647] / sum(veca)

plt.close()
plt.plot(motime, rcover)
plt.ylim(0, 100)
plt.title('NOAAGlobalTemp Data Coverage: Jan 1880-Jan 2017')
plt.xlabel('Year')
plt.ylabel('Percent area covered [%]')

plt.show()

Work with the NOAAGlobalTemp time series data

In [99]:
# download the NCEI spatial average time series of monthly data
# https://www1.ncdc.noaa.gov/pub/data/noaaglobaltemp/operational/
# timeseries/aravg.mon.land_ocean.90S.90N.v4.0.1.201702.asc
aveNCEI = pd.read_table('http://shen.sdsu.edu/data/' + 
                        'aravg.mon.land_ocean.90S.90N.v4.0.1.201703.asc.txt', 
                        header = None, delim_whitespace = True)
aveNCEI.shape 

aveNCEI = aveNCEI.values
avediff = np.subtract(avev, aveNCEI[0:1645, 2]) 
plt.close()
plt.plot(timemo, avediff, color = 'black')
plt.title('Difference of R average minus NCEI average of global temp')
plt.xlabel('Year')
plt.ylabel('Diffences [oC]')

plt.show()

Plot the annual time series of the NOAAGlobalTemp

In [100]:
# compute annual average, then plot the annual mean global average temp
avem = np.reshape(avev[0 : 1644], (-1, 12)) 
avem.shape #size 137 * 12, with row for the year, column for the month

# average over columns (annual average over 12 months), 
# there are 137 years, 1880/01 - 2016/12
annv = np.mean(avem, axis = 1)  

# plot the annual mean global average temp (1880-1996)
timeyr = np.arange(1880, 2017)

plt.close()
plt.plot(timeyr, annv, '-')
plt.title('Area-weighted global average of annual SAT anomalies: 1880-2016')
plt.xlabel('Year')
plt.ylabel('Temperature anomaly [oC]')

# plot linear regression
lm = LinearRegression()
lm.fit(timeyr.reshape(-1, 1), annv)
predictions = lm.predict(timeyr.reshape(-1 , 1))

plt.plot(timeyr, predictions, '-')
font = {'family': 'serif',
        'color':  'blue',
        'weight': 'normal',
        'size': 12,
        }
plt.text(1900, .4, 'Linear trend: 0.69 [oC] per century', fontdict = font)

font = {'family': 'serif',
        'color':  'red',
        'weight': 'normal',
        'size': 12,
        }
plt.text(1900, 0.05, 'Base line', fontdict = font)

plt.plot(timeyr, np.zeros(137), 'r-')

plt.show()
In [101]:
# one can compare with the NOAAGlobalTemp annual time series
# there are some small differences
aveannNCEI = pd.read_table('http://shen.sdsu.edu/data/' + 
                          'aravg.ann.land_ocean.90S.90N.v4.0.1.201703.asc.txt', 
                          header = None, delim_whitespace = True)
aveannNCEI.shape 

aveannNCEI = aveannNCEI.values 
diff2 = np.subtract(annv, aveannNCEI[0 : 137, 1])
# find the value range (minimum and maximum values in diff2 array)
[np.amin(diff2, axis = 0), np.amax(diff2, axis = 0)] 
Out[101]:
[-0.01609496875429861, 0.007508731446406958]

Nonlinear trend from polynomial fitting

In [102]:
# polynomial fitting to the global average annual mean
# the following polynomial fitting from numpy.polyfit is NOT orthogonal, 
# we know orthogonal polynomial fitting is usually better as we saw in the R code implemention
polyor9 = np.polyfit(timeyr, annv, 9)
polyor20 = np.polyfit(timeyr, annv, 20)

plt.close()
plt.plot(timeyr, annv, '-')
plt.title('Annual SAT time series and its orthogonal polynomial fits: 1880-2016')
plt.xlabel('Year')
plt.ylabel('Temperature anomaly [oC]')

plt.plot(timeyr, np.polyval(polyor9, timeyr), "-b", 
         label = "9th order orthogonal polynomial fit")
plt.plot(timeyr, np.polyval(polyor20, timeyr), "-r", 
         label = "20th order orthogonal polynomial fit")

plt.legend(loc = 'upper left')
plt.show()
/Users/sshen/opt/anaconda3/lib/python3.7/site-packages/IPython/core/interactiveshell.py:3326: RankWarning: Polyfit may be poorly conditioned
  exec(code_obj, self.user_global_ns, self.user_ns)
/Users/sshen/opt/anaconda3/lib/python3.7/site-packages/IPython/core/interactiveshell.py:3326: RankWarning: Polyfit may be poorly conditioned
  exec(code_obj, self.user_global_ns, self.user_ns)

Non-parametric smooth by LOWESS fitting

In [103]:
#import statsmodels.api as sm
lowess = sm.nonparametric.lowess
lowessfit = lowess(annv, timeyr, frac = 2./18)
plt.scatter(timeyr, annv, facecolors = 'none', edgecolors = 'b') 
plt.plot(lowessfit[:, 0], lowessfit[:, 1])
plt.title('Annual SAT time series with non-parametric smooth lowess fitting: 1880-2016')
plt.xlabel('Year')
plt.ylabel('Temperature anomaly [oC]')
plt.show()

Compute and plot the trend of gridded data

In [104]:
# Compute the trend for each box for the 20th century
timemo1 = np.linspace(1900, 2000, 1200)
temp1 = temp.copy()

# replace missing values (any value less than -490.00) with NaN in temp1 
# dataframe
temp1 = temp1.where(temp1 >= -490.0, np.NaN)
trendgl = np.zeros(2592)

# for grids in (Lon: 2.5-257.5, Lat: -87.5-87.5), perform linear regression 
# fitting on each grid temperature over months between Jan 1900 and Dec 1999
# linear slope cofficient will be saved in trendgl list to be used for 
# subsequent plotting 
for i in range(0, 2592):
    if not(np.isnan(temp1.iloc[i, 242]) or np.isnan(temp1.iloc[i, 1441])): 
        # column 242 corresponds to month 1990-01, 
        # and column 1441 corresponds to month 1999-12
        # temperature data for a grid from 1990-01 to 1999-12
        gridtemp = temp1.iloc[i, 242 : 1442].values 
        # filter out any month where grid temperature is missing 
        # (i.e., having a NaN value)
        idx = np.isfinite(timemo1) & np.isfinite(gridtemp) 
        # check if there is at least one month that has 
        # non-NaN number for linear regression
        if np.any(idx[:]): 
            ab = np.polyfit(timemo1[idx], gridtemp[idx], 1)
            # ab[0] has the linear regression slope coefficient
            trendgl[i] = ab[0] 
        else:
            trendgl[i] = np.NaN
    else:
        trendgl[i] = np.NaN

mapmat = np.reshape([n * 100 for n in trendgl], (-1, 72)) #36 X 72 
# this command compresses numbers to the range of -2 to 2 
mapmat = np.maximum(np.minimum(mapmat, 2), -2) 

dpi = 100
fig = plt.figure(figsize = (1100 / dpi, 1100 / dpi), dpi = dpi)
ax  = fig.add_axes([0.1, 0.1, 0.8, 0.9])

# create map
amap = Basemap(projection = 'cyl', llcrnrlat = -87.5, urcrnrlat = 87.5,
              resolution = 'c',  llcrnrlon = 2.5, urcrnrlon = 357.5)

# draw coastlines, state and country boundaries, edge of map
amap.drawcoastlines()
amap.drawstates()
amap.drawcountries()

# create and draw meridians and parallels grid lines
amap.drawparallels(np.arange( -50, 100, 50.),
                  labels = [1, 0, 0, 0], fontsize = 10)
amap.drawmeridians(np.arange(50, 350, 50.), 
                  labels = [0, 0, 0, 1], fontsize = 10)

# convert latitude/longitude values to plot x/y values
x, y = amap(*np.meshgrid(lon1, lat1))

# contour levels
clevs = np.linspace(-2, 2, 21)

# draw filled contours
cnplot = amap.contourf(x, y, mapmat, clevs, cmap = plt.cm.jet)

# add colorbar
# pad: distance between map and colorbar
cbar = amap.colorbar(cnplot, location = 'right', pad = "2%")      
cbar.set_label('[oC]')  # add colorbar title string
cbar.set_ticks(np.arange(-2, 2.6, 0.5))
cbar.set_ticklabels(np.arange(-2, 2.6, 0.5))
              
# add plot title
plt.title('Jan 1900-Dec 1999 temperature trends: [oC/century]')

# label x and y
plt.xlabel('Longitude', labelpad = 20)
plt.ylabel('Latitude', labelpad = 40)

plt.show()

Compute trend for each box for the 20th century - Version 2: Allow 2/3 of data, i.e., 1/3 missing

In [105]:
# compute the trend and plot the 20C V2 trend map 
timemo1 = np.linspace(1900, 2000, 1200)
temp1 = temp.iloc[:, 242 : 1442]

# replace missing values (any value less than -490.00) 
# with NaN in temp1 dataframe
temp1 = temp1.where(temp1 >= -490.0, np.NaN)
temptf = np.isnan(temp1)

countOfNotMissing = np.zeros(2592)

for i in range(0, 2592):
    countOfNotMissing[i] = temptf.iloc[i, :].values.tolist().count(False)

trend20c = np.zeros(2592)
for i in range(0, 2592):
    if countOfNotMissing[i] > 800: # allow 2/3 of data, i.e., 1/3 missing
        # column 242 corresponds to month 1990-01, 
        # and column 1441 corresponds to month 1999-12
        gridtemp = temp1.iloc[i, :].values # temperature for a grid 
                                           # from 1990-01 to 1999-12
        # filter out any month where grid temperature is missing 
        # (i.e., having a NaN value)
        idx = np.isfinite(timemo1) & np.isfinite(gridtemp) 
        ab = np.polyfit(timemo1[idx], gridtemp[idx], 1)
        # ab[0] has the linear regression slope coefficient
        trend20c[i] = ab[0] 
    else:
        trend20c[i] = np.NaN
    
mapmat = np.reshape([n * 120 for n in trend20c], (-1, 72)) #36 X 72 
# this command compresses numbers to the range of -3 to 3
mapmat = np.maximum(np.minimum(mapmat, 3), -3)  

dpi = 100
fig = plt.figure(figsize = (1100 / dpi, 1100 / dpi), dpi = dpi)
ax  = fig.add_axes([0.1, 0.1, 0.8, 0.9])

# create map
fmap = Basemap(projection = 'cyl', llcrnrlat = -87.5, urcrnrlat = 87.5,
              resolution = 'c',  llcrnrlon = 2.5, urcrnrlon = 357.5)

# draw coastlines, state and country boundaries, edge of map
fmap.drawcoastlines()
fmap.drawstates()
fmap.drawcountries()

# create and draw meridians and parallels grid lines
fmap.drawparallels(np.arange( -50, 100, 50.), 
                  labels = [1, 0, 0, 0], fontsize = 10)
fmap.drawmeridians(np.arange(50, 350, 50.), 
                  labels = [0, 0, 0, 1], fontsize = 10)

# convert latitude/longitude values to plot x/y values
x, y = fmap(*np.meshgrid(lon1, lat1))

# contour levels
clevs = np.linspace(-3, 3, 41)

# draw filled contours
cnplot = fmap.contourf(x, y, mapmat, clevs, cmap = plt.cm.jet)

# add colorbar
# pad: distance between map and colorbar
cbar = fmap.colorbar(cnplot, location = 'right', pad = "2%")      
cbar.set_label('[oC]') #Add colorbar title string
cbar.set_ticks(np.arange(-3, 3.1, 1))
cbar.set_ticklabels(np.arange(-3, 3.1, 1))
              
# add plot title
plt.title('Jan 1900-Dec 1999 temperature trends: [oC/decade]')

# label x and y
plt.xlabel('Longitude', labelpad = 20)
plt.ylabel('Latitude', labelpad = 40)

plt.show()

Trend from 1976-2016

In [106]:
timemo2 = np.linspace(1976, 2017, 492)
temp1 = temp.copy()

#-- replace missing values (any value < -490.00) with NaN in temp1 dataframe --
temp1 = temp1.where(temp1 >= -490.0, np.NaN)
trend7616 = np.zeros(2592)

# for grids in (Lon: 2.5-257.5, Lat: -87.5-87.5), perform linear regression 
# fitting on each grid temperature over months between Jan 1976 and Dec 2016
# linear slope cofficient will be saved in trendgl list to be used for 
# subsequent plotting 
for i in range(0, 2592):
    if not(np.isnan(temp1.iloc[i, 1154]) or np.isnan(temp1.iloc[i, 1645])): 
        # column 1154 corresponds to month 1976-01, and column 1645 
        # corresponds to month 2016-12
        # temperature data for a grid from 1976-01 to 2016-12
        gridtemp = temp1.iloc[i, 1154 : 1646].values 
        # filter out any month where grid temperature is missing 
        # (i.e., having a NaN value)
        idx = np.isfinite(timemo2) & np.isfinite(gridtemp) 
        # check if there is at least one month that has non-NaN number 
        # for linear regression
        if np.any(idx[:]): 
            ab = np.polyfit(timemo2[idx], gridtemp[idx], 1)
            # ab[0] has the linear regression slope coefficient
            trend7616[i] = ab[0] 
        else:
            trend7616[i] = np.NaN
    else:
        trend7616[i] = np.NaN

Plot US temp and prec times series on the same figure

In [107]:
Time = np.arange(2001, 2011)
Tmean = [12.06, 11.78, 11.81, 11.72, 12.02, 12.36, 12.03, 11.27, 11.33, 11.66]
Prec = [737.11, 737.87, 774.95, 844.55, 764.03, 757.43, \
        741.17, 793.50, 820.42, 796.80]

plt.close()
fig, ax1 = plt.subplots()

color = 'tab:red'
ax1.set_title('Contiguous U.S. Annual Mean Temperature and Total Precipitation')
ax1.set_xlabel('Year')
ax1.set_ylabel('Tmean [dec C]', color = color)
ax1.plot(Time, Tmean, color = color, marker = 'o', markerfacecolor = 'white')
ax1.tick_params(axis = 'y', labelcolor = color)
ax1.legend('Tmean', loc = 'upper left')

ax2 = ax1.twinx()  # instantiate a second axis that shares the same x-axis

color = 'tab:blue'
# we already handled the x-label with ax1
ax2.set_ylabel('Precipitation [mm]', color = color)  
ax2.plot(Time, Prec, color = color, marker = 'o', markerfacecolor = 'white')
ax2.tick_params(axis = 'y', labelcolor = color)
ax2.legend('Prec')

fig.tight_layout()  # otherwise the right y-label is slightly clipped

plt.show()

Margins, math symbol, and figure setups

In [108]:
x = pd.DataFrame(np.arange(-30, 31)) * 0.25 
y = np.sin(x)
x1 = x[np.sin(x) >= 0].dropna().transpose().values[0]
y1 = np.sin(x1)
x2 = x[np.sin(x) < 0].dropna().transpose().values[0]
y2 = np.sin(x2)

fig,ax1 = plt.subplots()

bar1 = ax1.bar(x1, y1, width = 0.04, ec = "k")
for i in range(len(bar1)):
    bar1[i].set_color('r')

ax1.set_ylim(-1, 1)
ax1.set_xticks(np.arange(-6, 6.1, step = 2))
ax1.set_yticks(np.arange(-1, 1.1, step = 0.5))

ax2 = ax1.twinx()  # instantiate a second axis that shares the same x-axis
bar2 = ax2.bar(x2, y2, width = 0.1, linestyle = '--', ec = "k")
for i in range(len(bar2)):
    bar2[i].set_color('b')

ax2.set_ylim(-1, 1)
ax2.set_yticks(np.arange(-1, 1.1, step = 0.5))
ax2.set_yticklabels(['A', 'B', 'C', 'D', 'E'], fontsize = 14, fontweight = 2)

font = {'family' : 'arial',
        'color' : 'green',
        'weight' : 'bold',
        'size' : 36,
        'style' : 'italic'
        }
plt.text(-5, .5, 'Sine waves', fontdict = font)

plt.text(-10.6, 0, "y=sin($\\theta$-$\hat\phi$)", color = 'b', size = 14, 
         rotation = 90, verticalalignment = 'center')
plt.text(0.5, -1.3, "Angle in radian: $\\theta$-$\phi_0$", color = 'r', 
         size = 17, horizontalalignment = 'center')
plt.text(0.5, 1.14, "Text outside of the figure on side 3", weight = 'bold', 
         size = 14, horizontalalignment = 'center')
plt.show()

Change the font sizes for axis labels, main title, and sub-title

In [109]:
np.random.seed(1)
plt.scatter([n * (1/20) for n in np.arange(1, 201)], np.random.randn(200), 
             facecolors = 'none', edgecolors = 'black')
plt.title('Normal random values', fontsize = 20, weight = 'bold')
plt.xlabel('Time', fontsize = 12)
plt.ylabel('Random values', fontsize = 12)

plt.text(5, -4.2, "Sub-title: 200 random values", size = 16, 
         horizontalalignment = 'center')
plt.show()

Wind directions due to the balance between PGF and Coriolis force using an arrow plot for vector fields on a map

In [110]:
NOAATemp = np.loadtxt("aravg.ann.land_ocean.90S.90N.v4.0.1.201805.asc.txt",skiprows=0)
print(NOAATemp)
x=NOAATemp[:,0]
y=NOAATemp[:,1]
z = [-99] * x.size
print(y.shape)
[[ 1.88000e+03 -3.57637e-01  9.78800e-03  1.44600e-03  8.50000e-04
   7.49200e-03]
 [ 1.88100e+03 -3.05737e-01  9.82100e-03  1.44600e-03  8.77000e-04
   7.49800e-03]
 [ 1.88200e+03 -3.10654e-01  9.81100e-03  1.44600e-03  8.97000e-04
   7.46800e-03]
 [ 1.88300e+03 -3.86558e-01  9.79800e-03  1.44600e-03  9.10000e-04
   7.44300e-03]
 [ 1.88400e+03 -4.46635e-01  9.76400e-03  1.44600e-03  9.22000e-04
   7.39500e-03]
 [ 1.88500e+03 -4.57948e-01  9.73800e-03  1.44600e-03  9.39000e-04
   7.35300e-03]
 [ 1.88600e+03 -4.41306e-01  9.72500e-03  1.44600e-03  9.55000e-04
   7.32400e-03]
 [ 1.88700e+03 -4.88767e-01  9.71900e-03  1.44600e-03  9.69000e-04
   7.30500e-03]
 [ 1.88800e+03 -3.88628e-01  9.43000e-03  1.13600e-03  9.79000e-04
   7.31400e-03]
 [ 1.88900e+03 -3.41075e-01  8.62000e-03  2.94000e-04  1.00200e-03
   7.32400e-03]
 [ 1.89000e+03 -5.61099e-01  8.64200e-03  2.84000e-04  1.02900e-03
   7.32900e-03]
 [ 1.89100e+03 -4.85176e-01  8.70900e-03  3.28000e-04  1.05400e-03
   7.32700e-03]
 [ 1.89200e+03 -5.40294e-01  8.63200e-03  2.39000e-04  1.07100e-03
   7.32100e-03]
 [ 1.89300e+03 -5.51343e-01  8.49900e-03  1.30000e-04  1.07300e-03
   7.29600e-03]
 [ 1.89400e+03 -5.13475e-01  8.50600e-03  1.60000e-04  1.05700e-03
   7.28900e-03]
 [ 1.89500e+03 -4.59617e-01  8.40300e-03  7.00000e-05  1.01600e-03
   7.31700e-03]
 [ 1.89600e+03 -3.24849e-01  8.34000e-03  2.50000e-05  9.53000e-04
   7.36200e-03]
 [ 1.89700e+03 -3.52654e-01  8.40800e-03  8.60000e-05  8.86000e-04
   7.43500e-03]
 [ 1.89800e+03 -4.95457e-01  8.38700e-03  5.20000e-05  8.22000e-04
   7.51400e-03]
 [ 1.89900e+03 -3.57434e-01  8.43900e-03  5.00000e-05  7.55000e-04
   7.63400e-03]
 [ 1.90000e+03 -3.05134e-01  8.47000e-03  3.70000e-05  6.83000e-04
   7.75000e-03]
 [ 1.90100e+03 -3.76472e-01  8.49900e-03  2.50000e-05  6.10000e-04
   7.86400e-03]
 [ 1.90200e+03 -4.83563e-01  8.62500e-03  1.07000e-04  5.36000e-04
   7.98200e-03]
 [ 1.90300e+03 -5.72895e-01  8.84600e-03  3.17000e-04  4.57000e-04
   8.07100e-03]
 [ 1.90400e+03 -6.56467e-01  8.85100e-03  3.13000e-04  3.73000e-04
   8.16500e-03]
 [ 1.90500e+03 -5.26817e-01  8.67100e-03  1.23000e-04  2.93000e-04
   8.25500e-03]
 [ 1.90600e+03 -4.52037e-01  8.68100e-03  7.80000e-05  2.22000e-04
   8.38000e-03]
 [ 1.90700e+03 -6.10281e-01  8.83600e-03  2.42000e-04  1.61000e-04
   8.43300e-03]
 [ 1.90800e+03 -6.79099e-01  9.05400e-03  5.38000e-04  1.09000e-04
   8.40700e-03]
 [ 1.90900e+03 -6.65786e-01  8.51900e-03  9.70000e-05  6.90000e-05
   8.35300e-03]
 [ 1.91000e+03 -6.21176e-01  8.32200e-03  2.50000e-05  4.50000e-05
   8.25200e-03]
 [ 1.91100e+03 -6.72995e-01  8.17000e-03  2.50000e-05  3.60000e-05
   8.11000e-03]
 [ 1.91200e+03 -5.69114e-01  8.04500e-03  2.50000e-05  3.10000e-05
   7.99000e-03]
 [ 1.91300e+03 -5.57853e-01  7.95500e-03  2.50000e-05  2.60000e-05
   7.90400e-03]
 [ 1.91400e+03 -3.78948e-01  8.13700e-03  2.49000e-04  2.30000e-05
   7.86400e-03]
 [ 1.91500e+03 -3.08562e-01  8.11500e-03  1.75000e-04  2.00000e-05
   7.92000e-03]
 [ 1.91600e+03 -5.29111e-01  8.16200e-03  1.39000e-04  1.60000e-05
   8.00700e-03]
 [ 1.91700e+03 -5.51143e-01  8.23100e-03  1.04000e-04  1.30000e-05
   8.11400e-03]
 [ 1.91800e+03 -4.38611e-01  8.36600e-03  1.39000e-04  9.00000e-06
   8.21800e-03]
 [ 1.91900e+03 -4.34260e-01  8.67800e-03  3.09000e-04  6.00000e-06
   8.36400e-03]
 [ 1.92000e+03 -4.44821e-01  8.97800e-03  4.05000e-04  6.00000e-06
   8.56800e-03]
 [ 1.92100e+03 -3.82941e-01  9.31100e-03  5.83000e-04  6.00000e-06
   8.72200e-03]
 [ 1.92200e+03 -4.65988e-01  9.34000e-03  3.61000e-04  6.00000e-06
   8.97300e-03]
 [ 1.92300e+03 -4.49293e-01  1.02720e-02  9.30000e-04  6.00000e-06
   9.33600e-03]
 [ 1.92400e+03 -4.85985e-01  1.05660e-02  8.74000e-04  7.00000e-06
   9.68500e-03]
 [ 1.92500e+03 -3.82667e-01  1.11830e-02  1.11200e-03  6.00000e-06
   1.00640e-02]
 [ 1.92600e+03 -2.98273e-01  1.10800e-02  7.08000e-04  6.00000e-06
   1.03660e-02]
 [ 1.92700e+03 -3.86029e-01  1.13140e-02  6.75000e-04  6.00000e-06
   1.06330e-02]
 [ 1.92800e+03 -4.09075e-01  1.15720e-02  6.77000e-04  6.00000e-06
   1.08900e-02]
 [ 1.92900e+03 -5.32540e-01  1.18890e-02  7.49000e-04  6.00000e-06
   1.11330e-02]
 [ 1.93000e+03 -3.37096e-01  1.18920e-02  5.69000e-04  6.00000e-06
   1.13170e-02]
 [ 1.93100e+03 -3.09187e-01  1.19910e-02  5.10000e-04  7.00000e-06
   1.14730e-02]
 [ 1.93200e+03 -3.56871e-01  1.21100e-02  5.35000e-04  9.00000e-06
   1.15660e-02]
 [ 1.93300e+03 -4.84051e-01  1.18210e-02  2.70000e-04  1.20000e-05
   1.15380e-02]
 [ 1.93400e+03 -3.42621e-01  1.16550e-02  2.77000e-04  1.90000e-05
   1.13600e-02]
 [ 1.93500e+03 -3.78112e-01  1.14300e-02  4.10000e-04  2.60000e-05
   1.09940e-02]
 [ 1.93600e+03 -3.53252e-01  1.10280e-02  5.51000e-04  3.30000e-05
   1.04440e-02]
 [ 1.93700e+03 -2.55534e-01  1.02950e-02  4.15000e-04  4.10000e-05
   9.83900e-03]
 [ 1.93800e+03 -2.66997e-01  9.89500e-03  6.53000e-04  5.30000e-05
   9.19000e-03]
 [ 1.93900e+03 -2.52497e-01  8.91100e-03  5.10000e-05  6.70000e-05
   8.79300e-03]
 [ 1.94000e+03 -1.43743e-01  9.04700e-03  2.39000e-04  8.40000e-05
   8.72400e-03]
 [ 1.94100e+03 -4.65080e-02  9.39100e-03  5.03000e-04  9.90000e-05
   8.78900e-03]
 [ 1.94200e+03 -9.02850e-02  9.64400e-03  8.37000e-04  1.12000e-04
   8.69400e-03]
 [ 1.94300e+03 -8.73680e-02  9.82600e-03  1.08000e-03  1.23000e-04
   8.62200e-03]
 [ 1.94400e+03  4.71970e-02  8.95800e-03  4.43000e-04  1.31000e-04
   8.38400e-03]
 [ 1.94500e+03 -7.40600e-02  8.56000e-03  2.61000e-04  1.33000e-04
   8.16600e-03]
 [ 1.94600e+03 -2.50149e-01  8.19800e-03  1.71000e-04  1.32000e-04
   7.89400e-03]
 [ 1.94700e+03 -2.94832e-01  7.79300e-03  2.50000e-05  1.30000e-04
   7.63800e-03]
 [ 1.94800e+03 -2.95243e-01  7.55200e-03  2.50000e-05  1.26000e-04
   7.40100e-03]
 [ 1.94900e+03 -3.04087e-01  7.36500e-03  2.50000e-05  1.19000e-04
   7.22100e-03]
 [ 1.95000e+03 -4.05392e-01  7.44200e-03  2.06000e-04  1.12000e-04
   7.12400e-03]
 [ 1.95100e+03 -2.55644e-01  8.08500e-03  7.87000e-04  1.05000e-04
   7.19300e-03]
 [ 1.95200e+03 -2.19037e-01  7.53900e-03  1.37000e-04  9.70000e-05
   7.30500e-03]
 [ 1.95300e+03 -1.49037e-01  7.50400e-03  2.50000e-05  8.50000e-05
   7.39400e-03]
 [ 1.95400e+03 -3.60860e-01  7.40400e-03  2.60000e-05  7.00000e-05
   7.30800e-03]
 [ 1.95500e+03 -3.80123e-01  6.92300e-03  2.90000e-05  5.30000e-05
   6.84200e-03]
 [ 1.95600e+03 -4.44864e-01  6.54900e-03  3.60000e-05  3.70000e-05
   6.47600e-03]
 [ 1.95700e+03 -1.96223e-01  6.44100e-03  1.22000e-04  2.40000e-05
   6.29500e-03]
 [ 1.95800e+03 -1.33472e-01  6.21700e-03  1.02000e-04  1.30000e-05
   6.10300e-03]
 [ 1.95900e+03 -1.84936e-01  6.13700e-03  1.07000e-04  5.00000e-06
   6.02500e-03]
 [ 1.96000e+03 -2.24694e-01  5.85400e-03  9.80000e-05  3.00000e-06
   5.75400e-03]
 [ 1.96100e+03 -1.66780e-01  5.48200e-03  2.50000e-05  2.00000e-06
   5.45400e-03]
 [ 1.96200e+03 -1.54053e-01  5.20200e-03  2.50000e-05  3.00000e-06
   5.17400e-03]
 [ 1.96300e+03 -1.36680e-01  4.99000e-03  2.50000e-05  3.00000e-06
   4.96200e-03]
 [ 1.96400e+03 -3.92866e-01  5.26400e-03  5.12000e-04  4.00000e-06
   4.74800e-03]
 [ 1.96500e+03 -3.21770e-01  4.96000e-03  4.43000e-04  5.00000e-06
   4.51300e-03]
 [ 1.96600e+03 -2.66119e-01  4.38500e-03  1.14000e-04  5.00000e-06
   4.26500e-03]
 [ 1.96700e+03 -2.57499e-01  4.07700e-03  7.10000e-05  5.00000e-06
   4.00100e-03]
 [ 1.96800e+03 -2.74683e-01  3.86700e-03  7.10000e-05  5.00000e-06
   3.79200e-03]
 [ 1.96900e+03 -1.51344e-01  3.61900e-03  5.90000e-05  5.00000e-06
   3.55500e-03]
 [ 1.97000e+03 -2.06525e-01  3.43200e-03  2.50000e-05  6.00000e-06
   3.40200e-03]
 [ 1.97100e+03 -3.21847e-01  3.32000e-03  1.09000e-04  7.00000e-06
   3.20400e-03]
 [ 1.97200e+03 -2.15943e-01  3.07000e-03  5.60000e-05  8.00000e-06
   3.00600e-03]
 [ 1.97300e+03 -7.98540e-02  2.80200e-03  2.50000e-05  1.00000e-05
   2.76800e-03]
 [ 1.97400e+03 -3.15911e-01  2.56900e-03  2.50000e-05  1.10000e-05
   2.53300e-03]
 [ 1.97500e+03 -2.40366e-01  2.53400e-03  2.50000e-05  1.20000e-05
   2.49800e-03]
 [ 1.97600e+03 -3.22965e-01  2.52700e-03  2.50000e-05  1.20000e-05
   2.49000e-03]
 [ 1.97700e+03 -4.66400e-02  2.49200e-03  2.50000e-05  1.20000e-05
   2.45500e-03]
 [ 1.97800e+03 -1.32289e-01  2.54500e-03  6.60000e-05  1.20000e-05
   2.46700e-03]
 [ 1.97900e+03 -1.73740e-02  2.62000e-03  5.10000e-05  1.20000e-05
   2.55700e-03]
 [ 1.98000e+03  1.97220e-02  2.85800e-03  8.20000e-05  1.30000e-05
   2.76400e-03]
 [ 1.98100e+03  5.61250e-02  3.05800e-03  2.50000e-05  1.40000e-05
   3.01900e-03]
 [ 1.98200e+03 -6.32100e-02  3.30600e-03  2.50000e-05  1.40000e-05
   3.26600e-03]
 [ 1.98300e+03  9.71060e-02  3.49200e-03  2.50000e-05  1.50000e-05
   3.45200e-03]
 [ 1.98400e+03 -9.61840e-02  3.88100e-03  2.85000e-04  1.50000e-05
   3.58100e-03]
 [ 1.98500e+03 -1.11573e-01  3.77800e-03  1.29000e-04  1.40000e-05
   3.63500e-03]
 [ 1.98600e+03 -1.59520e-02  3.74800e-03  1.42000e-04  1.30000e-05
   3.59300e-03]
 [ 1.98700e+03  1.25160e-01  3.69700e-03  1.41000e-04  1.20000e-05
   3.54400e-03]
 [ 1.98800e+03  1.30629e-01  3.60800e-03  9.60000e-05  1.00000e-05
   3.50100e-03]
 [ 1.98900e+03  5.13540e-02  3.57800e-03  1.16000e-04  9.00000e-06
   3.45200e-03]
 [ 1.99000e+03  1.85934e-01  3.39500e-03  5.00000e-05  8.00000e-06
   3.33700e-03]
 [ 1.99100e+03  1.59674e-01  3.34700e-03  2.50000e-05  7.00000e-06
   3.31500e-03]
 [ 1.99200e+03  1.17780e-02  3.36700e-03  2.50000e-05  7.00000e-06
   3.33500e-03]
 [ 1.99300e+03  3.71160e-02  3.45700e-03  5.90000e-05  6.00000e-06
   3.39200e-03]
 [ 1.99400e+03  9.36050e-02  3.52500e-03  7.20000e-05  6.00000e-06
   3.44800e-03]
 [ 1.99500e+03  2.10188e-01  3.56700e-03  6.70000e-05  5.00000e-06
   3.49600e-03]
 [ 1.99600e+03  7.62420e-02  3.66300e-03  1.11000e-04  4.00000e-06
   3.54900e-03]
 [ 1.99700e+03  2.70659e-01  3.72400e-03  1.07000e-04  3.00000e-06
   3.61500e-03]
 [ 1.99800e+03  3.87073e-01  4.06900e-03  3.12000e-04  2.00000e-06
   3.75500e-03]
 [ 1.99900e+03  1.96247e-01  4.41900e-03  5.05000e-04  2.00000e-06
   3.91300e-03]
 [ 2.00000e+03  1.79381e-01  4.59500e-03  4.77000e-04  1.00000e-06
   4.11700e-03]
 [ 2.00100e+03  3.00362e-01  4.66400e-03  3.31000e-04  2.00000e-06
   4.33100e-03]
 [ 2.00200e+03  3.56521e-01  4.98100e-03  4.44000e-04  2.00000e-06
   4.53500e-03]
 [ 2.00300e+03  3.67133e-01  5.15700e-03  4.22000e-04  2.00000e-06
   4.73300e-03]
 [ 2.00400e+03  3.31877e-01  5.79100e-03  8.59000e-04  2.00000e-06
   4.93100e-03]
 [ 2.00500e+03  4.12549e-01  5.98000e-03  8.38000e-04  2.00000e-06
   5.13900e-03]
 [ 2.00600e+03  3.66532e-01  6.69200e-03  1.38900e-03  2.00000e-06
   5.30000e-03]
 [ 2.00700e+03  3.64295e-01  6.91500e-03  1.46500e-03  2.00000e-06
   5.44800e-03]
 [ 2.00800e+03  2.95030e-01  6.18500e-03  6.17000e-04  2.00000e-06
   5.56500e-03]
 [ 2.00900e+03  3.91066e-01  5.78200e-03  1.30000e-04  2.00000e-06
   5.65100e-03]
 [ 2.01000e+03  4.56738e-01  5.87800e-03  1.84000e-04  2.00000e-06
   5.69300e-03]
 [ 2.01100e+03  3.33513e-01  5.87400e-03  1.58000e-04  2.00000e-06
   5.71300e-03]
 [ 2.01200e+03  3.77450e-01  5.88700e-03  1.58000e-04  2.00000e-06
   5.72600e-03]
 [ 2.01300e+03  4.23672e-01  5.87500e-03  1.58000e-04  2.00000e-06
   5.71500e-03]
 [ 2.01400e+03  4.98338e-01  5.87500e-03  1.58000e-04  2.00000e-06
   5.71500e-03]
 [ 2.01500e+03  6.61503e-01  5.87500e-03  1.58000e-04  2.00000e-06
   5.71500e-03]
 [ 2.01600e+03  7.01840e-01  5.87500e-03  1.58000e-04  2.00000e-06
   5.71500e-03]
 [ 2.01700e+03  6.00164e-01  5.87500e-03  1.58000e-04  2.00000e-06
   5.71500e-03]
 [ 2.01800e+03  5.06493e-01  1.33530e-02  1.58000e-04  2.00000e-06
   5.71500e-03]]
(139,)
In [111]:
def forz(z,y):
    for i in range(2,137):
        
        rslt = [(y[i-2],y[i-1],y[i],y[i+1],y[i+2])]
        z[i] = np.mean(rslt)
    return z
In [112]:
zz = forz(z,y)
print(zz)
[-99, -99, -0.36144419999999994, -0.38150639999999997, -0.4086202, -0.44424280000000005, -0.4446568, -0.4235448, -0.444175, -0.452949, -0.4632544, -0.4957974, -0.5302774, -0.509981, -0.47791560000000005, -0.44038760000000005, -0.4292104, -0.3980022, -0.36710560000000003, -0.3774302, -0.40361199999999997, -0.4190996, -0.47890620000000006, -0.5232428, -0.5383558, -0.5636994000000001, -0.5849401999999999, -0.5868040000000001, -0.6056758, -0.6498674, -0.641634, -0.6173848000000001, -0.5600172, -0.49749440000000006, -0.4687176, -0.4651234000000001, -0.441275, -0.45233740000000006, -0.47958920000000005, -0.45035520000000007, -0.43332420000000005, -0.43546060000000003, -0.4458056, -0.4333748, -0.41644120000000007, -0.4004494, -0.3924058, -0.4017168, -0.3926026, -0.39478540000000006, -0.3889538, -0.403949, -0.3659652, -0.3741684, -0.38298139999999997, -0.362714, -0.31930319999999995, -0.3012784, -0.2544046, -0.1930558, -0.160006, -0.12408020000000002, -0.0641414, -0.05020480000000001, -0.09093300000000001, -0.1318424, -0.1734174, -0.24367419999999998, -0.30994059999999996, -0.31103959999999997, -0.29588059999999994, -0.26663939999999997, -0.27799399999999996, -0.27294019999999997, -0.31078419999999995, -0.3062214, -0.3031084, -0.2679236, -0.2368378, -0.18122100000000002, -0.172787, -0.1734286, -0.2150146, -0.23442980000000002, -0.2542976, -0.2749868, -0.3025874, -0.254283, -0.231234, -0.24237959999999997, -0.2340684, -0.1951026, -0.228016, -0.2347842, -0.2350078, -0.2011472, -0.21163420000000005, -0.1519268, -0.0999092, -0.0240912, -0.027405199999999998, 0.0184738, 0.0027117999999999977, -0.023547200000000004, -0.0379626, -0.0002886000000000055, 0.006415999999999994, 0.0359236, 0.095425, 0.1305502, 0.10787379999999999, 0.0891712, 0.0976214, 0.10247220000000001, 0.0857858, 0.13756200000000002, 0.20755340000000003, 0.2280818, 0.2219204, 0.2667444, 0.28391679999999997, 0.2799288, 0.3070548, 0.3536884, 0.36692240000000004, 0.3684772, 0.3540566, 0.3658944, 0.37473220000000007, 0.36812839999999997, 0.3707594, 0.3964878, 0.41794220000000004, 0.4588952, 0.5325606, 0.5771033999999999, 0.5936676, -99, -99]
In [113]:
def n1func():
    n1 = []
    for i in range(0,139):
        if y[i] >= 0:
            n1.append(i)
    return n1

def n2func():
    n2 = []
    for i in range(0,139):
        if y[i] < 0:
            n2.append(i)
    return n2
    
        
        
In [114]:
n1 = n1func()
n2 = n2func()
print(n1,n2)
[64, 100, 101, 103, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 102, 104, 105, 106]
In [115]:
x1= x[n1]
y1= y[n1]
x2 = x[n2]
y2 = y[n2]
x3 = x[2:136]
y3 = z[2:136]
In [116]:
plt.bar(x1,y1,color='r',width=1.)
plt.bar(x2,y2,color='b',width=1.)
plt.plot(x3,y3,color='k')
plt.xlim(1877,2020)

plt.xlabel('Time')
plt.ylabel('Temperature $^{o}$C')
plt.title('NOAA Global Average Annual Mean Temperature Anomalies')
Out[116]:
Text(0.5, 1.0, 'NOAA Global Average Annual Mean Temperature Anomalies')
In [117]:
#Plot US temp and prec times series on the same figure
#lines 598-610
Time = [2001,2002,2003,2004,2005,2006,2007,2008,2009,2010]
Tmean = [12.06, 11.78,11.81,11.72,12.02,12.36,12.03,11.27,11.33,11.66]
Prec = [737.11,737.87,774.95,844.55,764.03,757.43,741.17,793.50,820.42,796.80]

plt.subplot(2,1,1)
plt.plot(Time,Tmean,color="r")
plt.ylabel("TMean ($^{o}$C)")
plt.title("US Annual Mean Temperature")

plt.subplot(2,1,2)
plt.plot(Time,Prec,color="b")
plt.ylabel("Prec (mm)")
plt.title("US Annual Total Precipitation")

plt.subplots_adjust(top=1.1, bottom=0.1, left=0.10, right=0.95, hspace=0.25,
                    wspace=0.35)
In [118]:
#Stack multiple panels together as a single figure
#lines 613-618
x1 = np.linspace(0.,20.,100)
x2 = np.linspace(0.,10.,100)
x3 = np.linspace(10.,20.,100)

y1 = np.sin(x1)
y2 = np.sin(x2)
y3 = np.sin(x3)

plt.subplot(221)
plt.plot(x1,y1)
plt.xlabel("x")
plt.ylabel("sin")

plt.subplot(222)
plt.plot(x2,y1)
plt.xlabel("x")
plt.ylabel("sin")

plt.subplot(223)
plt.plot(x3,y1)
plt.xlabel("x")
plt.ylabel("sin")
plt.subplots_adjust(top=1.05, bottom=0.07, left=0.10, right=0.95, hspace=0.25,
                    wspace=0.35)
In [119]:
a = np.linspace(-90.,90.,37)
b = np.linspace(0.,359.9,72)

mapdat = np.reshape(np.random.normal(loc=0.,size=72*37),(37,72))
In [120]:
plt.figure(figsize=(15., 7.))

#using the basemap package to put the cylindrical projection map into the image
ReAn = Basemap(projection='cyl',llcrnrlat=-90.,urcrnrlat=90.,\
            llcrnrlon=-180.,urcrnrlon=180.,resolution='c')

#shifting the data because 
lons, data = ReAn.shiftdata(b, datain = mapdat, lon_0=0)

#draw coastlines, latitudes, and longitutes on the map
ReAn.drawcoastlines(color='black', linewidth=1)
ReAn.drawparallels(np.arange(-90.,91.,30.), labels = [1,0,0,1])
ReAn.drawmeridians(np.arange(-180.,181.,60.), labels= [1,0,0,1])
ReAn.drawcountries()


limit=np.linspace(-3.,3.,81)
ReAn_plt = plt.contourf(np.array(lons),np.array(a),data,limit,cmap = 'jet')
ceb = plt.colorbar(ReAn_plt)
ceb.ax.set_ylabel('$^{o}$C')
plt.xlabel('Longitude',labelpad = 30)
plt.ylabel('Latitude',labelpad =30)
plt.title('Standard Normal Random Values on a World Map: 5 Lat-Lon Grid')
plt.show()
In [121]:
#Plot a regional map of a random dataset on a map
#Plot a 5-by-5 grid regional map to cover USA and Canada
#lines 646-657
c = np.linspace(10.,70.,13)
d = np.linspace(230.,295.,14)

mapdat2 = np.reshape(np.random.normal(loc=0.,size=14*13),(13,14))
In [122]:
plt.figure(figsize=(15., 7.))

#using the basemap package to put the cylindrical projection map into the image
ReAn = Basemap(projection='cyl',llcrnrlat=10.,urcrnrlat=70.,\
            llcrnrlon=-130.,urcrnrlon=-65.,resolution='c')

#shifting the data because 
lons2, data = ReAn.shiftdata(d, datain = mapdat2, lon_0=0)

#draw coastlines, latitudes, and longitutes on the map
ReAn.drawcoastlines(color='black', linewidth=1)
ReAn.drawparallels(np.arange(-90.,91.,10.), labels = [1,0,0,1])
ReAn.drawmeridians(np.arange(-180.,181.,10.), labels= [1,0,0,1])
ReAn.drawcountries()


limit=np.linspace(-3.,3.,81)
ReAn_plt = plt.contourf(np.array(lons2),np.array(c),data,limit,cmap = 'jet')
ceb = plt.colorbar(ReAn_plt)
ceb.ax.set_ylabel('$^{o}$C')
plt.xlabel('Longitude',labelpad = 20)
plt.ylabel('Latitude',labelpad =30)
plt.title('Standard Normal Random Values on a World Map to cover USA and Canada: 5 Lat-Lon Grid')
plt.show()
In [123]:
#R plot of NCEP/NCAR Reanalysis PSD monthly temp data .nc file
#http://www.esrl.noaa.gov/psd/data/gridded/data.ncep.reanalysis.derived.surface.html
#lines658-698
nc = ds("air.mon.mean.nc","r+")
In [124]:
#define the variables from the data set
lon_vals = nc.variables['lon']
lat_vals = nc.variables['lat']
time = nc.variables['time']
air = nc.variables['air']
time_unit = time.units
In [125]:
precnc = nc.variables['air']
nc_x = np.linspace(-90,90,73)
plt.plot(nc_x,precnc[0,:,71],ls="-",color='k' )
plt.xlabel('Latitude')
plt.ylabel('Temp[$^{\circ}$C]')
plt.title('90S-90N Temperature [$^{o}$C] Along a Meridional Line at 35E: Jan 1948')
plt.show()
In [126]:
def spmat(x,y):
    for i in range(0,845):
        y[:,i] = np.reshape(x[i,:,:],(144*73))
    return y
In [127]:
precst = np.zeros((10512,845))
temp = np.reshape(precnc[0,:,:],(144*73))
precst2 = spmat(precnc,precst)
In [128]:
#Build lat and lon for 10512 spatial positions using rep
def rep(x,y,n):
    for j in range(0,n):
        x = np.append(x,y)
    return x
In [129]:
arra = np.zeros((0))
LAT = rep(arra,lat_vals,144)
LON = rep(arra,lon_vals[0],73)
In [130]:
def recon():
    rslt = LON
    for jj in range(0,143):
        rslt = np.append(rslt,rep(arra,lon_vals[jj],73))
    return rslt
In [131]:
LON2 = recon()
In [132]:
#With 826 months spanning from 1948-2016
gpcpst_ = np.column_stack((LAT,(LON2)))
gpcpst = np.column_stack((gpcpst_,precst2))
#Creating an array with first two columns of lat and lon
ones = np.ones(845)
In [133]:
#define a function to create a list of months repeating to match
#the size of our datadef monthfunc():
def monthfunc():
    montharr = np.zeros(0)
    for i in range(1,71):
        for j in range(1,13):
             montharr = np.append(montharr,int(j))
    for k in range(1,6):
        montharr = np.append(montharr,int(k))
    return montharr
months = monthfunc()
In [134]:
#define a function to create a list of years to match the
#size of our data
def yearfunc():
    yeararr = np.zeros(0)
    for i in range(1948,2018):
        for j in range(1,13):
            yeararr = np.append(yeararr,i)
    for k in range(1,6):
        yeararr = np.append(yeararr,2018)
    return yeararr
years = yearfunc()
In [135]:
#define a function that pairs a string version of our year and
#month arrays with a dash in between
#these will serve as our column names for our data
def tmfunc():
    empty = np.zeros(0)
    for i in range(0,845):
        empty = np.append(empty,str(years[i])  + '-'  + str(months[i]))
    return empty
tm1 = tmfunc() 
In [136]:
#add the 'Lat' and 'Lon' columns to the beginning
#of our column names array
tm2 = np.append(['Lat','Lon'],tm1)
In [137]:
#use the package pandas.DataFrame to match the column names
#with the data
GPCPST = pd.DataFrame(gpcpst,columns=tm2)
In [138]:
#save the data as a .csv file
GPCPST.to_csv('ncepJan1948_May2018.csv')
In [139]:
monJ = np.array(range(0,844,12))
gpcpdat = gpcpst[:,2:844]
gpcpJ = gpcpdat[:,monJ]
climJ = gpcpJ.mean(axis=1)
In [140]:
def rowSDS():
    rslt = []
    for jj in range(0,10512):
        rslt = np.append(rslt,np.std(gpcpJ[jj,:]))
    return rslt
sdJ = rowSDS()
In [141]:
def repeat():
    q = np.zeros((144,73))
    u = climJ[0:73]
    for ii in range(0,144):
        q[ii,:] = u
    return q
In [142]:
Lat1 = np.linspace(90.,-90.,73)
Lat = -Lat1
mapmat = np.reshape(climJ,(73,144))
In [143]:
plt.figure(figsize=(15., 7.))

#using the basemap package to put the cylindrical projection map into the image
ReAn = Basemap(projection='cyl',llcrnrlat=-90.,urcrnrlat=90.,\
            llcrnrlon=-180.,urcrnrlon=180.,resolution='c')

#shifting the data because 
lons, data = ReAn.shiftdata(lon_vals, datain = mapmat, lon_0=0)


#draw coastlines, latitudes, and longitutes on the map
ReAn.drawcoastlines(color='black', linewidth=1)
ReAn.drawparallels(np.arange(-90.,91.,30.), labels = [1,0,0,1])
ReAn.drawmeridians(np.arange(-180.,181.,60.), labels= [1,0,0,1])
ReAn.drawcountries()

limit = np.linspace(-50.,50.,81)

ReAn_plt = plt.contourf(np.array(lons),np.array(lat_vals),data,limit,cmap = 'jet')
ceb = plt.colorbar(ReAn_plt)
ceb.ax.set_ylabel('$^{o}$C')
plt.xlabel('Longitude',labelpad = 20)
plt.ylabel('Latitude',labelpad =20)
plt.title('NCEP Jan SAT RA 1948-2018 climatology [$^{o}$C]')
Out[143]:
Text(0.5, 1.0, 'NCEP Jan SAT RA 1948-2018 climatology [$^{o}$C]')
In [144]:
#Compute standard deviation Jan 1948-Dec 2016
#lines730-744
Lat = np.linspace(90.,-90.,73)
Lat = -Lat
mapmat2 = np.reshape(sdJ,(73,144))
In [145]:
plt.figure(figsize=(15., 7.))


#using the basemap package to put the cylindrical projection map into the image
ReAnSd = Basemap(projection='cyl',llcrnrlat=-90.,urcrnrlat=90.,\
            llcrnrlon=-180.,urcrnrlon=180.,resolution='c')

#shifting the data because 
lons, data = ReAnSd.shiftdata(lon_vals, datain = mapmat2, lon_0=0)


#draw coastlines, latitudes, and longitutes on the map
ReAnSd.drawcoastlines(color='black', linewidth=1)
ReAnSd.drawparallels(np.arange(-90.,91.,30.), labels = [1,0,0,1])
ReAnSd.drawmeridians(np.arange(-180.,181.,60.), labels= [1,0,0,1])
ReAnSd.drawcountries()

limit = np.linspace(0.,10,40)

ReAnSd_plt = plt.contourf(np.array(lons),np.array(lat_vals),data,limit,cmap = 'jet')
ceb = plt.colorbar(ReAnSd_plt)
ceb.ax.set_ylabel('$^{o}$C')
plt.xlabel('Longitude',labelpad = 20)
plt.ylabel('Latitude',labelpad =20)
plt.title('NCEP Jan SAT RA 1948-2018 Standard Deviation [$^{o}$C]')

plt.show()
In [146]:
#Plot the January 1983 temperature using the above setup
#lines756-770
Lat = np.linspace(90.,-90.,73)
Lat = -Lat
mapmat83J = np.reshape(precnc[421,:,:],(73,144))
In [147]:
plt.figure(figsize=(15., 7.))

#using the basemap package to put the cylindrical projection map into the image
ReAnSd2 = Basemap(projection='cyl',llcrnrlat=-90.,urcrnrlat=90.,\
            llcrnrlon=-180.,urcrnrlon=180.,resolution='c')

#shifting the data  
lons, data3 = ReAnSd2.shiftdata(lon_vals, datain = mapmat83J, lon_0=0)


#draw coastlines, latitudes, and longitutes on the map
ReAnSd2.drawcoastlines(color='black', linewidth=1)
ReAnSd2.drawparallels(np.arange(-90.,91.,30.), labels = [1,0,0,1])
ReAnSd2.drawmeridians(np.arange(-180.,181.,60.), labels= [1,0,0,1])
ReAnSd2.drawcountries()

limit = np.linspace(-50.,50,80)

ReAnSd2_plt = plt.contourf(np.array(lons),np.array(lat_vals),data3,limit,cmap = 'jet')
ceb = plt.colorbar(ReAnSd2_plt)
ceb.ax.set_ylabel('$^{o}$C')
plt.xlabel('Longitude',labelpad = 20)
plt.ylabel('Latitude',labelpad =20)
plt.title('January 1983 Surface Air Temperature [$^{o}$C]')

plt.show()
In [148]:
#Plot the January 1983 temperature anomaly from NCEP data
#lines771-782
plt.figure(figsize=(15., 7.))

#using the basemap package to put the cylindrical projection map into the image
ReAnSd3 = Basemap(projection='cyl',llcrnrlat=-90.,urcrnrlat=90.,\
            llcrnrlon=-180.,urcrnrlon=180.,resolution='c')

#shifting the data because 
lons, data4 = ReAnSd3.shiftdata(lon_vals, datain = mapmat83J, lon_0=0)


#draw coastlines, latitudes, and longitutes on the map
ReAnSd3.drawcoastlines(color='black', linewidth=1)
ReAnSd3.drawparallels(np.arange(-90.,91.,30.), labels = [1,0,0,1])
ReAnSd3.drawmeridians(np.arange(-180.,181.,60.), labels= [1,0,0,1])
ReAnSd3.drawcountries()

limit = np.linspace(-50.,50.,81)

ReAnSd3_plt = plt.contourf(np.array(lons),np.array(lat_vals),data4,limit,cmap = 'jet')
ceb = plt.colorbar(ReAnSd3_plt)
ceb.ax.set_ylabel('$^{o}$C')
plt.xlabel('Longitude',labelpad = 20)
plt.ylabel('Latitude',labelpad =20)
plt.title('January 1983 Surface Air Temperature Anomaly  [$^{o}$C]')

plt.show()
In [149]:
#line 793 -- Creating arrays of longitudes and latituds
a = np.array([[-75, -45, -15, 15, 45, 75]])
b = np.array([[-165,-135,-105,-75,-45,-15,15,45,75,105,135,165]])

#concatenating, or combining each array so that they are the same size
lat = np.concatenate((a,a,a,a,a,a,a,a,a,a,a,a), axis = 0)
lon = np.concatenate((b,b,b,b,b,b), axis = 0)

#transposing the newly created arrays
lat_tran = np.transpose(lat)
lon_tran = np.transpose(lon)
In [150]:
#vectors u and v line 799 -- Creating vectors for wind directions
c = np.array([[-1,1,-1,-1,1,-1]])
d = np.array([[1,-1,1,-1,1,-1]])

#concatenating in a similar way as before to match sizes
u = np.concatenate((c,c,c,c,c,c,c,c,c,c,c,c), axis = 0)
v = np.concatenate((d,d,d,d,d,d,d,d,d,d,d,d), axis = 0)
In [151]:
#choosing an arbitrary size for the image
plt.figure(figsize=(10., 5.))


#using the basemap package to put the cylindrical projection map into the image
wmap = Basemap(projection='cyl',llcrnrlat=-90.,urcrnrlat=90.,\
            llcrnrlon=-180.,urcrnrlon=180.,resolution='c')


#draw coastlines, latitudes, and longitutes on the map
wmap.drawcoastlines(color='black', linewidth=1)
wmap.drawparallels(np.arange(-90.,91.,30.), labels = [1,0,0,1])
wmap.drawmeridians(np.arange(-180.,181.,30.), labels= [1,0,0,1])


#plot points in the center of each box made by latitude and longitude
x, y = lon_tran, lat
wmap.scatter(x,y,marker='D',color='k')


#plot vector arrows originating from the midpoints
wmap.quiver(x, y, u, v, scale=30, color = 'b' )


#add axis titles
plt.text(-95,-4, 'Intertropical Convergence Zone (ITCZ)', color='r', fontsize = 15)
plt.text(15,-35, 'Subtropical High', color = 'r', fontsize = 15)
plt.text(15,27, 'Subtropical High', color = 'r', fontsize = 15)
plt.text(-20,92, 'Polar High', color = 'r', fontsize = 15)

#plot
plt.show()
In [152]:
#The following is heavily based on the following stackoverflow link:
#https://stackoverflow.com/questions/39742305/how-to-use-basemap
#-python-to-plot-us-with-50-states

#Lambert Conformal map of lower 48 states.
ggplot = Basemap(llcrnrlon=-119,llcrnrlat=22,urcrnrlon=-64,urcrnrlat=49,
        projection='lcc',lat_1=33,lat_2=45,lon_0=-95)
#draw state boundaries.
#data from U.S Census Bureau
#http://www.census.gov/geo/www/cob/st2000.html
shp_info = ggplot.readshapefile('st99_d00','states',drawbounds=True)

#create a popdensity array to fill with random integers
popdensity = {
'New Jersey':0,
'Rhode Island':0,
'Massachusetts':0,
'Connecticut':0,
'Maryland':0,
'New York':0,
'Delaware':0,
'Florida':0,
'Ohio':0,
'Pennsylvania':0,
'Illinois':0,
'California':0,
'Hawaii':0,
'Virginia':0,
'Michigan':0,
'Indiana':0,
'North Carolina':0,
'Georgia':0,
'Tennessee':0,
'New Hampshire':0,
'South Carolina':0,
'Louisiana':0,
'Kentucky':0,
'Wisconsin':0,
'Washington':0,
'Alabama':0,
'Missouri':0,
'Texas':0,
'West Virginia':0,
'Vermont':0,
'Minnesota':0,
'Mississippi':0,
'Iowa':0,
'Arkansas':0,
'Oklahoma':0,
'Arizona':0,
'Colorado':0,
'Maine':0,
'Oregon':0,
'Kansas':0,
'Utah':0,
'Nebraska':0,
'Nevada':0,
'Idaho':0,
'New Mexico':0,
'South Dakota':0,
'North Dakota':0,
'Montana':0,
'Wyoming':0,
'Alaska':0}
colors={}
statenames=[]
cmap = plt.cm.jet # use 'jet' colormap
vmin = 0; vmax = 450 # set range.

#fill popdensity with random integers
for shapedict in ggplot.states_info:
    statename = shapedict['NAME']
    # skip DC and Puerto Rico.
    if statename not in ['District of Columbia','Puerto Rico']:
        popdensity[statename] = randint(0,100)


for shapedict in ggplot.states_info:
    statename = shapedict['NAME']
    # skip DC and Puerto Rico.
    if statename not in ['District of Columbia','Puerto Rico']:
        pop = popdensity[statename]
        # calling colormap with value between 0 and 1 returns
        # rgba value.  Invert color range (hot colors are high
        # population), take sqrt root to spread out colors more.
        colors[statename] = cmap(1.-np.sqrt((pop-vmin)/(vmax-vmin)))[:3]
    statenames.append(statename)
#cycle through state names, color each one.
ax = plt.gca() # get current axes instance
for nshape,seg in enumerate(ggplot.states):
    # skip DC and Puerto Rico.
    if statenames[nshape] not in ['District of Columbia','Puerto Rico']:
        color = rgb2hex(colors[statenames[nshape]]) 
        poly = Polygon(seg,facecolor=color,edgecolor=color)
        ax.add_patch(poly)
        
plt.title('Filling State Polygons with Random Integer data')
plt.show()

Plot the wind field over the ocean

In [153]:
#downloading data from said url
downloaded_data  = request.urlopen('ftp://eclipse.ncdc.noaa.gov/pub/seawinds/SI/uv/clm/uvclm95to05.nc','Users/louis/AnacondaProjects/uvclm95to05.nc')

#using netCDF4.Dataset to open the data
mincwind = ds("uvclm95to05.nc",'r+',"NETCDF4")
In [154]:
#print the information from the data along with its array shape
#shape = null
print(np.shape(mincwind))
print(mincwind)
()
<class 'netCDF4._netCDF4.Dataset'>
root group (NETCDF3_CLASSIC data model, file format NETCDF3):
    Conventions: COARDS, CF-1.0, Unidata Dataset Discovery v1.0
    title: NOAA Multiple-Satellite Blended 0.25-degree Sea Winds - climatological monthly means
    source: Multiple satellite observations: DMSP SSMI F08, F10, F11, F13,F14 F15; TMI; QuikSCAT; AMSR-E; Direction from NCEP Reanalysis-2
    summary: Gridded and blended sea surface vector winds from multiple satellites with direction from NCEP Reanalysis-2; Global ocean coverage with a 0.25-degree resolution; The whole datasets covers from July 1987 to present. Climatological (1995-2005) monthlies in this dataset; 6-hourly, daily & monthly resolutions are also available in other directories; See http://www.ncdc.noaa.gov/oa/rsad/blendedseawinds.html and links within for details. Include (u,v) means and scalar mean speed w for comparison
    Keywords: sea winds, ocean winds, sea surface winds, air-sea interaction, air-sea flux, wind-driven circulation, Ekman pumping, Ekman transport, ocean upwelling, wind stress, windstress
    references: links at http://www.ncdc.noaa.gov/oa/rsad/blendedseawinds.html
    History: Simple spatiotemporally weighted Interpolation (SI), V.1.2. Version 1.2 uses updated satellite retrievals by Remote Sensing System, released in September 2006: SSMI V06, TMI V04, QSCAT V03a. AMSRE V05 was also updated using the new SSMI rain rate
    institution: NOAA NESDIS National Climatic Data Center
    Contact: Huai-Min.Zhang AT noaa.gov or ncdc.satorder AT noaa.gov;    ph:1+828-271-4090 or 271-4800
    Acknowledgment: The gridded data were generated from the multiple satellite observations of DOD, NOAA and NASA (and future others) and wind retrievals of the Remote Sensing Systems, Inc. (http://www.remss.com), using scientific methods such as objective analysis (OA). The OA is only truly objective when the needed statistics are completely known, which may not be always the case.
    Comment: The time in the netCDF should be the center of a calendar month, and chosen as Day 15 for all months for simplicity. The climatolog period is 1995 - 2005
    dimensions(sizes): zlev(1), time(12), lat(719), lon(1440)
    variables(dimensions): int32 time(time), float32 zlev(zlev), float32 lat(lat), float32 lon(lon), float32 u(time,zlev,lat,lon), float32 v(time,zlev,lat,lon), float32 w(time,zlev,lat,lon)
    groups: 

In [155]:
#defining and extracting 'u' and 'v' variable
u_0 = mincwind.variables['u']
v_0 = mincwind.variables['v']

#reshaping the arrays into 3D
u = np.reshape(u_0,(12,719,1440))
v = np.reshape(v_0,(12,719,1440))



print(np.shape(v))
print(np.shape(u))
(12, 719, 1440)
(12, 719, 1440)
In [156]:
#basic filled contour plot
plt.figure(figsize=(12,7))
levels =range(-23,23)

ucont = plt.contourf(u[8,:,:],levels)

cbar = plt.colorbar(ucont)

plt.savefig('Figure2.png', bbox_inches='tight')
plt.show()
In [157]:
#a preset color map titled 'hot'
plt.figure(figsize=(12,7))
ucont2 = plt.contourf(u[8,:,:],levels,cmap = 'hot')

cbar = plt.colorbar(ucont2)

plt.show()
In [158]:
#a preset colormap titled 'Red to Blue'
plt.figure(figsize=(12,7))
ucont3 = plt.contourf(u[8,:,:],levels,cmap = 'RdBu')

cbar = plt.colorbar(ucont3)

plt.show()
In [159]:
#basic contour plot without filling
plt.figure(figsize=(12,7))
ucont4 = plt.contour(u[8,:,:],levels)


cbar = plt.colorbar(ucont4)

plt.show()

Generate 3D data

In [160]:
#create two arrays, x and y, ranging from 0 to 2pi 
#create a 3D array full of zeros for use later
x = y = np.linspace(0.,2.*np.pi,100)
mydat = np.zeros((100,100,10))
In [161]:
#define a function that iterates term by term for the two variables
#into a specific function
def zfunc(x,y):
    #the triple nested for loop accomplishes term by term order we are
    #looking for
    for t in range(1,11,1):
        for i in range(0,100):
            for j in range(0,100):
                mydat[i,j,t-1] = (np.sin(t)*(1./np.pi)*np.sin(x[i])*np.sin(y[j]) 
                    + np.exp(-.3*t)*(1./np.pi)*np.sin(8.*x[i])*np.sin(8.*y[j]))
    
    #print the shape and maximum of the result
    print(np.shape(mydat))
    print(np.max(mydat))
    return mydat


#plug in our x and y arrays 
mydata = zfunc(x,y)
(100, 100, 10)
0.4909421672431401

Plot the original z(x,y,t) waves for a given t.

In [162]:
#set a range for the contour plot
maxi = np.linspace(-.5,.5,100)

#define the contour plot using previous x, y, and mydata arrays
CS = plt.contourf(x,y,mydata[:,:,0],maxi,cmap='hsv')

#add titles for the graph and axes
plt.title('Original field at t=10')
plt.xlabel('x')
plt.ylabel('y')

#add a color bar with a label
cbar = plt.colorbar(CS)
cbar.ax.set_ylabel('Scale')

#save figure as a .png file
plt.savefig('Figure1.png',bbox_inches='tight')

3D array to 2D space-time matrix.

In [163]:
#create two arrays, x and y, ranging from 0 to 2pi 
x = y = np.linspace(0.,2.*np.pi,100)
In [164]:
#create a matrix of zeros of size (10000,10)
da = np.matrix(np.zeros((np.size(x)*np.size(y),10)))

print(np.shape(da))
(10000, 10)
In [165]:
#define a function that reshapes mydata into a new array of size
#(10000,10)
def twod_func(x,y,a):
    for i in range(0,10):
        a[:,i] = np.reshape(mydata[:,:,i],(10000,1))
    return a
    
In [166]:
da1 = twod_func(x,y,da)
In [167]:
#SVD for EOFs
u, s, vh = np.linalg.svd(da1,full_matrices=False) 
v = np.transpose(vh)
In [168]:
#Dimensions of EOFs, PCs, and Energy for the corresponding EOFs
print('Energy for the corresponding EOFs:',s.shape)
print('PCs:',v.shape)
print('EOFs:',u.shape)
Energy for the corresponding EOFs: (10,)
PCs: (10, 10)
EOFs: (10000, 10)
In [169]:
#to show the accuracy of the svd
x_a = np.dot(np.dot(u,np.diag(s)),vh)
print("Standard deviation of our original array:",np.std(da1))
print("Standard deviation of recreated array",np.std(x_a))
print("Standard deviation of original array minus recreated:",np.std(da1-x_a))
Standard deviation of our original array: 0.12421337800476684
Standard deviation of recreated array 0.12421337800476684
Standard deviation of original array minus recreated: 4.562994433933075e-17
In [170]:
print((s**2.)/np.sum(s**2.))
print(np.shape(u))
[8.34875067e-01 1.65124933e-01 1.44516547e-32 6.13297786e-33
 1.27373287e-33 6.40559476e-34 5.97250985e-34 4.00687853e-34
 2.95626312e-34 2.32787891e-34]
(10000, 10)
In [171]:
#create an array of zeros of size (100,100)
u2 = np.zeros((100,100))

#define a function that takes all the entries in the first column
#of the u matrix, transposes it and reshapes it into size (100,100)
def dig():
    y1 = np.transpose(u[:,0])
    u2 = np.reshape(-y1,(100,100))
    return u2


u_mat = dig()
print(u_mat[0,:])
[[-9.65276300e-17 -1.64914185e-16 -1.49232881e-31 -9.65799827e-33
   4.08220684e-33  4.33147219e-34 -8.83293749e-34 -2.25745800e-34
   2.18024380e-35 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00
  -0.00000000e+00 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00
  -0.00000000e+00 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00
  -0.00000000e+00 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00
  -0.00000000e+00 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00
  -0.00000000e+00 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00
  -0.00000000e+00 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00
  -0.00000000e+00 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00
  -0.00000000e+00 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00
  -0.00000000e+00 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00
  -0.00000000e+00 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00
  -0.00000000e+00 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00
  -0.00000000e+00 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00
  -0.00000000e+00 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00
  -0.00000000e+00 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00
  -0.00000000e+00 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00
  -0.00000000e+00 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00
  -0.00000000e+00 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00
  -0.00000000e+00 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00
  -0.00000000e+00 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00
  -0.00000000e+00 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00
  -0.00000000e+00 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00
  -0.00000000e+00 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00]]
In [172]:
#Graph the EOF
size = np.linspace(-2.5e-2,2.5e-2,25)
dig = plt.contourf(x,y,u_mat,size,cmap='hsv')

plt.title('SVD Mode 1: EOF1')
plt.xlabel('x')
plt.ylabel('y')

cbar = plt.colorbar(dig)
cbar.ax.set_ylabel('Scale')
plt.figure()
Out[172]:
<Figure size 432x288 with 0 Axes>
<Figure size 432x288 with 0 Axes>

Accurate spatial patterns from functions that generate data.

In [173]:
#Create an array of all zeros to fill with data
#line 961
x = y = np.linspace(0.,2.*np.pi,100)
zeros = np.zeros((100,100))
In [174]:
#define two different funcitons to be used below
def z1(x,y):
    return (1./np.pi)*np.sin(x)*np.sin(y)

def z2(x,y):
    return (1./np.pi)*np.sin(5.*x)*np.sin(5.*y)
In [175]:
#define two more functions that call upon the above z
#functions to generate spatial data
def fcn1(x,y):
    for i in range(0,100):
        for j in range(0,100):
            zeros[i,j] = z1(x[i],y[j])
    return zeros

def fcn2(x,y):
    for i in range(0,100):
        for j in range(0,100):
            zeros[i,j] = z2(x[i],y[j])
    return zeros
In [176]:
#plot the data
lvl = np.linspace(-.35,.35,35)
contour = plt.contourf(x,y,fcn1(x,y),lvl,cmap='hsv')

plt.title('Accurate Mode 1')
plt.xlabel('x')
plt.ylabel('y')

cbar = plt.colorbar(contour)
cbar.ax.set_ylabel('Scale')

plt.figure()
Out[176]:
<Figure size 432x288 with 0 Axes>
<Figure size 432x288 with 0 Axes>

Plot PCs and coefficitents of the functional patterns

In [177]:
t = np.array(range(1,11))

plt.plot(t,v[:,0],color='k',marker='o',label='PC1: 83% Variance')

plt.plot(t,v[:,1],color='r',marker='o',label='PC2: 17% Variance')

plt.plot(t,-np.sin(t),color='b',marker='o',label='Original mode 1 coefficient $-sin(t)$: 91% variance')

plt.plot(t,-np.exp(-.3*t),color='m',marker='o',label='Original mode 2 coefficient $e^{-0.3t}$: 9% variance')

plt.ylim(-1.1,1.1)

plt.xlabel('Time')
plt.ylabel('PC of Coefficient')
plt.title('SVD PCs vs. Accurate Temporal Coefficients')

plt.legend(loc=(1.1,.5))
plt.show()

Orthonormal Verification

In [178]:
#Create arrays to compute a dot product
t = np.array(range(1,11))
def sinarr(t):
    rslt = np.zeros(t)
    for ii in range(1,t+1):
        rslt[ii-1] = -np.sin(ii)
    return rslt


def exparr(t):
    rslt = np.zeros(t)
    for jj in range(1,t+1):
        rslt[jj-1] = -np.exp(-.3*jj)
    return rslt
In [179]:
sin = sinarr(10)
exp = exparr(10)

print('The dot product is:',np.dot(np.transpose(sin),exp))
The dot product is: 0.8625048359266786
In [180]:
#Now to find the variances and compare them
v1 = np.var(sin)
v2 = np.var(exp)
print(v1/(v1+v2))
0.9098719287510537
In [181]:
#Verify orthogonality of PCs
v_1 = np.transpose(v[:,0])
v_2 = v[:,1]
print("The dot product of PCs is:" , np.dot(v_1,v_2))
The dot product of PCs is: [[-5.55111512e-17]]

Data reconstruction from 2 modes

In [182]:
#create arrays that will be used below 
#and check the sizes of the arrays for matrix multiplication
sdiag = np.diag(s)
print(np.shape(sdiag[0:1,0:1]))
print(np.shape(u[:,0:1]))
print(np.shape(v[:,0:1]))
vtran = np.transpose(v[:,0:1])
(1, 1)
(10000, 1)
(10, 1)
In [183]:
#create two new arrays, B and B1 multiplying the previous
#matrices together, being careful to match their sizes
B = np.transpose(np.matmul(np.matmul(u[:,0:1],sdiag[0:1,0:1]),vtran))
B1 = np.matmul(np.matmul(u,sdiag),np.transpose(v))
In [184]:
#reshaping B to plot it
BB = np.reshape(B[4,:],(100,100))

scale = np.linspace(-.33,.36,25)
BBplot = plt.contourf(x,y,BB,scale,cmap = 'hsv')


plt.title('2-mode SVD reduced field t = 5')
plt.xlabel('x')
plt.ylabel('y')

cbar = plt.colorbar(BBplot)
cbar.ax.set_ylabel('Scale')

plt.figure()
Out[184]:
<Figure size 432x288 with 0 Axes>
<Figure size 432x288 with 0 Axes>

4 dimensions: lon, lat, level, time

In [185]:
#first, download the following data set from NOAA into your active folder
#https://www.esrl.noaa.gov/psd/repository/entry/show?
#entryid=synth%3Ae570c8f9-ec09-4e89-93b4-babd5651e7a9%
#3AL25jZXAucmVhbmFseXNpcy5kZXJpdmVkL3N1cmZhY2UvYWlyLm1vbi5tZWFuLm5j

#then use the Dataset function, ds, to use the data
nc = ds("air.mon.mean.nc","r+")
In [186]:
print(nc)
<class 'netCDF4._netCDF4.Dataset'>
root group (NETCDF4_CLASSIC data model, file format HDF5):
    description: Data from NCEP initialized reanalysis (4x/day).  These are the 0.9950 sigma level values
    platform: Model
    Conventions: COARDS
    NCO: 20121012
    history: Thu May  4 20:11:16 2000: ncrcat -d time,0,623 /Datasets/ncep.reanalysis.derived/surface/air.mon.mean.nc air.mon.mean.nc
Thu May  4 18:11:50 2000: ncrcat -d time,0,622 /Datasets/ncep.reanalysis.derived/surface/air.mon.mean.nc ./surface/air.mon.mean.nc
Mon Jul  5 23:47:18 1999: ncrcat ./air.mon.mean.nc /Datasets/ncep.reanalysis.derived/surface/air.mon.mean.nc /dm/dmwork/nmc.rean.ingest/combinedMMs/surface/air.mon.mean.nc
/home/hoop/crdc/cpreanjuke2farm/cpreanjuke2farm Mon Oct 23 21:04:20 1995 from air.sfc.gauss.85.nc
created 95/03/13 by Hoop (netCDF2.3)
Converted to chunked, deflated non-packed NetCDF4 2014/09
    title: monthly mean air.sig995 from the NCEP Reanalysis
    References: http://www.esrl.noaa.gov/psd/data/gridded/data.ncep.reanalysis.derived.html
    dataset_title: NCEP-NCAR Reanalysis 1
    dimensions(sizes): lat(73), lon(144), time(845)
    variables(dimensions): float32 lat(lat), float32 lon(lon), float64 time(time), float32 air(time,lat,lon)
    groups: 

In [187]:
#define the variables from the data set
lon_vals = nc.variables['lon']

lat_vals = nc.variables['lat']

time = nc.variables['time']

air = nc.variables['air']

time_unit = time.units
In [188]:
print("These are the first 6 entries for time:",'\n',time[0:6])
These are the first 6 entries for time: 
 [1297320. 1298064. 1298760. 1299504. 1300224. 1300968.]
In [189]:
precnc = nc.variables['air']
In [190]:
#Plot the data along a meridional line
#Plot a 90S-90N temp along a meridional line at 180E

nc_x = np.linspace(-90,90,73)
plt.plot(nc_x,precnc[0,:,71],ls="-",color='k' )
plt.xlabel('Latitude')
plt.ylabel('Temp[$^{\circ}$C]')
plt.title('90S-90N Temperature [$^{o}$C] Along a Meridional Line at 35E: Jan 1948')
plt.show()

Convert NCEP Reanalysis into a space-time dataset

Write the data as space-time matrix with a header

In [191]:
precst = np.zeros((10512,845))

temp = np.reshape(precnc[0,:,:],(144*73))
print(np.shape(temp))
print(temp[0:6])
(10512,)
[-34.926773 -34.926773 -34.926773 -34.926773 -34.926773 -34.926773]
In [192]:
def spmat(x,y):
    for i in range(0,845):
        y[:,i] = np.reshape(x[i,:,:],(144*73))
    return y
In [193]:
precst2 = spmat(precnc,precst)

print(np.shape(precst2))
(10512, 845)
In [194]:
#Build lat and lon for 10512 spatial positions using rep
def rep(x,y,n):
    for j in range(0,n):
        x = np.append(x,y)
    return x
In [195]:
arra = np.zeros((0))
LAT = rep(arra,lat_vals,144)
LON = rep(arra,lon_vals[0],73)
print(LON)
[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. 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.]
In [196]:
def recon():
    rslt = LON
    for jj in range(0,143):
        rslt = np.append(rslt,rep(arra,lon_vals[jj],73))
    return rslt
In [197]:
LON2 = recon()
print("The last 6 entries are:",LON2[-6:])
The last 6 entries are: [355. 355. 355. 355. 355. 355.]
In [198]:
#Creating an array with first two columns of lat and lon
#With 826 months spanning from 1948-2016
gpcpst_ = np.column_stack((LAT,(LON2)))
gpcpst = np.column_stack((gpcpst_,precst2))
In [199]:
print(np.shape(gpcpst))
print(gpcpst)
(10512, 847)
[[ 90.           0.         -34.92677307 ... -27.74436188 -17.96084213
   -3.70888162]
 [ 87.5          0.         -34.92677307 ... -27.74436188 -17.96084213
   -3.70888162]
 [ 85.           0.         -34.92677307 ... -27.74436188 -17.96084213
   -3.70888162]
 ...
 [-85.         355.         -17.69773293 ... -43.96936798 -51.88335037
  -49.89598083]
 [-87.5        355.         -17.69773293 ... -43.96936798 -51.88335037
  -49.89598083]
 [-90.         355.         -17.69773293 ... -43.96936798 -51.88335037
  -49.89598083]]

Convert the Julian day and hour into calendar mons for time

In [200]:
#create an array of all ones for the days of the data
ones = np.ones(845)
In [201]:
#define a function to create a list of months repeating to match
#the size of our data
def monthfunc():
    montharr = np.zeros(0)
    for i in range(1,71):
        for j in range(1,13):
             montharr = np.append(montharr,int(j))
    for k in range(1,6):
        montharr = np.append(montharr,int(k))
    return montharr

months = monthfunc()
print(np.shape(months))
(845,)
In [202]:
#define a functionto create a list of years to match the
#size of our data
def yearfunc():
    yeararr = np.zeros(0)
    for i in range(1948,2018):
        for j in range(1,13):
            yeararr = np.append(yeararr,i)
    for k in range(1,6):
        yeararr = np.append(yeararr,2018)
    return yeararr

years = yearfunc()
print(np.size(years))
845
In [203]:
#define a function that pairs a string version of our year and
#month arrays with a dash in between
#these will serve as our column names for our data
def tmfunc():
    empty = np.zeros(0)
    for i in range(0,845):
        empty = np.append(empty,str(years[i])  + '-'  + str(months[i]))
    return empty

tm1 = tmfunc()
print(tm1.shape)  
(845,)
In [204]:
#add the 'Lat' and 'Lon' columns to the beginning
#of our column names array
tm2 = np.append(['Lat','Lon'],tm1)
print(np.shape(tm2))
(847,)
In [205]:
#use the package pandas.DataFrame to match the column names
#with the data
GPCPST = pd.DataFrame(gpcpst,columns=tm2)
GPCPST
Out[205]:
Lat Lon 1948.0-1.0 1948.0-2.0 1948.0-3.0 1948.0-4.0 1948.0-5.0 1948.0-6.0 1948.0-7.0 1948.0-8.0 ... 2017.0-8.0 2017.0-9.0 2017.0-10.0 2017.0-11.0 2017.0-12.0 2018.0-1.0 2018.0-2.0 2018.0-3.0 2018.0-4.0 2018.0-5.0
0 90.0 0.0 -34.926773 -33.311375 -29.716127 -23.678997 -5.309671 1.345664 1.629359 -0.078063 ... 0.525796 -5.624173 -12.331459 -19.765009 -22.945978 -25.387915 -17.085718 -27.744362 -17.960842 -3.708882
1 87.5 0.0 -34.926773 -33.311375 -29.716127 -23.678997 -5.309671 1.345664 1.629359 -0.078063 ... 0.525796 -5.624173 -12.331459 -19.765009 -22.945978 -25.387915 -17.085718 -27.744362 -17.960842 -3.708882
2 85.0 0.0 -34.926773 -33.311375 -29.716127 -23.678997 -5.309671 1.345664 1.629359 -0.078063 ... 0.525796 -5.624173 -12.331459 -19.765009 -22.945978 -25.387915 -17.085718 -27.744362 -17.960842 -3.708882
3 82.5 0.0 -34.926773 -33.311375 -29.716127 -23.678997 -5.309671 1.345664 1.629359 -0.078063 ... 0.525796 -5.624173 -12.331459 -19.765009 -22.945978 -25.387915 -17.085718 -27.744362 -17.960842 -3.708882
4 80.0 0.0 -34.926773 -33.311375 -29.716127 -23.678997 -5.309671 1.345664 1.629359 -0.078063 ... 0.525796 -5.624173 -12.331459 -19.765009 -22.945978 -25.387915 -17.085718 -27.744362 -17.960842 -3.708882
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
10507 -80.0 355.0 -17.697733 -32.942413 -54.835476 -58.778332 -61.156456 -64.492996 -63.805809 -62.619354 ... -58.831455 -54.825848 -42.833076 -30.560844 -23.340338 -21.537106 -33.912514 -43.969368 -51.883350 -49.895981
10508 -82.5 355.0 -17.697733 -32.942413 -54.835476 -58.778332 -61.156456 -64.492996 -63.805809 -62.619354 ... -58.831455 -54.825848 -42.833076 -30.560844 -23.340338 -21.537106 -33.912514 -43.969368 -51.883350 -49.895981
10509 -85.0 355.0 -17.697733 -32.942413 -54.835476 -58.778332 -61.156456 -64.492996 -63.805809 -62.619354 ... -58.831455 -54.825848 -42.833076 -30.560844 -23.340338 -21.537106 -33.912514 -43.969368 -51.883350 -49.895981
10510 -87.5 355.0 -17.697733 -32.942413 -54.835476 -58.778332 -61.156456 -64.492996 -63.805809 -62.619354 ... -58.831455 -54.825848 -42.833076 -30.560844 -23.340338 -21.537106 -33.912514 -43.969368 -51.883350 -49.895981
10511 -90.0 355.0 -17.697733 -32.942413 -54.835476 -58.778332 -61.156456 -64.492996 -63.805809 -62.619354 ... -58.831455 -54.825848 -42.833076 -30.560844 -23.340338 -21.537106 -33.912514 -43.969368 -51.883350 -49.895981

10512 rows × 847 columns

In [206]:
#save the data as a .csv file
GPCPST.to_csv('ncepJan1948_May2018.csv')

Compute the January climatology and standard deviation is below

In [207]:
monJ = np.array(range(0,844,12))
In [208]:
#choose the 3rd column and onward
gpcpdat = GPCPST.iloc[:,2:844]

gpcpJ = gpcpdat.iloc[:,monJ]
In [209]:
climJ = gpcpJ.mean(axis=1)
print(np.shape(climJ))
print(np.shape(gpcpJ))
(10512,)
(10512, 71)
In [210]:
def rowSDS():
    rslt = []
    for jj in range(0,10512):
        rslt = np.append(rslt,np.std(gpcpJ.iloc[jj,:]))
    return rslt
In [211]:
sdJ = rowSDS()
print(np.max(sdJ))
8.478887102133573
In [212]:
def repeat():
    q = np.zeros((144,73))
    u = climJ[0:73]
    for ii in range(0,144):
        q[ii,:] = u
    return q
In [213]:
Lat1 = np.linspace(90.,-90.,73)
Lat = -Lat1
mapmat = np.reshape(np.array(climJ),(73,144))
print(np.shape(mapmat))
print(mapmat[:,0])
(73, 144)
[-30.12138708 -31.06097713 -30.89403972 -27.74156099 -19.6512798
 -10.59888713  -5.27115206  -2.26844563   0.07260826   1.5164092
   3.24956621   5.20804542   5.69055589   5.45525621   4.86409052
   5.42561528   5.16556439   5.00891383   5.71439819   4.44305458
   8.34966268  12.51033815   8.92974695   8.04727916  12.03000642
  14.00238506  15.542076    17.59608504  18.417429    20.79230196
  23.47893747  25.67865001  27.27342909  26.38909969  26.53499472
  27.00031007  26.48078929  25.84312044  25.19140821  24.74560206
  23.43143683  22.59735916  21.99494969  21.63159913  21.78614469
  21.56641264  21.36557496  21.10643755  20.72170139  19.66445832
  18.23194979  16.35171999  13.96073683  11.31292308   8.89635962
   6.7451914    4.50496426   2.69021587   1.0842967    0.56871767
  -0.10466067  -0.26949311  -0.88437662  -0.97460383  -0.43330201
 -10.95523496 -20.52701241 -18.93989269 -19.36527207 -19.88561785
 -19.90975698 -20.65777865 -22.39987782]
In [214]:
plt.figure(figsize=(15., 7.))


#using the basemap package to put the cylindrical projection map into the image
ReAn = Basemap(projection='cyl',llcrnrlat=-90.,urcrnrlat=90.,\
            llcrnrlon=-180.,urcrnrlon=180.,resolution='c')

#shifting the data because 
lons, data = ReAn.shiftdata(lon_vals, datain = mapmat, lon_0=0)


#draw coastlines, latitudes, and longitutes on the map
ReAn.drawcoastlines(color='black', linewidth=1)
ReAn.drawparallels(np.arange(-90.,91.,30.), labels = [1,0,0,1])
ReAn.drawmeridians(np.arange(-180.,181.,60.), labels= [1,0,0,1])
ReAn.drawcountries()

limit = np.linspace(-48.,48.,51)

ReAn_plt = plt.contourf(np.array(lons),np.array(lat_vals),data,limit,cmap = 'jet')
ceb = plt.colorbar(ReAn_plt)
ceb.ax.set_ylabel('$^{o}$C')
plt.xlabel('Longitude',labelpad = 20)
plt.ylabel('Latitude',labelpad =20)
plt.title('NCEP Jan SAT RA 1948-2018 climatology [$^{o}$C]')
plt.show()

Plot Jan Standard Deviation

In [215]:
Lat1 = np.linspace(90.,-90.,73)
Lat = -Lat1
mapmat2 = np.reshape(sdJ,(73,144))
In [216]:
print(mapmat2.shape)
(73, 144)
In [217]:
plt.figure(figsize=(15., 7.))


#using the basemap package to put the cylindrical projection map into the image
ReAnSd = Basemap(projection='cyl',llcrnrlat=-90.,urcrnrlat=90.,\
            llcrnrlon=-180.,urcrnrlon=180.,resolution='c')

#shifting the data
lons2, data2 = ReAnSd.shiftdata(lon_vals, datain = mapmat2, lon_0=0)


#draw coastlines, latitudes, and longitutes on the map
ReAnSd.drawcoastlines(color='black', linewidth=1)
ReAnSd.drawparallels(np.arange(-90.,91.,30.), labels = [1,0,0,1])
ReAnSd.drawmeridians(np.arange(-180.,181.,60.), labels= [1,0,0,1])
ReAnSd.drawcountries()

limit = np.linspace(0.,8.5,100)

ReAnSd_plt = plt.contourf(np.array(lons2),np.array(lat_vals),data2,limit,cmap = 'jet')
ceb = plt.colorbar(ReAnSd_plt)
ceb.ax.set_ylabel('$^{o}$C')
plt.xlabel('Longitude',labelpad = 20)
plt.ylabel('Latitude',labelpad =20)
plt.title('NCEP Jan SAT RA 1948-2018 Standard Deviation [$^{o}$C]')
plt.savefig('NCEPStandardDeviation.png',bbox_inches='tight')
plt.show()

Compute the Jan EOFs

In [218]:
monJ = np.array(range(0,846,12))
gpcpdat = GPCPST.iloc[:,2:844]
gpcpJ = gpcpdat.iloc[:,monJ]
climJ = gpcpJ.mean(axis=1)
In [219]:
print(np.shape(gpcpdat.iloc[:,monJ]))
print(climJ.shape)
print(sdJ.shape)
(10512, 71)
(10512,)
(10512,)
In [220]:
def rowSDS2():
    rslt = []
    for jj in range(0,10512):
        rslt = np.append(rslt,np.std(gpcpJ.iloc[jj,:]))
    return rslt
In [221]:
sdJ = gpcpJ.std(axis=1)
In [222]:
sdJ.shape
sdJ.iloc[0:1000]
Out[222]:
0      4.382533
1      4.382533
2      4.382533
3      4.382533
4      4.382533
         ...   
995    3.065339
996    3.139592
997    3.288153
998    3.493378
999    3.798825
Length: 1000, dtype: float64
In [223]:
def anomJ_func2():
    rslt = pd.DataFrame(np.zeros((10512, 71)),columns=tm1[monJ])
    for i in range(0,71):
        rslt.iloc[:,i] = (gpcpdat.iloc[:,monJ[i]]-climJ)/sdJ
    return rslt
In [224]:
anomJ = anomJ_func2()
In [225]:
anomJ.shape
anomJ
Out[225]:
1948.0-1.0 1949.0-1.0 1950.0-1.0 1951.0-1.0 1952.0-1.0 1953.0-1.0 1954.0-1.0 1955.0-1.0 1956.0-1.0 1957.0-1.0 ... 2009.0-1.0 2010.0-1.0 2011.0-1.0 2012.0-1.0 2013.0-1.0 2014.0-1.0 2015.0-1.0 2016.0-1.0 2017.0-1.0 2018.0-1.0
0 -1.096486 -1.638447 -0.417102 -2.843672 0.728207 -0.991891 -0.893481 -0.470762 -0.383907 -0.096253 ... 1.187066 -1.063216 0.524539 0.964998 1.011590 0.839719 0.118968 2.146811 1.715848 1.080077
1 -1.096486 -1.638447 -0.417102 -2.843672 0.728207 -0.991891 -0.893481 -0.470762 -0.383907 -0.096253 ... 1.187066 -1.063216 0.524539 0.964998 1.011590 0.839719 0.118968 2.146811 1.715848 1.080077
2 -1.096486 -1.638447 -0.417102 -2.843672 0.728207 -0.991891 -0.893481 -0.470762 -0.383907 -0.096253 ... 1.187066 -1.063216 0.524539 0.964998 1.011590 0.839719 0.118968 2.146811 1.715848 1.080077
3 -1.096486 -1.638447 -0.417102 -2.843672 0.728207 -0.991891 -0.893481 -0.470762 -0.383907 -0.096253 ... 1.187066 -1.063216 0.524539 0.964998 1.011590 0.839719 0.118968 2.146811 1.715848 1.080077
4 -1.096486 -1.638447 -0.417102 -2.843672 0.728207 -0.991891 -0.893481 -0.470762 -0.383907 -0.096253 ... 1.187066 -1.063216 0.524539 0.964998 1.011590 0.839719 0.118968 2.146811 1.715848 1.080077
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
10507 1.858985 1.525489 1.786165 2.523169 2.197580 2.039952 1.846995 1.442721 2.118256 2.109584 ... -0.644720 0.031327 -0.284569 -1.487068 -0.183182 -0.886264 -0.966424 -0.440677 -0.243958 0.341096
10508 1.858985 1.525489 1.786165 2.523169 2.197580 2.039952 1.846995 1.442721 2.118256 2.109584 ... -0.644720 0.031327 -0.284569 -1.487068 -0.183182 -0.886264 -0.966424 -0.440677 -0.243958 0.341096
10509 1.858985 1.525489 1.786165 2.523169 2.197580 2.039952 1.846995 1.442721 2.118256 2.109584 ... -0.644720 0.031327 -0.284569 -1.487068 -0.183182 -0.886264 -0.966424 -0.440677 -0.243958 0.341096
10510 1.858985 1.525489 1.786165 2.523169 2.197580 2.039952 1.846995 1.442721 2.118256 2.109584 ... -0.644720 0.031327 -0.284569 -1.487068 -0.183182 -0.886264 -0.966424 -0.440677 -0.243958 0.341096
10511 1.858985 1.525489 1.786165 2.523169 2.197580 2.039952 1.846995 1.442721 2.118256 2.109584 ... -0.644720 0.031327 -0.284569 -1.487068 -0.183182 -0.886264 -0.966424 -0.440677 -0.243958 0.341096

10512 rows × 71 columns

In [226]:
def anomJ_func3():
    rslt = pd.DataFrame(np.zeros((10512, 71)),columns=tm1[monJ])
    for i in range(0,71):
        rslt.iloc[:,i] = np.sqrt(np.cos(GPCPST.iloc[:,0]*np.pi/180.))*anomJ.iloc[:,i]
    return rslt
        
In [227]:
anomAW = anomJ_func3()
anomAW.shape
anomAW
Out[227]:
1948.0-1.0 1949.0-1.0 1950.0-1.0 1951.0-1.0 1952.0-1.0 1953.0-1.0 1954.0-1.0 1955.0-1.0 1956.0-1.0 1957.0-1.0 ... 2009.0-1.0 2010.0-1.0 2011.0-1.0 2012.0-1.0 2013.0-1.0 2014.0-1.0 2015.0-1.0 2016.0-1.0 2017.0-1.0 2018.0-1.0
0 -8.580123e-09 -1.282103e-08 -3.263872e-09 -2.225205e-08 5.698298e-09 -7.761659e-09 -6.991588e-09 -3.683764e-09 -3.004112e-09 -7.531929e-10 ... 9.288918e-09 -8.319784e-09 4.104577e-09 7.551214e-09 7.915805e-09 6.570895e-09 9.309338e-10 1.679903e-08 1.342670e-08 8.451720e-09
1 -2.290039e-01 -3.421939e-01 -8.711291e-02 -5.939081e-01 1.520879e-01 -2.071590e-01 -1.866058e-01 -9.831983e-02 -8.017991e-02 -2.010276e-02 ... 2.479217e-01 -2.220554e-01 1.095514e-01 2.015423e-01 2.112732e-01 1.753775e-01 2.484667e-02 4.483669e-01 3.583592e-01 2.255768e-01
2 -3.237062e-01 -4.837049e-01 -1.231376e-01 -8.395132e-01 2.149823e-01 -2.928277e-01 -2.637749e-01 -1.389791e-01 -1.133375e-01 -2.841606e-02 ... 3.504473e-01 -3.138843e-01 1.548553e-01 2.848881e-01 2.986432e-01 2.479032e-01 3.512177e-02 6.337848e-01 5.065552e-01 3.188619e-01
3 -3.961429e-01 -5.919450e-01 -1.506925e-01 -1.027373e+00 2.630895e-01 -3.583546e-01 -3.228005e-01 -1.700788e-01 -1.386994e-01 -3.477480e-02 ... 4.288679e-01 -3.841231e-01 1.895077e-01 3.486384e-01 3.654715e-01 3.033772e-01 4.298107e-02 7.756086e-01 6.199085e-01 3.902146e-01
4 -4.569179e-01 -6.827593e-01 -1.738112e-01 -1.184990e+00 3.034519e-01 -4.133322e-01 -3.723236e-01 -1.961718e-01 -1.599782e-01 -4.010984e-02 ... 4.946634e-01 -4.430541e-01 2.185813e-01 4.021254e-01 4.215410e-01 3.499203e-01 4.957509e-02 8.946000e-01 7.150129e-01 4.500801e-01
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
10507 7.746598e-01 6.356883e-01 7.443148e-01 1.051433e+00 9.157561e-01 8.500708e-01 7.696632e-01 6.011980e-01 8.827008e-01 8.790872e-01 ... -2.686622e-01 1.305434e-02 -1.185831e-01 -6.196777e-01 -7.633383e-02 -3.693161e-01 -4.027195e-01 -1.836349e-01 -1.016600e-01 1.421383e-01
10508 6.716217e-01 5.511349e-01 6.453129e-01 9.115807e-01 7.939507e-01 7.370022e-01 6.672898e-01 5.212322e-01 7.652921e-01 7.621592e-01 ... -2.329272e-01 1.131797e-02 -1.028102e-01 -5.372539e-01 -6.618061e-02 -3.201931e-01 -3.491534e-01 -1.592095e-01 -8.813812e-02 1.232324e-01
10509 5.488124e-01 4.503572e-01 5.273143e-01 7.448937e-01 6.487729e-01 6.022377e-01 5.452725e-01 4.259223e-01 6.253547e-01 6.227946e-01 ... -1.903353e-01 9.248424e-03 -8.401088e-02 -4.390144e-01 -5.407916e-02 -2.616442e-01 -2.853090e-01 -1.300972e-01 -7.202163e-02 1.006987e-01
10510 3.882538e-01 3.186023e-01 3.730451e-01 5.269702e-01 4.589702e-01 4.260492e-01 3.857495e-01 3.013160e-01 4.424031e-01 4.405920e-01 ... -1.346515e-01 6.542738e-03 -5.943295e-02 -3.105779e-01 -3.825795e-02 -1.850985e-01 -2.018400e-01 -9.203645e-02 -5.095124e-02 7.123868e-02
10511 1.454676e-08 1.193712e-08 1.397693e-08 1.974407e-08 1.719630e-08 1.596285e-08 1.445294e-08 1.128945e-08 1.657558e-08 1.650773e-08 ... -5.045008e-09 2.451377e-10 -2.226783e-09 -1.163647e-08 -1.433416e-09 -6.935112e-09 -7.562370e-09 -3.448343e-09 -1.908998e-09 2.669110e-09

10512 rows × 71 columns

In [228]:
#Computing SVD from the area-weighted anomaly matrix anomAW

U, S, Vh = np.linalg.svd(anomAW,full_matrices=False)

Plot the eigenvalues

In [229]:
xvals = np.linspace(0.,70.,71)
yvals = 100.*(S**2.)/np.sum(S**2)

#this creates two subplots
fig, ax1 = plt.subplots()
ax1.scatter(xvals,yvals,marker = "o",facecolors='none',edgecolors='k')
ax1.plot(xvals,yvals,color='k',label = "Percentage Variance ")
ax1.set_ylabel('Percentage Variance %',color='k')
ax1.tick_params(axis='y',labelcolor='k')


ax2 = ax1.twinx()

ax2.scatter(xvals,yvals.cumsum(),marker = "o",facecolors='none',edgecolors='b')
ax2.plot(xvals,yvals.cumsum(),color='b',label = "Cumulative Variance")
ax2.set_ylabel('Cumulative Variance %',color='b')
ax2.tick_params(axis='y',labelcolor='b')


ax1.legend(loc=(1.3,.5))
ax2.legend(loc=(1.3,.4))

plt.show()

Plot the EOF1: The physical EOF by taking out the area-factor

In [230]:
def mapmatr():
    q = np.zeros((144,73))
    for ii in range(0,144):
        q[ii,:] = U[:,0]/(np.sqrt(np.cos(gpcpst[:,0]
          *np.pi/180.)))
    return q
In [231]:
mapmatr = (np.reshape(U[:,0]/(np.sqrt(np.cos(gpcpst[:,0]
          *np.pi/180.))),(73,144)))

mapmatr2 = mapmatr[:,range(0,mapmatr[0,:].size)]
In [232]:
#Plot EOF1
plt.figure(figsize=(15., 7.))

#Using the basemap package to put the cylindrical projection map into the image
eof = Basemap(projection='cyl',llcrnrlat=-90.,urcrnrlat=90.,\
            llcrnrlon=-180.,urcrnrlon=180.,resolution='c')

#shifting the data because 
lons2, data2 = eof.shiftdata(lon_vals, datain = -mapmatr2, lon_0=0)


#draw coastlines, latitudes, and longitutes on the map
eof.drawcoastlines(color='black', linewidth=1)
eof.drawparallels(np.arange(-90.,91.,30.), labels = [1,0,0,1])
eof.drawmeridians(np.arange(-180.,181.,60.), labels= [1,0,0,1])
eof.drawcountries()

limits = np.linspace(-.04,.04,51)

eof_plt = plt.contourf(np.array(lons2),np.array(lat_vals),data2,limits,cmap = 'jet')
efo = plt.colorbar(eof_plt)
efo.ax.set_ylabel('$^{o}$C')
plt.xlabel('Longitude',labelpad = 20)
plt.ylabel('Latitude',labelpad =20)
plt.title('January EOF1 from 1948-2015 NCEP Temp Data')
plt.show()
In [233]:
print(GPCPST.iloc[:,0])
print(U[:,0])
0        90.0
1        87.5
2        85.0
3        82.5
4        80.0
         ... 
10507   -80.0
10508   -82.5
10509   -85.0
10510   -87.5
10511   -90.0
Name: Lat, Length: 10512, dtype: float64
[-1.46234805e-10 -3.90301161e-03 -5.51706456e-03 ...  5.68349034e-03
  4.02074845e-03  1.50645981e-10]
In [234]:
pcdat = Vh[0,:]
print(pcdat)
[ 0.22211168  0.24963545  0.25029769  0.29912976  0.16090973  0.17271085
  0.15785231  0.18307524  0.21882379  0.15122957  0.02578771 -0.00901693
  0.04782682  0.03054551  0.02991664  0.12125247  0.07598458  0.12186282
  0.07874001  0.13012775  0.04602599 -0.0122034  -0.01418596  0.14749956
  0.0531257  -0.08497004  0.06289873  0.07313316  0.13240528 -0.03173655
 -0.04297393  0.05119121 -0.07210772 -0.03317264  0.00215034 -0.05829302
 -0.03352802 -0.01870429  0.0054307  -0.02724541 -0.0798161   0.00978516
 -0.06992875 -0.06721644 -0.01113804  0.0556014  -0.04344844 -0.08763285
 -0.01928275  0.00965898 -0.19016959 -0.03455839 -0.1014894  -0.11059822
 -0.10405437 -0.13082936 -0.08126057 -0.1103388  -0.13322128 -0.09618101
 -0.06575333 -0.11632433 -0.09783654 -0.06640809 -0.13711015 -0.11125202
 -0.13996449 -0.13696117 -0.3315223  -0.14854232 -0.11574959]
In [235]:
#Plot PC1
TIME = np.array(range(1948,2019))

plt.figure()

plt.scatter(TIME,-pcdat,marker = "o",facecolors='none',edgecolors='k')
plt.plot(TIME,-pcdat,color='k')
plt.title("PC1 of NCEP TA Jan SAT: 1948-2018")
plt.xlabel("Year")
plt.ylabel("PC Values")
plt.show
Out[235]:
<function matplotlib.pyplot.show(*args, **kw)>

EOF from de-trended data

In [236]:
monJ = np.array(range(0,846,12))
gpcpdat = GPCPST.iloc[:,2:844]
gpcpJ = gpcpdat.iloc[:,monJ]
climJ = gpcpJ.mean(axis=1)
In [237]:
sdJ = gpcpJ.std(axis=1)
In [238]:
def anomJ_func2():
    rslt = pd.DataFrame(np.zeros((10512, 71)),columns=tm1[monJ])
    for i in range(0,71):
        rslt.iloc[:,i] = (gpcpdat.iloc[:,monJ[i]]-climJ)/sdJ
    return rslt
In [239]:
anomJ = anomJ_func2()
anomJ
Out[239]:
1948.0-1.0 1949.0-1.0 1950.0-1.0 1951.0-1.0 1952.0-1.0 1953.0-1.0 1954.0-1.0 1955.0-1.0 1956.0-1.0 1957.0-1.0 ... 2009.0-1.0 2010.0-1.0 2011.0-1.0 2012.0-1.0 2013.0-1.0 2014.0-1.0 2015.0-1.0 2016.0-1.0 2017.0-1.0 2018.0-1.0
0 -1.096486 -1.638447 -0.417102 -2.843672 0.728207 -0.991891 -0.893481 -0.470762 -0.383907 -0.096253 ... 1.187066 -1.063216 0.524539 0.964998 1.011590 0.839719 0.118968 2.146811 1.715848 1.080077
1 -1.096486 -1.638447 -0.417102 -2.843672 0.728207 -0.991891 -0.893481 -0.470762 -0.383907 -0.096253 ... 1.187066 -1.063216 0.524539 0.964998 1.011590 0.839719 0.118968 2.146811 1.715848 1.080077
2 -1.096486 -1.638447 -0.417102 -2.843672 0.728207 -0.991891 -0.893481 -0.470762 -0.383907 -0.096253 ... 1.187066 -1.063216 0.524539 0.964998 1.011590 0.839719 0.118968 2.146811 1.715848 1.080077
3 -1.096486 -1.638447 -0.417102 -2.843672 0.728207 -0.991891 -0.893481 -0.470762 -0.383907 -0.096253 ... 1.187066 -1.063216 0.524539 0.964998 1.011590 0.839719 0.118968 2.146811 1.715848 1.080077
4 -1.096486 -1.638447 -0.417102 -2.843672 0.728207 -0.991891 -0.893481 -0.470762 -0.383907 -0.096253 ... 1.187066 -1.063216 0.524539 0.964998 1.011590 0.839719 0.118968 2.146811 1.715848 1.080077
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
10507 1.858985 1.525489 1.786165 2.523169 2.197580 2.039952 1.846995 1.442721 2.118256 2.109584 ... -0.644720 0.031327 -0.284569 -1.487068 -0.183182 -0.886264 -0.966424 -0.440677 -0.243958 0.341096
10508 1.858985 1.525489 1.786165 2.523169 2.197580 2.039952 1.846995 1.442721 2.118256 2.109584 ... -0.644720 0.031327 -0.284569 -1.487068 -0.183182 -0.886264 -0.966424 -0.440677 -0.243958 0.341096
10509 1.858985 1.525489 1.786165 2.523169 2.197580 2.039952 1.846995 1.442721 2.118256 2.109584 ... -0.644720 0.031327 -0.284569 -1.487068 -0.183182 -0.886264 -0.966424 -0.440677 -0.243958 0.341096
10510 1.858985 1.525489 1.786165 2.523169 2.197580 2.039952 1.846995 1.442721 2.118256 2.109584 ... -0.644720 0.031327 -0.284569 -1.487068 -0.183182 -0.886264 -0.966424 -0.440677 -0.243958 0.341096
10511 1.858985 1.525489 1.786165 2.523169 2.197580 2.039952 1.846995 1.442721 2.118256 2.109584 ... -0.644720 0.031327 -0.284569 -1.487068 -0.183182 -0.886264 -0.966424 -0.440677 -0.243958 0.341096

10512 rows × 71 columns

In [240]:
# Create linear regression object
regr = linear_model.LinearRegression()
print(anomJ.iloc[0,:].shape)
(71,)
In [241]:
def trendV():
    trendV = np.zeros(10512)
    for i in range(0,10512):
        regr.fit(np.reshape(TIME,(71,1)),
                 np.reshape(np.array(anomJ.iloc[i,:]),(71,1)))
        trendV[i] = regr.coef_
    return trendV
 
In [242]:
def trendM():
    rslt = np.zeros((10512,71))
    trendM = pd.DataFrame(np.zeros((10512, 71)),columns=tm1[monJ])
    for i in range(0,10512):
        regr.fit(np.reshape(TIME,(71,1)),
                 np.reshape(np.array(anomJ.iloc[i,:]),(71,1)))
        rslt[i,:] = regr.intercept_
    return rslt
In [243]:
trendV = trendV()
print(trendV)
[ 0.0251475   0.0251475   0.0251475  ... -0.02922567 -0.02922567
 -0.02922567]
In [244]:
print(regr.fit(np.reshape(TIME,(71,1)),
                 np.reshape(np.array(anomJ.iloc[0,:]),(71,1))))
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None, normalize=False)
In [245]:
trendM = trendM()
In [246]:
print(trendM)
[[-49.86749244 -49.86749244 -49.86749244 ... -49.86749244 -49.86749244
  -49.86749244]
 [-49.86749244 -49.86749244 -49.86749244 ... -49.86749244 -49.86749244
  -49.86749244]
 [-49.86749244 -49.86749244 -49.86749244 ... -49.86749244 -49.86749244
  -49.86749244]
 ...
 [ 57.95450179  57.95450179  57.95450179 ...  57.95450179  57.95450179
   57.95450179]
 [ 57.95450179  57.95450179  57.95450179 ...  57.95450179  57.95450179
   57.95450179]
 [ 57.95450179  57.95450179  57.95450179 ...  57.95450179  57.95450179
   57.95450179]]
In [ ]:
 
In [247]:
#vArea = np.cos(GPCPST.iloc[:,0]*np.pi/180.)
In [248]:
#anomA = vArea*anomJ
In [249]:
#JanSAT = anomA.sum(axis=0)/vArea.sum
In [250]:
#plt.figure(figsize=(12,7))

#plt.plot(TIME, JanSAT)

Plot the trend of Jan SAT non-standardized anomaly data. Begin with the space time data matrix

In [251]:
monJ = np.array(range(0,846,12))
gpcpdat = GPCPST.iloc[:,2:844]
gpcpJ = gpcpdat.iloc[:,monJ]
climJ = gpcpJ.mean(axis=1)
In [252]:
def anomJ_func3():
    rslt = pd.DataFrame(np.zeros((10512, 71)),columns=tm1[monJ])
    for i in range(0,71):
        rslt.iloc[:,i] = gpcpdat.iloc[:,monJ[i]]-climJ
    return rslt
In [253]:
anomJ = anomJ_func3()
In [254]:
def trendV():
    trendV = np.zeros(10512)
    for i in range(0,10512):
        regr.fit(np.reshape(TIME,(71,1)),
                 np.reshape(np.array(anomJ.iloc[i,:]),(71,1)))
        trendV[i] = regr.coef_
    return trendV
In [255]:
trendV = trendV()
print(trendV)
[ 0.11020975  0.11020975  0.11020975 ... -0.07392385 -0.07392385
 -0.07392385]
In [256]:
mapmat1 = np.reshape([10*trendV],(73,144))
In [257]:
def mapv1_func():
    zeros = np.zeros((73,144))
    for i in range(0,73):
        for j in range(0,144):
            if mapmat1[i,j] >= 1.:
                zeros[i,j] = 1.
            else:
                zeros[i,j] = mapmat1[i,j]
    return zeros
In [258]:
mapv1 = mapv1_func()
In [259]:
print(mapv1[:,0])
[ 1.          1.          1.          1.          1.          1.
  0.68158268  0.34416337  0.17559463  0.17851172  0.13689676  0.20403919
  0.23218516  0.1721355   0.1506613   0.18890883  0.25093573  0.20644092
  0.16388107  0.09081452  0.09923048  0.18379275  0.1263803   0.07193365
  0.06240484  0.02373414 -0.0654138  -0.14294468 -0.22755851 -0.32434883
 -0.40569355 -0.41348683 -0.06018246  0.23283968  0.13365891  0.12978631
  0.18239662  0.1664313   0.21303226  0.22450912  0.18836426  0.12463248
  0.06579872  0.04906871  0.03877119  0.03885311  0.03294627  0.05287314
  0.08073579  0.06656256  0.0699334   0.06117304  0.05832809  0.07327676
  0.03140782 -0.03290004 -0.05565315 -0.06607404 -0.08640044 -0.05723924
 -0.03503173 -0.0513345  -0.08979873 -0.12368762 -0.19764816 -0.52881026
 -0.76251682 -0.73230752 -0.76972727 -0.71334797 -0.7199844  -0.85145011
 -0.73923851]
In [260]:
def mapmat_func():
    zeros = np.zeros((73,144))
    for i in range(0,73):
        for j in range(0,144):
            if mapv1[i,j] <= -1.:
                zeros[i,j] = -1.
            else:
                zeros[i,j] = mapv1[i,j]
    return zeros
In [261]:
mapmat3 = mapmat_func()
In [262]:
print(np.max(mapmat3))
print(np.min(mapmat3))
1.0
-1.0
In [263]:
plt.figure(figsize=(15., 7.))


#using the basemap package to put the cylindrical projection map into the image
trend = Basemap(projection='cyl',llcrnrlat=-90.,urcrnrlat=90.,\
            llcrnrlon=-180.,urcrnrlon=180.,resolution='c')

#shifting the data because 
lons2, data3 = trend.shiftdata(lon_vals, datain = mapmat3, lon_0=0)


#draw coastlines, latitudes, and longitutes on the map
trend.drawcoastlines(color='black', linewidth=1)
trend.drawparallels(np.arange(-90.,91.,30.), labels = [1,0,0,1])
trend.drawmeridians(np.arange(-180.,181.,60.), labels= [1,0,0,1])
trend.drawcountries()

limits = np.linspace(-1.1,1.1,50)

eof_plt = plt.contourf(np.array(lons2),np.array(lat_vals),data3,limits,cmap = 'jet')
efo = plt.colorbar(eof_plt)
efo.ax.set_ylabel('$^{o}$C')
plt.xlabel('Longitude',labelpad = 20)
plt.ylabel('Latitude',labelpad =20)
plt.title('Trend of the NCEP RA1 Jan 1948-2018 Anom Temp [$^{o}C/(10a)$]')
plt.show()

EOF analysis of the Tahiti and Darwin standardized SLP data SOI analysis

line 1241

Download pressure data for Tahiti from https://shen.sdsu.edu/data/PSTANDtahiti into your directory.

In [264]:
Pta = np.loadtxt("PSTANDtahiti.txt",skiprows=0)
In [265]:
PTA = np.reshape(Pta[:,1:],(780))
In [266]:
#make an array of years for our x-axis
xtime = np.linspace(1951,2016,(65*12)+1)[0:780]
In [267]:
plt.figure(figsize= (15.,7.))

plt.plot(xtime,PTA,color='r')

plt.xlabel('Year')
plt.ylabel('Pressure')
plt.title('Standardized Tahiti SLP Anomalies')

plt.show()

Download pressure data for Tahiti from http://shen.sdsu.edu/data/PSTANDdarwin.txt into your directory.

In [268]:
Pda = np.loadtxt('PSTANDdarwin.txt',skiprows=0)
PDA = np.reshape(Pda[:,1:],(780))
In [269]:
plt.figure(figsize=(15.,7.))

plt.plot(xtime,PDA,color='b')

plt.xlabel('Year')
plt.ylabel('Pressure')
plt.title('Standardized Darwin SLP Anomalies')

plt.show()

Plot the SOI index

In [270]:
fig, soi = plt.subplots(figsize=(15,7))

soi.plot(xtime,PTA-PDA,color='k')
soi.set_xlabel('Year')
soi.set_ylabel('SOI Index')

ax2 = soi.twiny()
ax2.plot(xtime,PTA-PDA,color='k')
ax2.set_xlabel('Year')

ax3 = soi.twinx()
ax3.plot(xtime,PTA-PDA,color='k')
ax3.set_ylabel('SOI Index')

plt.show()

Plot cumulative SOI: CSOI

In [271]:
SOI = PTA-PDA
cnegsoi = -SOI.cumsum()
In [272]:
plt.figure(figsize=(15,7))
plt.plot(xtime,cnegsoi,color='k')

plt.xlabel('Year')
plt.ylabel('Negative CSOI Index')
Out[272]:
Text(0, 0.5, 'Negative CSOI Index')
In [273]:
ptada = np.append([PTA],[PDA], axis=0)
print(ptada.shape)
(2, 780)
In [274]:
svdptd_u, svdptd_s, svdptd_vh = np.linalg.svd(ptada,full_matrices=False)
In [275]:
recontd = np.dot(np.dot(svdptd_u,np.diag(svdptd_s)),svdptd_vh)
In [276]:
print(recontd.shape)
print("Standard deviation of difference of arrays:",np.std(ptada-recontd))
(2, 780)
Standard deviation of difference of arrays: 2.3960864066830803e-16

Plot the WSOI 1

In [277]:
Dd = np.diag(svdptd_s)
Uu = svdptd_u
Vv = svdptd_vh

xtime = np.linspace(1951,2016,(65*12)+1)[0:780]

wsoi1 = Dd[0,0] * (Vv)[0,:]
In [278]:
print(wsoi1.shape)
(780,)
In [279]:
plt.figure(figsize=(15,7))

plt.plot(xtime,wsoi1,color='k')
plt.xlabel('Year')
plt.ylabel('Weighted SOI 1')

plt.show()

Plot the WSOI 2

In [280]:
wsoi2 = Dd[1,1] * Vv[1,:]
print(wsoi2.shape)
(780,)
In [281]:
fig, wsoi = plt.subplots(figsize=(15,7))

wsoi.plot(xtime,wsoi2,color='k')
wsoi.set_xlabel('Year')
wsoi.set_ylabel('SOI Index')

ax2 = wsoi.twiny()
ax2.plot(xtime,wsoi2,color='k')
ax2.set_xlabel('Year')


plt.show()

SVD Analysis for the Darwin-Tahiti Standardized SLP

In [282]:
# Remove the first column that is the year and create a vector
Pta = np.loadtxt("PSTANDtahiti.txt",skiprows=0)
PTA = np.reshape(Pta[:,1:],(780))

#Generate time ticks from Jan 1951 to Dec 2015
xtime = np.linspace(1951,2016,(65*12)+1)[0:780]
In [283]:
#Plot the Tahiti standardized SLP anomalies
plt.figure(figsize= (15.,7.))

plt.plot(xtime,PTA,color='r')
plt.xlabel('Year')
plt.ylabel('Pressure')
plt.title('Standardized Tahiti SLP Anomalies')

plt.show()

Do the same for Darwin SLP

In [284]:
Pda = np.loadtxt('PSTANDdarwin.txt',skiprows=0)
PDA = np.reshape(Pda[:,1:],(780))

plt.figure(figsize=(15.,7.))

plt.plot(xtime,PDA,color='b')
plt.xlabel('Year')
plt.ylabel('Pressure')
plt.title('Standardized Darwin SLP Anomalies')

plt.show()

Plot the SOI index

In [285]:
regr = linear_model.LinearRegression()
In [286]:
SOI = PTA - PDA

regr.fit(np.reshape(xtime,(780,1)), np.reshape(SOI,(780,1)))
ssoi = regr.predict(np.reshape(xtime,(780,1)))
In [287]:
fig, SOi = plt.subplots(figsize=(15,7))

SOi.plot(xtime,SOI,color='k')
SOi.set_xlabel('Year')
SOi.set_ylabel('SOI Index')
plt.plot(xtime,[0]*xtime.size,color='k')

ax2 = SOi.twiny()
ax2.plot(xtime,ssoi,color='r')
ax2.set_xlabel('Southern Oscillation Index')

ax3 = SOi.twinx()
ax3.plot(xtime,SOI,color='k')



plt.show()

Cumulative Negative SOI

In [288]:
CNegsoi = -SOI.cumsum()
In [289]:
plt.figure(figsize=(15,7))

plt.plot(xtime, CNegsoi,color='k')
plt.xlabel('Year')
plt.ylabel('Negative CSOI Index')
plt.title('Cumulative Southern Oscillation Index')

plt.show()

Space-time standardized SLP data matrix

In [290]:
ptada = np.append([PTA],[PDA], axis=0)
print(ptada.shape)
(2, 780)
In [291]:
uu, ss, vvh = np.linalg.svd(ptada,full_matrices=False)
In [292]:
EOF = uu
print(EOF)
[[-0.61467836  0.78877786]
 [ 0.78877786  0.61467836]]
In [293]:
WSOI = np.dot(np.transpose(EOF),ptada)
print(WSOI.shape)
(2, 780)

The two weighted SOI indices

In [294]:
WSOI1 = WSOI[0,:]
WSOI2 = WSOI[1,:]
In [295]:
fig, plott = plt.subplots(figsize=(15,7))


plott.plot(xtime,WSOI1,color='k',label='WSOI1')
plott.set_xlabel('Year')
plott.set_ylabel('WSOI Index')


ax2 = plott.twiny()
ax2.plot(xtime,WSOI2,color='b',label='WSOI2: 33% Variance')
ax2.set_xlabel('Weighted Southern Oscillation Index')

plott.legend(loc=(.05,.85))
ax2.legend(loc=(.05,.8))
plt.show()

Energy for each mode

In [296]:
Energy = ss

print(Energy)
[31.34581869 22.25420524]

Energy ratio

In [297]:
print('This is the Energy ratio:',(Energy**2.)/np.sum(Energy**2.))
This is the Energy ratio: [0.66487596 0.33512404]
Principal components
In [298]:
PC = vvh
In [299]:
PC1 = PC[0,:]
PC2 = PC[1,:]

Plot the PCs

In [300]:
plt.figure(figsize=(15,7))

plt.plot(xtime,PC1,color='k',label='Principal Component 1')
plt.plot(xtime,PC2,color='b',label='Principal Component 2')

plt.xlabel('Year')
plt.ylabel('PC Index')
plt.title('Tahiti-Darwin Principal Components')
plt.legend(loc=(.05,.89))

plt.show()

Plot the Cumulative PCs

In [301]:
plt.figure(figsize=(15,7))

plt.plot(xtime,PC1.cumsum(),color='k',label='Principal Component 1')
plt.plot(xtime,PC2.cumsum(),color='b',label='Principal Component 2')

plt.xlabel('Year')
plt.ylabel('Cumulative PC Index')
plt.title('Cumulative Tahiti-Darwin Principal Components')
plt.legend(loc=(.75,.85))

plt.show()

Display the two ENSO modes on a world map

In [302]:
#choosing an arbitrary size for the image
plt.figure(figsize=(15., 7.))


#using the basemap package to put the cylindrical projection map into the image
wmap2 = Basemap(projection='cyl',llcrnrlat=-90.,urcrnrlat=90.,\
            llcrnrlon=-180.,urcrnrlon=180.,resolution='c')


#draw coastlines, latitudes, and longitutes on the map
wmap2.drawcoastlines(color='black', linewidth=1)
wmap2.drawparallels(np.arange(-90.,91.,30.), labels = [1,0,0,1])
wmap2.drawmeridians(np.arange(-180.,181.,30.), labels= [1,0,0,1])


plt.scatter(131.,-12.,color='b',s=150)
plt.scatter(-129.,-30,color='r',s=150)

plt.text(100,-24, 'Darwin -0.79', color = 'b', fontsize = 22)
plt.text(-155,-42, 'Tahiti 0.61', color = 'r', fontsize = 22)

plt.text(-130,40, 'El Nino Southern Oscillation Model 1', color = 'm', fontsize = 30)


#plot
plt.show()
In [303]:
#choosing an arbitrary size for the image
plt.figure(figsize=(15., 7.))


#using the basemap package to put the cylindrical projection map into the image
wmap2 = Basemap(projection='cyl',llcrnrlat=-90.,urcrnrlat=90.,\
            llcrnrlon=-180.,urcrnrlon=180.,resolution='c')


#draw coastlines, latitudes, and longitutes on the map
wmap2.drawcoastlines(color='black', linewidth=1)
wmap2.drawparallels(np.arange(-90.,91.,30.), labels = [1,0,0,1])
wmap2.drawmeridians(np.arange(-180.,181.,30.), labels= [1,0,0,1])


plt.scatter(131.,-12.,color='r',s=150)
plt.scatter(-129.,-30,color='r',s=150)

plt.text(105,-24, 'Darwin 0.61', color = 'r', fontsize = 22)
plt.text(-155,-42, 'Tahiti 0.79', color = 'r', fontsize = 22)

plt.text(-130,40, 'El Nino Southern Oscillation Model 2', color = 'm', fontsize = 30)


#plot
plt.show()

Plot principal components of the Darwin and Tahiti SLP

In [304]:
Pta = np.loadtxt("PSTANDtahiti.txt",skiprows=0)
PTA = np.reshape(Pta[:,1:],(780))
Pda = np.loadtxt('PSTANDdarwin.txt',skiprows=0)
PDA = np.reshape(Pda[:,1:],(780))
ptada = np.append([PTA],[PDA], axis=0)

svdptd_u, svdptd_s, svdptd_vh = np.linalg.svd(ptada,full_matrices=False)
In [305]:
dat = pd.DataFrame(svdptd_u,columns=['EOF1','EOF2'],index=['Tahiti','Darwin'])
In [306]:
dat
Out[306]:
EOF1 EOF2
Tahiti -0.614678 0.788778
Darwin 0.788778 0.614678
In [307]:
lon = [131,231]
lat = [-24,-18]
In [308]:
plt.scatter(lon,lat)
plt.xlabel('Lon')
plt.ylabel('Lat')
plt.axis([0, 360, -90, 90])
plt.grid()
plt.show()
In [309]:
plt.figure(figsize=(12,7))

plt.scatter(np.linspace(1951,2015,780),svdptd_vh[0,:],marker = "o",facecolors='none',edgecolors='r')
plt.title("PC1 as the weighted SOI")
plt.xlabel("Year")
plt.ylabel("WSOI 1 Values")
plt.show
Out[309]:
<function matplotlib.pyplot.show(*args, **kw)>

Use NOAAGlobalTemp monthly time series as an example

In [310]:
d1 = np.loadtxt("aravg.ann.land_ocean.90S.90N.v4.0.1.201805.asc.txt"
                ,skiprows=0)

print(d1.shape)
(139, 6)
In [311]:
columns = ['V1','V2','V3','V4','V5','V6']
D1 = pd.DataFrame(d1,columns=columns)
D1
Out[311]:
V1 V2 V3 V4 V5 V6
0 1880.0 -0.357637 0.009788 0.001446 0.000850 0.007492
1 1881.0 -0.305737 0.009821 0.001446 0.000877 0.007498
2 1882.0 -0.310654 0.009811 0.001446 0.000897 0.007468
3 1883.0 -0.386558 0.009798 0.001446 0.000910 0.007443
4 1884.0 -0.446635 0.009764 0.001446 0.000922 0.007395
... ... ... ... ... ... ...
134 2014.0 0.498338 0.005875 0.000158 0.000002 0.005715
135 2015.0 0.661503 0.005875 0.000158 0.000002 0.005715
136 2016.0 0.701840 0.005875 0.000158 0.000002 0.005715
137 2017.0 0.600164 0.005875 0.000158 0.000002 0.005715
138 2018.0 0.506493 0.013353 0.000158 0.000002 0.005715

139 rows × 6 columns

Extract the temperature data from 1880-2015 and show the data.

In [312]:
tmean18 = D1.iloc[0:139,1]
tmean18
Out[312]:
0     -0.357637
1     -0.305737
2     -0.310654
3     -0.386558
4     -0.446635
         ...   
134    0.498338
135    0.661503
136    0.701840
137    0.600164
138    0.506493
Name: V2, Length: 139, dtype: float64

Linear trend

In [313]:
# Create linear regression object
regr = linear_model.LinearRegression()
In [314]:
yrtime18 = np.array(range(1880,2019))
reg8018 = regr.fit(np.reshape(yrtime18,(139,1)),np.reshape(tmean18,(139,)))
print("This is the coefficient:",reg8018.coef_)
print("This is the intercept:",[(reg8018.intercept_)])
This is the coefficient: [0.00691335]
This is the intercept: [-13.656119160176953]
In [315]:
# calculate the trendline for the data
z = np.polyfit(yrtime18, tmean18, 1)
p = np.poly1d(z)

Plot the temperature time series and its trend line.

In [316]:
plt.figure(figsize=(12,7))


plt.plot(yrtime18,tmean18,"k")
plt.plot(yrtime18,p(yrtime18),"r-")

plt.text(1900, .21,'Linear Temperature trend 0.67 $^{o}C$ per century',
         color ="r", fontsize =15)
plt.title("Global Annual Mean Land and Ocean Surface Temperature Anomalies 1880-2015")
plt.ylabel("Temperature $^{o}C$")
plt.xlabel("Year")

plt.show()

Histogram and its density fit.

In [317]:
h, mids = np.histogram(tmean18)
In [318]:
from scipy.stats import norm
xfit = np.linspace(np.min(tmean18),np.max(tmean18),30)
areat = (np.diff(mids[0:2])*np.size(tmean18))

yfit = areat*(norm.pdf(xfit,np.mean(tmean18),np.std(tmean18)))
# yfit = areat*(mlab.normpdf(xfit,np.mean(tmean18),np.std(tmean18)))
In [319]:
plt.figure(figsize=(10,8))


plt.hist(tmean18,facecolor='w',edgecolor="k")

plt.plot(xfit,yfit)

plt.title("Histogram of 1880-2018 Temperature Anomalies")
plt.xlabel("Temperature anomalies")
plt.ylabel("Frequency")

plt.show()

Scatter plot

In [320]:
ust = pd.read_csv('USJantemp1951-2016-nohead.csv',header=None)
soi = pd.read_csv('soi-data-nohead.csv',header=None)
soid = np.array(soi.iloc[:,1])

soim = np.reshape(soid,(66,12))
soij = soim[:,0]
ustj = np.array(ust.iloc[:,2])

plt.figure(figsize=(12,7))

plt.scatter(soij,ustj,color='k')

#Create a linear trendline for the data
lin = np.polyfit(soij, ustj, 1)
linreg = np.poly1d(lin)

plt.plot(soij,linreg(soij),'r')

plt.ylabel('US Temp $^{o}$F')
plt.xlabel('SOI[dimensionless]')
plt.title("Scatter plot between Jan SOI and US temp",fontsize=15)

plt.show()