added plotting shellscript

This commit is contained in:
weygoldt 2023-02-21 13:59:22 +01:00
parent c520dedc58
commit 3ddec7d0b3
39 changed files with 2536 additions and 1722 deletions

1421
chirpdetection.bib Normal file

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@ import matplotlib.pyplot as plt
import matplotlib.gridspec as gr
from scipy.signal import find_peaks
from thunderfish.powerspectrum import spectrogram, decibel
from sklearn.preprocessing import normalize
# from sklearn.preprocessing import normalize
from modules.filters import bandpass_filter, envelope, highpass_filter
from modules.filehandling import ConfLoader, LoadData, make_outputdir
@ -18,7 +18,7 @@ from modules.datahandling import (
purge_duplicates,
group_timestamps,
instantaneous_frequency,
minmaxnorm
minmaxnorm,
)
logger = makeLogger(__name__)
@ -101,13 +101,9 @@ class ChirpPlotBuffer:
self.t0_old = self.t0
self.t0 = 0
fig = plt.figure(
figsize=(14 * ps.cm, 18 * ps.cm)
)
fig = plt.figure(figsize=(14 * ps.cm, 18 * ps.cm))
gs0 = gr.GridSpec(
3, 1, figure=fig, height_ratios=[1, 1, 1]
)
gs0 = gr.GridSpec(3, 1, figure=fig, height_ratios=[1, 1, 1])
gs1 = gs0[0].subgridspec(1, 1)
gs2 = gs0[1].subgridspec(3, 1, hspace=0.4)
gs3 = gs0[2].subgridspec(3, 1, hspace=0.4)
@ -133,10 +129,9 @@ class ChirpPlotBuffer:
data_oi,
self.data.raw_rate,
self.t0 - 5,
[np.min(self.frequency) - 300, np.max(self.frequency) + 300]
[np.min(self.frequency) - 300, np.max(self.frequency) + 300],
)
ax0.set_ylim(np.min(self.frequency) - 100,
np.max(self.frequency) + 200)
ax0.set_ylim(np.min(self.frequency) - 100, np.max(self.frequency) + 200)
for track_id in self.data.ids:
@ -155,11 +150,9 @@ class ChirpPlotBuffer:
# tmask = (t >= t0_track) & (t <= (t0_track + dt_track))
t = self.data.time[self.data.idx[window_idx]]
if track_id == self.track_id:
ax0.plot(t-self.t0_old, f, lw=lw,
zorder=10, color=ps.gblue1)
ax0.plot(t - self.t0_old, f, lw=lw, zorder=10, color=ps.gblue1)
else:
ax0.plot(t-self.t0_old, f, lw=lw,
zorder=10, color=ps.black)
ax0.plot(t - self.t0_old, f, lw=lw, zorder=10, color=ps.black)
# ax0.fill_between(
# np.arange(self.t0, self.t0 + self.dt, 1 / self.data.raw_rate),
@ -181,10 +174,12 @@ class ChirpPlotBuffer:
# alpha=0.5,
# )
ax0.axhline(q50 - self.config.minimal_bandwidth / 2,
color=ps.gblue1, lw=1, ls="dashed")
ax0.axhline(q50 + self.config.minimal_bandwidth / 2,
color=ps.gblue1, lw=1, ls="dashed")
ax0.axhline(
q50 - self.config.minimal_bandwidth / 2, color=ps.gblue1, lw=1, ls="dashed"
)
ax0.axhline(
q50 + self.config.minimal_bandwidth / 2, color=ps.gblue1, lw=1, ls="dashed"
)
ax0.axhline(search_lower, color=ps.gblue2, lw=1, ls="dashed")
ax0.axhline(search_upper, color=ps.gblue2, lw=1, ls="dashed")
@ -197,7 +192,10 @@ class ChirpPlotBuffer:
if len(chirps) > 0:
for chirp in chirps:
ax0.scatter(
chirp, np.median(self.frequency), c=ps.red, marker=".",
chirp,
np.median(self.frequency),
c=ps.red,
marker=".",
edgecolors=ps.black,
facecolors=ps.red,
zorder=10,
@ -205,27 +203,43 @@ class ChirpPlotBuffer:
)
# plot waveform of filtered signal
ax1.plot(self.time, self.baseline * waveform_scaler,
c=ps.gray, lw=lw, alpha=0.5)
ax1.plot(self.time, self.baseline_envelope_unfiltered *
waveform_scaler, c=ps.gblue1, lw=lw, label="baseline envelope")
ax1.plot(
self.time, self.baseline * waveform_scaler, c=ps.gray, lw=lw, alpha=0.5
)
ax1.plot(
self.time,
self.baseline_envelope_unfiltered * waveform_scaler,
c=ps.gblue1,
lw=lw,
label="baseline envelope",
)
# plot waveform of filtered search signal
ax2.plot(self.time, self.search * waveform_scaler,
c=ps.gray, lw=lw, alpha=0.5)
ax2.plot(self.time, self.search_envelope_unfiltered *
waveform_scaler, c=ps.gblue2, lw=lw, label="search envelope")
ax2.plot(self.time, self.search * waveform_scaler, c=ps.gray, lw=lw, alpha=0.5)
ax2.plot(
self.time,
self.search_envelope_unfiltered * waveform_scaler,
c=ps.gblue2,
lw=lw,
label="search envelope",
)
# plot baseline instantaneous frequency
ax3.plot(self.frequency_time, self.frequency,
c=ps.gblue3, lw=lw, label="baseline inst. freq.")
ax3.plot(
self.frequency_time,
self.frequency,
c=ps.gblue3,
lw=lw,
label="baseline inst. freq.",
)
# plot filtered and rectified envelope
ax4.plot(self.time, self.baseline_envelope *
waveform_scaler, c=ps.gblue1, lw=lw)
ax4.plot(
self.time, self.baseline_envelope * waveform_scaler, c=ps.gblue1, lw=lw
)
ax4.scatter(
(self.time)[self.baseline_peaks],
(self.baseline_envelope*waveform_scaler)[self.baseline_peaks],
(self.baseline_envelope * waveform_scaler)[self.baseline_peaks],
edgecolors=ps.black,
facecolors=ps.red,
zorder=10,
@ -235,11 +249,10 @@ class ChirpPlotBuffer:
)
# plot envelope of search signal
ax5.plot(self.time, self.search_envelope *
waveform_scaler, c=ps.gblue2, lw=lw)
ax5.plot(self.time, self.search_envelope * waveform_scaler, c=ps.gblue2, lw=lw)
ax5.scatter(
(self.time)[self.search_peaks],
(self.search_envelope*waveform_scaler)[self.search_peaks],
(self.search_envelope * waveform_scaler)[self.search_peaks],
edgecolors=ps.black,
facecolors=ps.red,
zorder=10,
@ -249,8 +262,7 @@ class ChirpPlotBuffer:
)
# plot filtered instantaneous frequency
ax6.plot(self.frequency_time,
self.frequency_filtered, c=ps.gblue3, lw=lw)
ax6.plot(self.frequency_time, self.frequency_filtered, c=ps.gblue3, lw=lw)
ax6.scatter(
self.frequency_time[self.frequency_peaks],
self.frequency_filtered[self.frequency_peaks],
@ -284,8 +296,7 @@ class ChirpPlotBuffer:
# ax7.spines.bottom.set_bounds((0, 5))
ax0.set_xlim(0, self.config.window)
plt.subplots_adjust(left=0.165, right=0.975,
top=0.98, bottom=0.074, hspace=0.2)
plt.subplots_adjust(left=0.165, right=0.975, top=0.98, bottom=0.074, hspace=0.2)
fig.align_labels()
if plot == "show":
@ -306,7 +317,7 @@ def plot_spectrogram(
signal: np.ndarray,
samplerate: float,
window_start_seconds: float,
ylims: list[float]
ylims: list[float],
) -> np.ndarray:
"""
Plot a spectrogram of a signal.
@ -390,9 +401,7 @@ def extract_frequency_bands(
q25, q75 = q50 - minimal_bandwidth / 2, q50 + minimal_bandwidth / 2
# filter baseline
filtered_baseline = bandpass_filter(
raw_data, samplerate, lowf=q25, highf=q75
)
filtered_baseline = bandpass_filter(raw_data, samplerate, lowf=q25, highf=q75)
# filter search area
filtered_search_freq = bandpass_filter(
@ -442,15 +451,13 @@ def window_median_all_track_ids(
window_idx = np.arange(len(data.idx))[
(data.ident == track_id)
& (data.time[data.idx] >= window_start_seconds)
& (
data.time[data.idx]
<= (window_start_seconds + window_duration_seconds)
)
& (data.time[data.idx] <= (window_start_seconds + window_duration_seconds))
]
if len(data.freq[window_idx]) > 0:
frequency_percentiles.append(
np.percentile(data.freq[window_idx], [25, 50, 75]))
np.percentile(data.freq[window_idx], [25, 50, 75])
)
track_ids.append(track_id)
# convert to numpy array
@ -477,7 +484,7 @@ def array_center(array: np.ndarray) -> float:
"""
if len(array) % 2 == 0:
return np.mean(array[int(len(array) / 2) - 1:int(len(array) / 2) + 1])
return np.mean(array[int(len(array) / 2) - 1 : int(len(array) / 2) + 1])
else:
return array[int(len(array) / 2)]
@ -533,8 +540,7 @@ def find_searchband(
# get tracks that fall into search window
check_track_ids = percentiles_ids[
(q25 > current_median) & (
q75 < search_window[-1])
(q25 > current_median) & (q75 < search_window[-1])
]
# iterate through theses tracks
@ -547,8 +553,7 @@ def find_searchband(
bool_lower[search_window > q25_temp - config.search_res] = False
bool_upper[search_window < q75_temp + config.search_res] = False
search_window_bool[(bool_lower == False) &
(bool_upper == False)] = False
search_window_bool[(bool_lower == False) & (bool_upper == False)] = False
# find gaps in search window
search_window_indices = np.arange(len(search_window))
@ -567,9 +572,7 @@ def find_searchband(
# if the first value is -1, the array starst with true, so a gap
if nonzeros[0] == -1:
stops = search_window_indices[search_window_gaps == -1]
starts = np.append(
0, search_window_indices[search_window_gaps == 1]
)
starts = np.append(0, search_window_indices[search_window_gaps == 1])
# if the last value is -1, the array ends with true, so a gap
if nonzeros[-1] == 1:
@ -605,7 +608,7 @@ def find_searchband(
return config.default_search_freq
def chirpdetection(datapath: str, plot: str, debug: str = 'false') -> None:
def chirpdetection(datapath: str, plot: str, debug: str = "false") -> None:
assert plot in [
"save",
@ -623,7 +626,7 @@ def chirpdetection(datapath: str, plot: str, debug: str = 'false') -> None:
assert plot == "show", "debug mode only runs when plot is 'show'"
# load raw file
print('datapath', datapath)
print("datapath", datapath)
data = LoadData(datapath)
# load config file
@ -691,9 +694,7 @@ def chirpdetection(datapath: str, plot: str, debug: str = 'false') -> None:
)
# iterate through all fish
for tr, track_id in enumerate(
np.unique(data.ident[~np.isnan(data.ident)])
):
for tr, track_id in enumerate(np.unique(data.ident[~np.isnan(data.ident)])):
logger.debug(f"Processing track {tr} of {len(data.ids)}")
@ -714,11 +715,11 @@ def chirpdetection(datapath: str, plot: str, debug: str = 'false') -> None:
# approximate sampling rate to compute expected durations if there
# is data available for this time window for this fish id
# track_samplerate = np.mean(1 / np.diff(data.time))
# expected_duration = (
# (window_start_seconds + window_duration_seconds)
# - window_start_seconds
# ) * track_samplerate
# track_samplerate = np.mean(1 / np.diff(data.time))
# expected_duration = (
# (window_start_seconds + window_duration_seconds)
# - window_start_seconds
# ) * track_samplerate
# check if tracked data available in this window
if len(current_frequencies) < 3:
@ -731,17 +732,16 @@ def chirpdetection(datapath: str, plot: str, debug: str = 'false') -> None:
nanchecker = np.unique(np.isnan(current_powers))
if (len(nanchecker) == 1) and nanchecker[0] is True:
logger.warning(
f"No powers available for track {track_id} window {st},"
"skipping."
f"No powers available for track {track_id} window {st}," "skipping."
)
continue
# find the strongest electrodes for the current fish in the current
# window
best_electrode_index = np.argsort(
np.nanmean(current_powers, axis=0)
)[-config.number_electrodes:]
best_electrode_index = np.argsort(np.nanmean(current_powers, axis=0))[
-config.number_electrodes :
]
# find a frequency above the baseline of the current fish in which
# no other fish is active to search for chirps there
@ -763,8 +763,7 @@ def chirpdetection(datapath: str, plot: str, debug: str = 'false') -> None:
for el, electrode_index in enumerate(best_electrode_index):
logger.debug(
f"Processing electrode {el+1} of "
f"{len(best_electrode_index)}"
f"Processing electrode {el+1} of " f"{len(best_electrode_index)}"
)
# LOAD DATA FOR CURRENT ELECTRODE AND CURRENT FISH ------------
@ -773,9 +772,7 @@ def chirpdetection(datapath: str, plot: str, debug: str = 'false') -> None:
current_raw_data = data.raw[
window_start_index:window_stop_index, electrode_index
]
current_raw_time = raw_time[
window_start_index:window_stop_index
]
current_raw_time = raw_time[window_start_index:window_stop_index]
# EXTRACT FEATURES --------------------------------------------
@ -897,9 +894,7 @@ def chirpdetection(datapath: str, plot: str, debug: str = 'false') -> None:
baseline_frequency_time <= no_edges_t1
)
baseline_frequency_filtered = baseline_frequency_filtered[
no_edges
]
baseline_frequency_filtered = baseline_frequency_filtered[no_edges]
baseline_frequency = baseline_frequency[no_edges]
baseline_frequency_time = (
baseline_frequency_time[no_edges] + window_start_seconds
@ -934,11 +929,8 @@ def chirpdetection(datapath: str, plot: str, debug: str = 'false') -> None:
# DETECT CHIRPS IN SEARCH WINDOW ------------------------------
# get the peak timestamps from the peak indices
baseline_peak_timestamps = current_raw_time[
baseline_peak_indices
]
search_peak_timestamps = current_raw_time[
search_peak_indices]
baseline_peak_timestamps = current_raw_time[baseline_peak_indices]
search_peak_timestamps = current_raw_time[search_peak_indices]
frequency_peak_timestamps = baseline_frequency_time[
frequency_peak_indices
@ -953,7 +945,7 @@ def chirpdetection(datapath: str, plot: str, debug: str = 'false') -> None:
or len(frequency_peak_timestamps) == 0
)
if one_feature_empty and (debug == 'false'):
if one_feature_empty and (debug == "false"):
continue
# group peak across feature arrays but only if they
@ -974,18 +966,18 @@ def chirpdetection(datapath: str, plot: str, debug: str = 'false') -> None:
# check it there are chirps detected after grouping, continue
# with the loop if not
if (len(singleelectrode_chirps) == 0) and (debug == 'false'):
if (len(singleelectrode_chirps) == 0) and (debug == "false"):
continue
# append chirps from this electrode to the multilectrode list
multielectrode_chirps.append(singleelectrode_chirps)
# only initialize the plotting buffer if chirps are detected
chirp_detected = (el == (config.number_electrodes - 1)
& (plot in ["show", "save"])
)
chirp_detected = el == (config.number_electrodes - 1) & (
plot in ["show", "save"]
)
if chirp_detected or (debug != 'elecrode'):
if chirp_detected or (debug != "elecrode"):
logger.debug("Detected chirp, ititialize buffer ...")
@ -1016,9 +1008,8 @@ def chirpdetection(datapath: str, plot: str, debug: str = 'false') -> None:
logger.debug("Buffer initialized!")
if debug == "electrode":
logger.info(f'Plotting electrode {el} ...')
buffer.plot_buffer(
chirps=singleelectrode_chirps, plot=plot)
logger.info(f"Plotting electrode {el} ...")
buffer.plot_buffer(chirps=singleelectrode_chirps, plot=plot)
logger.debug(
f"Processed all electrodes for fish {track_id} for this"
@ -1028,7 +1019,7 @@ def chirpdetection(datapath: str, plot: str, debug: str = 'false') -> None:
# check if there are chirps detected in multiple electrodes and
# continue the loop if not
if (len(multielectrode_chirps) == 0) and (debug == 'false'):
if (len(multielectrode_chirps) == 0) and (debug == "false"):
continue
# validate multielectrode chirps, i.e. check if they are
@ -1054,11 +1045,14 @@ def chirpdetection(datapath: str, plot: str, debug: str = 'false') -> None:
# chirps, otheswise try to delete the buffer if it exists
if debug == "fish":
logger.info(f'Plotting fish {track_id} ...')
logger.info(f"Plotting fish {track_id} ...")
buffer.plot_buffer(multielectrode_chirps_validated, plot)
if ((len(multielectrode_chirps_validated) > 0) &
(plot in ["show", "save"]) & (debug == 'false')):
if (
(len(multielectrode_chirps_validated) > 0)
& (plot in ["show", "save"])
& (debug == "false")
):
try:
buffer.plot_buffer(multielectrode_chirps_validated, plot)
del buffer
@ -1085,9 +1079,7 @@ def chirpdetection(datapath: str, plot: str, debug: str = 'false') -> None:
# add flattened chirps to the list
multiwindow_chirps_flat.extend(current_track_chirps)
multiwindow_ids_flat.extend(
list(np.ones_like(current_track_chirps) * track_id)
)
multiwindow_ids_flat.extend(list(np.ones_like(current_track_chirps) * track_id))
# purge duplicates, i.e. chirps that are very close to each other
# duplites arise due to overlapping windows

View File

@ -16,8 +16,8 @@ def PlotStyle() -> None:
# colors
black = "#111116"
white = "#e0e4f7"
gray = "#6c6e7d"
white = "#111116"
gray = "#111116"
blue = "#89b4fa"
sapphire = "#74c7ec"
sky = "#89dceb"
@ -216,10 +216,10 @@ def PlotStyle() -> None:
SMALL_SIZE = 12
MEDIUM_SIZE = 14
BIGGER_SIZE = 16
black = "#111116"
white = "#e0e4f7"
gray = "#6c6e7d"
dark_gray = "#2a2a32"
black = "#e0e4f7"
white = "#111116"
# gray = "#6c6e7d"
# dark_gray = "#2a2a32"
# rcparams
plt.rc("font", size=MEDIUM_SIZE) # controls default text sizes
@ -250,46 +250,46 @@ def PlotStyle() -> None:
# plt.rcParams["font.sans-serif"] = "Helvetica Now Text"
# dark mode modifications
plt.rcParams["boxplot.flierprops.color"] = white
plt.rcParams["boxplot.flierprops.markeredgecolor"] = gray
plt.rcParams["boxplot.boxprops.color"] = gray
plt.rcParams["boxplot.whiskerprops.color"] = gray
plt.rcParams["boxplot.capprops.color"] = gray
plt.rcParams["boxplot.medianprops.color"] = black
plt.rcParams["text.color"] = white
plt.rcParams["axes.facecolor"] = black # axes background color
plt.rcParams["axes.edgecolor"] = white # axes edge color
# plt.rcParams["axes.grid"] = True # display grid or not
# plt.rcParams["axes.grid.axis"] = "y" # which axis the grid is applied to
plt.rcParams["axes.labelcolor"] = white
plt.rcParams["axes.axisbelow"] = True # draw axis gridlines and ticks:
plt.rcParams["axes.spines.left"] = True # display axis spines
plt.rcParams["axes.spines.bottom"] = True
plt.rcParams["axes.spines.top"] = False
plt.rcParams["axes.spines.right"] = False
plt.rcParams["axes.prop_cycle"] = cycler(
"color",
[
"#b4befe",
"#89b4fa",
"#74c7ec",
"#89dceb",
"#94e2d5",
"#a6e3a1",
"#f9e2af",
"#fab387",
"#eba0ac",
"#f38ba8",
"#cba6f7",
"#f5c2e7",
],
)
plt.rcParams["xtick.color"] = white # color of the ticks
plt.rcParams["ytick.color"] = white # color of the ticks
plt.rcParams["grid.color"] = white # grid color
plt.rcParams["figure.facecolor"] = black # figure face color
plt.rcParams["figure.edgecolor"] = black # figure edge color
plt.rcParams["savefig.facecolor"] = black # figure face color when saving
# plt.rcParams["boxplot.flierprops.color"] = white
# plt.rcParams["boxplot.flierprops.markeredgecolor"] = gray
# plt.rcParams["boxplot.boxprops.color"] = gray
# plt.rcParams["boxplot.whiskerprops.color"] = gray
# plt.rcParams["boxplot.capprops.color"] = gray
# plt.rcParams["boxplot.medianprops.color"] = black
# plt.rcParams["text.color"] = white
# plt.rcParams["axes.facecolor"] = black # axes background color
# plt.rcParams["axes.edgecolor"] = white # axes edge color
# # plt.rcParams["axes.grid"] = True # display grid or not
# # plt.rcParams["axes.grid.axis"] = "y" # which axis the grid is applied to
# plt.rcParams["axes.labelcolor"] = white
# plt.rcParams["axes.axisbelow"] = True # draw axis gridlines and ticks:
# plt.rcParams["axes.spines.left"] = True # display axis spines
# plt.rcParams["axes.spines.bottom"] = True
# plt.rcParams["axes.spines.top"] = False
# plt.rcParams["axes.spines.right"] = False
# plt.rcParams["axes.prop_cycle"] = cycler(
# "color",
# [
# "#b4befe",
# "#89b4fa",
# "#74c7ec",
# "#89dceb",
# "#94e2d5",
# "#a6e3a1",
# "#f9e2af",
# "#fab387",
# "#eba0ac",
# "#f38ba8",
# "#cba6f7",
# "#f5c2e7",
# ],
# )
# plt.rcParams["xtick.color"] = white # color of the ticks
# plt.rcParams["ytick.color"] = white # color of the ticks
# plt.rcParams["grid.color"] = white # grid color
# plt.rcParams["figure.facecolor"] = black # figure face color
# plt.rcParams["figure.edgecolor"] = black # figure edge color
# plt.rcParams["savefig.facecolor"] = black # figure face color when saving
return style

View File

@ -0,0 +1,407 @@
import cmocean as cmo
import matplotlib.pyplot as plt
import numpy as np
from cycler import cycler
from matplotlib.colors import ListedColormap
def PlotStyle() -> None:
class style:
# lightcmap = cmocean.tools.lighten(cmocean.cm.haline, 0.8)
# units
cm = 1 / 2.54
mm = 1 / 25.4
# colors
black = "#111116"
white = "#e0e4f7"
gray = "#6c6e7d"
blue = "#89b4fa"
sapphire = "#74c7ec"
sky = "#89dceb"
teal = "#94e2d5"
green = "#a6e3a1"
yellow = "#f9d67f"
orange = "#faa472"
maroon = "#eb8486"
red = "#e0e4f7"
purple = "#d89bf7"
pink = "#f59edb"
lavender = "#b4befe"
gblue1 = "#f37588"
gblue2 = "#faa472"
gblue3 = "#f9d67f"
g = "#f3626c"
@classmethod
def lims(cls, track1, track2):
"""Helper function to get frequency y axis limits from two
fundamental frequency tracks.
Args:
track1 (array): First track
track2 (array): Second track
start (int): Index for first value to be plotted
stop (int): Index for second value to be plotted
padding (int): Padding for the upper and lower limit
Returns:
lower (float): lower limit
upper (float): upper limit
"""
allfunds_tmp = (
np.concatenate(
[
track1,
track2,
]
)
.ravel()
.tolist()
)
lower = np.min(allfunds_tmp)
upper = np.max(allfunds_tmp)
return lower, upper
@classmethod
def circled_annotation(cls, text, axis, xpos, ypos, padding=0.25):
axis.text(
xpos,
ypos,
text,
ha="center",
va="center",
zorder=1000,
bbox=dict(
boxstyle=f"circle, pad={padding}", fc="white", ec="black", lw=1
),
)
@classmethod
def fade_cmap(cls, cmap):
my_cmap = cmap(np.arange(cmap.N))
my_cmap[:, -1] = np.linspace(0, 1, cmap.N)
my_cmap = ListedColormap(my_cmap)
return my_cmap
@classmethod
def hide_ax(cls, ax):
ax.xaxis.set_visible(False)
plt.setp(ax.spines.values(), visible=False)
ax.tick_params(left=False, labelleft=False)
ax.patch.set_visible(False)
@classmethod
def hide_xax(cls, ax):
ax.xaxis.set_visible(False)
ax.spines["bottom"].set_visible(False)
@classmethod
def hide_yax(cls, ax):
ax.yaxis.set_visible(False)
ax.spines["left"].set_visible(False)
@classmethod
def set_boxplot_color(cls, bp, color):
plt.setp(bp["boxes"], color=color)
plt.setp(bp["whiskers"], color=white)
plt.setp(bp["caps"], color=white)
plt.setp(bp["medians"], color=black)
@classmethod
def label_subplots(cls, labels, axes, fig):
for axis, label in zip(axes, labels):
X = axis.get_position().x0
Y = axis.get_position().y1
fig.text(X, Y, label, weight="bold")
@classmethod
def letter_subplots(
cls, axes=None, letters=None, xoffset=-0.1, yoffset=1.0, **kwargs
):
"""Add letters to the corners of subplots (panels). By default each axis is
given an uppercase bold letter label placed in the upper-left corner.
Args
axes : list of pyplot ax objects. default plt.gcf().axes.
letters : list of strings to use as labels, default ["A", "B", "C", ...]
xoffset, yoffset : positions of each label relative to plot frame
(default -0.1,1.0 = upper left margin). Can also be a list of
offsets, in which case it should be the same length as the number of
axes.
Other keyword arguments will be passed to annotate() when panel letters
are added.
Returns:
list of strings for each label added to the axes
Examples:
Defaults:
>>> fig, axes = plt.subplots(1,3)
>>> letter_subplots() # boldfaced A, B, C
Common labeling schemes inferred from the first letter:
>>> fig, axes = plt.subplots(1,4)
# panels labeled (a), (b), (c), (d)
>>> letter_subplots(letters='(a)')
Fully custom lettering:
>>> fig, axes = plt.subplots(2,1)
>>> letter_subplots(axes, letters=['(a.1)', '(b.2)'], fontweight='normal')
Per-axis offsets:
>>> fig, axes = plt.subplots(1,2)
>>> letter_subplots(axes, xoffset=[-0.1, -0.15])
Matrix of axes:
>>> fig, axes = plt.subplots(2,2, sharex=True, sharey=True)
# fig.axes is a list when axes is a 2x2 matrix
>>> letter_subplots(fig.axes)
"""
# get axes:
if axes is None:
axes = plt.gcf().axes
# handle single axes:
try:
iter(axes)
except TypeError:
axes = [axes]
# set up letter defaults (and corresponding fontweight):
fontweight = "bold"
ulets = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ"[: len(axes)])
llets = list("abcdefghijklmnopqrstuvwxyz"[: len(axes)])
if letters is None or letters == "A":
letters = ulets
elif letters == "(a)":
letters = ["({})".format(lett) for lett in llets]
fontweight = "normal"
elif letters == "(A)":
letters = ["({})".format(lett) for lett in ulets]
fontweight = "normal"
elif letters in ("lower", "lowercase", "a"):
letters = llets
# make sure there are x and y offsets for each ax in axes:
if isinstance(xoffset, (int, float)):
xoffset = [xoffset] * len(axes)
else:
assert len(xoffset) == len(axes)
if isinstance(yoffset, (int, float)):
yoffset = [yoffset] * len(axes)
else:
assert len(yoffset) == len(axes)
# defaults for annotate (kwargs is second so it can overwrite these defaults):
my_defaults = dict(
fontweight=fontweight,
fontsize="large",
ha="center",
va="center",
xycoords="axes fraction",
annotation_clip=False,
)
kwargs = dict(list(my_defaults.items()) + list(kwargs.items()))
list_txts = []
for ax, lbl, xoff, yoff in zip(axes, letters, xoffset, yoffset):
t = ax.annotate(lbl, xy=(xoff, yoff), **kwargs)
list_txts.append(t)
return list_txts
pass
# rcparams text setup
SMALL_SIZE = 12
MEDIUM_SIZE = 14
BIGGER_SIZE = 16
black = "#111116"
white = "#e0e4f7"
gray = "#6c6e7d"
dark_gray = "#2a2a32"
# rcparams
plt.rc("font", size=MEDIUM_SIZE) # controls default text sizes
plt.rc("axes", titlesize=MEDIUM_SIZE) # fontsize of the axes title
plt.rc("axes", labelsize=MEDIUM_SIZE) # fontsize of the x and y labels
plt.rc("xtick", labelsize=SMALL_SIZE) # fontsize of the tick labels
plt.rc("ytick", labelsize=SMALL_SIZE) # fontsize of the tick labels
plt.rc("legend", fontsize=SMALL_SIZE) # legend fontsize
plt.rc("figure", titlesize=BIGGER_SIZE) # fontsize of the figure title
plt.rcParams["image.cmap"] = "cmo.thermal"
plt.rcParams["axes.xmargin"] = 0.05
plt.rcParams["axes.ymargin"] = 0.1
plt.rcParams["axes.titlelocation"] = "left"
plt.rcParams["axes.titlesize"] = BIGGER_SIZE
# plt.rcParams["axes.titlepad"] = -10
plt.rcParams["legend.frameon"] = False
plt.rcParams["legend.loc"] = "best"
plt.rcParams["legend.borderpad"] = 0.4
plt.rcParams["legend.facecolor"] = black
plt.rcParams["legend.edgecolor"] = black
plt.rcParams["legend.framealpha"] = 0.7
plt.rcParams["legend.borderaxespad"] = 0.5
plt.rcParams["legend.fancybox"] = False
# # specify the custom font to use
# plt.rcParams["font.family"] = "sans-serif"
# plt.rcParams["font.sans-serif"] = "Helvetica Now Text"
# dark mode modifications
plt.rcParams["boxplot.flierprops.color"] = white
plt.rcParams["boxplot.flierprops.markeredgecolor"] = gray
plt.rcParams["boxplot.boxprops.color"] = gray
plt.rcParams["boxplot.whiskerprops.color"] = gray
plt.rcParams["boxplot.capprops.color"] = gray
plt.rcParams["boxplot.medianprops.color"] = black
plt.rcParams["text.color"] = white
plt.rcParams["axes.facecolor"] = black # axes background color
plt.rcParams["axes.edgecolor"] = white # axes edge color
# plt.rcParams["axes.grid"] = True # display grid or not
# plt.rcParams["axes.grid.axis"] = "y" # which axis the grid is applied to
plt.rcParams["axes.labelcolor"] = white
plt.rcParams["axes.axisbelow"] = True # draw axis gridlines and ticks:
plt.rcParams["axes.spines.left"] = True # display axis spines
plt.rcParams["axes.spines.bottom"] = True
plt.rcParams["axes.spines.top"] = False
plt.rcParams["axes.spines.right"] = False
plt.rcParams["axes.prop_cycle"] = cycler(
"color",
[
"#b4befe",
"#89b4fa",
"#74c7ec",
"#89dceb",
"#94e2d5",
"#a6e3a1",
"#f9e2af",
"#fab387",
"#eba0ac",
"#f38ba8",
"#cba6f7",
"#f5c2e7",
],
)
plt.rcParams["xtick.color"] = white # color of the ticks
plt.rcParams["ytick.color"] = white # color of the ticks
plt.rcParams["grid.color"] = white # grid color
plt.rcParams["figure.facecolor"] = black # figure face color
plt.rcParams["figure.edgecolor"] = black # figure edge color
plt.rcParams["savefig.facecolor"] = black # figure face color when saving
return style
if __name__ == "__main__":
s = PlotStyle()
import matplotlib.cbook as cbook
import matplotlib.cm as cm
import matplotlib.pyplot as plt
from matplotlib.patches import PathPatch
from matplotlib.path import Path
# Fixing random state for reproducibility
np.random.seed(19680801)
delta = 0.025
x = y = np.arange(-3.0, 3.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-(X**2) - Y**2)
Z2 = np.exp(-((X - 1) ** 2) - (Y - 1) ** 2)
Z = (Z1 - Z2) * 2
fig1, ax = plt.subplots()
im = ax.imshow(
Z,
interpolation="bilinear",
cmap=cm.RdYlGn,
origin="lower",
extent=[-3, 3, -3, 3],
vmax=abs(Z).max(),
vmin=-abs(Z).max(),
)
plt.show()
fig, axs = plt.subplots(nrows=1, ncols=2, figsize=(9, 4))
# Fixing random state for reproducibility
np.random.seed(19680801)
# generate some random test data
all_data = [np.random.normal(0, std, 100) for std in range(6, 10)]
# plot violin plot
axs[0].violinplot(all_data, showmeans=False, showmedians=True)
axs[0].set_title("Violin plot")
# plot box plot
axs[1].boxplot(all_data)
axs[1].set_title("Box plot")
# adding horizontal grid lines
for ax in axs:
ax.yaxis.grid(True)
ax.set_xticks(
[y + 1 for y in range(len(all_data))], labels=["x1", "x2", "x3", "x4"]
)
ax.set_xlabel("Four separate samples")
ax.set_ylabel("Observed values")
plt.show()
# Fixing random state for reproducibility
np.random.seed(19680801)
# Compute pie slices
N = 20
theta = np.linspace(0.0, 2 * np.pi, N, endpoint=False)
radii = 10 * np.random.rand(N)
width = np.pi / 4 * np.random.rand(N)
colors = cmo.cm.haline(radii / 10.0)
ax = plt.subplot(projection="polar")
ax.bar(theta, radii, width=width, bottom=0.0, color=colors, alpha=0.5)
plt.show()
methods = [
None,
"none",
"nearest",
"bilinear",
"bicubic",
"spline16",
"spline36",
"hanning",
"hamming",
"hermite",
"kaiser",
"quadric",
"catrom",
"gaussian",
"bessel",
"mitchell",
"sinc",
"lanczos",
]
# Fixing random state for reproducibility
np.random.seed(19680801)
grid = np.random.rand(4, 4)
fig, axs = plt.subplots(
nrows=3, ncols=6, figsize=(9, 6), subplot_kw={"xticks": [], "yticks": []}
)
for ax, interp_method in zip(axs.flat, methods):
ax.imshow(grid, interpolation=interp_method)
ax.set_title(str(interp_method))
plt.tight_layout()
plt.show()

View File

@ -277,7 +277,7 @@ def main(datapath: str):
scatterloser, chirps_loser, color=loser_color)
ax1.set_xticklabels(['Winner', 'Loser'])
ax1.text(0.1, 0.95, f'n={len(chirps_winner)}',
ax1.text(0.1, 0.85, f'n={len(chirps_loser)}',
transform=ax1.transAxes, color=ps.white)
for w, l in zip(chirps_winner, chirps_loser):
@ -294,7 +294,7 @@ def main(datapath: str):
ax2.scatter(size_diffs_loser, size_chirps_loser,
color=loser_color, label='Loser')
ax2.text(0.05, 0.95, f'n={len(size_chirps_winner)}',
ax2.text(0.05, 0.85, f'n={len(size_chirps_loser)}',
transform=ax2.transAxes, color=ps.white)
ax2.set_xlabel('Size difference [cm]')
@ -303,7 +303,7 @@ def main(datapath: str):
ax3.scatter(freq_diffs_higher, freq_chirps_winner, color=winner_color)
ax3.scatter(freq_diffs_lower, freq_chirps_loser, color=loser_color)
ax3.text(0.1, 0.95, f'n={len(freq_chirps_loser)}',
ax3.text(0.1, 0.85, f'n={len(np.asarray(freq_chirps_winner)[~np.isnan(freq_chirps_loser)])}',
transform=ax3.transAxes, color=ps.white)
ax3.set_xlabel('EODf [Hz]')

View File

@ -282,8 +282,8 @@ def main(dataroot):
kde_time,
np.percentile(loser_offsets_boot[-1], 1, axis=0),
np.percentile(loser_offsets_boot[-1], 99, axis=0),
color=ps.white,
alpha=0.4)
color='gray',
alpha=0.8)
ax[i].plot(kde_time, np.median(loser_offsets_boot[-1], axis=0),
color=ps.black, linewidth=2)

14
code/requirements.txt Normal file
View File

@ -0,0 +1,14 @@
audioio==0.9.5
cmocean==2.0
cycler==0.11.0
ipython==8.10.0
matplotlib==3.7.0
numpy==1.23.5
pandas==1.5.3
paramiko==2.11.1
PyYAML==6.0
scikit_learn==1.2.1
scipy==1.10.1
scp==0.14.5
thunderfish==1.9.9
tqdm==4.64.1

10
code/run_plots.sh Executable file
View File

@ -0,0 +1,10 @@
#!/usr/bin/env bash
color='\033[1;91m'
nocolor='\033[0m'
message='Running scripts in directory: '
for py_file in $(ls plot_*); do
echo -e $message$color$py_file$nocolor
python $py_file
done

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
poster_frozen/figs/kde.pdf Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

BIN
poster_frozen/main.pdf Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

View File

@ -1,119 +0,0 @@
\documentclass[25pt, a0paper, landscape, margin=0mm, innermargin=20mm,
blockverticalspace=2mm, colspace=20mm, subcolspace=0mm]{tikzposter} %Default values for poster format options.
\input{packages}
\input{style}
\begin{document}
\renewcommand{\baselinestretch}{1}
\title{\parbox{1900pt}{Pushing the limits of time-frequency uncertainty in the
detection of transient communication signals in weakly electric fish}}
\author{Sina Prause, Alexander Wendt, Patrick Weygoldt}
\institute{Supervised by Till Raab \& Jan Benda, Neurothology Group,
University of Tübingen}
\usetitlestyle[]{sampletitle}
\maketitle
\renewcommand{\baselinestretch}{1.4}
\begin{columns}
\column{0.5}
\myblock[TranspBlock]{Introduction}{
\begin{minipage}[t]{0.55\linewidth}
The time-frequency tradeoff makes reliable signal detecion and simultaneous
sender identification of freely interacting individuals impossible.
This profoundly limits our current understanding of chirps to experiments
with single - or physically separated - individuals.
\end{minipage} \hfill
\begin{minipage}[t]{0.40\linewidth}
\vspace{-1.5cm}
\begin{tikzfigure}[]
\label{tradeoff}
\includegraphics[width=\linewidth]{figs/introplot}
\end{tikzfigure}
\end{minipage}
}
\myblock[TranspBlock]{A chirp detection algorithm}{
\begin{tikzfigure}[]
\label{modulations}
\includegraphics[width=\linewidth]{figs/algorithm}
\end{tikzfigure}
}
\column{0.5}
\myblock[TranspBlock]{Chirps and diadic competitions}{
\begin{minipage}[t]{0.7\linewidth}
\begin{tikzfigure}[]
\label{modulations}
\includegraphics[width=\linewidth]{figs/placeholder1}
\end{tikzfigure}
\end{minipage} \hfill
\begin{minipage}[t]{0.25\linewidth}
\lipsum[3][1-3]
\end{minipage}
\begin{minipage}[t]{0.7\linewidth}
\begin{tikzfigure}[]
\label{modulations}
\includegraphics[width=\linewidth]{figs/placeholder1}
\end{tikzfigure}
\end{minipage} \hfill
\begin{minipage}[t]{0.25\linewidth}
\lipsum[3][1-3]
\end{minipage}
\begin{minipage}[t]{0.7\linewidth}
\begin{tikzfigure}[]
\label{modulations}
\includegraphics[width=\linewidth]{figs/placeholder1}
\end{tikzfigure}
\end{minipage} \hfill
\begin{minipage}[t]{0.25\linewidth}
\lipsum[3][1-3]
\end{minipage}
}
\myblock[TranspBlock]{Conclusion}{
\lipsum[3][1-9]
}
% \column{0.3}
% \myblock[TranspBlock]{More Results}{
% \begin{tikzfigure}[]
% \label{results}
% \includegraphics[width=\linewidth]{example-image-a}
% \end{tikzfigure}
% \begin{multicols}{2}
% \lipsum[5][1-8]
% \end{multicols}
% \vspace{-1cm}
% }
% \myblock[TranspBlock]{Conclusion}{
% \begin{itemize}
% \setlength\itemsep{0.5em}
% \item \lipsum[1][1]
% \item \lipsum[1][1]
% \item \lipsum[1][1]
% \end{itemize}
% \vspace{0.2cm}
% }
\end{columns}
\node[
above right,
text=white,
outer sep=45pt,
minimum width=\paperwidth,
align=center,
draw,
fill=boxes,
color=boxes,
] at (-0.51\paperwidth,-43.5) {
\textcolor{text}{\normalsize Contact: \{name\}.\{surname\}@student.uni-tuebingen.de}};
\end{document}

View File

@ -1,11 +0,0 @@
\usepackage[utf8]{inputenc}
\usepackage[scaled]{helvet}
\renewcommand\familydefault{\sfdefault}
\usepackage[T1]{fontenc}
\usepackage{wrapfig}
\usepackage{setspace}
\usepackage{multicol}
\setlength{\columnsep}{1.5cm}
\usepackage{xspace}
\usepackage{tikz}
\usepackage{lipsum}

View File

@ -1,119 +0,0 @@
\tikzposterlatexaffectionproofoff
\usetheme{Default}
\definecolor{text}{HTML}{e0e4f7}
\definecolor{background}{HTML}{111116}
\definecolor{boxes}{HTML}{2a2a32}
\definecolor{unired}{HTML}{a51e37}
\colorlet{blocktitlefgcolor}{text}
\colorlet{backgroundcolor}{background}
\colorlet{blocktitlebgcolor}{background}
\colorlet{blockbodyfgcolor}{text}
\colorlet{innerblocktitlebgcolor}{background}
\colorlet{innerblocktitlefgcolor}{text}
\colorlet{notefrcolor}{text}
\colorlet{notefgcolor}{background}
\colorlet{notebgcolor}{background}
% Title setup
\settitle{
% Rearrange the order of the minipages to e.g. center the title between the logos
\begin{minipage}[c]{0.6\paperwidth}
% \centering
\vspace{2.5cm}\hspace{1.5cm}
\color{text}{\Huge{\textbf{\@title}} \par}
\vspace*{2em}\hspace{1.5cm}
\color{text}{\LARGE \@author \par}
\vspace*{2em}\hspace{1.5cm}
\color{text}{\Large \@institute}
\vspace{2.5cm}
\end{minipage}
\begin{minipage}[c]{0.2\paperwidth}
% \centering
\vspace{1cm}\hspace{1cm}
\includegraphics[scale=1]{example-image-a}
\end{minipage}
\begin{minipage}[c]{0.2\paperwidth}
% \vspace{1cm}\hspace{1cm}
\centering
\includegraphics[scale=1]{example-image-a}
\end{minipage}}
% definie title style with background box
\definetitlestyle{sampletitle}{
width=1189mm,
roundedcorners=0,
linewidth=0pt,
innersep=15pt,
titletotopverticalspace=0mm,
titletoblockverticalspace=5pt
}{
\begin{scope}[line width=\titlelinewidth, rounded corners=\titleroundedcorners]
\draw[fill=text, color=boxes]
(\titleposleft,\titleposbottom)
rectangle
(\titleposright,\titlepostop);
\end{scope}
}
% define coustom block style for visible blocks
\defineblockstyle{GrayBlock}{
titlewidthscale=1,
bodywidthscale=1,
% titlecenter,
titleleft,
titleoffsetx=0pt,
titleoffsety=-30pt,
bodyoffsetx=0pt,
bodyoffsety=-40pt,
bodyverticalshift=0mm,
roundedcorners=25,
linewidth=1pt,
titleinnersep=20pt,
bodyinnersep=38pt
}{
\draw[rounded corners=\blockroundedcorners, inner sep=\blockbodyinnersep,
line width=\blocklinewidth, color=background,
top color=boxes, bottom color=boxes,
]
(blockbody.south west) rectangle (blockbody.north east); %
\ifBlockHasTitle%
\draw[rounded corners=\blockroundedcorners, inner sep=\blocktitleinnersep,
top color=background, bottom color=background,
line width=2, color=background, %fill=blocktitlebgcolor
]
(blocktitle.south west) rectangle (blocktitle.north east); %
\fi%
}
\newcommand\myblock[3][GrayBlock]{\useblockstyle{#1}\block{#2}{#3}\useblockstyle{Default}}
% Define blockstyle for tranparent block
\defineblockstyle{TranspBlock}{
titlewidthscale=0.99,
bodywidthscale=0.99,
titleleft,
titleoffsetx=15pt,
titleoffsety=-40pt,
bodyoffsetx=0pt,
bodyoffsety=-40pt,
bodyverticalshift=0mm,
roundedcorners=25,
linewidth=1pt,
titleinnersep=20pt,
bodyinnersep=38pt
}{
\draw[rounded corners=\blockroundedcorners, inner sep=\blockbodyinnersep,
line width=\blocklinewidth, color=background,
top color=background, bottom color=background,
]
(blockbody.south west) rectangle (blockbody.north east); %
\ifBlockHasTitle%
\draw[rounded corners=\blockroundedcorners, inner sep=\blocktitleinnersep,
top color=background, bottom color=background,
line width=2, color=background, %fill=blocktitlebgcolor
]
(blocktitle.south west) rectangle (blocktitle.north east); %
\fi%
}
\renewcommand\myblock[3][TranspBlock]{\useblockstyle{#1}\block{#2}{#3}\useblockstyle{Default}}

View File

@ -1,140 +1,14 @@
asttokens==2.2.1
backcall==0.2.0
contourpy==1.0.6
cycler==0.11.0
decorator==5.1.1
executing==1.2.0
fonttools==4.38.0
ipython==8.8.0
jedi==0.18.2
kiwisolver==1.4.4
matplotlib==3.6.2
matplotlib-inline==0.1.6
numpy==1.24.1
packaging==23.0
pandas==1.5.2
parso==0.8.3
pexpect==4.8.0
pickleshare==0.7.5
Pillow==9.4.0
prompt-toolkit==3.0.36
ptyprocess==0.7.0
pure-eval==0.2.2
Pygments==2.14.0
pyparsing==3.0.9
python-dateutil==2.8.2
pytz==2022.7
scipy==1.10.0
six==1.16.0
sklearn==0.0.post1
stack-data==0.6.2
traitlets==5.8.0
wcwidth==0.2.5
asttokens==2.2.1
audioio==0.10.0
backcall==0.2.0
contourpy==1.0.6
cycler==0.11.0
decorator==5.1.1
executing==1.2.0
fonttools==4.38.0
ipython==8.8.0
jedi==0.18.2
kiwisolver==1.4.4
matplotlib==3.6.2
matplotlib-inline==0.1.6
numpy==1.24.1
packaging==23.0
pandas==1.5.2
parso==0.8.3
pexpect==4.8.0
pickleshare==0.7.5
Pillow==9.4.0
prompt-toolkit==3.0.36
ptyprocess==0.7.0
pure-eval==0.2.2
Pygments==2.14.0
pyparsing==3.0.9
python-dateutil==2.8.2
pytz==2022.7
scipy==1.10.0
six==1.16.0
sklearn==0.0.post1
stack-data==0.6.2
thunderfish==1.9.10
traitlets==5.8.0
wcwidth==0.2.5
appnope==0.1.3
asttokens==2.2.1
audioio==0.10.0
backcall==0.2.0
contourpy==1.0.6
cycler==0.11.0
decorator==5.1.1
executing==1.2.0
fonttools==4.38.0
ipython==8.8.0
jedi==0.18.2
kiwisolver==1.4.4
matplotlib==3.6.2
matplotlib-inline==0.1.6
numpy==1.24.1
packaging==23.0
pandas==1.5.2
parso==0.8.3
pexpect==4.8.0
pickleshare==0.7.5
Pillow==9.4.0
prompt-toolkit==3.0.36
ptyprocess==0.7.0
pure-eval==0.2.2
Pygments==2.14.0
pyparsing==3.0.9
python-dateutil==2.8.2
pytz==2022.7
scipy==1.10.0
six==1.16.0
sklearn==0.0.post1
stack-data==0.6.2
thunderfish==1.9.10
traitlets==5.8.0
wcwidth==0.2.5
asttokens==2.2.1
audioio==0.10.0
backcall==0.2.0
audioio==0.9.5
cmocean==2.0
contourpy==1.0.6
cycler==0.11.0
decorator==5.1.1
executing==1.2.0
fonttools==4.38.0
ipython==8.8.0
jedi==0.18.2
joblib==1.2.0
kiwisolver==1.4.4
matplotlib==3.6.2
matplotlib-inline==0.1.6
numpy==1.24.1
packaging==23.0
pandas==1.5.2
parso==0.8.3
pexpect==4.8.0
pickleshare==0.7.5
Pillow==9.4.0
prompt-toolkit==3.0.36
ptyprocess==0.7.0
pure-eval==0.2.2
Pygments==2.14.0
pyparsing==3.0.9
python-dateutil==2.8.2
pytz==2022.7
ipython==8.10.0
matplotlib==3.7.0
numpy==1.24.2
pandas==1.5.3
paramiko==2.11.1
PyYAML==6.0
scikit-learn==1.2.0
scipy==1.10.0
six==1.16.0
sklearn==0.0.post1
stack-data==0.6.2
threadpoolctl==3.1.0
thunderfish==1.9.10
traitlets==5.8.0
wcwidth==0.2.5
scikit_learn==1.2.1
scipy==1.10.1
scp==0.14.5
thunderfish==1.9.9
tqdm==4.64.1