Assignment 05#

Due date: 03.05.2023

This week’s assignment has to be returned in the form a jupyter notebook. Use jupyter notebooks for what they excel at: combining text, code, and figures. Use the markdown format to structure your assignments, and write text when asked to.

As explained in the video, I recommend to download this notebook in order to answer the questions directly in it.

Don’t forget the instructions!

Introduction: live meteorological data#

Warning

Online access to ACINN data does not work anymore. For a similar excercise I recommend to have a look at GeoSphere Austria’s API. I may update the resources here in the future.

The institute website provides raw data from several stations around Innsbruck using a live feed at the following addresses:

The datasets for other stations are available, per analogy:

The data is shared by ACINN under a Creative Commons Attribution-ShareAlike 4.0 International License.

The data is provided in the json format, often used for web applications. Fortunately, this is very easy to read in python. To make your life easier, I have prepared a reading function below.

It is not important that you understand how it works exactly, but you should understand how to call the function as explained in the examples below.

import numpy as np

def acinn_meteo_data(station="innsbruck", ndays=3):
    """Parse live meteorological data from the ACINN servers.

    Requires an internet connection!

    Parameters
    ----------
    station : str
        one of "innsbruck", "obergurgl"
    ndays : int
        either 3 or 7 days

    Returns
    -------
    (time, air_temp, wind_speed, wind_dir) :
        the meteorological data (python lists)

    Examples
    --------
    >>> time, air_temp, wind_speed, wind_dir = acinn_meteo_data()
    >>> type(time)
    <class 'numpy.ndarray'>
    >>> len(time) > 0
    True
    """
    from urllib.request import Request, urlopen
    from datetime import datetime, timedelta
    import json

    url = f'https://acinn-data.uibk.ac.at/{station}/{ndays}'
    req = urlopen(Request(url)).read()
    # Read the data
    data = json.loads(req.decode('utf-8'))

    # Convert the time
    time = np.array([datetime(1970, 1, 1) + timedelta(milliseconds=ds) for ds in data['datumsec']])

    # Read the data
    wind_speed = np.array(data['ff'])
    wind_dir = np.array(data['dd'])
    air_temp = np.array(data['tl'])
    
    # This is for missing data filtering - more on this in the next class
    air_temp[air_temp < -99] = np.NaN

    is_finite = np.isfinite(air_temp) & np.isfinite(wind_speed) & np.isfinite(wind_dir)
    if np.any(~is_finite):
        time = time[is_finite]
        air_temp = air_temp[is_finite]
        wind_speed = wind_speed[is_finite]
        wind_dir = wind_dir[is_finite]

    # Return the data
    return time, air_temp, wind_speed, wind_dir

# Testing
import doctest
doctest.testmod()
**********************************************************************
File "__main__", line 22, in __main__.acinn_meteo_data
Failed example:
    time, air_temp, wind_speed, wind_dir = acinn_meteo_data()
Exception raised:
    Traceback (most recent call last):
      File "/Users/uu23343/.mambaforge/envs/oggm_env/lib/python3.12/doctest.py", line 1368, in __run
        exec(compile(example.source, filename, "single",
      File "<doctest __main__.acinn_meteo_data[0]>", line 1, in <module>
        time, air_temp, wind_speed, wind_dir = acinn_meteo_data()
                                               ^^^^^^^^^^^^^^^^^^
      File "/var/folders/29/f4w56zjd1c1b_gdh_b7bjs3c0000gq/T/ipykernel_55317/3280285223.py", line 33, in acinn_meteo_data
        req = urlopen(Request(url)).read()
              ^^^^^^^^^^^^^^^^^^^^^
      File "/Users/uu23343/.mambaforge/envs/oggm_env/lib/python3.12/urllib/request.py", line 215, in urlopen
        return opener.open(url, data, timeout)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/Users/uu23343/.mambaforge/envs/oggm_env/lib/python3.12/urllib/request.py", line 521, in open
        response = meth(req, response)
                   ^^^^^^^^^^^^^^^^^^^
      File "/Users/uu23343/.mambaforge/envs/oggm_env/lib/python3.12/urllib/request.py", line 630, in http_response
        response = self.parent.error(
                   ^^^^^^^^^^^^^^^^^^
      File "/Users/uu23343/.mambaforge/envs/oggm_env/lib/python3.12/urllib/request.py", line 559, in error
        return self._call_chain(*args)
               ^^^^^^^^^^^^^^^^^^^^^^^
      File "/Users/uu23343/.mambaforge/envs/oggm_env/lib/python3.12/urllib/request.py", line 492, in _call_chain
        result = func(*args)
                 ^^^^^^^^^^^
      File "/Users/uu23343/.mambaforge/envs/oggm_env/lib/python3.12/urllib/request.py", line 639, in http_error_default
        raise HTTPError(req.full_url, code, msg, hdrs, fp)
    urllib.error.HTTPError: HTTP Error 404: Not Found
**********************************************************************
File "__main__", line 23, in __main__.acinn_meteo_data
Failed example:
    type(time)
Exception raised:
    Traceback (most recent call last):
      File "/Users/uu23343/.mambaforge/envs/oggm_env/lib/python3.12/doctest.py", line 1368, in __run
        exec(compile(example.source, filename, "single",
      File "<doctest __main__.acinn_meteo_data[1]>", line 1, in <module>
        type(time)
             ^^^^
    NameError: name 'time' is not defined. Did you forget to import 'time'
**********************************************************************
File "__main__", line 25, in __main__.acinn_meteo_data
Failed example:
    len(time) > 0
Exception raised:
    Traceback (most recent call last):
      File "/Users/uu23343/.mambaforge/envs/oggm_env/lib/python3.12/doctest.py", line 1368, in __run
        exec(compile(example.source, filename, "single",
      File "<doctest __main__.acinn_meteo_data[2]>", line 1, in <module>
        len(time) > 0
            ^^^^
    NameError: name 'time' is not defined. Did you forget to import 'time'
**********************************************************************
1 items had failures:
   3 of   3 in __main__.acinn_meteo_data
***Test Failed*** 3 failures.
TestResults(failed=3, attempted=3)

The function can be called as follows:

time, air_temp, wind_speed, wind_dir = acinn_meteo_data(station='innsbruck', ndays=7)
---------------------------------------------------------------------------
HTTPError                                 Traceback (most recent call last)
Cell In[2], line 1
----> 1 time, air_temp, wind_speed, wind_dir = acinn_meteo_data(station='innsbruck', ndays=7)

Cell In[1], line 33, in acinn_meteo_data(station, ndays)
     30 import json
     32 url = f'https://acinn-data.uibk.ac.at/{station}/{ndays}'
---> 33 req = urlopen(Request(url)).read()
     34 # Read the data
     35 data = json.loads(req.decode('utf-8'))

File ~/.mambaforge/envs/oggm_env/lib/python3.12/urllib/request.py:215, in urlopen(url, data, timeout, cafile, capath, cadefault, context)
    213 else:
    214     opener = _opener
--> 215 return opener.open(url, data, timeout)

File ~/.mambaforge/envs/oggm_env/lib/python3.12/urllib/request.py:521, in OpenerDirector.open(self, fullurl, data, timeout)
    519 for processor in self.process_response.get(protocol, []):
    520     meth = getattr(processor, meth_name)
--> 521     response = meth(req, response)
    523 return response

File ~/.mambaforge/envs/oggm_env/lib/python3.12/urllib/request.py:630, in HTTPErrorProcessor.http_response(self, request, response)
    627 # According to RFC 2616, "2xx" code indicates that the client's
    628 # request was successfully received, understood, and accepted.
    629 if not (200 <= code < 300):
--> 630     response = self.parent.error(
    631         'http', request, response, code, msg, hdrs)
    633 return response

File ~/.mambaforge/envs/oggm_env/lib/python3.12/urllib/request.py:559, in OpenerDirector.error(self, proto, *args)
    557 if http_err:
    558     args = (dict, 'default', 'http_error_default') + orig_args
--> 559     return self._call_chain(*args)

File ~/.mambaforge/envs/oggm_env/lib/python3.12/urllib/request.py:492, in OpenerDirector._call_chain(self, chain, kind, meth_name, *args)
    490 for handler in handlers:
    491     func = getattr(handler, meth_name)
--> 492     result = func(*args)
    493     if result is not None:
    494         return result

File ~/.mambaforge/envs/oggm_env/lib/python3.12/urllib/request.py:639, in HTTPDefaultErrorHandler.http_error_default(self, req, fp, code, msg, hdrs)
    638 def http_error_default(self, req, fp, code, msg, hdrs):
--> 639     raise HTTPError(req.full_url, code, msg, hdrs, fp)

HTTPError: HTTP Error 404: Not Found

Exercise 1: data exploration#

Please answer the questions one by one in the cells below.

What is the dtype of the data stored in the air_temp, wind_speed, wind_dir arrays? And their size? And their shape?

# your answer here

What is the dtype of the data stored in the time array? Try to ask the following questions:

print(time.dtype)
print(type(time[0]))
# your answer here

What is the time resolution of the data? To answer this question, you can print the two first elements of the time array.

# your answer here

Exercise 2: plotting#

Create a graph of the temperature and wind in Innsbruck over the last 3 days. Start by noticing that matplotlib understands datetime objects very well, and create a graph which looks approximately like this: example image (note that your plot can be different with respect to dates, colors, etc. But be careful with the axis labels and legends).

# your answer here

Note: date formatting on axes is something which is very difficult to do automatically. If you feel like it you can try things like shown on the example below (optional).

# Define the date format
from matplotlib.dates import DateFormatter, HourLocator
ax.xaxis.set_major_locator(HourLocator(byhour=[0, 12]))
ax.xaxis.set_minor_locator(HourLocator())
ax.xaxis.set_major_formatter(DateFormatter("%b %d, %HH"))
fig.autofmt_xdate()

Now create a graph of the temperature at Innsbruck and Obergurgl over the last 7 days. The graph should look approximately like this: example image (note that your plot can be different with respect to dates, colors, etc. But be careful with the axis labels and legends).

# your answer here

Exercise 3: statistics#

Compute the average, maximum and minimum temperature in Innsbruck over the past 7 days.

# your answer here

Compute the average, maximum and minimum temperature in Innsbruck over the past 24H. For this, assume that the data has no missing timestamp and that the time resolution is constant (this is sometimes wrong but we don’t care here).

# your answer here

Compute the number of times that the 10m wind speed in Innsbruck was above 5 m/s over the past 7 days. Now convert this number to a percentage over the past 7 days.

# your answer here

Compute the number of times that the air temperature in Innsbruck was between 0°C and 10°C over the past 7 days. Now convert this number to a percentage over the past 7 days.

# your answer here

Bonus: average wind direction#

Look at the plot you made about wind direction. Can you think of what problem would occur if you try to compute the average of the wind direction?

Next week, we will address this problem together - so if you are bored you can start to think about how to solve it already ;-)