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 |
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",
);
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"]);
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"]);
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 |