<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Kapr v medu
 moto
Step1:  Pokud vzpomínáte, tak ke sloupci T jsme přistupovali takto
Step2:  Proč jsme nemohli jednoduše vykonat následující?
Step3:  Alias v hodnota
Step4:  Alias je (zhruba řečeno) pojmenované místo v paměti počítače. Tedy pomocí T označujeme při  běhu programu  místo v paměti, kam jsme si již předtím  něco  uložili (v tomto případě "písmeno" T). Ne vše může být aliasem (např. žádná proměnná nemůže začínat číslem)
Step5:  Naopak následující příkaz
Step6:  právě vytvořil  nové "místo"  v paměti počítače, kam  uložil  hodnotu (v binární podobě), která reprezentuje přirozené číslo 300. Že jsme si místo (jeho adresu) nezapamatovali, je náš problém. Nyní už neexistuje žádný způsob, jak zjistit, kde vlastně je ono místo a tudíž jej nemůžeme použít dále při běhu programu. Jupyter nám dokonce dává jasně najevo, že jsme si ono místo neuložili do proměnné (viz Out[6]
Step7:  Jinými slovy
Step8:  Na identitu (tedy že daný alias ukazuje na stejné místo v paměti) se ptáme slůvkem is
Step9:  V obou místech je však uložená stejná hodnota (číslo 300), tedy
Step10:  String
Řetězec (string) v Pythonu reprezentuje text. Python nerozlišuje mezi znakem (character) a sekvencí znaků (string). Znak je v Pythonu reprezentován jako string o velikosti 1
Step11:  Nyní můžeme udělat to, co nám v Úvodu nefungovalo
Step13:  V Pythonu není rozdíl mezi jednoduchou uvozovkou ' a dvojitou ", následující definice jsou si ekvivalentní
Step14:  Tři uvozovky (ať už jednoduché či dvojité) se můžou rozprostírat na víc řádků.
Step15:  Stringy lze opět porovnávat, v tomto případě použijeme operátor nerovnosti
Step16:  Naopak následující může být trochu překvapivé (Python pochopitelně porovnává jednotlivé znaky, sémantice nerozumí)
Step17:  Jako dobrá představa stringu je seznam (list) znaků. Vskutku, se stringem lze zacházet obdobně jako se seznamem
Step18:  Dva či více stringů lze spojit pomocí operátoru +
Step19:  Podobnost s listem však pokulhává v jedné zásadní věci
Step20:  Mutanti
Souhrnným označením mutanti se označují datové typy, které se mohou měnit (mutable data type). Jeden takový už známe. Vzpomenete si který?
Step21:  Jak už víte, prvky seznamu (narozdíl od stringu) můžeme měnit
Step22:  Dokážete však vysvětlit následující?
Step23:  Jak je možné, že se změnil list l1, když jsme modifikovali l2? Pro jistotu
Step24:  Odpověď je poměrně jednoduchá - l1 i l2 ukazují na stejné místo v paměti (jedná se o stejné "objekty")
Step25:  Pokud bychom chtěli vytvořit opravdovou kopii, tak aby se nám změny neprojevily na l1
Step26:  Pro jistotu se ještě přesvědčíme, že se opravdu nejedná o stejné objekty
Step27:  Nemutanti
Jak jsme viděli před chvílí, stringy nelze modifikovat. Pokud bychom chtěli změnit string 'Panda' na 'Fanda', nezbyde nám nic jiného než vytvořit string nový
Step28:  Obdobně se chová i datový typ integer (celé číslo). Porovnejte následující s předchozími hrátky s listy l1 a l2
Step29:  Toto je velice důležité chování Pythonu. Pokud si do nějaké proměnné uložíte nějaké číslo, pak se nikdy nemůže změnit jinak, než že ho sami explicitně změníte!
Mutanti vs nemutanti
Nemutanti
Step30:  Proměnná f nyní představuje tzv. file handle. Neobsahuje žádná data (ta zatím stále leží na disku), je to jenom prostředek, skrze který můžeme komunikovat s operačním systémem. Samotný transfer dat můžeme zahájit zavoláním funkce read
Step31:  Nyní máme data v operační paměti a uložili jsme si je do proměnné data. Nyní je záhodno sdělit operačnímu systému, že se souborem již nebudeme dále pracovat a soubor takzvaně uzavřít
Step32:  Podívejme se blíže na proměnnou data
Step33:  Vidíme, že tentokrát máme k dispozici něco úplně jiného, než co nám vrátila panda v první lekci zavoláním funkce read_csv. Tentokrát se jedná o string, ony záhadné \n označují znak nového řádku, Python jim rozumí a pokud použijeme funkci print, pak se vše zobrazí správně (viz výše).
V této podobě jsou nicméně data velice špatně použitelná. Nezbývá nám nic jiného, než si data sami upravit. Existuje mnoho způsobů, jak to udělat. Ten který si teď ukážeme není sice nejelegantnější, je však velice názorný.
Nejprve data rozdělíme na jednotlivé řádky
Step34:  Následně každý řádek rozdělíme podle čárky na jednotlivé hodnoty
Step35:  Naším cílem je vytvořit starý známý DataFrame. Po pozorném přečtení dokumentace vidíme, že nejprve potrebujeme vytvořit numpy vektor (v tomto případě 2D vektor - správně bychom mělí říci tenzor či matice)
Step36:  2D numpy vektory, jsou velice podobné 1D vektorům, k prvnímu řádku této matice lze přistoupit takto
Step37:  K prvnímu elementu prvního řádku pak takto
Step38:  Nyní máme konečně vše nachystané k vytvoření DataFrame
Step39:  Asi jsme něco udělali špatně. Co se to vlastně stalo?
Datové konverze
Projděme si celý proces načítání dat ještě jednou. Nadefinujme si k tomu dvě užitečné funkce
Step40:  Problém spočívá v tom, že data jsou stringy, ne floaty. Musíme je ručně převést. K tomu slouží funkce float
Step41:  Pokud bychom měli následující vnořenou strukturu
Step42:  a chtěli vytvořit novou identickou strukturu jen převést všechny stringy na floaty, pak to můžeme udělat např. následovně
Step43:  Poznamenejme jen, že se vlastně jedná o dva vnořené for cykly. V prvním procházíme prvky listu strings - cykly tedy budou dva
Step44:  Poznámka
Step45:  Elegance Pythonu
Vzhledem k tomu, že práce se souborem je velice častá, Python nabízí několik elegantních metod, jak si ulehčit život. Prvně si představíme tzv. "with block", který nás zbaví nutnosti soubor ručně zavírat a pak si ukážeme, jak efektivně iterovat přes jednotlivé řádky souboru.
Step46:  Uvedený způsob skýtá dvě velké výhody.
With block se automaticky postará o zavření souboru. I kdybychom udělali nějakou chybu v kódu ve with blocku, Python stejně automaticky zavře soubor, takže po nás nezůstane nic otevřeného (existuje samozřejmě způsob, jak to ošetřit i ručně, ale tím se zde zabývat nebudeme)
Důležitý rozdíl při iteraci přes soubor oproti použití funkce read je v tom, že jsme NENAČETLI celý obsah souboru naráz. Operační systém nám v každé iteraci vrátil pouze jeden řádek. My jsme si sice všechna data uložili do proměnné data, protože jsme na konec chtěli vytvořit DataFrame. Pokud bychom však chtěli např. pouze sečíst všechny hodnoty v prvním sloupci, mohli bychom to udělat například následovně
<ASSISTANT_TASK:>
Python Code:
import pandas as pd
data = pd.read_csv('data.csv')
data
Explanation: Kapr v medu
 moto: 
Spadne kapr do medu a říká:
"Hustý, to je hustý..."
Z.Janák, písemka z TM
Osnova
Úvod
Alias vs hodnota
String
Mutanti a nemutanti
Práce se souborem
Elegance pythonu
Závěrečné cvičení
Úvod
V této lekci se ponoříme (zabředneme) do úplných základů Pythonu. Daná tématika se může zdát možná až příliš abstraktní a v praxi nepoužitelná, nicméně opak je pravdou! Uvidíme, že díky solidním základům bude například načítání dat ze souboru jednoduchou a přímočarou záležitostí.
Na začátku zavzpomínáme na (dnes již klasický) Fišerův problém. Data jsme tehdy měli uložená v csv souboru data.csv. Vše jsme načetli jednoduše pomocí pandy:
End of explanation
data['T']
Explanation: Pokud vzpomínáte, tak ke sloupci T jsme přistupovali takto:
End of explanation
data[T]
Explanation: Proč jsme nemohli jednoduše vykonat následující?
End of explanation
     T = 'T'
#alias v hodnota
Explanation: Alias v hodnota
End of explanation
3 = 'T'
Explanation: Alias je (zhruba řečeno) pojmenované místo v paměti počítače. Tedy pomocí T označujeme při  běhu programu  místo v paměti, kam jsme si již předtím  něco  uložili (v tomto případě "písmeno" T). Ne vše může být aliasem (např. žádná proměnná nemůže začínat číslem):
End of explanation
300
Explanation: Naopak následující příkaz:
End of explanation
x = 1       # zde vznikne místo v paměti, do které se uloží číslo 1 a proměnná x ukazuje na to místo
x = x + 1   # zde se vezme obsah proměnné x (číslo 1) a provede se operace součtu,
            # výsledek se uloží se do x
x           # výsledek je pochopitelně 2
Explanation: právě vytvořil  nové "místo"  v paměti počítače, kam  uložil  hodnotu (v binární podobě), která reprezentuje přirozené číslo 300. Že jsme si místo (jeho adresu) nezapamatovali, je náš problém. Nyní už neexistuje žádný způsob, jak zjistit, kde vlastně je ono místo a tudíž jej nemůžeme použít dále při běhu programu. Jupyter nám dokonce dává jasně najevo, že jsme si ono místo neuložili do proměnné (viz Out[6]: 300).
Tím se dostáváme k tomu, co vlastně znamená symbol = ve zdrojovém kódu. Jedná se o tzv. operátor přiřazení. Aliasu přiřazuje místo v paměti, na které "ukazuje". Rozeberme tedy podrobně, co znamená následující:
End of explanation
300 == 300
300 == 301
Explanation: Jinými slovy: to co je na pravé straně operátoru = se vyhodnotí (získají se hodnoty uložené v paměti), aplikují se všechny operace a adresu místa s výsledkem si uložíme do aliasu na levé straně.
Klasický, z matematiky známý, operátor rovnosti se v Pythonu označuje == (porovnává hodnoty uložené na daných místech v paměti)
End of explanation
a = 300
b = 300
a is b          # a ukazuje na jiné místo v paměti než b
Explanation: Na identitu (tedy že daný alias ukazuje na stejné místo v paměti) se ptáme slůvkem is:
End of explanation
a == b
Explanation: V obou místech je však uložená stejná hodnota (číslo 300), tedy:
End of explanation
T = 'T'
len(T)    #Příkaz len vrací délku (v tomto případě délku řetězce)
Explanation: String
Řetězec (string) v Pythonu reprezentuje text. Python nerozlišuje mezi znakem (character) a sekvencí znaků (string). Znak je v Pythonu reprezentován jako string o velikosti 1:
End of explanation
data[T]   # T se vyhodnotí na písmeno "T"
Explanation: Nyní můžeme udělat to, co nám v Úvodu nefungovalo:
End of explanation
T1 = 'T'
T2 = "T"
T3 = T
T4 = '''T'''
T1 == T2 == T3 == T4
Explanation: V Pythonu není rozdíl mezi jednoduchou uvozovkou ' a dvojitou ", následující definice jsou si ekvivalentní
End of explanation
print('''
Hroch a Panda
jsou dobří přátelé
''')
Explanation: Tři uvozovky (ať už jednoduché či dvojité) se můžou rozprostírat na víc řádků.
End of explanation
'Hroch' != 'Zikán'
Explanation: Stringy lze opět porovnávat, v tomto případě použijeme operátor nerovnosti:
End of explanation
'hroch' == 'zvíře'
Explanation: Naopak následující může být trochu překvapivé (Python pochopitelně porovnává jednotlivé znaky, sémantice nerozumí):
End of explanation
s = 'Panda'
print(s[0])
print(s[1:5])
Explanation: Jako dobrá představa stringu je seznam (list) znaků. Vskutku, se stringem lze zacházet obdobně jako se seznamem:
End of explanation
'Hroch' + ' a ' + 'Panda'
Explanation: Dva či více stringů lze spojit pomocí operátoru +:
End of explanation
s[0] = 'F'
Explanation: Podobnost s listem však pokulhává v jedné zásadní věci:
End of explanation
l1 = ['a', 'a', 'n', 'd']
l1.append('a')
Explanation: Mutanti
Souhrnným označením mutanti se označují datové typy, které se mohou měnit (mutable data type). Jeden takový už známe. Vzpomenete si který?
End of explanation
l1[0] = 'P'
l1
Explanation: Jak už víte, prvky seznamu (narozdíl od stringu) můžeme měnit:
End of explanation
l2 = l1
l2.append('!')
l1
Explanation: Dokážete však vysvětlit následující?
End of explanation
l2     # l2 se změnil také
Explanation: Jak je možné, že se změnil list l1, když jsme modifikovali l2? Pro jistotu:
End of explanation
l1 is l2
Explanation: Odpověď je poměrně jednoduchá - l1 i l2 ukazují na stejné místo v paměti (jedná se o stejné "objekty"):
End of explanation
l3 = l1.copy()
l3
l3.append('!')
l1
l3
Explanation: Pokud bychom chtěli vytvořit opravdovou kopii, tak aby se nám změny neprojevily na l1:
End of explanation
l3 is l1
Explanation: Pro jistotu se ještě přesvědčíme, že se opravdu nejedná o stejné objekty:
End of explanation
s = 'Panda'
s[1:]
'F' + s[1:]
Explanation: Nemutanti
Jak jsme viděli před chvílí, stringy nelze modifikovat. Pokud bychom chtěli změnit string 'Panda' na 'Fanda', nezbyde nám nic jiného než vytvořit string nový:
End of explanation
a = 1
b = a
a = 2
print(a)
print(b)
Explanation: Obdobně se chová i datový typ integer (celé číslo). Porovnejte následující s předchozími hrátky s listy l1 a l2:
End of explanation
f = open('data.csv')
f
Explanation: Toto je velice důležité chování Pythonu. Pokud si do nějaké proměnné uložíte nějaké číslo, pak se nikdy nemůže změnit jinak, než že ho sami explicitně změníte!
Mutanti vs nemutanti
Nemutanti:
int (-5, 2, 150333)
float (-5.1, -2., 1.50333e5)
string ('Hroch')
...
Mutanti:
* list ([1, 2])
* DataFrame
* vektor
* ...
* ...
Práce se souborem
Soubor označuje a sdružuje data uložená na disku. Abychom se k datům v souboru dostali a mohli s nimi pracovat, musíme požádat operační systém. Nebojte se, v Pythonu to není nic složitého. Prvním nezbytným krokem je soubor otevřít:
End of explanation
data = f.read()
print(data)
Explanation: Proměnná f nyní představuje tzv. file handle. Neobsahuje žádná data (ta zatím stále leží na disku), je to jenom prostředek, skrze který můžeme komunikovat s operačním systémem. Samotný transfer dat můžeme zahájit zavoláním funkce read:
End of explanation
f.close()
Explanation: Nyní máme data v operační paměti a uložili jsme si je do proměnné data. Nyní je záhodno sdělit operačnímu systému, že se souborem již nebudeme dále pracovat a soubor takzvaně uzavřít:
End of explanation
print(type(data))
data
Explanation: Podívejme se blíže na proměnnou data:
End of explanation
data = data.split('\n')
data
Explanation: Vidíme, že tentokrát máme k dispozici něco úplně jiného, než co nám vrátila panda v první lekci zavoláním funkce read_csv. Tentokrát se jedná o string, ony záhadné \n označují znak nového řádku, Python jim rozumí a pokud použijeme funkci print, pak se vše zobrazí správně (viz výše).
V této podobě jsou nicméně data velice špatně použitelná. Nezbývá nám nic jiného, než si data sami upravit. Existuje mnoho způsobů, jak to udělat. Ten který si teď ukážeme není sice nejelegantnější, je však velice názorný.
Nejprve data rozdělíme na jednotlivé řádky:
End of explanation
data = [line.split(',') for line in data if line != '']
data
Explanation: Následně každý řádek rozdělíme podle čárky na jednotlivé hodnoty:
End of explanation
import numpy as np
arr = np.array(data[1:])
arr
Explanation: Naším cílem je vytvořit starý známý DataFrame. Po pozorném přečtení dokumentace vidíme, že nejprve potrebujeme vytvořit numpy vektor (v tomto případě 2D vektor - správně bychom mělí říci tenzor či matice):
End of explanation
arr[0]
Explanation: 2D numpy vektory, jsou velice podobné 1D vektorům, k prvnímu řádku této matice lze přistoupit takto:
End of explanation
arr[0][0]
Explanation: K prvnímu elementu prvního řádku pak takto:
End of explanation
import pandas as pd
data = pd.DataFrame(arr, columns=data[0])
data
data['C'] = data['A'] + data['B']
data
Explanation: Nyní máme konečně vše nachystané k vytvoření DataFrame:
End of explanation
def load_data(file_name):
    f = open(file_name)
    data = f.read()
    f.close()
    return data
def make_dataframe(data):
    data = data.split('\n')
    data = [line.split(',') for line in data if line != '']
    arr = np.array(data[1:])
    return pd.DataFrame(arr, columns=data[0])
data = make_dataframe(load_data('data.csv'))
data
x = data['A'][0]
print(x)
print(type(x))
Explanation: Asi jsme něco udělali špatně. Co se to vlastně stalo?
Datové konverze
Projděme si celý proces načítání dat ještě jednou. Nadefinujme si k tomu dvě užitečné funkce:
End of explanation
print(float(x))
print(type(float(x)))
Explanation: Problém spočívá v tom, že data jsou stringy, ne floaty. Musíme je ručně převést. K tomu slouží funkce float:
End of explanation
strings = [['1', '2', '4'], ['5', '9', '9']]
strings
Explanation: Pokud bychom měli následující vnořenou strukturu
End of explanation
floats = []
for rec in strings:
    floats.append([float(num) for num in rec])
floats
Explanation: a chtěli vytvořit novou identickou strukturu jen převést všechny stringy na floaty, pak to můžeme udělat např. následovně:
End of explanation
def make_dataframe(data):
    data = data.split('\n')
    data = [line.split(',') for line in data if line != '']
    
    floats = []
    for rec in data[1:]:
        floats.append([float(num) for num in rec])
        
    arr = np.array(floats)
    return pd.DataFrame(arr, columns=data[0])
data = make_dataframe(load_data('data.csv'))
data
print(type(data['A'][0]))
data['C'] = data['A'] + data['B']
data
Explanation: Poznamenejme jen, že se vlastně jedná o dva vnořené for cykly. V prvním procházíme prvky listu strings - cykly tedy budou dva: první s ['1', '2', '4'] a druhý s ['5', '9', '9']. Vnořený for cylkus bude nejprve pro hodnoty 1, 2 a 4 a následně pro 5, 9 a 9. Výsledekem každého vnitřního for cyklu bude vždy nový list: [1.0, 2.0, 4.0] a [5.0, 9.0, 9.0]. Tyto listy vždy přilepíme na konec listu float, který si na začátku inicializujeme na prázdný list.
Naši funkci make_dataframe tedy můžeme upravit například následujícím způsobem:
End of explanation
def make_dataframe(data):
    data = data.split('\n')
    data = [line.split(',') for line in data if line != '']
    arr = np.array(data[1:])
    return pd.DataFrame(arr, columns=data[0], dtype='f')
data = make_dataframe(load_data('data.csv'))
data
print(type(data['A'][0]))
Explanation: Poznámka: pokud bychom četli dokumentaci opravdu pozorně zjistili bychom, že panda za nás může konverzi provést sama:
End of explanation
with open('data.csv') as f:
    data = []
    for line in f:
        row = line.strip().split(',')
        data.append(row)
data = pd.DataFrame(np.array(data[1:]), columns=data[0], dtype='f')
data
Explanation: Elegance Pythonu
Vzhledem k tomu, že práce se souborem je velice častá, Python nabízí několik elegantních metod, jak si ulehčit život. Prvně si představíme tzv. "with block", který nás zbaví nutnosti soubor ručně zavírat a pak si ukážeme, jak efektivně iterovat přes jednotlivé řádky souboru.
End of explanation
total_sum = 0
with open('data.csv') as f:
    f.readline() # zahodíme hlavičku
    for line in f:
        total_sum += float(line.split(',')[0])
total_sum
Explanation: Uvedený způsob skýtá dvě velké výhody.
With block se automaticky postará o zavření souboru. I kdybychom udělali nějakou chybu v kódu ve with blocku, Python stejně automaticky zavře soubor, takže po nás nezůstane nic otevřeného (existuje samozřejmě způsob, jak to ošetřit i ručně, ale tím se zde zabývat nebudeme)
Důležitý rozdíl při iteraci přes soubor oproti použití funkce read je v tom, že jsme NENAČETLI celý obsah souboru naráz. Operační systém nám v každé iteraci vrátil pouze jeden řádek. My jsme si sice všechna data uložili do proměnné data, protože jsme na konec chtěli vytvořit DataFrame. Pokud bychom však chtěli např. pouze sečíst všechny hodnoty v prvním sloupci, mohli bychom to udělat například následovně:
End of explanation
<END_TASK>