Climatology#
Overview#
This section covers the following climatology functions from NCL, which can be replicated in Python using xarray and/or geocat.comp:
Functions#
calcDayAnom*#
calcDayAnomTLL calculates daily anomalies from a daily data climatology.
Grab and Go#
import xarray as xr
import geocat.datafiles as gcd
from matplotlib import pyplot as plt
ds = xr.open_dataset(gcd.get("applications_files/inputs/CMIP6_sea_ice_daily_subset.nc"))
DayTLL = ds.aice_d.groupby("time.dayofyear")
clmDayTLL = DayTLL.mean("time")
calcDayAnomTLL = DayTLL - clmDayTLL
calcDayAnomTLL[0, :, :].assign_attrs(long_name="sea ice anomaly").plot();
---------------------------------------------------------------------------
RemoteDisconnected Traceback (most recent call last)
File ~/micromamba/envs/geocat-applications/lib/python3.14/site-packages/urllib3/connectionpool.py:788, in HTTPConnectionPool.urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, preload_content, decode_content, **response_kw)
787 # Make the request on the HTTPConnection object
--> 788 response = self._make_request(
789 conn,
790 method,
791 url,
792 timeout=timeout_obj,
793 body=body,
794 headers=headers,
795 chunked=chunked,
796 retries=retries,
797 response_conn=response_conn,
798 preload_content=preload_content,
799 decode_content=decode_content,
800 **response_kw,
801 )
803 # Everything went great!
File ~/micromamba/envs/geocat-applications/lib/python3.14/site-packages/urllib3/connectionpool.py:534, in HTTPConnectionPool._make_request(self, conn, method, url, body, headers, retries, timeout, chunked, response_conn, preload_content, decode_content, enforce_content_length)
533 try:
--> 534 response = conn.getresponse()
535 except (BaseSSLError, OSError) as e:
File ~/micromamba/envs/geocat-applications/lib/python3.14/site-packages/urllib3/connection.py:571, in HTTPConnection.getresponse(self)
570 # Get the response from http.client.HTTPConnection
--> 571 httplib_response = super().getresponse()
573 try:
File ~/micromamba/envs/geocat-applications/lib/python3.14/http/client.py:1459, in HTTPConnection.getresponse(self)
1458 try:
-> 1459 response.begin()
1460 except ConnectionError:
File ~/micromamba/envs/geocat-applications/lib/python3.14/http/client.py:336, in HTTPResponse.begin(self)
335 while True:
--> 336 version, status, reason = self._read_status()
337 if status != CONTINUE:
File ~/micromamba/envs/geocat-applications/lib/python3.14/http/client.py:305, in HTTPResponse._read_status(self)
302 if not line:
303 # Presumably, the server closed the connection before
304 # sending a valid response.
--> 305 raise RemoteDisconnected("Remote end closed connection without"
306 " response")
307 try:
RemoteDisconnected: Remote end closed connection without response
During handling of the above exception, another exception occurred:
ProtocolError Traceback (most recent call last)
File ~/micromamba/envs/geocat-applications/lib/python3.14/site-packages/requests/adapters.py:696, in HTTPAdapter.send(self, request, stream, timeout, verify, cert, proxies)
695 try:
--> 696 resp = conn.urlopen(
697 method=request.method,
698 url=url,
699 body=request.body, # type: ignore[arg-type] # urllib3 stubs don't accept Iterable[bytes | str]
700 headers=request.headers, # type: ignore[arg-type] # urllib3#3072
701 redirect=False,
702 assert_same_host=False,
703 preload_content=False,
704 decode_content=False,
705 retries=self.max_retries,
706 timeout=resolved_timeout,
707 chunked=chunked,
708 )
710 except (ProtocolError, OSError) as err:
File ~/micromamba/envs/geocat-applications/lib/python3.14/site-packages/urllib3/connectionpool.py:842, in HTTPConnectionPool.urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, preload_content, decode_content, **response_kw)
840 new_e = ProtocolError("Connection aborted.", new_e)
--> 842 retries = retries.increment(
843 method, url, error=new_e, _pool=self, _stacktrace=sys.exc_info()[2]
844 )
845 retries.sleep()
File ~/micromamba/envs/geocat-applications/lib/python3.14/site-packages/urllib3/util/retry.py:498, in Retry.increment(self, method, url, response, error, _pool, _stacktrace)
497 if read is False or method is None or not self._is_method_retryable(method):
--> 498 raise reraise(type(error), error, _stacktrace)
499 elif read is not None:
File ~/micromamba/envs/geocat-applications/lib/python3.14/site-packages/urllib3/util/util.py:38, in reraise(tp, value, tb)
37 if value.__traceback__ is not tb:
---> 38 raise value.with_traceback(tb)
39 raise value
File ~/micromamba/envs/geocat-applications/lib/python3.14/site-packages/urllib3/connectionpool.py:788, in HTTPConnectionPool.urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, preload_content, decode_content, **response_kw)
787 # Make the request on the HTTPConnection object
--> 788 response = self._make_request(
789 conn,
790 method,
791 url,
792 timeout=timeout_obj,
793 body=body,
794 headers=headers,
795 chunked=chunked,
796 retries=retries,
797 response_conn=response_conn,
798 preload_content=preload_content,
799 decode_content=decode_content,
800 **response_kw,
801 )
803 # Everything went great!
File ~/micromamba/envs/geocat-applications/lib/python3.14/site-packages/urllib3/connectionpool.py:534, in HTTPConnectionPool._make_request(self, conn, method, url, body, headers, retries, timeout, chunked, response_conn, preload_content, decode_content, enforce_content_length)
533 try:
--> 534 response = conn.getresponse()
535 except (BaseSSLError, OSError) as e:
File ~/micromamba/envs/geocat-applications/lib/python3.14/site-packages/urllib3/connection.py:571, in HTTPConnection.getresponse(self)
570 # Get the response from http.client.HTTPConnection
--> 571 httplib_response = super().getresponse()
573 try:
File ~/micromamba/envs/geocat-applications/lib/python3.14/http/client.py:1459, in HTTPConnection.getresponse(self)
1458 try:
-> 1459 response.begin()
1460 except ConnectionError:
File ~/micromamba/envs/geocat-applications/lib/python3.14/http/client.py:336, in HTTPResponse.begin(self)
335 while True:
--> 336 version, status, reason = self._read_status()
337 if status != CONTINUE:
File ~/micromamba/envs/geocat-applications/lib/python3.14/http/client.py:305, in HTTPResponse._read_status(self)
302 if not line:
303 # Presumably, the server closed the connection before
304 # sending a valid response.
--> 305 raise RemoteDisconnected("Remote end closed connection without"
306 " response")
307 try:
ProtocolError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))
During handling of the above exception, another exception occurred:
ConnectionError Traceback (most recent call last)
Cell In[1], line 2
1 import xarray as xr
----> 2 import geocat.datafiles as gcd
3 from matplotlib import pyplot as plt
4
5 ds = xr.open_dataset(gcd.get("applications_files/inputs/CMIP6_sea_ice_daily_subset.nc"))
File ~/micromamba/envs/geocat-applications/lib/python3.14/site-packages/geocat/datafiles/__init__.py:38
35 if dev_data_path is not None and os.path.exists(dev_data_path):
36 POOCH.path = dev_data_path
---> 38 _update_registry(POOCH)
40 def get(fname):
41 return POOCH.fetch(fname)
File ~/micromamba/envs/geocat-applications/lib/python3.14/site-packages/geocat/datafiles/__init__.py:28, in _update_registry(p)
24 """
25 Updates pooch registry.txt
26 """
27 old_sha256 = p.registry["registry.txt"]
---> 28 new_sha256 = hashlib.sha256(requests.get(p.get_url("registry.txt")).content).hexdigest()
29 if old_sha256 != new_sha256:
30 p.registry["registry.txt"] = new_sha256
File ~/micromamba/envs/geocat-applications/lib/python3.14/site-packages/requests/api.py:87, in get(url, params, **kwargs)
74 def get(
75 url: _t.UriType, params: _t.ParamsType = None, **kwargs: Unpack[_t.GetKwargs]
76 ) -> Response:
77 r"""Sends a GET request.
78
79 :param url: URL for the new :class:`Request` object.
(...) 84 :rtype: requests.Response
85 """
---> 87 return request("get", url, params=params, **kwargs)
File ~/micromamba/envs/geocat-applications/lib/python3.14/site-packages/requests/api.py:71, in request(method, url, **kwargs)
67 # By using the 'with' statement we are sure the session is closed, thus we
68 # avoid leaving sockets open which can trigger a ResourceWarning in some
69 # cases, and look like a memory leak in others.
70 with sessions.Session() as session:
---> 71 return session.request(method=method, url=url, **kwargs)
File ~/micromamba/envs/geocat-applications/lib/python3.14/site-packages/requests/sessions.py:651, in Session.request(self, method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert, json)
646 send_kwargs = {
647 "timeout": timeout,
648 "allow_redirects": allow_redirects,
649 }
650 send_kwargs.update(settings)
--> 651 resp = self.send(prep, **send_kwargs)
653 return resp
File ~/micromamba/envs/geocat-applications/lib/python3.14/site-packages/requests/sessions.py:784, in Session.send(self, request, **kwargs)
781 start = preferred_clock()
783 # Send the request
--> 784 r = adapter.send(request, **kwargs)
786 # Total elapsed time of the request (approximately)
787 elapsed = preferred_clock() - start
File ~/micromamba/envs/geocat-applications/lib/python3.14/site-packages/requests/adapters.py:711, in HTTPAdapter.send(self, request, stream, timeout, verify, cert, proxies)
696 resp = conn.urlopen(
697 method=request.method,
698 url=url,
(...) 707 chunked=chunked,
708 )
710 except (ProtocolError, OSError) as err:
--> 711 raise ConnectionError(err, request=request)
713 except MaxRetryError as e:
714 if isinstance(e.reason, ConnectTimeoutError):
715 # TODO: Remove this in 3.0.0: see #2811
ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))
from geocat.comp import climate_anomaly
import geocat.datafiles as gcd
ds = xr.open_dataset(gcd.get("applications_files/inputs/CMIP6_sea_ice_daily_subset.nc"))
calcDayAnomTLL = climate_anomaly(ds.aice_d, "day")
calcMonAnom*#
calcMonAnomTLL, calcMonAnomTLLL, calcMonAnomLLT, and calcMonAnomLLLT calculate monthly anomalies by subtracting the long-term mean from each point.
Grab and Go#
import xarray as xr
import geocat.datafiles as gcd
ds = xr.open_dataset(
gcd.get("applications_files/inputs/CMIP6_sea_ice_monthly_subset.nc")
)
MonTLL = ds.aice.groupby("time.month")
clmMonTLL = MonTLL.mean("time")
calcMonAnomTLL = MonTLL - clmMonTLL
calcMonAnomTLL = calcMonAnomTLL.assign_attrs(long_name="sea ice anomaly")
calcMonAnomTLL[0, :, :].plot();
from geocat.comp import climate_anomaly
import geocat.datafiles as gcd
ds = xr.open_dataset(
gcd.get("applications_files/inputs/CMIP6_sea_ice_monthly_subset.nc")
)
calcMonAnomTLL = climate_anomaly(ds.aice, "month")
clmDay*#
clmDayTLL and clmDayTLLL calculate long-term daily means (daily climatology) from daily data.
Grab and Go#
import xarray as xr
import geocat.datafiles as gcd
ds = xr.open_dataset(gcd.get("applications_files/inputs/CMIP6_sea_ice_daily_subset.nc"))
clmDayTLL = ds.aice_d.groupby("time.dayofyear").mean("time")
clmDayTLL[:, 10, 10].plot()
plt.title("daily climatology")
plt.xlabel("day of year")
plt.ylabel("sea ice area");
from geocat.comp import climatology_average
import geocat.datafiles as gcd
ds = xr.open_dataset(gcd.get("applications_files/inputs/CMIP6_sea_ice_daily_subset.nc"))
clmDayTLL = climatology_average(ds.aice_d, "day")
clmMon*#
clmMonTLL, clmMonTLLL, clmMonLLT, and clmMonLLLT calculate long-term monthly means (monthly climatology) from monthly data.
Grab and Go#
import xarray as xr
import geocat.datafiles as gcd
ds = xr.open_dataset(
gcd.get("applications_files/inputs/CMIP6_sea_ice_monthly_subset.nc")
)
clmMonTLL = ds.aice.groupby("time.month").mean("time")
clmMonTLL[:, 10, 10].plot()
plt.title("monthly climatology")
plt.xlabel("month of year")
plt.ylabel("sea ice area");
from geocat.comp import climatology_average
import geocat.datafiles as gcd
ds = xr.open_dataset(
gcd.get("applications_files/inputs/CMIP6_sea_ice_monthly_subset.nc")
)
clmMonTLL = climatology_average(ds.aice, "month")
month_to_season#
month_to_season computes a user-specified three-month seasonal mean (DJF, JFM, FMA, MAM, AMJ, MJJ, JJA, JAS, ASO, SON, OND, NDJ).
Note
You can do something similar directly with Xarray as shown here in the Xarray documentation. However, it requires more code.
Grab and Go#
import xarray as xr
import geocat.datafiles as gcd
from geocat.comp import month_to_season
ds = xr.open_dataset(
gcd.get("applications_files/inputs/CMIP6_sea_ice_monthly_subset.nc")
)
mon_to_season = month_to_season(ds.aice, "ASO").assign_attrs(long_name="sea ice area")
mon_to_season[0, :, :].plot()
plt.title("2010 seasonal mean");
rmMonAnnCyc*#
rmMonAnnCycTLL, rmMonAnnCycLLT, and rmMonAnnCycLLLT remove the annual cycle from monthly data.
Grab and Go#
import xarray as xr
import geocat.datafiles as gcd
ds = xr.open_dataset(
gcd.get("applications_files/inputs/CMIP6_sea_ice_monthly_subset.nc")
)
MonTLL = ds.aice.groupby("time.month")
clmMonTLL = MonTLL.mean("time")
rmMonAnnCycTLL = MonTLL - clmMonTLL
rmMonAnnCycTLL[:, 10, 10].plot()
plt.title("annual cycle removed")
plt.ylabel("sea ice area");
stdMon*#
stdMonTLL, stdMonTLLL, stdMonLLT, and stdMonLLLT calculate standard deviations of monthly means.
Grab and Go#
import xarray as xr
import geocat.datafiles as gcd
ds = xr.open_dataset(
gcd.get("applications_files/inputs/CMIP6_sea_ice_monthly_subset.nc")
)
stdMonTLL = ds.aice.groupby("time.month").std(ddof=1)
stdMonTLL[0, :, :].plot();