Tabulky s None a NaN

Tabulky s None a NaN#

S tabulkami se pracuje pomocí knihovny Pandas. Nejsnazší je tvořit tabulky z matice nebo po sloupcích. Přitom musí mít každý sloupec stejný počet dat. Potom jsou všechny sloupce stejně dlouhé. Toto se však nedá zařídit vždy, někdy je nutno do tabulky přidat sloupec, který je kratší. Řešením je přidat do tabulky sloupec s libovolnými hodnotami a poté nahradit ta data, která máme k dispozici. Vhodnou výplní nedefinovaných dat jsou nuly nebo hodnoty None nebo np.nan.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp

N = 11
df = pd.DataFrame()
df["a"] = np.linspace(1,20,N)

# založíme prázdný sloupec a potom jeho začátek přepíšeme daty
df["b"] = np.nan
data = np.array([2,3,4,5])
df.loc[:len(data)-1,"b"] = data

# jiná varianta je nejprve doplnit data a potom zakládat sloupec
data = np.array([15,12,9])
doplnena_data = np.append(data,np.full(N-len(data),np.nan))
df["c"] = doplnena_data

df.plot()
df
a b c
0 1.0 2.0 15.0
1 2.9 3.0 12.0
2 4.8 4.0 9.0
3 6.7 5.0 NaN
4 8.6 NaN NaN
5 10.5 NaN NaN
6 12.4 NaN NaN
7 14.3 NaN NaN
8 16.2 NaN NaN
9 18.1 NaN NaN
10 20.0 NaN NaN
../_images/ebcf3cbf3ba4a0bda18106cbfa4b66d50db2853ea126b7899479086d9780be44.png

Ukázkou je rovnice konstantního lovu v logistické rovnici, kdy pro některé počáteční podmínky je populace přelovená a dochází k její destrukci. Řešení se nemusí zastavit dosažením cílového času, ale při poklesu velikosti populace na nulu. Pokud se snažíme zapsat dat do jedné tabulky, musíme neobsazená místa zaplnit nedefinovanými hodnotami.

### Příprava funkcí a parametrů
pocatecni_podminky = np.round(np.arange(0.1,1.3,0.02),2)  # počáteční podmínka nebo podmínky
meze = [0,15]  # interval, na kterém hledáme řešení
N = 100 # počet dělících bodů

def model(t, N, r=1,K=1,h=0.2):
    return  r*N*(1-N/K) - h

def destrukce_populace(t,x,r=1,K=1,h=0.15):  # Pokud x klesne na nulu, zastavíme výpočet
    return x
destrukce_populace.terminal = True

### Řešení modelu
t=np.linspace(*meze, N)  # definiční obor, v těchto bodech budeme hledat řešení
df = pd.DataFrame(index=t)
df.index.name = "Čas"

for pocatecni_podminka in pocatecni_podminky:
    reseni = solve_ivp(
                       model,
                       meze,
                       [pocatecni_podminka],
                       t_eval=t,
                       events=destrukce_populace,
                       )
    data = reseni.y[0]
    if len(data)==N:
        df[pocatecni_podminka] = data
    else:
        df[pocatecni_podminka] = None
        sloupec = df[pocatecni_podminka]
        sloupec.iloc[:len(data)] = data
        # jina varianta je nejprve doplnit data a potom zakladat sloupec
        # doplnena_data = np.append(data,np.full(N-len(data),np.nan))
        # df[pocatecni_podminka] = doplnena_data

### Vizualizace řešení

ax = df.plot(lw=0.5,legend=False,color="C0")
ax.set(
    ylim = (0,None),
    title = "Model lovu konstantní rychlostí",
    ylabel="Velikost populace",
);
../_images/ce0ce970fcb95797185de02013154e7526b14a8150cb0c34a54bfa5496466d8b.png

Můžeme i barevně rozlišit, které křivky vedou k vymření přelovené populace.

fig,ax = plt.subplots()
plt.plot([0,0],[-1,-1])
plt.plot([0,0],[-1,-1])
for pocatecni_podminka in pocatecni_podminky:
    data = np.array(df[pocatecni_podminka])
    if pd.isna(data).any():
        barva = "C1"
    else:
        barva = "C0"
    ax.plot(t,data, color=barva, lw=0.5)

ax.set(
    ylim = (0,None),
    title = "Model lovu konstantní rychlostí",
    ylabel="Velikost populace",
)
plt.legend(["trvalá udržitelnost při vysokých počátečních hodnotách","přelovená populace při malých počátečních hodnotách"]);
../_images/2f0bb7dae14e1cd4ab50225c9f3e5ac847677c10d106020303443bba5152bb71.png

Místo cyklu přes všechna řešení můžeme vykreslit jednou barvou sloupce s nedefinovanými hodnotami a druhou barvou sloupce bez nich. Toto bude efektivní zejména pro rozsáhlé soubory dat.

fig,ax = plt.subplots()
plt.plot([0,0],[-1,-1])
plt.plot([0,0],[-1,-1])

# viz https://stackoverflow.com/questions/47414848/pandas-select-all-columns-without-nan
df[df.columns[~df.isnull().any()]].plot(color="C0", ax=ax, lw=0.5)
df[list(df.columns[df.isnull().any()])].plot(color="C1", ax=ax, lw=0.5)

ax.set(
    ylim = (0,None),
    title = "Model lovu konstantní rychlostí",
    xlabel="čas",
    ylabel="velikost populace",
)
plt.legend(["trvalá udržitelnost při vysokých počátečních hodnotách","přelovená populace při malých počátečních hodnotách"]);
../_images/232e848d825fd6502f6c277aa874044f88fcffe6f4e8887a132de46bc070f3ae.png
df
0.10 0.12 0.14 0.16 0.18 0.20 0.22 0.24 0.26 0.28 ... 1.10 1.12 1.14 1.16 1.18 1.20 1.22 1.24 1.26 1.28
Čas
0.000000 0.1 0.12 0.14 0.16 0.18 0.2 0.22 0.24 0.26 0.280000 ... 1.100000 1.120000 1.140000 1.160000 1.180000 1.200000 1.220000 1.240000 1.260000 1.280000
0.151515 0.082256 0.104823 0.127245 0.149523 0.171659 0.193653 0.215508 0.237225 0.258806 0.280251 ... 1.056946 1.073683 1.090328 1.106880 1.123341 1.139711 1.155991 1.172181 1.188284 1.204298
0.303030 0.062067 0.08767 0.112927 0.137843 0.162424 0.186676 0.210605 0.234217 0.25752 0.280519 ... 1.020253 1.034409 1.048422 1.062293 1.076024 1.089618 1.103077 1.116403 1.129598 1.142664
0.454545 0.039067 0.06827 0.096848 0.124819 0.152198 0.179003 0.20525 0.230955 0.256135 0.280805 ... 0.988909 1.001039 1.013003 1.024804 1.036447 1.047935 1.059272 1.070461 1.081506 1.092409
0.606061 0.012689 0.046222 0.078731 0.110263 0.140859 0.170559 0.199401 0.227417 0.254644 0.281111 ... 0.962271 0.972819 0.983196 0.993406 1.003455 1.013345 1.023082 1.032668 1.042108 1.051405
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
14.393939 None None None None None None None None None 0.650293 ... 0.724021 0.724059 0.724068 0.724063 0.724059 0.724058 0.724057 0.724058 0.724059 0.724061
14.545455 None None None None None None None None None 0.654381 ... 0.724007 0.724039 0.724038 0.724033 0.724030 0.724028 0.724028 0.724028 0.724030 0.724032
14.696970 None None None None None None None None None 0.658266 ... 0.723991 0.724016 0.724009 0.724005 0.724002 0.724001 0.724000 0.724001 0.724002 0.724005
14.848485 None None None None None None None None None 0.661957 ... 0.723972 0.723990 0.723983 0.723979 0.723976 0.723975 0.723974 0.723975 0.723977 0.723979
15.000000 None None None None None None None None None 0.665471 ... 0.723951 0.723965 0.723958 0.723954 0.723952 0.723951 0.723950 0.723951 0.723952 0.723955

100 rows × 60 columns

Začátek a konec tabulky s jedním předčasně končícím řešením.

df[[0.24,0.50,0.80]].head()
0.24 0.50 0.80
Čas
0.000000 0.24 0.500000 0.800000
0.151515 0.237225 0.507573 0.794205
0.303030 0.234217 0.515129 0.788898
0.454545 0.230955 0.522650 0.784023
0.606061 0.227417 0.530120 0.779548
df[[0.24,0.50,0.80]].tail()
0.24 0.50 0.80
Čas
14.393939 None 0.722876 0.723763
14.545455 None 0.722922 0.723753
14.696970 None 0.722965 0.723743
14.848485 None 0.723006 0.723734
15.000000 None 0.723045 0.723726