Jakość powietrza w Polsce #2 – Rok 2017 oczami oddychającego Polaka

Rok 2018 zbliża się powoli ku końcowi. Jest to często czas podsumowań i refleksji. Pomyślałem więc, że również pozwolę sobie na małą refleksję odnośnie do jakości powietrza w Polsce w roku 2017 (sic!).

Dlaczego rok 2017? Bo akurat w momencie pisana tego artykułu jest to najnowsza paczka danych zawierająca archiwalne pomiary dostępna na stronie GIOŚ. Fakt, możemy dobrać się do aktualnych danych GIOŚ przez API, ale pozwala ono na dostanie się do ostatnich trzech dni kalendarzowych. Jeśli więc nie zbieraliśmy danych wcześniej w 2018 roku, to żeby zrobić podsumowanie roku 2018, będziemy czekać na odpowiednią paczkę danych, która pojawi się jakoś w trakcie roku 2019. Proste.

Ideą podsumowania roku 2017 będzie zbudowanie listy miast i ułożenie jej według jakości powietrza. Coś podobnego robiłem już wcześniej, tym razem będą to nieco inne dane, więcej miast i trochę dodatkowych przemyśleń. Do dzieła więc.

Dane

Dane, które będę wykorzystywał, pochodzą ze wspomnianego powyżej archiwum. Znajdziemy je w pliku 2017.zip. Potrzebny nam też będzie plik z informacją o współrzędnych stacji pomiarowych – Metadane_wer20180829.xlsx. Do pełni szczęścia potrzebny nam jeszcze plik z listą miast i ich współrzędnymi. Plik ten przygotowałem przy pomocy Wikipedii oraz OpenStreetMaps, a pobierzesz go tutaj (po pobraniu zmień rozszerzenie z txt na csv).

Proces

Proces tworzenia podsumowania składa się z kilku etapów. Pierwszy etap to wczytanie i przetworzenie danych. Tutaj wszystko oparłem o pętlę, która przechodzi po każdym z czynników szkodzących. W takiej pętli mam kolejną pętlę, która przechodzi po liście miast. Dla każdego miasta obliczam odległość do wszystkich stacji mierzących ten czynnik szkodzący i wybieram najbliższą. Wtedy pobieram dane dla tego czynnika z tej stacji i wyliczam wartość średnią z całego roku. Tak uzyskaną średnią zapisuję w ramce danych.

Jak można się domyślić 7 czynników, 930 miast i około 100 stacji na każdy czynnik daje trochę złożoności obliczeniowej. I to jest na razie, chyba najsłabszy element mojej analizy. Mam już pewien pomysł, jak przyspieszyć ten element, ale o tym na końcu.

Drugim elementem jest normalizacja odczytów. Jako że czynniki szkodzące mierzone są w różnych jednostkach i różne ich stężenie ma odmienny wpływ na zdrowie człowieka, postanowiłem je znormalizować, żeby traktować je porównywalnie. Wartość „bardzo zły” da po normalizacji wartość 1.

Ostatnim elementem jest wyznaczenie wartości średniej tak znormalizowanych czynników i posortowanie tabeli. Proste, nie?

Problem

Otóż nie jest to takie proste. Fakt, aktualnie zadowalam się powyższym rozwiązaniem. Ale to dlatego, że nie wymyśliłem jeszcze jak rozwiązać pewien problem. Problemem tym jest odległość miasta od czujnika, a raczej problem braku uwzględnienia tej odległości.

Ale o co chodzi? Otóż okazuje się, że GIOŚ dostarcza całkiem mało niektórych rodzajów czujników. Najbardziej „lubiany” w Polsce czynnik PM2.5 jest monitorowany w około 50 miejscach w Polsce. Nie udało mi się ustalić dlaczego. Skoro mamy 930 miast w Polsce, a czujników 50, to istnieje całkiem spora szansa, że jakieś miasta będą położone dość daleko od tego typu czujników. Myślę, że warto byłoby uwzględnić ten „efekt” w podsumowaniu, ale jak?

Pomysłem, który mam, jest wyznaczenie średniej ważonej, gdzie wagi są odległością. Im mniejsza odległość, tym waga jest większa. Jako hipoteza brzmi to dobrze i nawet pokusiłem się o sprawdzenie rankingu z uwzględnieniem wag znormalizowanych po odległości wszystkich czujników i alternatywnie znormalizowanych po odległościach pogrupowanych czujników. Oczywiście Python przyjmie wszystko, więc taki ranking powstał. Co się okazało, jako jedne z najlepszych powychodziły miasta, które miały bardzo daleko do czujników PM2.5. Dlaczego? No bo PM2.5 jest w Polsce najbardziej problematyczne. Pozostałe czynniki nie są aż takie złe. No i mamy więcej czujników, które je monitorują. Do średniej ważonej wpadły więc niskie wartości (bo np. nie mamy problemu z O3) z dużymi wagami (bo są blisko), a PM2.5 mimo iż duże, miało małą wagę, bo jest mierzone daleko. Gdzie tkwi więc haczyk?

Jeśli handlowałbym np. nieruchomościami i byłbym na tyle dużym graczem, że sterowałbym rynkiem, to moja metryka (średnia ważona) mogłaby wytworzyć popyt na mieszkania. W interesie takich miast byłoby utrzymanie wysokiej pozycji w rankingu najlepszego powietrza, bo wskazywałbym je jako dobre miejsce do osiedlenia się. Nie inwestowałyby więc w czujniki PM2.5, bo skoro, w rankingu jest dobrze, to jeśli okaże się, że w mieście jest źle, to waga tego pomiaru by wzrosła. Wtedy spadłyby w rankingu i by straciły. Taka metryka ze średnią ważoną jest więc w mojej opinii w tej chwili zła, bo optymalizacja zakłada nieinwestowanie w czujniki najbardziej problematycznego czynnika.

Nie wymyśliłem jeszcze jak rozgryźć ten problem. Nie chciałbym ustawiać żadnych sztywnych granic, progów i tym podobnych. W idealnym świecie każde miasto powinno mieć w geometrycznym centrum baterię 7 czujników. Problem odległości przestałby wtedy istnieć. I tak pewnie będzie za jakiś czas. W tej chwili myślę, że warto byłoby jakoś uwzględnić tę odległość. Może Ty masz jakiś pomysł jak to ugryźć?

Wyniki

Zacznijmy może od odległości od czujników. Gdy wyszukiwałem najbliższy czujnik danego czynnika dla każdego miasta, oprócz zapisania sobie jego średniego odczytu z całego roku, zapisywałem również jego odległość. Mogę wiec sprawdzić, jak ma się np. 5 największych odległości dla każdego czynnika:

                   PM25 PM25_dist
city                             
Mielno (ZP)     28.3591    126.39
Koszalin (ZP)   28.3591   117.773
Tychowo (ZP)    28.3591   114.676
Barwice (ZP)    28.3591   114.397
Białogard (ZP)  20.0019   111.765
                   PM10 PM10_dist
city                             
Terespol (LB)   29.0385   91.8119
Przasnysz (MZ)  24.9663   80.6835
Włodawa (LB)    32.5306   75.6202
Ciechanów (MZ)  31.5396   71.6462
Mława (MZ)       26.855   70.4742
                            C6H6 C6H6_dist
city                                      
Krynki (PL)              1.52227   220.139
Sokółka (PL)             1.03138   204.031
Michałowo (PL)           1.52227   198.077
Czarna Białostocka (PL)  1.03138   193.413
Lipsk (PL)               1.03138   192.507
                           NO2 NO2_dist
city                                   
Sokołów Podlaski (MZ)   11.595  74.4007
Łobez (ZP)             14.5201  69.7483
Gryfice (ZP)           22.9793  68.5706
Dziwnów (ZP)           22.9793  67.2836
Resko (ZP)             14.5201  65.7629
                           SO2 SO2_dist
city                                   
Sokołów Podlaski (MZ)   5.3082  74.4007
Łobez (ZP)              2.3102  69.7483
Gryfice (ZP)           4.67442  68.5706
Dziwnów (ZP)           4.67442  67.2836
Resko (ZP)              2.3102  65.7629
                         O3  O3_dist
city                                
Czaplinek (ZP)      44.5335  106.062
Kołobrzeg (ZP)      50.2521  100.889
Połczyn-Zdrój (ZP)  50.2521  99.5969
Gościno (ZP)        44.5335  99.2115
Wałcz (ZP)          42.6561  98.4846
                       CO  CO_dist
city                              
Terespol (LB)    0.403192   116.04
Łaszczów (LB)    0.244055   114.43
Tyszowce (LB)    0.403192  106.784
Hrubieszów (LB)  0.403192  105.403
Ostrołęka (MZ)   0.432785  102.196

W powyższej tabeli widzimy, że odległości 60+ kilometrów nie są niczym zaskakujące. Najbardziej zastanawiają liczby powyżej 100, a nawet 200 kilometrów. Okazuje się zatem (o ile nie popełniłem żadnego błędu), że mieszkańcy miasta Krynki w 2017 roku najbliższy państwowy czujnik benzenu mieli w odległości około 220 kilometrów. Z tego, co widziałem w aktualnych danych, to Białystok ma już taki czujnik, więc sytuacja wygląda nieco lepiej. Ale o tym w kolejnym artykule – poszukiwanie czujnikowych białych plam.

Okej, przejdźmy więc do najczystszych i najbrudniejszych miast w Polsce. Przypominam metodologię – każde ma przyporządkowaną grupę najbliższych czujników – po jednym na każdy czynnik szkodliwy. Bierzemy średnią z cogodzinnych średnich z całego roku 2017. Wartości te normalizujemy tak, że granica „bardzo zły” daje jeden. Na końcu wyliczamy średnią tych znormalizowanych wartości i sortujemy tabelę.

Najczystsze miasta w Polsce w 2017:

  1. Żukowo (PM)
  2. Pruszcz Gdański (PM)
  3. Gdynia (PM)

Najbrudniejsze miasta w Polsce w 2017:

  1. Skała (MP)
  2. Czerwionka-Leszczyny (ŚL)
  3. Żory (ŚL)

Jak widać, województwo pomorskie całkiem dobrze przędzie w jakości powietrza. Cóż, wynika to pewnie z faktu, że województwo to przylega do morza. Możemy więc sobie wyobrazić, że nawet jeśli mieszkańcy tych miast nieźle ładowaliby do kotłów, to i tak morze stanowi całkiem spory rezerwuar pustej przestrzeni, która może przyjąć te czynnik szkodliwe. Nie mówiąc już o tym, że samo morze często powoduje dość sporą intensywność wiatrów. Może o to więc chodzi?

Co do najbrudniejszych miast cóż, nie jestem specjalistą w tej dziedzinie, ale pamiętam, że uczono mnie w szkole, że właśnie w tamtych obszarach leżących na terenie województw małopolskiego i śląskiego mamy w Polsce najwięcej przedsiębiorstw wydobywczych, przetwórczych i produkcyjnych. Oczywiście chodzi mi o przemysł ciężki i surowce geologiczne. Czyli po prostu kopalnie, huty i fabryki. Plus, z jednej strony obszar ten jest „ograniczony” przez Karpaty, więc to też może niejako „blokować” rozchodzenie się smogu. Ale to tylko takie zgadywanki, nie traktuj więc tego i powyższego akapitu na poważnie ;).

Cały proces wyznaczania miasta, jak i funkcja „debug” pokazująca informacje o czujnikach, które zostały przypisane do miasta, znajduje się w notebooku, do którego link znajdziesz na końcu artykułu. Zachęcam do samodzielnego poeksperymentowania ;).

Możemy jeszcze się przyjrzeć, z których stacji pobrane zostały dane dla poszczególnych miast. Poniższe mapy są zrzutami ekranu z notebooka z analizą.

Żukowo
Żukowo
Pruszcz Gdański
Pruszcz Gdański
Gdynia
Gdynia
Skała
Skała
Czerwionka-Leszczyny
Czerwionka-Leszczyny
Żory
Żory

Widzimy, że w mojej analizie największym problemem jest odległość do czujników. No bo odległość czujników od miasteczka Skała jest znacząca, nie mówiąc już o tym, że czujniki te są umieszczone w różnych obszarach. Cóż, pozostawię to więc tak jak jest, do przyszłych rozważań.

Przyszłość

Tak jak wspominałem, aktualnie najwięcej czasu marnuję na namierzenie najbliższego czujnika dla miasta. Myślę, że można by spróbować zaprząc Scikit-Learn do rozwiązania tego problemu. Na pierwszy rzut oka metoda NearestNeighbors wydaje się obiecująca. Myślę, że w jakimś kolejnym artykule spróbuję ją wykorzystać do przyspieszenia działania skryptu.

Jeżeli natrafię na jakiś fajny sposób na uwzględnienie odległości w wyznaczaniu jakości powietrza, to również postaram się go zastosować. Mam jednak przeczucie, że nie będzie miało to za dużo sensu i chyba sensowniej będzie lobbować GIOŚ o postawienie nowych czujników i o posiłkowanie się danymi z Airly (znasz może jeszcze jakieś API?).

Wciąż jeszcze nie ruszyłem modelowania czasoprzestrzennego. Kto wie, może tutaj uda mi się zbudować coś ciekawego, i być może użycie takiego modelu da nieco więcej wglądu w stan zanieczyszczenia powietrza w Polsce. Szczególnie że właśnie odkryłem, że IMGW również udostępnia historyczne paczki z danymi pogodowymi (aww yiss).

Cóż, liczę na to, że z roku na rok dostępnych będzie coraz więcej czujników. Liczę też, że nauczę się nowych technik analitycznych i będę mógł lepiej przygotować kolejne raporty. A na razie, musimy zadowolić się tym, co mamy.

Pełny kod użyty w raporcie znajduje się tutaj.

0 0 votes
Article Rating
Subscribe
Powiadom o
guest

2 komentarzy
najstarszy
najnowszy oceniany
Inline Feedbacks
View all comments
Paweł
Paweł
5 lat temu

Znajdowanie najbliższych sąsiadów dla 930 miast i 100 czujników nie powinno być wyzwaniem obliczeniowym. To jest złożoność O(n*m) W kodach widzę dwa miejsca, które powodują spadek wydajności. Pierwsze to linia:
nearest_station = tmp_stations.sort_values([„dist”]).iloc[0]
Sortowanie nie jest potrzebne. Interesuje nas jeden, najbliższy czujnik, więc pewnie funkcja min wystarczy. Domyślam się, że sortowanie przydawało się w debugowaniu, do narysowania kilku najbliższych czujników. Ale przez to wydajność tej operacji spada z O(n) do O(n logn)
Drugi powód grzania procesora to linia:
dist = geopy.distance.vincenty(coords_1, coords_2).km
geopy to całkiem przydatny pakiet i bardzo precyzyjnie oblicza odległość między punktami. Ale tu nie potrzebujemy milimetrowej dokładności i uwzględniania takich niuansów jak spłaszczenie Ziemii. Przy rzadkiej sieci czujników i bardzo zgrubnych pozycjach miast nawet 10% różnica długości równoleżnikowej między południem i północą Polski nie ma znaczenia. Można Polskę zrzutować na płaszczyznę i wszystkie współrzędne geograficzne zmapować na współrzędne kartezjańskie odległości względem np. Łodzi. Dla poprawienia precyzji można nawet za mapę Polski przyjąć nie kwadrat a trapez. Odległość między punktami jest wtedy zwykłą długością odcinka pomiędzy punktami, a jej obliczenie to dwa odejmowania, dwa mnożenia i pierwiastkowanie. 930 miast * 100 czujników powinno się przeliczyć w jakąś milisekundę. sklearn.neighbours w niczym tu nie pomoże, a raczej pogorszy sprawę.

A jeśli chodzi o samo mierzenie zanieczyszczeń, to założenie o pobieraniu danych z najbliższego czujnika jest baaaaardzo daleko idące. W ten sposób Sieraków położony między jeziorami przy Puszczy Noteckiej ma poziom PM25 jak w Poznaniu. Zanieczyszczenia nie rozkładają się równomiernie, ale są skoncentrowane wokół dużych miast i ośrodków przemysłowych. A nawet lokalnie pomiary potrafią się znacząco różnić w zależności od ukształtowania terenu i pobliskich emitentów. Przy tak przyjętych założeniach wyniki naniesione na mapę pokażą plamy o jednolitej wartości skoncentrowane dookoła czujników. Podobnie mało użyteczne to podejście by było dla przedstawienie średniego wynagrodzenia, gdzie dane są dostępne tylko dla niektórych miejsc w Polsce. Tu też Sieraków miałby pewnie taką samą wartość jak Poznań i też nijak by się to nie miało do rzeczywistości.