Commit 545d089a authored by lucas_miranda's avatar lucas_miranda
Browse files

Changed all type() checks for isinstance() to take inheritance into account

parent 48ef8ea8
......@@ -40,8 +40,8 @@ import warnings
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
# DEFINE CUSTOM ANNOTATED TYPES #
Coordinates = deepof.utils.NewType("Coordinates", deepof.utils.Any)
Table_dict = deepof.utils.NewType("Table_dict", deepof.utils.Any)
Coordinates = deepof.utils.Newisinstance("Coordinates", deepof.utils.Any)
Table_dict = deepof.utils.Newisinstance("Table_dict", deepof.utils.Any)
# CLASSES FOR PREPROCESSING AND DATA WRANGLING
......@@ -549,7 +549,7 @@ class coordinates:
- self._scales[i][1] / 2
)
elif type(center) == str and center != "arena":
elif isinstance(center, str) and center != "arena":
for i, (key, value) in enumerate(tabs.items()):
......@@ -583,7 +583,7 @@ class coordinates:
for key, tab in tabs.items():
tabs[key].index = pd.timedelta_range(
"00:00:00", length, periods=tab.shape[0] + 1, closed="left"
).astype("timedelta64[s]")
).asisinstance("timedelta64[s]")
if align:
assert (
......@@ -667,7 +667,7 @@ class coordinates:
for key, tab in tabs.items():
tabs[key].index = pd.timedelta_range(
"00:00:00", length, periods=tab.shape[0] + 1, closed="left"
).astype("timedelta64[s]")
).asisinstance("timedelta64[s]")
if propagate_labels:
for key, tab in tabs.items():
......@@ -732,7 +732,7 @@ class coordinates:
for key, tab in tabs.items():
tabs[key].index = pd.timedelta_range(
"00:00:00", length, periods=tab.shape[0] + 1, closed="left"
).astype("timedelta64[s]")
).asisinstance("timedelta64[s]")
if propagate_labels:
for key, tab in tabs.items():
......@@ -833,7 +833,7 @@ class coordinates:
)
pbar.update(1)
if type(video_output) == list:
if isinstance(video_output, list):
vid_idxs = video_output
elif video_output == "all":
vid_idxs = list(self._tables.keys())
......
......@@ -25,7 +25,7 @@ import warnings
warnings.filterwarnings("ignore", message="All-NaN slice encountered")
# Create custom string type
Coordinates = NewType("Coordinates", Any)
Coordinates = Newisinstance("Coordinates", Any)
def close_single_contact(
......@@ -53,12 +53,12 @@ def close_single_contact(
close_contact = None
if type(right) == str:
if isinstance(right, str):
close_contact = (
np.linalg.norm(pos_dframe[left] - pos_dframe[right], axis=1) * arena_abs
) / arena_rel < tol
elif type(right) == list:
elif isinstance(right, list):
close_contact = np.any(
[
(np.linalg.norm(pos_dframe[left] - pos_dframe[r], axis=1) * arena_abs)
......@@ -528,7 +528,7 @@ def max_behaviour(
speeds = [col for col in behaviour_dframe.columns if "speed" in col.lower()]
behaviour_dframe = behaviour_dframe.drop(speeds, axis=1).astype("float")
behaviour_dframe = behaviour_dframe.drop(speeds, axis=1).asisinstance("float")
win_array = behaviour_dframe.rolling(window_size, center=True).sum()
if stepped:
win_array = win_array[::window_size]
......@@ -678,8 +678,8 @@ def rule_based_tagging(
return deepof.utils.smooth_boolean_array(
close_single_contact(
coords,
(left if type(left) != list else right),
(right if type(left) != list else left),
(left if not isinstance(left, list) else right),
(right if not isinstance(left, list) else left),
params["close_contact_tol"],
arena_abs,
arena[1][1],
......
......@@ -26,7 +26,7 @@ from typing import Tuple, Any, List, Union, NewType
# DEFINE CUSTOM ANNOTATED TYPES #
Coordinates = NewType("Coordinates", Any)
Coordinates = Newisinstance("Coordinates", Any)
# CONNECTIVITY FOR DLC MODELS
......@@ -750,7 +750,7 @@ def cluster_transition_matrix(
# Stores all possible transitions between clusters
clusters = [str(i) for i in range(nclusts)]
cluster_sequence = cluster_sequence.astype(str)
cluster_sequence = cluster_sequence.asisinstance(str)
trans = {t: 0 for t in product(clusters, clusters)}
k = len(clusters)
......
%% Cell type:code id: tags:
``` python
%load_ext autoreload
%autoreload 2
```
%% Cell type:code id: tags:
``` python
import os
os.chdir(os.path.dirname("../"))
```
%% Cell type:code id: tags:
``` python
import cv2
import deepof.data
import deepof.models
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import pandas as pd
import re
import seaborn as sns
from sklearn.preprocessing import StandardScaler, MinMaxScaler
import tensorflow as tf
import tqdm.notebook as tqdm
from ipywidgets import interact
```
%% Cell type:code id: tags:
``` python
from sklearn.manifold import TSNE
from sklearn.decomposition import PCA
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
```
%% Cell type:code id: tags:
``` python
import umap
```
%% Cell type:markdown id: tags:
# Retrieve phenotypes
%% Cell type:code id: tags:
``` python
flatten = lambda t: [item for sublist in t for item in sublist]
```
%% Cell type:code id: tags:
``` python
# Load first batch
dset11 = pd.ExcelFile(
"../../Desktop/deepof-data/tagged_videos/Individual_datasets/DLC_batch_1/DLC_single_CDR1_1/1.Openfield_data-part1/JB05.1-OF-SI-part1.xlsx"
)
dset12 = pd.ExcelFile(
"../../Desktop/deepof-data/tagged_videos/Individual_datasets/DLC_batch_1/DLC_single_CDR1_1/2.Openfielddata-part2/AnimalID's-JB05.1-part2.xlsx"
)
dset11 = pd.read_excel(dset11, "Tabelle2")
dset12 = pd.read_excel(dset12, "Tabelle2")
dset11.Test = dset11.Test.apply(lambda x: "Test {}_s11".format(x))
dset12.Test = dset12.Test.apply(lambda x: "Test {}_s12".format(x))
dset1 = {"CSDS":list(dset11.loc[dset11.Treatment.isin(["CTR+CSDS","NatCre+CSDS"]), "Test"]) +
list(dset12.loc[dset12.Treatment.isin(["CTR+CSDS","NatCre+CSDS"]), "Test"]),
"NS": list(dset11.loc[dset11.Treatment.isin(["CTR+nonstressed","NatCre+nonstressed"]), "Test"]) +
list(dset12.loc[dset12.Treatment.isin(["CTR+nonstressed","NatCre+nonstressed"]), "Test"]),}
dset1inv = {}
for i in flatten(list(dset1.values())):
if i in dset1["CSDS"]:
dset1inv[i] = "CSDS"
else:
dset1inv[i] = "NS"
assert len(dset1inv) == dset11.shape[0] + dset12.shape[0], "You missed some labels!"
```
%% Cell type:code id: tags:
``` python
# Load second batch
dset21 = pd.read_excel(
"../../Desktop/deepof-data/tagged_videos/Individual_datasets/DLC_batch_2/Part1/2_Single/stressproject22.04.2020genotypes-openfieldday1.xlsx"
)
dset22 = pd.read_excel(
"../../Desktop/deepof-data/tagged_videos/Individual_datasets/DLC_batch_2/Part2/2_Single/OpenFieldvideos-part2.xlsx"
)
dset21.Test = dset21.Test.apply(lambda x: "Test {}_s21".format(x))
dset22.Test = dset22.Test.apply(lambda x: "Test {}_s22".format(x))
dset2 = {"CSDS":list(dset21.loc[dset21.Treatment == "Stress", "Test"]) +
list(dset22.loc[dset22.Treatment == "Stressed", "Test"]),
"NS": list(dset21.loc[dset21.Treatment == "Nonstressed", "Test"]) +
list(dset22.loc[dset22.Treatment == "Nonstressed", "Test"])}
dset2inv = {}
for i in flatten(list(dset2.values())):
if i in dset2["CSDS"]:
dset2inv[i] = "CSDS"
else:
dset2inv[i] = "NS"
assert len(dset2inv) == dset21.shape[0] + dset22.shape[0], "You missed some labels!"
```
%% Cell type:code id: tags:
``` python
# Load third batch
dset31 = pd.read_excel(
"../../Desktop/deepof-data/tagged_videos/Individual_datasets/DLC_batch_3/1.Day2OF-SIpart1/JB05 2Female-ELS-OF-SIpart1.xlsx",
sheet_name=1
)
dset32 = pd.read_excel(
"../../Desktop/deepof-data/tagged_videos/Individual_datasets/DLC_batch_3/2.Day3OF-SIpart2/JB05 2FEMALE-ELS-OF-SIpart2.xlsx",
sheet_name=1
)
dset31.Test = dset31.Test.apply(lambda x: "Test {}_s31".format(x))
dset32.Test = dset32.Test.apply(lambda x: "Test {}_s32".format(x))
dset3 = {"CSDS":[],
"NS": list(dset31.loc[:, "Test"]) +
list(dset32.loc[:, "Test"])}
dset3inv = {}
for i in flatten(list(dset3.values())):
if i in dset3["CSDS"]:
dset3inv[i] = "CSDS"
else:
dset3inv[i] = "NS"
assert len(dset3inv) == dset31.shape[0] + dset32.shape[0], "You missed some labels!"
```
%% Cell type:code id: tags:
``` python
# Load fourth batch
dset41 = os.listdir("../../Desktop/deepof-data/tagged_videos/Individual_datasets/DLC_batch_4/JB05.4-OpenFieldvideos/")
# Remove empty video!
dset41 = [vid for vid in dset41 if "52" not in vid]
dset4 = {"CSDS":[],
"NS": [i[:-4]+"_s41" for i in dset41]}
dset4inv = {}
for i in flatten(list(dset4.values())):
if i in dset4["CSDS"]:
dset4inv[i] = "CSDS"
else:
dset4inv[i] = "NS"
assert len(dset4inv) == len(dset41), "You missed some labels!"
```
%% Cell type:code id: tags:
``` python
# Merge phenotype dicts and serialise!
aggregated_dset = {**dset1inv, **dset2inv, **dset3inv, **dset4inv}
```
%% Cell type:code id: tags:
``` python
from collections import Counter
print(Counter(aggregated_dset.values()))
print(115+52)
```
%%%% Output: stream
Counter({'NS': 115, 'CSDS': 52})
167
%% Cell type:code id: tags:
``` python
# Save aggregated dataset to disk
import pickle
with open("../../Desktop/deepof-data/deepof_single_topview/deepof_exp_conditions.pkl", "wb") as handle:
pickle.dump(aggregated_dset, handle, protocol=pickle.HIGHEST_PROTOCOL)
```
%% Cell type:markdown id: tags:
# Define and run project
%% Cell type:code id: tags:
``` python
%%time
deepof_main = deepof.data.project(path=os.path.join("..","..","Desktop","deepoftesttemp"),
smooth_alpha=0.99,
arena_dims=[380],
exclude_bodyparts=["Tail_1", "Tail_2", "Tail_tip", "Tail_base"],
exp_conditions=aggregated_dset
)
```
%%%% Output: stream
CPU times: user 111 ms, sys: 14 ms, total: 125 ms
Wall time: 123 ms
%% Cell type:code id: tags:
``` python
%%time
deepof_main = deepof_main.run(verbose=True)
print(deepof_main)
```
%%%% Output: stream
Loading trajectories...
Smoothing trajectories...
Interpolating outliers...
Iterative imputation of ocluded bodyparts...
Computing distances...
Computing angles...
Done!
Coordinates of 2 videos across 2 conditions
CPU times: user 4.8 s, sys: 806 ms, total: 5.61 s
Wall time: 4.32 s
%% Cell type:code id: tags:
``` python
all_quality = pd.concat([tab for tab in deepof_main.get_quality().values()])
```
%% Cell type:code id: tags:
``` python
all_quality.boxplot(rot=45)
plt.ylim(0.99985, 1.00001)
plt.show()
```
%%%% Output: display_data
![](data:image/png;base64,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)
%% Cell type:code id: tags:
``` python
@interact(quality_top=(0., 1., 0.01))
def low_quality_tags(quality_top):
pd.DataFrame(pd.melt(all_quality).groupby("bodyparts").value.apply(
lambda y: sum(y<quality_top) / len(y) * 100)
).sort_values(by="value", ascending=False).plot.bar(rot=45)
plt.xlabel("body part")
plt.ylabel("Tags with quality under {} (%)".format(quality_top * 100))
plt.tight_layout()
plt.legend([])
plt.show()
```
%%%% Output: display_data
%% Cell type:markdown id: tags:
# Generate coords
%% Cell type:code id: tags:
``` python
%%time
deepof_coords = deepof_main.get_coords(center="Center", polar=False, speed=0, align="Spine_1", align_inplace=True, propagate_labels=False)
#deepof_dists = deepof_main.get_distances(propagate_labels=False)
#deepof_angles = deepof_main.get_angles(propagate_labels=False)
```
%%%% Output: stream
CPU times: user 624 ms, sys: 27 ms, total: 651 ms
Wall time: 662 ms
%% Cell type:markdown id: tags:
# Visualization
%% Cell type:code id: tags:
``` python
%%time
tf.keras.backend.clear_session()
print("Preprocessing training set...")
deepof_train = deepof_coords.preprocess(
window_size=24,
window_step=24,
conv_filter=None,
scale="standard",
shuffle=False,
test_videos=0,
)[0]
# print("Loading pre-trained model...")
# encoder, decoder, grouper, gmvaep, = deepof.models.SEQ_2_SEQ_GMVAE(
# loss="ELBO",
# number_of_components=20,
# compile_model=True,
# kl_warmup_epochs=20,
# montecarlo_kl=10,
# encoding=6,
# mmd_warmup_epochs=20,
# predictor=0,
# phenotype_prediction=0,
# ).build(deepof_train.shape)[:4]
```
%%%% Output: stream
Preprocessing training set...
CPU times: user 18.1 ms, sys: 13 ms, total: 31.1 ms
Wall time: 37.4 ms
%% Cell type:code id: tags:
``` python
weights = ["./latreg_trained_weights/"+i for i in os.listdir("./latreg_trained_weights/") if "encoding=8" in i]
weights
```
%%%% Output: execute_result
['./latreg_trained_weights/GMVAE_loss=ELBO_encoding=8_k=25_latreg=none_20210212-021944_final_weights.h5',
'./latreg_trained_weights/GMVAE_loss=ELBO_encoding=8_k=25_latreg=categorical_20210212-031749_final_weights.h5',
'./latreg_trained_weights/GMVAE_loss=ELBO_encoding=8_k=25_latreg=categorical+variance_20210212-022008_final_weights.h5',
'./latreg_trained_weights/GMVAE_loss=ELBO_encoding=8_k=25_latreg=variance_20210212-023839_final_weights.h5']
%% Cell type:code id: tags:
``` python
trained_network = weights[2]
print(trained_network)
l = int(re.findall("encoding=(\d+)_", trained_network)[0])
k = int(re.findall("k=(\d+)_", trained_network)[0])
pheno = 0
encoder, decoder, grouper, gmvaep, = deepof.models.SEQ_2_SEQ_GMVAE(
loss="ELBO",
number_of_components=k,
compile_model=True,
kl_warmup_epochs=20,
montecarlo_kl=10,
encoding=l,
mmd_warmup_epochs=20,
predictor=0,
phenotype_prediction=pheno,
reg_cat_clusters=("categorical" in trained_network),
reg_cluster_variance=("variance" in trained_network),
).build(deepof_train.shape)[:4]
gmvaep.load_weights(trained_network)
```
%%%% Output: stream
./latreg_trained_weights/GMVAE_loss=ELBO_encoding=8_k=25_latreg=categorical+variance_20210212-022008_final_weights.h5
%% Cell type:code id: tags:
``` python
# Get data to pass through the models
trained_distribution = encoder(deepof_train)
categories = tf.keras.models.Model(encoder.input, encoder.layers[15].output)(deepof_train).numpy()
# Fit a scaler to unscale the reconstructions later on
video_key = np.random.choice(list(deepof_coords.keys()), 1)[0]
scaler = StandardScaler()
scaler.fit(np.array(pd.concat(list(deepof_coords.values()))))
```
%%%% Output: execute_result
StandardScaler()
%% Cell type:code id: tags:
``` python
# Retrieve latent distribution parameters and sample from posterior
def get_median_params(component, categories, cluster, param):
# means = [np.median(component.mean().numpy(), axis=0) for component in mix_components]
# stddevs = [np.median(component.stddev().numpy(), axis=0) for component in mix_components]
if param == "mean":
component = component.mean().numpy()
elif param == "stddev":
component = component.stddev().numpy()
cluster_select = np.argmax(categories, axis=1)==cluster
if np.sum(cluster_select) == 0:
return None
component = component[cluster_select]
return np.median(component, axis=0)
```
%% Cell type:code id: tags:
``` python
def retrieve_latent_parameters(distribution, reduce=False, plot=False, categories=None, filt=0, save=True):
mix_components = distribution.components
# The main problem is here! We need to select only those training instances in which a given cluster was selected.
# Then compute the median for those only
means = [get_median_params(component, categories, i, "mean") for i,component in enumerate(mix_components)]
stddevs = [get_median_params(component, categories, i, "stddev") for i,component in enumerate(mix_components)]
means = [i for i in means if i is not None]
stddevs = [i for i in stddevs if i is not None]
if filter:
filts = np.max(categories, axis=0) > filt
means = [i for i,j in zip(means, filts) if j]
stddevs = [i for i,j in zip(stddevs, filts) if j]
if reduce:
data = [np.random.normal(size=[1000, len(means[0])],
loc=meanvec,
scale=stddevvec)[:,np.newaxis] for meanvec, stddevvec in zip(means, stddevs)]
data = np.concatenate(data, axis=1).reshape([1000*len(means), len(means[0])])
reducer = PCA(n_components=3)
data = reducer.fit_transform(data)
data = data.reshape([1000, len(means), 3])
if plot == 2:
for i in range(len(means)):
plt.scatter(data[:,i,0], data[:,i,1], label=i)
plt.title("Mean representation of latent space - K={}/{} - L={} - filt={}".format(len(means),
len(mix_components),
len(means[0]), filt))
plt.xlabel("PCA 1")
plt.ylabel("PCA 2")
#plt.legend()
if save:
plt.savefig("Mean representation of latent space - K={}.{} - L={} - filt={}.png".format(len(means),
len(mix_components),
len(means[0]), filt).replace(" ", "_"))
plt.show()
elif plot == 3:
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
for i in range(len(means)):
ax.scatter(data[:,i,0], data[:,i,1], data[:,i,2], label=i)
plt.title("Mean representation of latent space - K={}/{} - L={} - filt={}".format(len(means),
len(mix_components),
len(means[0]), filt))
ax.set_xlabel("PCA 1")
ax.set_ylabel("PCA 2")
ax.set_zlabel("PCA 3")
#plt.legend()
if save:
plt.savefig("Mean representation of latent space - K={}.{} - L={} - filt={}.png".format(len(means),
len(mix_components),
len(means[0]), filt).replace(" ", "_"))
plt.show()
elif plot > 3:
raise ValueError("Can't plot in more than 3 dimensions!")
return means, stddevs
def sample_from_posterior(decoder, parameters, component, enable_variance=False, video_output=False, samples=1):
means, stddevs = parameters
sample = np.random.normal(size=[samples, len(means[component])], loc=means[component], scale=(stddevs[component] if enable_variance else 0))