ESIMERKKI: Ilmakehäluotaukset#
Ilmakehää tutkitaan jatkuvasti monipuolisilla koelaitteistoilla ympäri maapalloa. Tässä esimerkissä vilkaistaan millaista jatkuvasti päivittyvää aineistoa Ilmatieteen laitoksen havaintopalloilta saadaan näkyviin.
1. Aineisto#
# Asennetaan fmiopendata-paketti (https://github.com/pnuu/fmiopendata)
!pip install fmiopendata
from fmiopendata.wfs import download_stored_query
# Ladataan luotaukset fmiopendata-paketin avulla
snd = download_stored_query("fmi::observations::weather::sounding::multipointcoverage")
Collecting fmiopendata
Downloading fmiopendata-0.4.3.tar.gz (26 kB)
Preparing metadata (setup.py) ... ?25l-
done
?25hRequirement already satisfied: numpy in /opt/hostedtoolcache/Python/3.13.3/x64/lib/python3.13/site-packages (from fmiopendata) (2.2.6)
Requirement already satisfied: requests in /opt/hostedtoolcache/Python/3.13.3/x64/lib/python3.13/site-packages (from fmiopendata) (2.32.3)
Collecting defusedxml (from fmiopendata)
Downloading defusedxml-0.7.1-py2.py3-none-any.whl.metadata (32 kB)
Requirement already satisfied: charset-normalizer<4,>=2 in /opt/hostedtoolcache/Python/3.13.3/x64/lib/python3.13/site-packages (from requests->fmiopendata) (3.4.2)
Requirement already satisfied: idna<4,>=2.5 in /opt/hostedtoolcache/Python/3.13.3/x64/lib/python3.13/site-packages (from requests->fmiopendata) (3.10)
Requirement already satisfied: urllib3<3,>=1.21.1 in /opt/hostedtoolcache/Python/3.13.3/x64/lib/python3.13/site-packages (from requests->fmiopendata) (2.4.0)
Requirement already satisfied: certifi>=2017.4.17 in /opt/hostedtoolcache/Python/3.13.3/x64/lib/python3.13/site-packages (from requests->fmiopendata) (2025.4.26)
Downloading defusedxml-0.7.1-py2.py3-none-any.whl (25 kB)
Building wheels for collected packages: fmiopendata
DEPRECATION: Building 'fmiopendata' using the legacy setup.py bdist_wheel mechanism, which will be removed in a future version. pip 25.3 will enforce this behaviour change. A possible replacement is to use the standardized build interface by setting the `--use-pep517` option, (possibly combined with `--no-build-isolation`), or adding a `pyproject.toml` file to the source tree of 'fmiopendata'. Discussion can be found at https://github.com/pypa/pip/issues/6334
Building wheel for fmiopendata (setup.py) ... ?25l-
\
done
?25h Created wheel for fmiopendata: filename=fmiopendata-0.4.3-py3-none-any.whl size=30961 sha256=cb3d6dd1c1b1f9abffcab13ea62dae0a03ef9545eeae9264eace347d331b89f7
Stored in directory: /home/runner/.cache/pip/wheels/8b/08/38/ca3e95e1d2083a04c6c4147e4d76c62b4d3b5f75dcb7edb285
Successfully built fmiopendata
Installing collected packages: defusedxml, fmiopendata
?25l
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2/2 [fmiopendata]
?25h
Successfully installed defusedxml-0.7.1 fmiopendata-0.4.3
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
Cell In[1], line 6
3 from fmiopendata.wfs import download_stored_query
5 # Ladataan luotaukset fmiopendata-paketin avulla
----> 6 snd = download_stored_query("fmi::observations::weather::sounding::multipointcoverage")
File /opt/hostedtoolcache/Python/3.13.3/x64/lib/python3.13/site-packages/fmiopendata/wfs.py:123, in download_stored_query(query_id, args)
120 else:
121 raise NotImplementedError("No parser available for %s" % query_id)
--> 123 return download_and_parse(query_id, args=args)
File /opt/hostedtoolcache/Python/3.13.3/x64/lib/python3.13/site-packages/fmiopendata/sounding.py:111, in download_and_parse(query_id, args)
109 url = url + "&" + "&".join(args)
110 xml = read_url(url)
--> 111 return ParseSoundings(xml)
File /opt/hostedtoolcache/Python/3.13.3/x64/lib/python3.13/site-packages/fmiopendata/sounding.py:67, in ParseSoundings.__init__(self, xml)
65 self._xml = ET.fromstring(xml)
66 self.soundings = []
---> 67 self._parse()
File /opt/hostedtoolcache/Python/3.13.3/x64/lib/python3.13/site-packages/fmiopendata/sounding.py:100, in ParseSoundings._parse(self)
98 fields = member.findall(wfs.SWE_FIELD)
99 for i, field in enumerate(fields):
--> 100 setattr(sounding, FIELD_NAMES[field.attrib["name"]], data[i::len(fields)])
102 self.soundings.append(sounding)
KeyError: 'RHP_PT1S_AVG'
# Tuodaan tarvittavat työkalukirjastot
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
2. Kuvaajien teko#
# Otetaan ensimmäinen luotaus, joka löytyy listasta.
sounding0 = snd.soundings[0]
# Laitetaan paineet ja muut parametrit taulukoihin.
paine0 = sounding0.pressures
lampotila0 = sounding0.temperatures
kastepiste0 = sounding0.dew_points
alkuaika0 = sounding0.start_time
loppuaika0 = sounding0.end_time
nimi0 = sounding0.name
aika0 = sounding0.nominal_time
korkeus0 = sounding0.altitudes
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[3], line 3
1 # Otetaan ensimmäinen luotaus, joka löytyy listasta.
----> 3 sounding0 = snd.soundings[0]
5 # Laitetaan paineet ja muut parametrit taulukoihin.
7 paine0 = sounding0.pressures
NameError: name 'snd' is not defined
# Tarkistetaan nopeasti montako riviä havaintoja valitussa luotauksessa on.
len(paine0)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[4], line 3
1 # Tarkistetaan nopeasti montako riviä havaintoja valitussa luotauksessa on.
----> 3 len(paine0)
NameError: name 'paine0' is not defined
# Piirretään kuvaaja
fig, ax = plt.subplots(figsize = (10, 10)) # Kuvaajan koko
# Ensimmäinen käyrä
color = "blue"
ax.set_xlabel("Lämpötila (°C)", fontsize = 15)
ax.set_ylabel("Paine (hPa)", c = color, fontsize = 15)
ax.plot(lampotila0, paine0, label = "Lämpötila", c = color) # Piirretään (T, p)
ax.plot(kastepiste0, paine0, label = "Kastepiste", c = "cyan") # Piirretään (Td, p)
ax.tick_params(axis = "y", labelcolor = color)
# Käännetään skaala ja luodaan toinen y-akseli (myös ax.set_ylim(alaraja, yläraja) toimii)
ax.set_ylim(ax.get_ylim()[::-1])
ax2 = ax.twinx()
# Toinen käyrä
color = "red"
ax2.set_ylabel("Korkeus (m)", c = color, fontsize = 15)
ax2.plot(lampotila0, korkeus0, label = "Korkeus", c = color)
ax2.tick_params(axis = "y", labelcolor = color)
# Piirretään taustaristikko
ax.set_axisbelow(True)
ax.yaxis.grid(color = 'gray', linestyle = 'dashed')
ax.xaxis.grid(color = 'gray', linestyle = 'dashed')
# Nimetään kuvaaja: Paikkakunta muuttujasta, aika muuttujasta (päivämäärä ja kellonaika)
ax.set_title(nimi0 + " " + aika0.strftime("%d.%m.%Y, %H:%M") + " UTC \n", fontsize = 20)
# Piirretään selite
plt.figlegend(bbox_to_anchor = (1.20, 0.5), fontsize = 15)
plt.show()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[5], line 8
6 ax.set_xlabel("Lämpötila (°C)", fontsize = 15)
7 ax.set_ylabel("Paine (hPa)", c = color, fontsize = 15)
----> 8 ax.plot(lampotila0, paine0, label = "Lämpötila", c = color) # Piirretään (T, p)
9 ax.plot(kastepiste0, paine0, label = "Kastepiste", c = "cyan") # Piirretään (Td, p)
10 ax.tick_params(axis = "y", labelcolor = color)
NameError: name 'lampotila0' is not defined

# Otetaan toinen luotaus, joka löytyy listasta
sounding1 = snd.soundings[1]
# Laitetaan paineet ja muut parametrit taulukoihin
paine1 = sounding1.pressures
lampotila1 = sounding1.temperatures
kastepiste1 = sounding1.dew_points
alkuaika1 = sounding1.start_time
loppuaika1 = sounding1.end_time
nimi1 = sounding1.name
aika1 = sounding1.nominal_time
korkeus1 = sounding1.altitudes
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[6], line 2
1 # Otetaan toinen luotaus, joka löytyy listasta
----> 2 sounding1 = snd.soundings[1]
4 # Laitetaan paineet ja muut parametrit taulukoihin
5 paine1 = sounding1.pressures
NameError: name 'snd' is not defined
# Tarkistetaan jälleen havaintojen määrä
len(paine1)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[7], line 2
1 # Tarkistetaan jälleen havaintojen määrä
----> 2 len(paine1)
NameError: name 'paine1' is not defined
# Piirretään kuvaaja
fig, ax = plt.subplots(figsize = (10, 10)) # Kuvaajan koko
# Ensimmäinen käyrä
color = "blue"
ax.set_xlabel("Lämpötila (°C)", fontsize = 15)
ax.set_ylabel("Paine (hPa)", c = color, fontsize = 15)
ax.plot(lampotila1, paine1, label = "Lämpötila", c = color) # Piirretään (T, p)
ax.plot(kastepiste1, paine1, label = "Kastepiste", c = "cyan") # Piirretään (Td, p)
ax.tick_params(axis = "y", labelcolor = color)
# Käännetään skaala ja luodaan toinen y-akseli
ax.set_ylim(ax.get_ylim()[::-1])
ax2 = ax.twinx()
# Toinen käyrä
color = "red"
ax2.set_ylabel("Korkeus (m)", c = color, fontsize = 15)
ax2.plot(lampotila1, korkeus1, label = "Korkeus", c = color)
ax2.tick_params(axis = "y", labelcolor = color)
# Piirretään taustaristikko
ax.set_axisbelow(True)
ax.yaxis.grid(color = 'gray', linestyle = 'dashed')
ax.xaxis.grid(color = 'gray', linestyle = 'dashed')
# Nimetään kuvaaja: Paikkakunta muuttujasta, aika muuttujasta (päivämäärä ja kellonaika)
ax.set_title(nimi1 + " " + aika1.strftime("%d.%m.%Y, %H:%M") + " UTC \n", fontsize = 20)
# Piirretään selite
plt.figlegend(bbox_to_anchor = (1.20, 0.5), fontsize = 15)
plt.show()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[8], line 8
6 ax.set_xlabel("Lämpötila (°C)", fontsize = 15)
7 ax.set_ylabel("Paine (hPa)", c = color, fontsize = 15)
----> 8 ax.plot(lampotila1, paine1, label = "Lämpötila", c = color) # Piirretään (T, p)
9 ax.plot(kastepiste1, paine1, label = "Kastepiste", c = "cyan") # Piirretään (Td, p)
10 ax.tick_params(axis = "y", labelcolor = color)
NameError: name 'lampotila1' is not defined
