# Pandase sissejuhatus

Selles vihikus ma näitan põgusalt kuidas teha lihtsamat andmeanalüüsi Pythoni vihikus kasutades Pandast ja vähemal määral ka Numpyt.

[Pandas](http://pandas.pydata.org/) on Pythoni andme analüüsi teek mis pakub mitmeid mugandusi tavaliste andmeanalüüsi käigus vajaminevate protseduuride tegemiseks. Pandas ise on üles ehitatud suures osas [Numpy](http://www.numpy.org/)-l mida kasutatakse Pythonis keerulisemate arvutuste tegemiseks (arvutused maatriksitega jne). Numpy-t ma antud vihikus väga palju ei tutvusta.

Et kasutada Pandast tuleb see importida. Importimisel Python laeb mällu `pandas` ja `numpy` teegid et neid saaks kasutada.

```python
import numpy as np    #Numpy import
import pandas as pd   #Pandase import

# jooniste kuvamiseks hiljem tuleb sisestada see rida...
%matplotlib notebook
```

## DataFrame

`DataFrame` on Pandase üks põhilisi andmestruktuure. Põhimõttelised võib seda võtta nagu Exceli tabelit.

DataFrame saab luua Pandase meetodiga `.DataFrame` edastades sellele *dictionary*.

Andmed on paigutatud Pandase `DataFrames`-is tulpadesse. Andmed on indekseeridud rea kaupa. Kui indeks on määramata siis sellek on lihtsalt rea number.

```python
test_dict = {
    'samplename': ['Sample1A' ,'Sample1B' ,'Sample1C',
                   'Sample2A' ,'Sample2B' ,'Sample2C',
                   'Sample3A' ,'Sample3B' ,'Sample3C',
                   'Sample4A' ,'Sample4B' ,'Sample4C',],
    'konstrukt' : ['plasmiid1','plasmiid1','plasmiid1',
                   'plasmiid1','plasmiid1','plasmiid1',
                   'plasmiid2','plasmiid2','plasmiid2',
                   'plasmiid2','plasmiid2','plasmiid2'],
    't88tlus' :   ['dmso','dmso','dmso',
                   'aine','aine','aine',
                   'dmso','dmso','dmso',
                   'aine','aine','aine'],
    'luc2p' : [ 450, 543, 243,
                456, 644, 453,
               1234,1342,1532,
               4500,6453,4800]}
```

```python
df = pd.DataFrame(test_dict)
df
```

      konstrukt
      luc2p
      samplename
      t88tlus

      0
      plasmiid1
      450
      Sample1A
      dmso

      1
      plasmiid1
      543
      Sample1B
      dmso

      2
      plasmiid1
      243
      Sample1C
      dmso

      3
      plasmiid1
      456
      Sample2A
      aine

      4
      plasmiid1
      644
      Sample2B
      aine

      5
      plasmiid1
      453
      Sample2C
      aine

      6
      plasmiid2
      1234
      Sample3A
      dmso

      7
      plasmiid2
      1342
      Sample3B
      dmso

      8
      plasmiid2
      1532
      Sample3C
      dmso

      9
      plasmiid2
      4500
      Sample4A
      aine

      10
      plasmiid2
      6453
      Sample4B
      aine

      11
      plasmiid2
      4800
      Sample4C
      aine

`DataFrame`-i loomisel saab ette ka anda tulpade nimede järjekorra. Muidu tuleb see tähestikulises järjekorras (vist).

```python
df = pd.DataFrame(test_dict,
                  columns = ['samplename','konstrukt','t88tlus','luc2p'])
df
```

      samplename
      konstrukt
      t88tlus
      luc2p

      0
      Sample1A
      plasmiid1
      dmso
      450

      1
      Sample1B
      plasmiid1
      dmso
      543

      2
      Sample1C
      plasmiid1
      dmso
      243

      3
      Sample2A
      plasmiid1
      aine
      456

      4
      Sample2B
      plasmiid1
      aine
      644

      5
      Sample2C
      plasmiid1
      aine
      453

      6
      Sample3A
      plasmiid2
      dmso
      1234

      7
      Sample3B
      plasmiid2
      dmso
      1342

      8
      Sample3C
      plasmiid2
      dmso
      1532

      9
      Sample4A
      plasmiid2
      aine
      4500

      10
      Sample4B
      plasmiid2
      aine
      6453

      11
      Sample4C
      plasmiid2
      aine
      4800

`DataFrame`-il on andmed paigtatud ridadesse `index`-i järgi. Kui seda pole määratud siis on selleks lihtsalt järjekorra number. Samas on hea kui andmed on paigutatud `DataFrame`-i ridadesse meile endale kasuliku tähistuse järgi. Näiteks proovide nimede järgi:

```python
df = pd.DataFrame(test_dict,
                  columns = ['konstrukt','t88tlus','luc2p'],
                  index = test_dict['samplename'])
df
```

      konstrukt
      t88tlus
      luc2p

      Sample1A
      plasmiid1
      dmso
      450

      Sample1B
      plasmiid1
      dmso
      543

      Sample1C
      plasmiid1
      dmso
      243

      Sample2A
      plasmiid1
      aine
      456

      Sample2B
      plasmiid1
      aine
      644

      Sample2C
      plasmiid1
      aine
      453

      Sample3A
      plasmiid2
      dmso
      1234

      Sample3B
      plasmiid2
      dmso
      1342

      Sample3C
      plasmiid2
      dmso
      1532

      Sample4A
      plasmiid2
      aine
      4500

      Sample4B
      plasmiid2
      aine
      6453

      Sample4C
      plasmiid2
      aine
      4800

Juba tehtud DataFrame-ile saab indeksit luua meetodiga `set_index('tulp')`. See tagastab uue `DataFrame`-i millel on meie määratud tulp muudetud index-iks.

```python
df = pd.DataFrame(test_dict,
                  columns = ['samplename','konstrukt','t88tlus','luc2p'])
df
```

      samplename
      konstrukt
      t88tlus
      luc2p

      0
      Sample1A
      plasmiid1
      dmso
      450

      1
      Sample1B
      plasmiid1
      dmso
      543

      2
      Sample1C
      plasmiid1
      dmso
      243

      3
      Sample2A
      plasmiid1
      aine
      456

      4
      Sample2B
      plasmiid1
      aine
      644

      5
      Sample2C
      plasmiid1
      aine
      453

      6
      Sample3A
      plasmiid2
      dmso
      1234

      7
      Sample3B
      plasmiid2
      dmso
      1342

      8
      Sample3C
      plasmiid2
      dmso
      1532

      9
      Sample4A
      plasmiid2
      aine
      4500

      10
      Sample4B
      plasmiid2
      aine
      6453

      11
      Sample4C
      plasmiid2
      aine
      4800

## Andmete laadimine failidest

Andmeid saab laadida `DataFrame`-i ka failidest. Toetatud faili tüüpide nimekiri on pikk ja seda saab vaadata [Pandase dokumentatsioonist](https://pandas.pydata.org/pandas-docs/stable/io.html). Meile ühed tavalisemad ettejuhtuvad failid on Exceli (`.xlsx`) ja *comma separated values* (`.csv`) failid. Mõlemat suudab Pandas ilusti laadida.

Exceli faili laadimiseks tuleb kasutada Pandase `.read_xlsx` meetodit:

```python
df = pd.read_excel("example_data/02-exp1.xlsx")
df
```

      samplename
      konstrukt
      t88tlus
      luc2p

      0
      Sample1A
      plasmiid1
      dmso
      450

      1
      Sample1B
      plasmiid1
      dmso
      543

      2
      Sample1C
      plasmiid1
      dmso
      243

      3
      Sample2A
      plasmiid1
      aine
      456

      4
      Sample2B
      plasmiid1
      aine
      644

      5
      Sample2C
      plasmiid1
      aine
      453

      6
      Sample3A
      plasmiid2
      dmso
      1234

      7
      Sample3B
      plasmiid2
      dmso
      1342

      8
      Sample3C
      plasmiid2
      dmso
      1532

      9
      Sample4A
      plasmiid2
      aine
      4500

      10
      Sample4B
      plasmiid2
      aine
      6453

      11
      Sample4C
      plasmiid2
      aine
      4800

Lihstalt faili nime andes laeb pandas sellest ainult esimese töölehe. Et saada kätte teisi tuleb anda `.read_excel` funktsioonile ka lehe nimi:

```python
df = pd.read_excel("example_data/02-exp1.xlsx", sheetname = "Sheet2")
df
```

      samplename
      konstrukt
      t88tlus
      luc2p

      0
      Sample5A
      plasmiid1
      dmso
      576

      1
      Sample5B
      plasmiid1
      dmso
      565

      2
      Sample5C
      plasmiid1
      dmso
      676

      3
      Sample6A
      plasmiid1
      aine
      908

      4
      Sample6B
      plasmiid1
      aine
      890

      5
      Sample6C
      plasmiid1
      aine
      890

      6
      Sample7A
      plasmiid2
      dmso
      4324

      7
      Sample7B
      plasmiid2
      dmso
      2421

      8
      Sample7C
      plasmiid2
      dmso
      1242

      9
      Sample8A
      plasmiid2
      aine
      6785

      10
      Sample8B
      plasmiid2
      aine
      3567

      11
      Sample8C
      plasmiid2
      aine
      7896

Andmete laadimise saab ära määrata ka millist tulpa võiks Pandas kasutada `index`-ina. Seda saab teha `index_col` argumendiga. Tulpade järjekord algab nullist.

```python
df = pd.read_excel("example_data/02-exp1.xlsx",
                   sheetname="Sheet2",
                   index_col = 0)
df
```

      konstrukt
      t88tlus
      luc2p

      samplename

      Sample5A
      plasmiid1
      dmso
      576

      Sample5B
      plasmiid1
      dmso
      565

      Sample5C
      plasmiid1
      dmso
      676

      Sample6A
      plasmiid1
      aine
      908

      Sample6B
      plasmiid1
      aine
      890

      Sample6C
      plasmiid1
      aine
      890

      Sample7A
      plasmiid2
      dmso
      4324

      Sample7B
      plasmiid2
      dmso
      2421

      Sample7C
      plasmiid2
      dmso
      1242

      Sample8A
      plasmiid2
      aine
      6785

      Sample8B
      plasmiid2
      aine
      3567

      Sample8C
      plasmiid2
      aine
      7896

## Andmete väljavõtmine `DataFrame`-ist

Lõike `DataFrame`-ist saab välja võtta meetoditega `.loc` ja `.iloc`. Esimene on selle jaoks et võtta välja lõike ridade ja tulpade nimede järgi. Teist saab kasutada võttes lõike välja nö kordinaatide järgi. All olevate näidete jaoks laen ma andmed exceli faili esimeselt lehelt:

```python
df = pd.read_excel("example_data/02-exp1.xlsx", index_col=0)
df
```

      konstrukt
      t88tlus
      luc2p

      samplename

      Sample1A
      plasmiid1
      dmso
      450

      Sample1B
      plasmiid1
      dmso
      543

      Sample1C
      plasmiid1
      dmso
      243

      Sample2A
      plasmiid1
      aine
      456

      Sample2B
      plasmiid1
      aine
      644

      Sample2C
      plasmiid1
      aine
      453

      Sample3A
      plasmiid2
      dmso
      1234

      Sample3B
      plasmiid2
      dmso
      1342

      Sample3C
      plasmiid2
      dmso
      1532

      Sample4A
      plasmiid2
      aine
      4500

      Sample4B
      plasmiid2
      aine
      6453

      Sample4C
      plasmiid2
      aine
      4800

```python
df[ ["konstrukt", "luc2p"] ]
```

      konstrukt
      luc2p

      samplename

      Sample1A
      plasmiid1
      450

      Sample1B
      plasmiid1
      543

      Sample1C
      plasmiid1
      243

      Sample2A
      plasmiid1
      456

      Sample2B
      plasmiid1
      644

      Sample2C
      plasmiid1
      453

      Sample3A
      plasmiid2
      1234

      Sample3B
      plasmiid2
      1342

      Sample3C
      plasmiid2
      1532

      Sample4A
      plasmiid2
      4500

      Sample4B
      plasmiid2
      6453

      Sample4C
      plasmiid2
      4800

### `.loc[ [read], [tulbad] ]`

Selleks tuleb edastada sellele Pythoni *list* või *tuple* nimekirjad vastavate ridade või tulpadega mida tahetakse:
```
df.loc[ [... rea indexite nimekiri...] , [tulpade nimede nimekiri] ]
```

```python
df.loc[ ['Sample1A', 'Sample2A'] , ['konstrukt','luc2p'] ]
```

      konstrukt
      luc2p

      samplename

      Sample1A
      plasmiid1
      450

      Sample2A
      plasmiid1
      456

Asendades ühe või teise kooloniga `:` saab kätte kõik nende ridadega tulbad või siis vastavalt
kõik vastavate tulpadega read.

```python
df.loc[ ['Sample1A', 'Sample2A'] , : ]
```

      konstrukt
      t88tlus
      luc2p

      samplename

      Sample1A
      plasmiid1
      dmso
      450

      Sample2A
      plasmiid1
      aine
      456

```python
df.loc[ : , ['konstrukt','luc2p'] ]
```

      konstrukt
      luc2p

      samplename

      Sample1A
      plasmiid1
      450

      Sample1B
      plasmiid1
      543

      Sample1C
      plasmiid1
      243

      Sample2A
      plasmiid1
      456

      Sample2B
      plasmiid1
      644

      Sample2C
      plasmiid1
      453

      Sample3A
      plasmiid2
      1234

      Sample3B
      plasmiid2
      1342

      Sample3C
      plasmiid2
      1532

      Sample4A
      plasmiid2
      4500

      Sample4B
      plasmiid2
      6453

      Sample4C
      plasmiid2
      4800

### `.iloc[ [ ridade numbrid ], [ tulpade numbrid ] ]`

Teine viis tulpades ja ridades olevatele andmetele ligi pääseda on kasutata `.iloc` meetodit.
Selle meetodiga saab ligi andmetele kasutades tulpade ja ridade numbrilisi indekseid.

```python
df.iloc[ [0, 1] , [0, 1] ]
```

      konstrukt
      t88tlus

      samplename

      Sample1A
      plasmiid1
      dmso

      Sample1B
      plasmiid1
      dmso

```python
df.iloc[ [0, 1] , : ]
```

      konstrukt
      t88tlus
      luc2p

      samplename

      Sample1A
      plasmiid1
      dmso
      450

      Sample1B
      plasmiid1
      dmso
      543

```python
df.iloc[ : , [0, 1] ]
```

      konstrukt
      t88tlus

      samplename

      Sample1A
      plasmiid1
      dmso

      Sample1B
      plasmiid1
      dmso

      Sample1C
      plasmiid1
      dmso

      Sample2A
      plasmiid1
      aine

      Sample2B
      plasmiid1
      aine

      Sample2C
      plasmiid1
      aine

      Sample3A
      plasmiid2
      dmso

      Sample3B
      plasmiid2
      dmso

      Sample3C
      plasmiid2
      dmso

      Sample4A
      plasmiid2
      aine

      Sample4B
      plasmiid2
      aine

      Sample4C
      plasmiid2
      aine

Läbi `iloc` ja `loc` meetodi saab ka olemasolevates tulpades andmeid muuta.

```python
df
```

      konstrukt
      t88tlus
      luc2p

      samplename

      Sample1A
      plasmiid1
      dmso
      450

      Sample1B
      plasmiid1
      dmso
      543

      Sample1C
      plasmiid1
      dmso
      243

      Sample2A
      plasmiid1
      aine
      456

      Sample2B
      plasmiid1
      aine
      644

      Sample2C
      plasmiid1
      aine
      453

      Sample3A
      plasmiid2
      dmso
      1234

      Sample3B
      plasmiid2
      dmso
      1342

      Sample3C
      plasmiid2
      dmso
      1532

      Sample4A
      plasmiid2
      aine
      4500

      Sample4B
      plasmiid2
      aine
      6453

      Sample4C
      plasmiid2
      aine
      4800

```python
df_muudetud = df.copy()
df_muudetud.loc[["Sample1A"], : ] = 10000000
df_muudetud
```

      konstrukt
      t88tlus
      luc2p

      samplename

      Sample1A
      10000000
      10000000
      10000000

      Sample1B
      plasmiid1
      dmso
      543

      Sample1C
      plasmiid1
      dmso
      243

      Sample2A
      plasmiid1
      aine
      456

      Sample2B
      plasmiid1
      aine
      644

      Sample2C
      plasmiid1
      aine
      453

      Sample3A
      plasmiid2
      dmso
      1234

      Sample3B
      plasmiid2
      dmso
      1342

      Sample3C
      plasmiid2
      dmso
      1532

      Sample4A
      plasmiid2
      aine
      4500

      Sample4B
      plasmiid2
      aine
      6453

      Sample4C
      plasmiid2
      aine
      4800

## Arvutuste tegemine `DataFrame`-iga

Arvutuste tegemine `DataFrame`-iga käib tulpade kaupa. Lihtsamateks arvutusteks ei pea kasutama tulpade andmetele ligipääsemiseks `.loc` või `.iloc` meetodeid vaid saab lihtsalt kasutada tulba nime index-ina.

Kasutan jällegi näidisena Exceli failist esimese lehe andmeid:

```python
df_algne = pd.read_excel("example_data/02-exp1.xlsx", index_col=0)
df_algne
```

      konstrukt
      t88tlus
      luc2p

      samplename

      Sample1A
      plasmiid1
      dmso
      450

      Sample1B
      plasmiid1
      dmso
      543

      Sample1C
      plasmiid1
      dmso
      243

      Sample2A
      plasmiid1
      aine
      456

      Sample2B
      plasmiid1
      aine
      644

      Sample2C
      plasmiid1
      aine
      453

      Sample3A
      plasmiid2
      dmso
      1234

      Sample3B
      plasmiid2
      dmso
      1342

      Sample3C
      plasmiid2
      dmso
      1532

      Sample4A
      plasmiid2
      aine
      4500

      Sample4B
      plasmiid2
      aine
      6453

      Sample4C
      plasmiid2
      aine
      4800

Nii saab tulpadega teha standard arvutusi: korrutamine, jagamine jne.

```python
# teen siin laetud exceli tabelist koopia lihtsalt eksperimenteerimiseks
df = df_algne.copy()
```

```python
df
```

      konstrukt
      t88tlus
      luc2p

      samplename

      Sample1A
      plasmiid1
      dmso
      450

      Sample1B
      plasmiid1
      dmso
      543

      Sample1C
      plasmiid1
      dmso
      243

      Sample2A
      plasmiid1
      aine
      456

      Sample2B
      plasmiid1
      aine
      644

      Sample2C
      plasmiid1
      aine
      453

      Sample3A
      plasmiid2
      dmso
      1234

      Sample3B
      plasmiid2
      dmso
      1342

      Sample3C
      plasmiid2
      dmso
      1532

      Sample4A
      plasmiid2
      aine
      4500

      Sample4B
      plasmiid2
      aine
      6453

      Sample4C
      plasmiid2
      aine
      4800

```python
df["luc2p-10x"] = df["luc2p"] / 10
df
```

      konstrukt
      t88tlus
      luc2p
      luc2p-10x

      samplename

      Sample1A
      plasmiid1
      dmso
      450
      45.0

      Sample1B
      plasmiid1
      dmso
      543
      54.3

      Sample1C
      plasmiid1
      dmso
      243
      24.3

      Sample2A
      plasmiid1
      aine
      456
      45.6

      Sample2B
      plasmiid1
      aine
      644
      64.4

      Sample2C
      plasmiid1
      aine
      453
      45.3

      Sample3A
      plasmiid2
      dmso
      1234
      123.4

      Sample3B
      plasmiid2
      dmso
      1342
      134.2

      Sample3C
      plasmiid2
      dmso
      1532
      153.2

      Sample4A
      plasmiid2
      aine
      4500
      450.0

      Sample4B
      plasmiid2
      aine
      6453
      645.3

      Sample4C
      plasmiid2
      aine
      4800
      480.0

Ning kasutada ka teiste tulpade andmeid arvutustes:

```python
df["luc2p-ratio"] = df["luc2p"] - df["luc2p-10x"]
df
```

      konstrukt
      t88tlus
      luc2p
      luc2p-10x
      luc2p-ratio

      samplename

      Sample1A
      plasmiid1
      dmso
      450
      45.0
      405.0

      Sample1B
      plasmiid1
      dmso
      543
      54.3
      488.7

      Sample1C
      plasmiid1
      dmso
      243
      24.3
      218.7

      Sample2A
      plasmiid1
      aine
      456
      45.6
      410.4

      Sample2B
      plasmiid1
      aine
      644
      64.4
      579.6

      Sample2C
      plasmiid1
      aine
      453
      45.3
      407.7

      Sample3A
      plasmiid2
      dmso
      1234
      123.4
      1110.6

      Sample3B
      plasmiid2
      dmso
      1342
      134.2
      1207.8

      Sample3C
      plasmiid2
      dmso
      1532
      153.2
      1378.8

      Sample4A
      plasmiid2
      aine
      4500
      450.0
      4050.0

      Sample4B
      plasmiid2
      aine
      6453
      645.3
      5807.7

      Sample4C
      plasmiid2
      aine
      4800
      480.0
      4320.0

**Märkus/Hoiatus** - Kui te selliseid arvutusi teete siis tasub alati tulemus salvestada uude tulpa. Mitte kasutada tulemuse tulpa praeguses arvutuses või arvutada eelmise tulba väärtust üle. Seda sellepärast et siis hakkab arvutuse tulemus sõltuma sellest mitu korda on koodi läbi jooksutatud!!!

```python
df_vale = df.copy()
```

```python
df_vale["luc2p-10x"] = df_vale["luc2p-10x"]*10
df_vale
```

      konstrukt
      t88tlus
      luc2p
      luc2p-10x
      luc2p-ratio

      samplename

      Sample1A
      plasmiid1
      dmso
      450
      450.0
      405.0

      Sample1B
      plasmiid1
      dmso
      543
      543.0
      488.7

      Sample1C
      plasmiid1
      dmso
      243
      243.0
      218.7

      Sample2A
      plasmiid1
      aine
      456
      456.0
      410.4

      Sample2B
      plasmiid1
      aine
      644
      644.0
      579.6

      Sample2C
      plasmiid1
      aine
      453
      453.0
      407.7

      Sample3A
      plasmiid2
      dmso
      1234
      1234.0
      1110.6

      Sample3B
      plasmiid2
      dmso
      1342
      1342.0
      1207.8

      Sample3C
      plasmiid2
      dmso
      1532
      1532.0
      1378.8

      Sample4A
      plasmiid2
      aine
      4500
      4500.0
      4050.0

      Sample4B
      plasmiid2
      aine
      6453
      6453.0
      5807.7

      Sample4C
      plasmiid2
      aine
      4800
      4800.0
      4320.0

Tulpadega arvutuste jaoks annab kasutada ka Numpy funktsioone. Näiteks

  * `np.log & np.log10`
  * `np.pow`
  * `np.exp`

```python
# võtan värske `DataFrame`-i koopiast
df = df_algne.copy()
```

```python
df["log-luc2p"] = np.log( df["luc2p"] )
df
```

      konstrukt
      t88tlus
      luc2p
      log-luc2p

      samplename

      Sample1A
      plasmiid1
      dmso
      450
      6.109248

      Sample1B
      plasmiid1
      dmso
      543
      6.297109

      Sample1C
      plasmiid1
      dmso
      243
      5.493061

      Sample2A
      plasmiid1
      aine
      456
      6.122493

      Sample2B
      plasmiid1
      aine
      644
      6.467699

      Sample2C
      plasmiid1
      aine
      453
      6.115892

      Sample3A
      plasmiid2
      dmso
      1234
      7.118016

      Sample3B
      plasmiid2
      dmso
      1342
      7.201916

      Sample3C
      plasmiid2
      dmso
      1532
      7.334329

      Sample4A
      plasmiid2
      aine
      4500
      8.411833

      Sample4B
      plasmiid2
      aine
      6453
      8.772300

      Sample4C
      plasmiid2
      aine
      4800
      8.476371

  * `np.min`
  * `np.max`
  * `np.mean`
  * `np.sdt`

`np.min` ja `np.max` funktsioonidega saab kogu tulba miinimumi kasutada arvutustes. Niisama Pandase DataFrame-i sellesse funktsiooni panne santakse kõige väiksema väärtusega rida meile.

Andes ette tulba antakse kogu tulba väikseim/suurim jne väärtus:

```python
print("Min: {}, Max:{} , Mean:{}".format( np.min(df["luc2p"]),
                                          np.max(df["luc2p"]),
                                          np.mean(df["luc2p"]) ) )
```

    Min: 243, Max:6453 , Mean:1887.5

```python
df
```

      konstrukt
      t88tlus
      luc2p
      log-luc2p

      samplename

      Sample1A
      plasmiid1
      dmso
      450
      6.109248

      Sample1B
      plasmiid1
      dmso
      543
      6.297109

      Sample1C
      plasmiid1
      dmso
      243
      5.493061

      Sample2A
      plasmiid1
      aine
      456
      6.122493

      Sample2B
      plasmiid1
      aine
      644
      6.467699

      Sample2C
      plasmiid1
      aine
      453
      6.115892

      Sample3A
      plasmiid2
      dmso
      1234
      7.118016

      Sample3B
      plasmiid2
      dmso
      1342
      7.201916

      Sample3C
      plasmiid2
      dmso
      1532
      7.334329

      Sample4A
      plasmiid2
      aine
      4500
      8.411833

      Sample4B
      plasmiid2
      aine
      6453
      8.772300

      Sample4C
      plasmiid2
      aine
      4800
      8.476371

Mean-centering

```python
df["luc2p-meancenter"] = df["luc2p"] - np.mean( df["luc2p"] )
df
```

      konstrukt
      t88tlus
      luc2p
      log-luc2p
      luc2p-meancenter

      samplename

      Sample1A
      plasmiid1
      dmso
      450
      6.109248
      -1437.5

      Sample1B
      plasmiid1
      dmso
      543
      6.297109
      -1344.5

      Sample1C
      plasmiid1
      dmso
      243
      5.493061
      -1644.5

      Sample2A
      plasmiid1
      aine
      456
      6.122493
      -1431.5

      Sample2B
      plasmiid1
      aine
      644
      6.467699
      -1243.5

      Sample2C
      plasmiid1
      aine
      453
      6.115892
      -1434.5

      Sample3A
      plasmiid2
      dmso
      1234
      7.118016
      -653.5

      Sample3B
      plasmiid2
      dmso
      1342
      7.201916
      -545.5

      Sample3C
      plasmiid2
      dmso
      1532
      7.334329
      -355.5

      Sample4A
      plasmiid2
      aine
      4500
      8.411833
      2612.5

      Sample4B
      plasmiid2
      aine
      6453
      8.772300
      4565.5

      Sample4C
      plasmiid2
      aine
      4800
      8.476371
      2912.5

Näiteks saab teha kogu andmestikul andmete skaleerimise 0 ja 1 piiresse, nö *min-max* skaleerimine:

\begin{equation}
x_{minmax} = \frac{x - X_{min} }{X_{max} - X_{min} } \\
\end{equation}

```python
df["luc2p-minmax"] = ( df["luc2p"] - np.min(df["luc2p"]) ) / ( np.max(df["luc2p"]) - np.min(df["luc2p"]) )
df
```

      konstrukt
      t88tlus
      luc2p
      log-luc2p
      luc2p-meancenter
      luc2p-minmax

      samplename

      Sample1A
      plasmiid1
      dmso
      450
      6.109248
      -1437.5
      0.033333

      Sample1B
      plasmiid1
      dmso
      543
      6.297109
      -1344.5
      0.048309

      Sample1C
      plasmiid1
      dmso
      243
      5.493061
      -1644.5
      0.000000

      Sample2A
      plasmiid1
      aine
      456
      6.122493
      -1431.5
      0.034300

      Sample2B
      plasmiid1
      aine
      644
      6.467699
      -1243.5
      0.064573

      Sample2C
      plasmiid1
      aine
      453
      6.115892
      -1434.5
      0.033816

      Sample3A
      plasmiid2
      dmso
      1234
      7.118016
      -653.5
      0.159581

      Sample3B
      plasmiid2
      dmso
      1342
      7.201916
      -545.5
      0.176973

      Sample3C
      plasmiid2
      dmso
      1532
      7.334329
      -355.5
      0.207568

      Sample4A
      plasmiid2
      aine
      4500
      8.411833
      2612.5
      0.685507

      Sample4B
      plasmiid2
      aine
      6453
      8.772300
      4565.5
      1.000000

      Sample4C
      plasmiid2
      aine
      4800
      8.476371
      2912.5
      0.733816

Et seda skaleerimist oleks mugavam kasutada ka teiste tulpade puhul saab selle jaoks kirjutada funktsiooni:

```python
def minmax_scale(x):
    """Teisendab andmed 0 ja 1 vahele."""
    return ( x - np.min(x) ) / ( np.max(x) - np.min(x) )
```

```python
df["minmax-norm-func"] = minmax_scale( df["luc2p"] )
df
```

      konstrukt
      t88tlus
      luc2p
      log-luc2p
      luc2p-meancenter
      luc2p-minmax
      minmax-norm-func

      samplename

      Sample1A
      plasmiid1
      dmso
      450
      6.109248
      -1437.5
      0.033333
      0.033333

      Sample1B
      plasmiid1
      dmso
      543
      6.297109
      -1344.5
      0.048309
      0.048309

      Sample1C
      plasmiid1
      dmso
      243
      5.493061
      -1644.5
      0.000000
      0.000000

      Sample2A
      plasmiid1
      aine
      456
      6.122493
      -1431.5
      0.034300
      0.034300

      Sample2B
      plasmiid1
      aine
      644
      6.467699
      -1243.5
      0.064573
      0.064573

      Sample2C
      plasmiid1
      aine
      453
      6.115892
      -1434.5
      0.033816
      0.033816

      Sample3A
      plasmiid2
      dmso
      1234
      7.118016
      -653.5
      0.159581
      0.159581

      Sample3B
      plasmiid2
      dmso
      1342
      7.201916
      -545.5
      0.176973
      0.176973

      Sample3C
      plasmiid2
      dmso
      1532
      7.334329
      -355.5
      0.207568
      0.207568

      Sample4A
      plasmiid2
      aine
      4500
      8.411833
      2612.5
      0.685507
      0.685507

      Sample4B
      plasmiid2
      aine
      6453
      8.772300
      4565.5
      1.000000
      1.000000

      Sample4C
      plasmiid2
      aine
      4800
      8.476371
      2912.5
      0.733816
      0.733816

```python
df["minmax-norm-log"] = minmax_scale(df["log-luc2p"])
df
```

      konstrukt
      t88tlus
      luc2p
      log-luc2p
      luc2p-meancenter
      luc2p-minmax
      minmax-norm-func
      minmax-norm-log

      samplename

      Sample1A
      plasmiid1
      dmso
      450
      6.109248
      -1437.5
      0.033333
      0.033333
      0.187905

      Sample1B
      plasmiid1
      dmso
      543
      6.297109
      -1344.5
      0.048309
      0.048309
      0.245193

      Sample1C
      plasmiid1
      dmso
      243
      5.493061
      -1644.5
      0.000000
      0.000000
      0.000000

      Sample2A
      plasmiid1
      aine
      456
      6.122493
      -1431.5
      0.034300
      0.034300
      0.191944

      Sample2B
      plasmiid1
      aine
      644
      6.467699
      -1243.5
      0.064573
      0.064573
      0.297214

      Sample2C
      plasmiid1
      aine
      453
      6.115892
      -1434.5
      0.033816
      0.033816
      0.189931

      Sample3A
      plasmiid2
      dmso
      1234
      7.118016
      -653.5
      0.159581
      0.159581
      0.495528

      Sample3B
      plasmiid2
      dmso
      1342
      7.201916
      -545.5
      0.176973
      0.176973
      0.521113

      Sample3C
      plasmiid2
      dmso
      1532
      7.334329
      -355.5
      0.207568
      0.207568
      0.561492

      Sample4A
      plasmiid2
      aine
      4500
      8.411833
      2612.5
      0.685507
      0.685507
      0.890076

      Sample4B
      plasmiid2
      aine
      6453
      8.772300
      4565.5
      1.000000
      1.000000
      1.000000

      Sample4C
      plasmiid2
      aine
      4800
      8.476371
      2912.5
      0.733816
      0.733816
      0.909757

## Arvutused gruppidel

Et teha arvutusi pandases gruppidel tuleb kasutada meetodit `.groupby`. Saadud gruppidel saab viia läbi lihtsamaid arvutusi kasutades pandase gruppidesse sisse ehitatud funktsioone `.mean(), std(), min(), .max, count()`. Need funktsioonid kõik tagastavad jällegi Pandase `DataFrame`-i.

### `.mean(), std(), min(), .max(), .count()`

```python
#praegune arvutustega DataFrame
df
```

      konstrukt
      t88tlus
      luc2p
      log-luc2p
      luc2p-meancenter
      luc2p-minmax
      minmax-norm-func
      minmax-norm-log

      samplename

      Sample1A
      plasmiid1
      dmso
      450
      6.109248
      -1437.5
      0.033333
      0.033333
      0.187905

      Sample1B
      plasmiid1
      dmso
      543
      6.297109
      -1344.5
      0.048309
      0.048309
      0.245193

      Sample1C
      plasmiid1
      dmso
      243
      5.493061
      -1644.5
      0.000000
      0.000000
      0.000000

      Sample2A
      plasmiid1
      aine
      456
      6.122493
      -1431.5
      0.034300
      0.034300
      0.191944

      Sample2B
      plasmiid1
      aine
      644
      6.467699
      -1243.5
      0.064573
      0.064573
      0.297214

      Sample2C
      plasmiid1
      aine
      453
      6.115892
      -1434.5
      0.033816
      0.033816
      0.189931

      Sample3A
      plasmiid2
      dmso
      1234
      7.118016
      -653.5
      0.159581
      0.159581
      0.495528

      Sample3B
      plasmiid2
      dmso
      1342
      7.201916
      -545.5
      0.176973
      0.176973
      0.521113

      Sample3C
      plasmiid2
      dmso
      1532
      7.334329
      -355.5
      0.207568
      0.207568
      0.561492

      Sample4A
      plasmiid2
      aine
      4500
      8.411833
      2612.5
      0.685507
      0.685507
      0.890076

      Sample4B
      plasmiid2
      aine
      6453
      8.772300
      4565.5
      1.000000
      1.000000
      1.000000

      Sample4C
      plasmiid2
      aine
      4800
      8.476371
      2912.5
      0.733816
      0.733816
      0.909757

Need funktsioonid teevad arvutused kõigil tulpadel eraldi:

```python
df.groupby(['konstrukt']).mean()
```

      luc2p
      log-luc2p
      luc2p-meancenter
      luc2p-minmax
      minmax-norm-func
      minmax-norm-log

      konstrukt

      plasmiid1
      464.833333
      6.100917
      -1422.666667
      0.035722
      0.035722
      0.185365

      plasmiid2
      3310.166667
      7.885794
      1422.666667
      0.493908
      0.493908
      0.729661

Kui me oleksime huvitatud ainult ühe tulba keskmistest siis enne grupeerimist peab selle välja valima:

```python
df[["konstrukt","luc2p"]].groupby("konstrukt").mean()
```

      luc2p

      konstrukt

      plasmiid1
      464.833333

      plasmiid2
      3310.166667

Grupeerimist saab läbi viia ka mitmel tasemel. Näiteks grupeerida konstrukti ja töötluse järgi ning seejärel arvutada luc2P signaali keskmised:

```python
df.groupby( ['konstrukt','t88tlus'] ).mean()
```

      luc2p
      log-luc2p
      luc2p-meancenter
      luc2p-minmax
      minmax-norm-func
      minmax-norm-log

      konstrukt
      t88tlus

      plasmiid1
      aine
      517.666667
      6.235361
      -1369.833333
      0.044230
      0.044230
      0.226363

      dmso
      412.000000
      5.966473
      -1475.500000
      0.027214
      0.027214
      0.144366

      plasmiid2
      aine
      5251.000000
      8.553501
      3363.500000
      0.806441
      0.806441
      0.933278

      dmso
      1369.333333
      7.218087
      -518.166667
      0.181374
      0.181374
      0.526045

Teine võimalus saada kätte ennast huvitavaid andmeid on need hiljem saadud DataFrame-ist välja võtta:

```python
df_means = df.groupby(['konstrukt','t88tlus']).mean()
df_means
```

      luc2p
      log-luc2p
      luc2p-meancenter
      luc2p-minmax
      minmax-norm-func
      minmax-norm-log

      konstrukt
      t88tlus

      plasmiid1
      aine
      517.666667
      6.235361
      -1369.833333
      0.044230
      0.044230
      0.226363

      dmso
      412.000000
      5.966473
      -1475.500000
      0.027214
      0.027214
      0.144366

      plasmiid2
      aine
      5251.000000
      8.553501
      3363.500000
      0.806441
      0.806441
      0.933278

      dmso
      1369.333333
      7.218087
      -518.166667
      0.181374
      0.181374
      0.526045

Kasutades selleks niisama indeksi läbi pöördumist:

```python
df_means[ ["luc2p"] ]
```

      luc2p

      konstrukt
      t88tlus

      plasmiid1
      aine
      517.666667

      dmso
      412.000000

      plasmiid2
      aine
      5251.000000

      dmso
      1369.333333

Praegu saadud `DataFrame`-il on index mitmetasandiline. See võib olla mõndadel juhtudel vajalik kuid et sellest lahti saada saab kasutada `.reset_index()` meetodit. Indeksi määramine käib jällegi meetodiga `.set_index()`.

```python
df_means
```

      luc2p
      log-luc2p
      luc2p-meancenter
      luc2p-minmax
      minmax-norm-func
      minmax-norm-log

      konstrukt
      t88tlus

      plasmiid1
      aine
      517.666667
      6.235361
      -1369.833333
      0.044230
      0.044230
      0.226363

      dmso
      412.000000
      5.966473
      -1475.500000
      0.027214
      0.027214
      0.144366

      plasmiid2
      aine
      5251.000000
      8.553501
      3363.500000
      0.806441
      0.806441
      0.933278

      dmso
      1369.333333
      7.218087
      -518.166667
      0.181374
      0.181374
      0.526045

```python
df_uus = df_means.reset_index()
df_uus
```

      konstrukt
      t88tlus
      luc2p
      log-luc2p
      luc2p-meancenter
      luc2p-minmax
      minmax-norm-func
      minmax-norm-log

      0
      plasmiid1
      aine
      517.666667
      6.235361
      -1369.833333
      0.044230
      0.044230
      0.226363

      1
      plasmiid1
      dmso
      412.000000
      5.966473
      -1475.500000
      0.027214
      0.027214
      0.144366

      2
      plasmiid2
      aine
      5251.000000
      8.553501
      3363.500000
      0.806441
      0.806441
      0.933278

      3
      plasmiid2
      dmso
      1369.333333
      7.218087
      -518.166667
      0.181374
      0.181374
      0.526045

```python
df_uus.set_index(['konstrukt'])
```

      t88tlus
      luc2p
      log-luc2p
      luc2p-meancenter
      luc2p-minmax
      minmax-norm-func
      minmax-norm-log

      konstrukt

      plasmiid1
      aine
      517.666667
      6.235361
      -1369.833333
      0.044230
      0.044230
      0.226363

      plasmiid1
      dmso
      412.000000
      5.966473
      -1475.500000
      0.027214
      0.027214
      0.144366

      plasmiid2
      aine
      5251.000000
      8.553501
      3363.500000
      0.806441
      0.806441
      0.933278

      plasmiid2
      dmso
      1369.333333
      7.218087
      -518.166667
      0.181374
      0.181374
      0.526045

### `.agg` - aggregate

Teine viis teha arvutusi gruppidel on gruppide `.agg` funktsioon.

`.agg` käitub natuke erinevalt vastavalt sellele mis talle ette anda.

Andes listi numpy funtsioonidega arvutab ta iga grupi sees vastava funktsiooni tulemuse:

```python
df.groupby(['konstrukt', 't88tlus']).agg( [ np.mean, np.std ] )
```

      luc2p
      log-luc2p
      luc2p-meancenter
      luc2p-minmax
      minmax-norm-func
      minmax-norm-log

      mean
      std
      mean
      std
      mean
      std
      mean
      std
      mean
      std
      mean
      std

      konstrukt
      t88tlus

      plasmiid1
      aine
      517.666667
      109.418158
      6.235361
      0.201237
      -1369.833333
      109.418158
      0.044230
      0.017620
      0.044230
      0.017620
      0.226363
      0.061367

      dmso
      412.000000
      153.567575
      5.966473
      0.420609
      -1475.500000
      153.567575
      0.027214
      0.024729
      0.027214
      0.024729
      0.144366
      0.128264

      plasmiid2
      aine
      5251.000000
      1051.714315
      8.553501
      0.192214
      3363.500000
      1051.714315
      0.806441
      0.169358
      0.806441
      0.169358
      0.933278
      0.058615

      dmso
      1369.333333
      150.868596
      7.218087
      0.109059
      -518.166667
      150.868596
      0.181374
      0.024294
      0.181374
      0.024294
      0.526045
      0.033258

Andes dictionary funktsioonidega siis kasutatakse dictionary võtmeid tulpadele määratud funktsioonide jaoks. Tagastatud `DataFrame`-il on ainult tulbad mille arvutusi sooviti:

```python
df.groupby(['konstrukt', 't88tlus']).agg( { 'luc2p' : np.mean } )
```

      luc2p

      konstrukt
      t88tlus

      plasmiid1
      aine
      517.666667

      dmso
      412.000000

      plasmiid2
      aine
      5251.000000

      dmso
      1369.333333

Nii saab dictionarys sisestada ka mitut funktsiooni:

```python
df.groupby(['konstrukt', 't88tlus']).agg( {'luc2p' : [ np.mean, np.std ] })
```

      luc2p

      mean
      std

      konstrukt
      t88tlus

      plasmiid1
      aine
      517.666667
      109.418158

      dmso
      412.000000
      153.567575

      plasmiid2
      aine
      5251.000000
      1051.714315

      dmso
      1369.333333
      150.868596

Ja ka ise kirjutatud funktsioone. Samas peavad kõik grupeerimisel kasutatavad funktsioonid olema redutseerivad, see tähendab et nende vastuseks peab olema üks kindel tulemus mis saadakse arvutusega grupi peal.

```python
def tobe_funktsioon(xgroup):
    """Funktsioon mis lihtsalt paneb grupi siseselt null väärtused"""
    return 0
```

```python
def test_mean(xgroup):
    return np.mean(xgroup) / np.std(xgroup)
```

```python
df.groupby(['konstrukt', 't88tlus']).agg({'luc2p': [ np.mean, np.std, tobe_funktsioon ]})
```

      luc2p

      mean
      std
      tobe_funktsioon

      konstrukt
      t88tlus

      plasmiid1
      aine
      517.666667
      109.418158
      0

      dmso
      412.000000
      153.567575
      0

      plasmiid2
      aine
      5251.000000
      1051.714315
      0

      dmso
      1369.333333
      150.868596
      0

Ka saab grupeerimisel teha midagi keerulisemat gruppidel. Kasutades `for` tsükklit saab `groupby` funktsioonilt kätte grupi nime ja grupi `DataFrame`-i.

```python
for name, df_group in df.groupby(["konstrukt"]):
    print(name)
    print(df_group)
```

    plasmiid1
                konstrukt t88tlus  luc2p  log-luc2p  luc2p-meancenter  \
    samplename
    Sample1A    plasmiid1    dmso    450   6.109248           -1437.5
    Sample1B    plasmiid1    dmso    543   6.297109           -1344.5
    Sample1C    plasmiid1    dmso    243   5.493061           -1644.5
    Sample2A    plasmiid1    aine    456   6.122493           -1431.5
    Sample2B    plasmiid1    aine    644   6.467699           -1243.5
    Sample2C    plasmiid1    aine    453   6.115892           -1434.5

                luc2p-minmax  minmax-norm-func  minmax-norm-log
    samplename
    Sample1A        0.033333          0.033333         0.187905
    Sample1B        0.048309          0.048309         0.245193
    Sample1C        0.000000          0.000000         0.000000
    Sample2A        0.034300          0.034300         0.191944
    Sample2B        0.064573          0.064573         0.297214
    Sample2C        0.033816          0.033816         0.189931
    plasmiid2
                konstrukt t88tlus  luc2p  log-luc2p  luc2p-meancenter  \
    samplename
    Sample3A    plasmiid2    dmso   1234   7.118016            -653.5
    Sample3B    plasmiid2    dmso   1342   7.201916            -545.5
    Sample3C    plasmiid2    dmso   1532   7.334329            -355.5
    Sample4A    plasmiid2    aine   4500   8.411833            2612.5
    Sample4B    plasmiid2    aine   6453   8.772300            4565.5
    Sample4C    plasmiid2    aine   4800   8.476371            2912.5

                luc2p-minmax  minmax-norm-func  minmax-norm-log
    samplename
    Sample3A        0.159581          0.159581         0.495528
    Sample3B        0.176973          0.176973         0.521113
    Sample3C        0.207568          0.207568         0.561492
    Sample4A        0.685507          0.685507         0.890076
    Sample4B        1.000000          1.000000         1.000000
    Sample4C        0.733816          0.733816         0.909757

## Näide - kontrolli vastu normaliseerimine

Üks kasulik asi tulpadega arvutusi tehes on see et Pandas teeb arvutusi tulpade vahel vastavalt ridade index-ile. See lubab meil index-iga mängides normaliseeriga andmeid kontrolli vastu.

Arvutame kõigepealt katse sisesed keskmised:

```python
df_mean = df.groupby(['konstrukt', 't88tlus']).agg( {'luc2p':np.mean} )
df_mean
```

      luc2p

      konstrukt
      t88tlus

      plasmiid1
      aine
      517.666667

      dmso
      412.000000

      plasmiid2
      aine
      5251.000000

      dmso
      1369.333333

Kuna meil keskmiste tabelil on mitmetasandiline index (konstrukt ja t88tlus) siis saame sellest lahti praegu:

```python
df_mean = df_mean.reset_index()
df_mean
```

      konstrukt
      t88tlus
      luc2p

      0
      plasmiid1
      aine
      517.666667

      1
      plasmiid1
      dmso
      412.000000

      2
      plasmiid2
      aine
      5251.000000

      3
      plasmiid2
      dmso
      1369.333333

Kuna me tahame normaliseerida konstruktide siseselt siis peame index-i üles ehitama konstruktil:

```python
df_allmeans = df_mean.set_index('konstrukt')
df_allmeans
```

      t88tlus
      luc2p

      konstrukt

      plasmiid1
      aine
      517.666667

      plasmiid1
      dmso
      412.000000

      plasmiid2
      aine
      5251.000000

      plasmiid2
      dmso
      1369.333333

Võtame esialgsest keskmiste tabelist välja ainult kontrolli väärtused kõigepealt muutes index-i "t88tlus"-eks, seejärel valides välja ainult kontrolli (ehk "dmso") väärtused. Siis eemaldades index-i ja muutes indeksi uuesti "konstrukt"-iks.

```python
df_mean.set_index('t88tlus')
```

      konstrukt
      luc2p

      t88tlus

      aine
      plasmiid1
      517.666667

      dmso
      plasmiid1
      412.000000

      aine
      plasmiid2
      5251.000000

      dmso
      plasmiid2
      1369.333333

```python
df_mean.set_index('t88tlus').loc[ ["dmso"] , : ]
```

      konstrukt
      luc2p

      t88tlus

      dmso
      plasmiid1
      412.000000

      dmso
      plasmiid2
      1369.333333

```python
df_mean.set_index('t88tlus').loc[["dmso"],:].reset_index()
```

      t88tlus
      konstrukt
      luc2p

      0
      dmso
      plasmiid1
      412.000000

      1
      dmso
      plasmiid2
      1369.333333

```python
df_mean.set_index('t88tlus').loc[["dmso"],:].reset_index().set_index('konstrukt')
```

      t88tlus
      luc2p

      konstrukt

      plasmiid1
      dmso
      412.000000

      plasmiid2
      dmso
      1369.333333

Salvestame DataFrame-i normaliseerimise väärtustega eraldi

```python
df_control = df_mean.set_index('t88tlus').loc[["dmso"],:].reset_index().set_index('konstrukt')
df_control
```

      t88tlus
      luc2p

      konstrukt

      plasmiid1
      dmso
      412.000000

      plasmiid2
      dmso
      1369.333333

Nüüd on meil kaks `DataFrame`-i:
  * `df_allmeans` - sisaldab kõiki meie katse keskmisi
  * `df_control` - sisaldab ainult kontrolli (ehk "dmso") keskmisi

```python
df_allmeans
```

      t88tlus
      luc2p

      konstrukt

      plasmiid1
      aine
      517.666667

      plasmiid1
      dmso
      412.000000

      plasmiid2
      aine
      5251.000000

      plasmiid2
      dmso
      1369.333333

```python
df_control
```

      t88tlus
      luc2p

      konstrukt

      plasmiid1
      dmso
      412.000000

      plasmiid2
      dmso
      1369.333333

Kuna arvutusi tehakse index-i spetsiifiliselt siis saab nüüd normaliseerida kummagi index-i rühma väärtused eraldi:

```python
df_allmeans["ctr-norm"] = df_allmeans["luc2p"] / df_control["luc2p"]
```

```python
df_allmeans
```

      t88tlus
      luc2p
      ctr-norm

      konstrukt

      plasmiid1
      aine
      517.666667
      1.256472

      plasmiid1
      dmso
      412.000000
      1.000000

      plasmiid2
      aine
      5251.000000
      3.834713

      plasmiid2
      dmso
      1369.333333
      1.000000

## Andmete salvestamine Excelisse

Olles saanud kätte meid huvitava `DataFrame`-i saame selle salvestada excelisse meetodiga `.to_excel`:

https://pandas.pydata.org/pandas-docs/stable/io.html#io-excel-writer

```python
df_allmeans.to_excel("single-exp-means.xlsx", sheet_name="means")
```

Kui me tahame salvestada mitut `DataFrame`-i tuleb seda teha eraldi kasutades Pandase ExcelWriter klassi:

```python
with pd.ExcelWriter("manysheets.xlsx") as writer:
    df_mean.to_excel(writer, sheet_name="means")
    df_allmeans.to_excel(writer, sheet_name="allmeans")
```

## Kogu katse andmete analüüs

Olles teinud mitu katset on võimalik nende analüüs kokku võtta mitmel viisil. Kõige lihtsam oleks teha sarnaselt eelnevale katse sisene keskmiste arvutus ja normaliseerimine eraldi igal katseandmete `DataFrame`-il. Seejärel need andmed ühendada ja teha teha sarnane analüüs (koos ka t-testiga) ühendatud `DataFrame`-il.

```python
df_katse1 = pd.read_excel("example_data/02-exp1.xlsx", sheetname="Sheet1")
df_katse2 = pd.read_excel("example_data/02-exp1.xlsx", sheetname="Sheet2")
df_katse3 = pd.read_excel("example_data/02-exp1.xlsx", sheetname="Sheet3")
```

```python
df_katse1_means = df_katse1.groupby(['konstrukt','t88tlus']).mean().reset_index().set_index('konstrukt')
df_katse1_means
```

      t88tlus
      luc2p

      konstrukt

      plasmiid1
      aine
      517.666667

      plasmiid1
      dmso
      412.000000

      plasmiid2
      aine
      5251.000000

      plasmiid2
      dmso
      1369.333333

```python
df_katse1_control = df_katse1.groupby(['konstrukt','t88tlus']).mean().reset_index().set_index('t88tlus').loc["dmso",:].reset_index().set_index("konstrukt")
df_katse1_control
```

      t88tlus
      luc2p

      konstrukt

      plasmiid1
      dmso
      412.000000

      plasmiid2
      dmso
      1369.333333

```python
df_katse1_means["fold"] = df_katse1_means["luc2p"] / df_katse1_control["luc2p"]
```

```python
df_katse1_means = df_katse1_means.reset_index()
df_katse1_means
```

      konstrukt
      t88tlus
      luc2p
      fold

      0
      plasmiid1
      aine
      517.666667
      1.256472

      1
      plasmiid1
      dmso
      412.000000
      1.000000

      2
      plasmiid2
      aine
      5251.000000
      3.834713

      3
      plasmiid2
      dmso
      1369.333333
      1.000000

```python
df_katse1_means["katse"]="katse1"
```

Sama moodi saab teha arvutused ära teistele katsete andmetele:

```python
df_katse2_means = df_katse2.groupby(['konstrukt','t88tlus']).mean().reset_index().set_index('konstrukt')
df_katse2_control = df_katse2.groupby(['konstrukt','t88tlus']).mean().reset_index().set_index('t88tlus').loc["dmso",:].reset_index().set_index("konstrukt")
df_katse2_means["fold"] = df_katse2_means["luc2p"] / df_katse2_control["luc2p"]
df_katse2_means = df_katse2_means.reset_index()
df_katse2_means["katse"]="katse2"
```

```python
df_katse3_means = df_katse3.groupby(['konstrukt','t88tlus']).mean().reset_index().set_index('konstrukt')
df_katse3_control = df_katse3.groupby(['konstrukt','t88tlus']).mean().reset_index().set_index('t88tlus').loc["dmso",:].reset_index().set_index("konstrukt")
df_katse3_means["fold"] = df_katse3_means["luc2p"] / df_katse3_control["luc2p"]
df_katse3_means = df_katse3_means.reset_index()
```

```python
df_katse3_means["katse"]="katse3"
```

Nüüd on meil kolm erinevat tabelit katsesiseste normaliseeritud andmetega. Need saab omavahel ühendada kasutades pandase `.concat()` funktsiooni. Selle jaoks tuleb tabelid paigutada *list*-i ja edastada `.concat`-ile argumendina:

```python
df_koos = pd.concat( [ df_katse1_means, df_katse2_means, df_katse3_means ] )
df_koos
```

      konstrukt
      t88tlus
      luc2p
      fold
      katse

      0
      plasmiid1
      aine
      517.666667
      1.256472
      katse1

      1
      plasmiid1
      dmso
      412.000000
      1.000000
      katse1

      2
      plasmiid2
      aine
      5251.000000
      3.834713
      katse1

      3
      plasmiid2
      dmso
      1369.333333
      1.000000
      katse1

      0
      plasmiid1
      aine
      896.000000
      1.479362
      katse2

      1
      plasmiid1
      dmso
      605.666667
      1.000000
      katse2

      2
      plasmiid2
      aine
      6082.666667
      2.284713
      katse2

      3
      plasmiid2
      dmso
      2662.333333
      1.000000
      katse2

      0
      plasmiid1
      aine
      1329.000000
      1.114310
      katse3

      1
      plasmiid1
      dmso
      1192.666667
      1.000000
      katse3

      2
      plasmiid2
      aine
      11278.333333
      1.830205
      katse3

      3
      plasmiid2
      dmso
      6162.333333
      1.000000
      katse3

Arvutamaks katsetevaheliste foldide keskmisi saab jälle kasutada group by-d:

```python
df_koos.groupby(['konstrukt','t88tlus']).agg( { "fold" : [np.mean, np.std] })
```

      fold

      mean
      std

      konstrukt
      t88tlus

      plasmiid1
      aine
      1.283381
      0.184008

      dmso
      1.000000
      0.000000

      plasmiid2
      aine
      2.649877
      1.050962

      dmso
      1.000000
      0.000000

Et teha t-testi arvutatud foldidel kasutame Scipy stats funktsiooni `ttest_rel`.

https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.ttest_ind.html#scipy.stats.ttest_ind

```python
from scipy.stats import ttest_ind
```

Sellega saab arvutada t-testi edastades sellele parameetritena kaks gruppi mida omavahel testitakse. Tagastatavas muutujas on p-value kirjas:

```python
test = ttest_ind([1.0, 1.0, 1.0 ],[10.0, 10.0, 100])
test.pvalue
```

    0.26345159647122413

Et saaks t-testi jooksutada Pandase grupeeringutega võime selle jaoks kirjutada funktsiooni:

```python
def calc_p_value(x):
    #kontroll grupi väärtused
    ctr_group = x.size * [1.0]
    pval = ttest_ind(ctr_group, x)
    return pval.pvalue
```

```python
df_all = df_koos.groupby(['konstrukt','t88tlus']).agg({"fold":[np.mean,
                                                      np.std,
                                                      calc_p_value]})

df_all
```

      fold

      mean
      std
      calc_p_value

      konstrukt
      t88tlus

      plasmiid1
      aine
      1.283381
      0.184008
      0.055954

      dmso
      1.000000
      0.000000
      NaN

      plasmiid2
      aine
      2.649877
      1.050962
      0.053036

      dmso
      1.000000
      0.000000
      NaN