Budeme pracovat se dvěma datovými soubory:
Nejprve naimportujeme knihovnu pandas
a zkusíme si s ní trochu hrát.
import pandas
Načtení souboru ve formátu CSV uděláme pomocí funkce read_csv
. První argument udává jméno souboru. Další argumenty jsou:
index_col
: Název sloupce, pomocí kterého chceme data indexovat. Pokud nic nezadáme, Pandas vytvoří nový sloupec s čísly.parse_dates
: Tímto dané sloupce načteme jako data místo řetězců. Pozorný čtenář se všimne, že ve výpisu je sloupců s daty víc, ale my budeme potřebovat pouze tento jeden a na ostatních nám až tak moc nezáleží.dayfirst
: Pandas zkouší uhodnout, v jakém formátu jsou data zadaná. Den a měsíc je ale problematické rozlišit, takže tímto řekneme, že nejdříve je v datu den, až potom měsíc.data = pandas.read_csv("2016-zavady_vo-1.csv", index_col="Číslo závady",
parse_dates=["Datum nahlášení závady"],
dayfirst=True)
data
Můžeme si nechat data popsat:
data.describe()
Nebo si taky můžeme nechat vykreslit graf. Osa x je daná indexem, na ose y je vybraný sloupec. Datum je v našich datech jediný sloupec, kde dává trochu smysl vykreslovat graf.
data["Datum nahlášení závady"].plot()
Můžeme si taky vybrat jenom určitou podmnožinu dat. Třeba prvních pět řádků.
data[:5]
Nebo jenom jeden sloupec z prvních pěti řádků:
data["Katastr"][:5]
Můžeme vybrat několik sloupců:
data[["Ulice", "Katastr"]]
Základní analýza pro sloupce s řetězci může mýt spočítání četností: kolikrát se která hodnota ve sloupci objevuje. Zjistíme 10 ulic s nejvíce poruchami:
data["Ulice"].value_counts()[:10]
Tato data můžeme zase vykreslit do grafu. Výchozí čárový graf tady není vhodný, proto radši použijeme sloupcový graf (v horizontální variantě).
data["Ulice"].value_counts()[:10].plot(kind="barh", figsize=(15,8))
Podmnožinu dat můžeme vybrat stejným způsobem jako v numpy pomocí pravdivostního výrazu. Nejpreve najdeme všechny řádky, které mají daný typ poruchy, a potom si vybereme pouze tyto řádky.
blikajici = data[ data["Typ závady"] == "S svítidlo-bliká" ]
blikajici
Na této podmnožině můžeme zase spočítat četnosti:
pocty_blikajicich = blikajici["Katastr"].value_counts()
pocty_blikajicich
Co třeba zkusit zjistit, ve kterém katastru je nejvíce hlášených blikajících světel v poměru k ostatním poruchám?
celkove_pocty = data["Katastr"].value_counts()
pomery = pocty_blikajicich / celkove_pocty
pomery = pomery.dropna().sort_values(ascending=False)
pomery
Tato data opět můžeme dostat do grafu:
pomery.plot(kind="bar", figsize=(15,5))
Můžeme zkusit zjistit, jestli je nějaký den, kdy je hlášeno více poruch než v jiné dny. Pro jednoduchost se omezíme na jeden katastr.
Začneme výběrem podmnožiny dat.
slatina = data[data["Katastr"] == "Slatina"]
slatina = slatina[["Katastr", "Ulice", "Datum nahlášení závady"]]
slatina
Na začátku jsme si nastavili index na číslo poruchy. Pro tuto analýzu ale bude praktičtější indexovat pomocí data nahlášení poruchy. Index můžeme změnít, takže to můžeme napravit bez nového načítání dat. Nejprve se ale podívejme, jak vypadá index teď.
slatina.index
Nastavíme nový index a podíváme se na data:
slatina_podle_data = slatina.set_index("Datum nahlášení závady")
slatina_podle_data[:5]
I na index samotný.
slatina_podle_data.index
Pokud je indexem datum, můžeme s ním pracovat a podívat se třeba jenom na den v měsíci:
slatina_podle_data.index.day
Nebo na den v týdnu. Překvapivě 0 znamená pondělí:
slatina_podle_data.index.weekday
Takto získaný den v týdnu si můžeme přidat do datového rámce:
slatina_podle_data.loc[:, "den v týdnu"] = slatina_podle_data.index.weekday
slatina_podle_data[:5]
Pomocí metody groupby
můžeme data shlukovat podle hodnot určitého sloupce. Tato metoda vrací objekt reprezentující několik shluků. Nás primárně zajímá, kolik je v každé skupině položek (tedy kolik poruch bylo hlášeno v ten který den).
Další možnosti agregování skupin jsou třeba sum
nebo mean
. Většina ale dává smysl hlavně pro číselné sloupce.
pocty_podle_dne = slatina_podle_data.groupby("den v týdnu").aggregate("size")
pocty_podle_dne
Pro snažší čtení by bylo lepší zobrazovat jména dní místo čísel. Můžeme si nastavit nový index čistě pomocí seznamu hodnot:
pocty_podle_dne.index = ["Po", "Út", "St", "Čt", "Pá", "So", "Ne"]
pocty_podle_dne
A nakonec vykreslíme graf:
pocty_podle_dne.plot(kind="bar")
Pro vykreslování map existuje mnoho různých knihoven. Tady si ukážeme geopandas
, která má poměrně hezké rozhraní. Její velkou nevýhodou je komplikovaná instalace na Windows.
Nejprve si knohovnu naimportujeme, potom načteme soubor s daty a vybereme pouze Brno.
Dostaneme datový rámec jako v Pandas. Několik posledních sloupců ale obsahuje zajímavé hodnoty. Kromě souřadnic tam najdeme geometry
: definici tvaru nějaké oblasti v mapě.
import geopandas
mapa = geopandas.read_file("Městské_obvody_a_městské_části__polygony.shp", encoding="utf-8")
brno = mapa[mapa["NAZ_OBEC"] == "Brno"].copy()
brno.head()
Zkusíme si nakreslit mapu všech brněnských částí:
from matplotlib import pyplot
brno.plot(figsize=(10,10))
To by asi mohlo být Brno. Kdybychom ale měli popisky pro jednotlivé oblasti, možná by to bylo přehlednější. Můžeme si je přidat. Nejprve ale potřebujeme vědět, kam popisek nakreslit. Můžeme si pro každou oblast spočítat vhodný bod, a přidat ho do dat.
brno["coords"] = brno["geometry"].apply(lambda x: x.representative_point().coords[:])
brno["coords"] = [coords[0] for coords in brno["coords"]]
Teď můžeme vykreslit mapu jako předtím, a potom pro každou oblast přidat anotaci:
brno.plot(figsize=(10,10))
for idx, radek in brno.iterrows():
pyplot.annotate(s=radek["NAZ_MOaMC"], xy=radek["coords"], horizontalalignment="center")
Jako poslední příklad si do mapy zkusíme znázornit, kolik v dané oblasti je hlášeno poruch veřejného osvětlení. Na to budeme muset spočítat, kolik ji vlastně je, a přidat data do rámce s mapovými informace.
Spojování datových rámců je možné jako po řádcích, tak po sloupcích. Pokud chceme spojovat po sloupcích, potřebujeme index, podle kterého se dají poznat stejné řádky. Nastavíme si proto index na název městské části.
brno = brno.set_index("NAZ_MOaMC", drop=False)
brno.head()
Počet poruch v různých katastrech už máme spočítaný. Potřebujeme ale data trochu zpracovat, aby si odpovídaly názvy částí. Bohužel ne každý katastr patří do jediné městské části, takže tady dojde k určitým nepřesnostem.
poruchy = dict(celkove_pocty)
poruchy["Řečkovice a Mokrá Hora"] = poruchy.pop("Řečkovice") + poruchy.pop("Mokrá Hora")
poruchy["Maloměřice a Obřany"] = poruchy.pop("Maloměřice") + poruchy.pop("Obřany")
poruchy["Ivanovice"] = poruchy.pop("Brněnské Ivanovice")
poruchy["střed"] = poruchy.pop("Město Brno") + poruchy.pop("Staré Brno") + poruchy.pop("Štýřice") + poruchy.pop("Veveří") + poruchy.pop("Pisárky") + poruchy.pop("Stránice")
poruchy["Útěchov"] = poruchy.pop("Útěchov u Brna")
poruchy["sever"] = poruchy.pop("Soběšice") + poruchy.pop("Lesná") + poruchy.pop("Husovice") + poruchy.pop("Černá Pole") + poruchy.pop("Zábrdovice")
poruchy["jih"] = poruchy.pop("Dolní Heršpice") + poruchy.pop("Horní Heršpice") + poruchy.pop("Komárov") + poruchy.pop("Přízřenice") + poruchy.pop("Trnitá")
poruchy["Královo Pole"] += poruchy.pop("Ponava") + poruchy.pop("Sadová")
poruchy["Tuřany"] += poruchy.pop("Dvorska") + poruchy.pop("Holásky")
poruchy = [(f"Brno-{k}", v) for k, v in poruchy.items()]
poruchy_df = pandas.DataFrame.from_records(poruchy, columns=["NAZ", "pocet"], index="NAZ")
poruchy_df
Tento nový rámec teď můžeme spojit s mapovými podklady. Výchozí spojování je po řádcích, proto musíme určit osu.
brno_s_poruchami = pandas.concat([brno, poruchy_df], sort=False, axis=1)
brno_s_poruchami
Nakonec můžeme vykreslit graf, ve kterém budeme definovat barvy pomocí hodnot ve sloupci pocet
. Taky si přidáme legendu.
from matplotlib import pyplot
brno_s_poruchami.plot(figsize=(10,10), column="pocet", legend=True)
for idx, radek in brno_s_poruchami.iterrows():
pyplot.annotate(s=radek["NAZ_MOaMC"], xy=radek["coords"], horizontalalignment="center")