1- """
2- Extra functionality for plotting and post-processing.
3- """
1+ """Extra functionality for plotting and post-processing."""
42
53import matplotlib .pyplot as plt
64import numpy as np
7- import numpy . ma as ma
8- from pandas import Series
5+ import pandas as pd
6+ from numpy import ma
97
108
119def _extrap1d (interpolator ):
12- """
13- How to make scipy.interpolate return an extrapolated result beyond the
10+ """How to make scipy.interpolate return an extrapolated result beyond the
1411 input range.
1512
1613 This is usually bad interpolation! But sometimes useful for pretty pictures,
@@ -25,10 +22,9 @@ def pointwise(x):
2522 """Pointwise interpolation."""
2623 if x < xs [0 ]:
2724 return ys [0 ] + (x - xs [0 ]) * (ys [1 ] - ys [0 ]) / (xs [1 ] - xs [0 ])
28- elif x > xs [- 1 ]:
25+ if x > xs [- 1 ]:
2926 return ys [- 1 ] + (x - xs [- 1 ]) * (ys [- 1 ] - ys [- 2 ]) / (xs [- 1 ] - xs [- 2 ])
30- else :
31- return interpolator (x )
27+ return interpolator (x )
3228
3329 def ufunclike (xs ):
3430 """Return an interpolation ufunc."""
@@ -39,30 +35,34 @@ def ufunclike(xs):
3935
4036def get_maxdepth (self ):
4137 """Return the maximum depth/pressure of a cast."""
42- valid_last_depth = self .apply (Series .notnull ).values .T
43- return np .float_ (self .index .values * valid_last_depth ).max (axis = 1 )
44-
45-
46- def extrap_sec (data , dist , depth , w1 = 1.0 , w2 = 0 ):
47- """
48- Extrapolates `data` to zones where the shallow stations are shadowed by
38+ valid_last_depth = self .apply (pd .Series .notnull ).to_numpy ().T
39+ return np .float64 (self .index .to_numpy () * valid_last_depth ).max (axis = 1 )
40+
41+
42+ def extrap_sec (
43+ data : np .ndarray ,
44+ dist : np .ndarray ,
45+ depth : np .ndarray ,
46+ w1 : float = 1.0 ,
47+ w2 : float = 0 ,
48+ ) -> np .ndarray :
49+ """Extrapolate `data` to zones where the shallow stations are shadowed by
4950 the deep stations. The shadow region usually cannot be extrapolates via
5051 linear interpolation.
5152
5253 The extrapolation is applied using the gradients of the `data` at a certain
5354 level.
5455
55- Parameters
56- ----------
57- data : array_like
58- Data to be extrapolated
59- dist : array_like
60- Stations distance
61- fd : float
62- Decay factor [0-1]
56+ Inputs
57+ ------
58+ data : Data to be extrapolated
59+ dist : Stations distance
60+ depth : Depth of the profile
61+ w1 : weights [0-1]
62+ w2 : weights [0-1]
6363
6464
65- Returns
65+ Outputs
6666 -------
6767 Sec_extrap : array_like
6868 Extrapolated variable
@@ -72,39 +72,45 @@ def extrap_sec(data, dist, depth, w1=1.0, w2=0):
7272
7373 new_data1 = []
7474 for row in data :
75+ new_row = row .copy ()
7576 mask = ~ np .isnan (row )
7677 if mask .any ():
7778 y = row [mask ]
7879 if y .size == 1 :
79- row = np .repeat (y , len (mask ))
80+ new_row = np .repeat (y , len (mask ))
8081 else :
8182 x = dist [mask ]
8283 f_i = interp1d (x , y )
8384 f_x = _extrap1d (f_i )
84- row = f_x (dist )
85- new_data1 .append (row )
85+ new_row = f_x (dist )
86+ new_data1 .append (new_row )
8687
8788 new_data2 = []
8889 for col in data .T :
90+ new_col = col .copy ()
8991 mask = ~ np .isnan (col )
9092 if mask .any ():
9193 y = col [mask ]
9294 if y .size == 1 :
93- col = np .repeat (y , len (mask ))
95+ new_col = np .repeat (y , len (mask ))
9496 else :
9597 z = depth [mask ]
9698 f_i = interp1d (z , y )
9799 f_z = _extrap1d (f_i )
98- col = f_z (depth )
99- new_data2 .append (col )
100+ new_col = f_z (depth )
101+ new_data2 .append (new_col )
100102
101- new_data = np .array (new_data1 ) * w1 + np .array (new_data2 ).T * w2
102- return new_data
103+ return np .array (new_data1 ) * w1 + np .array (new_data2 ).T * w2
103104
104105
105- def gen_topomask (h , lon , lat , dx = 1.0 , kind = "linear" , plot = False ):
106- """
107- Generates a topography mask from an oceanographic transect taking the
106+ def gen_topomask (
107+ h : np .ndarray ,
108+ lon : np .ndarray ,
109+ lat : np .ndarray ,
110+ dx : float = 1.0 ,
111+ kind : str = "linear" ,
112+ ) -> tuple :
113+ """Generate a topography mask from an oceanographic transect taking the
108114 deepest CTD scan as the depth of each station.
109115
110116 Inputs
@@ -119,8 +125,6 @@ def gen_topomask(h, lon, lat, dx=1.0, kind="linear", plot=False):
119125 kind : string, optional
120126 Type of the interpolation to be performed.
121127 See scipy.interpolate.interp1d documentation for details.
122- plot : bool
123- Whether to plot mask for visualization.
124128
125129 Outputs
126130 -------
@@ -134,26 +138,33 @@ def gen_topomask(h, lon, lat, dx=1.0, kind="linear", plot=False):
134138 André Palóczy Filho (paloczy@gmail.com) -- October/2012
135139
136140 """
137-
138141 import gsw
139142 from scipy .interpolate import interp1d
140143
141144 h , lon , lat = list (map (np .asanyarray , (h , lon , lat )))
142145 # Distance in km.
143146 x = np .append (0 , np .cumsum (gsw .distance (lon , lat )[0 ] / 1e3 ))
144147 h = - gsw .z_from_p (h , lat .mean ())
145- Ih = interp1d (x , h , kind = kind , bounds_error = False , fill_value = h [- 1 ])
148+ ih = interp1d (x , h , kind = kind , bounds_error = False , fill_value = h [- 1 ])
146149 xm = np .arange (0 , x .max () + dx , dx )
147- hm = Ih (xm )
150+ hm = ih (xm )
148151
149152 return xm , hm
150153
151154
152- def plot_section (self , reverse = False , filled = False , ** kw ):
155+ def plot_section ( # noqa: PLR0915
156+ self : pd .DataFrame ,
157+ * ,
158+ reverse : bool = False ,
159+ filled : bool = False ,
160+ ** kw : dict ,
161+ ) -> tuple :
153162 """Plot a sequence of CTD casts as a section."""
154163 import gsw
155164
156- lon , lat , data = list (map (np .asanyarray , (self .lon , self .lat , self .values )))
165+ lon , lat , data = list (
166+ map (np .asanyarray , (self .lon , self .lat , self .to_numpy ())),
167+ )
157168 data = ma .masked_invalid (data )
158169 h = self .get_maxdepth ()
159170 if reverse :
@@ -163,7 +174,7 @@ def plot_section(self, reverse=False, filled=False, **kw):
163174 h = h [::- 1 ]
164175 lon , lat = map (np .atleast_2d , (lon , lat ))
165176 x = np .append (0 , np .cumsum (gsw .distance (lon , lat )[0 ] / 1e3 ))
166- z = self .index .values .astype (float )
177+ z = self .index .to_numpy () .astype (float )
167178
168179 if filled : # CAVEAT: this method cause discontinuities.
169180 data = data .filled (fill_value = np .nan )
@@ -248,51 +259,53 @@ def plot_section(self, reverse=False, filled=False, **kw):
248259 return fig , ax , cb
249260
250261
251- def cell_thermal_mass (temperature , conductivity ):
252- """
253- Sample interval is measured in seconds.
262+ def cell_thermal_mass (
263+ temperature : pd .Series ,
264+ conductivity : pd .Series ,
265+ ) -> pd .Series :
266+ """Sample interval is measured in seconds.
254267 Temperature in degrees.
255268 CTM is calculated in S/m.
256269
257270 """
258-
259271 alpha = 0.03 # Thermal anomaly amplitude.
260272 beta = 1.0 / 7 # Thermal anomaly time constant (1/beta).
261273
262274 sample_interval = 1 / 15.0
263275 a = 2 * alpha / (sample_interval * beta + 2 )
264276 b = 1 - (2 * a / alpha )
265- dCodT = 0.1 * (1 + 0.006 * [temperature - 20 ])
266- dT = np .diff (temperature )
267- ctm = - 1.0 * b * conductivity + a * (dCodT ) * dT # [S/m]
268- return ctm
277+ dc_o_dt = 0.1 * (1 + 0.006 * [temperature - 20 ])
278+ dt = np .diff (temperature )
279+ return - 1.0 * b * conductivity + a * (dc_o_dt ) * dt # [S/m]
269280
270281
271- def mixed_layer_depth (CT , method = "half degree" ):
282+ def mixed_layer_depth (ct : pd . Series , method : str = "half degree" ) -> pd . Series :
272283 """Return the mixed layer depth based on the "half degree" criteria."""
273- if method == "half degree" :
274- mask = CT [0 ] - CT < 0.5
275- else :
276- mask = np .zeros_like (CT )
277- return Series (mask , index = CT .index , name = "MLD" )
284+ half_degree = 0.5
285+ mask = (
286+ ct [0 ] - ct < half_degree
287+ if method == "half degree"
288+ else np .zeros_like (ct )
289+ )
290+ return pd .Series (mask , index = ct .index , name = "MLD" )
278291
279292
280- def barrier_layer_thickness (SA , CT ) :
281- """
282- Compute the thickness of water separating the mixed surface layer from the
283- thermocline. A more precise definition would be the difference between
284- mixed layer depth (MLD) calculated from temperature minus the mixed layer
285- depth calculated using density.
293+ def barrier_layer_thickness (sa : pd . Series , ct : pd . Series ) -> pd . Series :
294+ """Compute the thickness of water separating the mixed surface layer from
295+ the thermocline.
296+ A more precise definition would be the difference between mixed layer depth
297+ (MLD) calculated from temperature minus the mixed layer depth calculated
298+ using density.
286299
287300 """
288301 import gsw
289302
290- sigma_theta = gsw .sigma0 (SA , CT )
291- mask = mixed_layer_depth (CT )
303+ sigma_theta = gsw .sigma0 (sa , ct )
304+ mask = mixed_layer_depth (ct )
292305 mld = np .where (mask )[0 ][- 1 ]
293306 sig_surface = sigma_theta [0 ]
294- sig_bottom_mld = gsw .sigma0 (SA [0 ], CT [mld ])
307+ sig_bottom_mld = gsw .sigma0 (sa [0 ], ct [mld ])
295308 d_sig_t = sig_surface - sig_bottom_mld
296309 d_sig = sigma_theta - sig_bottom_mld
297310 mask = d_sig < d_sig_t # Barrier layer.
298- return Series (mask , index = SA .index , name = "BLT" )
311+ return pd . Series (mask , index = sa .index , name = "BLT" )
0 commit comments