Python

Mapa miejsc w których rozgrywano międzynarodowe mecze piłki nożnej

Jeżeli kiedykolwiek zastanawialiście się jak w łatwy i przyjemny sposób przedstawić duży zbiór danych na mapie to w poniższym wpisie znajdziecie na to gotowe rozwiązanie. Ponadto w trakcie naszych analiz sprawdziliśmy, gdzie reprezentacja Polski rozegrała mecze w latach 2000 – 2020 i gdzie zwyciężyła.  Do badań użyliśmy moduł Python Folium, który jest wrapperem do biblioteki Leaflet.js. W naszej ocenie w sposób znaczący ułatwia on wizualizację danych na mapie. Do obróbki zbioru danych użyliśmy popularny moduł Python o nazwie Pandas.

Źródła danych

[1] Dane międzynarodowych meczy piłki nożnej, rozgrywanych w latach 1872 – 2020

[2]  Zbiór danych zawierający szerokość i długość geograficzną miast

[3] Lista skrótów państw

Kluczowym do naszej analizy był [1] zbiór danych zawierający dane dotyczące meczów piłki nożnej, rozegranych w latach 1872-2020. Dwa pozostałe zbiory danych [2] i [3] użyliśmy do zamiany skrótów państw na współrzędne geograficzne lokalizacji w których rozgrywane były mecze.

Import modułów

import pandas as pd
import folium
from folium import Marker
from folium.plugins import  MarkerCluster

Import danych z plików CSV

Do wczytania danych z plików CSV służy metoda read_csv i jako parametr podawana jest w niej ścieżka do pliku. Dane wczytane w ten sposób zapisywane są w pamięci w obiekcie zwanym DataFrame. Jest to dwuwymiarowa struktura tabelaryczna.

cities = pd.read_csv('./worldcitiespop.csv')
matches = pd.read_csv('./results.csv')
abreviations = pd.read_csv('./data.csv')

Oczyszczanie danych

Zbiór danych ‘cities’ [2]  zawiera długość i szerokość geograficzną wszystkich miast na świecie. Niestety w kolumnie “państwo” znajduję się jedynie skrót nazwy. Dlatego do zamiany skrótów na pełne nazwy państw, zdecydowaliśmy się wykorzystać zbiór [3].  Przed scaleniem zbiorów trzeba było jeszcze doprowadzić  skróty do jednakowej postaci, więc w [3] zapisaliśmy je małymi literami.

abreviations['Code'] = abreviations['Code'].str.lower()

Scalenie zbiorów wykonuję się metodą merge, podając przy tym sposób scalenia (left, right, outer, inner) oraz kolumny po których ma ono nastąpić.

countries_cities = cities.merge(abreviations, how='left', left_on='Country', right_on='Code')

Następnie w nowym DataFrame zostawimy tylko kolumny, które będą nam potrzebne do dalszej pracy.

countries_cities = countries_cities[['AccentCity','Name','Latitude','Longitude']]

W celu ujednolicenia zapisów w zbiorach, zapisaliśmy kolumnę “city” w zbiorze [1] matches oraz kolumnę “AccentCity” w nowym zbiorze countries_cities małymi literami.

matches['city'] = matches['city'].str.lower()
countries_cities['AccentCity'] = countries_cities['AccentCity'].str.lower()

Po weryfikacji okazało się, że niektóre państwa i miasta w zbiorach zostały zapisane w różny sposób. Na przykład w zbiorze [1] zamiast państwa “Wielka Brytania” użyto nazw “Scotland”, “England”, “Northern Ireland”, “Ireland”, “Wales”, więc za pomocą metody replace, zmodyfikowaliśmy wszystkie różnice.

matches['country'] = matches['country'].str.replace('Scotland','United Kingdom')
matches['country'] = matches['country'].str.replace('England','United Kingdom')
matches['country'] = matches['country'].str.replace('Northern Ireland','United Kingdom')
matches['country'] = matches['country'].str.replace('Ireland','United Kingdom')
matches['country'] = matches['country'].str.replace('Wales','United Kingdom')
matches['country'] = matches['country'].str.replace('Republic of United Kingdom','United Kingdom')
matches['country'] = matches['country'].str.replace('Republic of Ireland','Ireland')
matches['country'] = matches['country'].str.replace('Russia','Russian Federation')
matches['country'] = matches['country'].str.replace('Bohemia','Czech Republic')
matches['country'].loc[matches['city'] == 'dublin'] = matches['country'].loc[matches['city'] == 'dublin'].str.replace('United Kingdom','Ireland')

Po tych operacjach mogliśmy przeprowadzić ostatnie scalenie, czyli połączenie zbioru z wynikami meczy [1] matches z nowym zbiorem countries_cities, zawierającym wszystkie państwa, miasta i ich współrzędne geograficzne.

result= matches.merge(countries_cities, how='left', left_on=['city','country'], right_on=['AccentCity','Name'])
result = result.rename(columns={'Latitude':'latitude', 'Longitude': 'longitude'}) #zapisanie nazw kolumn małymi literami

Po weryfikacji dataframe result okazało się, że istnieją jeszcze wiersze z pustymi wartościami w kolumnie “AccentCity”, spowodowane różnym zapisem nazw miast. Jednak postanowiliśmy, że już dalej nie będziemy uwspólniać zapisów w zbiorach i usunęliśmy puste wartości za pomocą metody notna .

result = result[result['AccentCity'].notna()]

W kolejnym kroku ograniczyliśmy analizowany zbiór meczy do tych, które zostały rozegrane w latach 2000-2020 oraz zresetowaliśmy indeks dataframe.

result = result[(result['date'] > '2000-01-01') & (result['date'] < '2020-11-01')]
result = result.reset_index(drop=True) # reset indeksu dataframe

Wizualizacja danych na mapie

Po wstępnej obróbce zbiór danych był już gotowy do wizualizacji na mapie. Na początku przedstawiliśmy cały zbiór danych za pomocą znaczników.

worldMap = folium.Map(location=[result.loc[0,:].latitude,result.loc[0,:].longitude],zoom_start=5)

for x, row in result.iterrows():
    Marker([row['latitude'], row['longitude']]).add_to(worldMap)

W efekcie otrzymaliśmy mapę na której każdy znacznik reprezentuję pojedynczy mecz.

Jeżeli w Waszym edytorze kodu mapa się nie wyświetla to spróbujcie zapisać ją do pliku html.

worldMap.save("worldMap.html")

Mecze reprezentacji Polski w latach 2000-2020

Do dalszej analizy zredukowaliśmy zbiór tylko do meczów reprezentacji Polski oraz zresetowaliśmy indeks dataframe.

polish_matches = result[(result['home_team'] == 'Poland') | (result['away_team'] == 'Poland')]
polish_matches = polish_matches.reset_index(drop=True) #reset indeksu dataframe

Na pierwszej mapie wyświetliliśmy dane w sposób analogiczny do poprzedniego.

polishMatchesMap = folium.Map(location=[polish_matches.loc[0,:].latitude,polish_matches.loc[0,:].longitude],zoom_start=5)
for x, row in polish_matches.iterrows():
    Marker([row['latitude'], row['longitude']]).add_to(polishMatchesMap)

Duża liczba znaczników powoduję, że mapa staje się nieczytelna. Rozwiązaniem tego problemu mogą być klastry, łączące pobliskie znaczniki w grupy i wyświetlające ich liczbę.

polishMatchesClusterMap = folium.Map(location=[polish_matches.loc[0,:].latitude,polish_matches.loc[0,:].longitude],zoom_start=5)
markers = MarkerCluster()
for x, row in polish_matches.iterrows():
        markers.add_child(Marker([row['latitude'], row['longitude']]))        
polishMatchesClusterMap.add_child(markers)

Na koniec, jako ciekawostkę przedstawimy mapę z zaznaczonymi miejscami, gdzie reprezentacja Polski wygrała mecze w latach 2000-2020. Na początku wyświetliliśmy obecną postać dataframe polish_matches.

Wyniki meczów przechowywane są w kolumnach home_score oraz away_score, a więc zbiór trzeba przefiltrować:

1. Gdy Polska była drużyną gospodarzy i wynik gospodarzy był większy niż wynik gości (61 wierszy)

2. Gdy Polska była drużyną gości i wynik gości był większy niż gospodarzy. (54 wiersze)

Obydwa warunki w jednym wierszu można zapisać w sposób następujący.

polishWonmatches = polish_matches[((polish_matches['away_team'] == 'Poland') & (polish_matches['away_score'] > polish_matches['home_score'])) | (polish_matches['home_team'] == 'Poland') & (polish_matches['home_score'] > polish_matches['away_score'])]
 polishWonmatches = polishWonmatches.reset_index(drop=True) # reset indeksu dataframe

W efekcie otrzymaliśmy zbiór zawierający 115 wierszy. Na podstawie tej analizy dowiedzieliśmy się, że Polska w latach 2000-2020 rozegrała 278 meczów z czego 115 wygrała. Na koniec przedstawiliśmy miejsca wygranych meczów na mapie.

polishWonMatchesMapClusters = folium.Map(location=[polishWonmatches.loc[0,:].latitude,polishWonmatches.loc[0,:].longitude],zoom_start=5)
markers = MarkerCluster()
for x, row in polishWonmatches.iterrows():
        markers.add_child(Marker([row['latitude'], row['longitude']]))
polishWonMatchesMapClusters.add_child(markers)

Im większe zbliżenie tym widocznych jest więcej znaczników, a klastry zmniejszą swoją liczebność.

 

Komentarze z Facebooka

Mogą Ci się również spodobać...

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *