From 342914a71944b4f19cc1a3ad2921f9fece68de81 Mon Sep 17 00:00:00 2001 From: "sarah.eisele" Date: Fri, 18 Oct 2024 14:59:41 +0000 Subject: [PATCH 1/8] Add analysis_1 --- analysis_1 | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 analysis_1 diff --git a/analysis_1 b/analysis_1 new file mode 100644 index 0000000..e69de29 From 7c32c6e4b86bad2fc4c8177d78902acd72f818c1 Mon Sep 17 00:00:00 2001 From: "sarah.eisele" Date: Fri, 18 Oct 2024 15:03:44 +0000 Subject: [PATCH 2/8] Upload files to "/" --- analysis_1.py | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 analysis_1.py diff --git a/analysis_1.py b/analysis_1.py new file mode 100644 index 0000000..b6d6497 --- /dev/null +++ b/analysis_1.py @@ -0,0 +1,154 @@ +import rlxnix as rlx +import numpy as np +import matplotlib.pyplot as plt +import os +from scipy.signal import welch + +# close all currently open figures +plt.close('all') + +'''FUNCTIONS''' +def plot_vt_spikes(t, v, spike_t): + fig = plt.figure(figsize=(5, 2.5)) + # alternative to ax = axs[0] + ax = fig.add_subplot() + # plot vt diagram + ax.plot(t[t<0.1], v[t<0.1]) + # plot spikes into vt diagram, at max V + ax.scatter(spike_t[spike_t<0.1], np.ones_like(spike_t[spike_t<0.1]) * np.max(v)) + plt.show() + +def scatter_plot(colormap, stimuli_list, stimulus_count): + '''plot scatter plot for one sam with all 3 stims''' + fig = plt.figure() + ax = fig.add_subplot() + + ax.eventplot(stimuli_list, colors=colormap) + ax.set_xlabel('Spike Times [ms]') + ax.set_ylabel('Loop #') + ax.set_yticks(range(stimulus_count)) + ax.set_title('Spikes of SAM 3') + plt.show() + +# create binary array with ones for spike times +def binary_spikes(spike_times, duration , dt): + '''Converts spike times to binary representation + Params + ------ + spike_times: np.array + spike times + duration: float + trial duration + dt: float + temporal resolution + + Returns + -------- + binary: np.array + The binary representation of the spike times + ''' + binary = np.zeros(int(duration//dt)) # // is truncated division, returns number w/o decimals, same as np.round + spike_indices = np.asarray(np.round(spike_times//dt), dtype=int) + binary[spike_indices] = 1 + return binary + +# function to plot psth +def firing_rates(binary_spikes, box_width=0.01, dt=0.000025): + box = np.ones(int(box_width // dt)) + box /= np.sum(box * dt) # normalize box kernel w interal of 1 + rate = np.convolve(binary_spikes, box, mode='same') + return rate + +def power_spectrum(rate, dt): + f, p = welch(rate, fs = 1./dt, nperseg=2**16, noverlap=2**15) + # algorithm makes rounding mistakes, we want to calc many spectra and take mean of those + # nperseg: length of segments in # datapoints + # noverlap: # datapoints that overlap in segments + return f, p + +def power_spectrum_plot(f, p): + # plot power spectrum + fig = plt.figure() + ax = fig.add_subplot() + ax.plot(freq, power) + ax.set_xlabel('Frequency [Hz]') + ax.set_ylabel('Power [1/Hz]') + ax.set_xlim(0, 1000) + plt.show() + +'''IMPORT DATA''' +datafolder = '../data' #./ wo ich gerade bin; ../ eine ebene höher; ../../ zwei ebenen höher + +example_file = os.path.join('..', 'data', '2024-10-16-ac-invivo-1.nix') + +'''EXTRACT DATA''' +dataset = rlx.Dataset(example_file) + +# get sams +sams = dataset.repro_runs('SAM') +sam = sams[2] + +# get potetial over time (vt curve) +potential, time = sam.trace_data('V-1') + +# get spike times +spike_times, _ = sam.trace_data('Spikes-1') + +# get stim count +stim_count = sam.stimulus_count + +# extract spike times of all 3 loops of current sam +stimuli = [] +for i in range(stim_count): + # get stim i from sam + stim = sam.stimuli[i] + potential_stim, time_stim = stim.trace_data('V-1') + # get spike_times + spike_times_stim, _ = stim.trace_data('Spikes-1') + stimuli.append(spike_times_stim) + +eodf = stim.metadata[stim.name]['EODF'][0][0] +df = stim.metadata['RePro-Info']['settings']['deltaf'][0][0] +stimulus_freq = df + eodf + +'''PLOT''' +# create colormap +colors = plt.cm.prism(np.linspace(0, 1, stim_count)) + +# timeline of whole rec +dataset.plot_timeline() + +# voltage and spikes of current sam +plot_vt_spikes(time, potential, spike_times) + +# spike times of all loops +scatter_plot(colors, stimuli, stim_count) + + +'''POWER SPECTRUM''' +# define variables for binary spikes function +spikes, _ = stim.trace_data('Spikes-1') +ti = stim.trace_info('V-1') +dt = ti.sampling_interval +duration = stim.duration + +### spectrum +# vector with binary values for wholes length of stim +binary = binary_spikes(spikes, duration, dt) + +# calculate firing rate +rate = firing_rates(binary, 0.01, dt) # box width of 10 ms + +# plot psth or whatever +# plt.plot(time_stim, rate) +# plt.show() + +freq, power = power_spectrum(binary, dt) + +power_spectrum_plot(freq, power) + + +### TODO: + # then loop over sams/dfs, all stims, intensities + # when does stim start in eodf/ at which phase and how does that influence our signal --> alignment problem: egal wenn wir spectren haben + # we want to see peaks at phase locking to own and stim frequency, and at amp modulation frequency \ No newline at end of file From 72a97f18ce31d9e7a5b41d7d50a3e0ca8cc8930d Mon Sep 17 00:00:00 2001 From: mbergmann Date: Fri, 18 Oct 2024 15:04:26 +0000 Subject: [PATCH 3/8] =?UTF-8?q?analysis=5F1=20gel=C3=B6scht?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit was empty and not needed --- analysis_1 | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 analysis_1 diff --git a/analysis_1 b/analysis_1 deleted file mode 100644 index e69de29..0000000 From 052210c1eadff598467ef034b447b50b512d2f61 Mon Sep 17 00:00:00 2001 From: Diana Date: Mon, 21 Oct 2024 12:31:36 +0000 Subject: [PATCH 4/8] Dateien nach "/" hochladen Integrale unter Peaks berechnen --- GP_Code.py | 192 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 GP_Code.py diff --git a/GP_Code.py b/GP_Code.py new file mode 100644 index 0000000..6ff4428 --- /dev/null +++ b/GP_Code.py @@ -0,0 +1,192 @@ +# -*- coding: utf-8 -*- +""" +Created on Thu Oct 17 09:23:10 2024 + +@author: diana +""" +# -*- coding: utf-8 -*- + +import glob +import os +import rlxnix as rlx +import numpy as np +import matplotlib.pyplot as plt +import scipy.signal as sig +from scipy.integrate import quad + + +### FUNCTIONS ### +def binary_spikes(spike_times, duration, dt): + """ Converts the spike times to a binary representation. + Zeros when there is no spike, One when there is. + + Parameters + ---------- + spike_times : np.array + The spike times. + duration : float + The trial duration. + dt : float + the temporal resolution. + + Returns + ------- + binary : np.array + The binary representation of the spike times. + + """ + binary = np.zeros(int(np.round(duration / dt))) #Vektor, der genauso lang ist wie die stim time + spike_indices = np.asarray(np.round(spike_times / dt), dtype=int) + binary[spike_indices] = 1 + return binary + + +def firing_rate(binary_spikes, box_width, dt=0.000025): + """Calculate the firing rate from binary spike data. + + This function computes the firing rate using a boxcar (moving average) + filter of a specified width. + + Parameters + ---------- + binary_spikes : np.array + A binary array representing spike occurrences. + box_width : float + The width of the box filter in seconds. + dt : float, optional + The temporal resolution (time step) in seconds. Default is 0.000025 seconds. + + Returns + ------- + rate : np.array + An array representing the firing rate at each time step. + """ + box = np.ones(int(box_width // dt)) + box /= np.sum(box) * dt #Normalization of box kernel to an integral of 1 + rate = np.convolve(binary_spikes, box, mode="same") + return rate + + +def powerspectrum(rate, dt): + """Compute the power spectrum of a given firing rate. + + This function calculates the power spectrum using the Welch method. + + Parameters + ---------- + rate : np.array + An array of firing rates. + dt : float + The temporal resolution (time step) in seconds. + + Returns + ------- + frequency : np.array + An array of frequencies corresponding to the power values. + power : np.array + An array of power spectral density values. + """ + frequency, power = sig.welch(rate, fs=1/dt, nperseg=2**15, noverlap=2**14) + return frequency, power + + +def prepare_harmonics(frequencies, categories, num_harmonics, colors): + points_categories = {} + for idx, (freq, category) in enumerate(zip(frequencies, categories)): + points_categories[category] = [freq * (i + 1) for i in range(num_harmonics[idx])] + + points = [p for harmonics in points_categories.values() for p in harmonics] + color_mapping = {category: colors[idx] for idx, category in enumerate(categories)} + + return points, color_mapping, points_categories + + +def plot_power_spectrum_with_integrals(frequency, power, points, delta, color_mapping, points_categories): + """Create a figure of the power spectrum with integrals highlighted around specified points. + + This function creates a plot of the power spectrum and shades areas around + specified harmonic points to indicate the calculated integrals. + + Parameters + ---------- + frequency : np.array + An array of frequencies corresponding to the power values. + power : np.array + An array of power spectral density values. + points : list + A list of harmonic frequencies to highlight. + delta : float + Half-width of the range for integration around each point. + color_mapping : dict + A mapping of point categories to colors. + points_categories : dict + A mapping of categories to lists of points. + + Returns + ------- + fig : matplotlib.figure.Figure + The created figure object. + """ + fig, ax = plt.subplots() + ax.plot(frequency, power) + integrals = [] + + for point in points: + indices = (frequency >= point - delta) & (frequency <= point + delta) + integral = np.trapz(power[indices], frequency[indices]) + integrals.append(integral) + + # Get color based on point category + color = next((c for cat, c in color_mapping.items() if point in points_categories[cat]), 'gray') + ax.axvspan(point - delta, point + delta, color=color, alpha=0.3, label=f'{point:.2f} Hz') + print(f"Integral around {point:.2f} Hz: {integral:.5e}") + + ax.set_xlim([0, 1200]) + ax.set_xlabel('Frequency (Hz)') + ax.set_ylabel('Power') + ax.set_title('Power Spectrum with marked Integrals') + ax.legend() + + return fig + + + + +### Data retrieval ### +datafolder = "../data" # Geht in der Hierarchie einen Ordern nach oben (..) und dann in den Ordner 'data' +example_file = os.path.join("..", "data", "2024-10-16-ad-invivo-1.nix") +dataset = rlx.Dataset(example_file) +sams = dataset.repro_runs("SAM") +sam = sams[2] + +## Daten für Funktionen +df = sam.metadata["RePro-Info"]["settings"]["deltaf"][0][0] +stim = sam.stimuli[1] +potential, time = stim.trace_data("V-1") +spikes, _ = stim.trace_data("Spikes-1") +duration = stim.duration +dt = stim.trace_info("V-1").sampling_interval + + +### Anwendung Functionen ### +b = binary_spikes(spikes, duration, dt) +rate = firing_rate(b, box_width=0.05, dt=dt) +frequency, power = powerspectrum(b, dt) + +## Important stuff +eodf = stim.metadata[stim.name]["EODf"][0][0] +stimulus_frequency = eodf + df +AM = 50 # Hz +#print(f"EODf: {eodf}, Stimulus Frequency: {stimulus_frequency}, AM: {AM}") + +frequencies = [AM, eodf, stimulus_frequency] +categories = ["AM", "EODf", "Stimulus frequency"] +num_harmonics = [4, 2, 2] +colors = ["green", "orange", "red"] +delta = 2.5 + + +### Peaks im Powerspektrum finden ### +points, color_mapping, points_categories = prepare_harmonics(frequencies, categories, num_harmonics, colors) +fig = plot_power_spectrum_with_integrals(frequency, power, points, delta, color_mapping, points_categories) +plt.show() From b0adb74ae33d9b2ad9ba5eed6af84977d9d1a35b Mon Sep 17 00:00:00 2001 From: mbergmann Date: Tue, 22 Oct 2024 07:14:44 +0000 Subject: [PATCH 5/8] Datei test.py nach "code" hochladen --- code/test.py | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 code/test.py diff --git a/code/test.py b/code/test.py new file mode 100644 index 0000000..f2b9a4e --- /dev/null +++ b/code/test.py @@ -0,0 +1,101 @@ +import glob +import pathlib +import numpy as np +import matplotlib.pyplot as plt +import rlxnix as rlx +from IPython import embed +from scipy.signal import welch + +def binary_spikes(spike_times, duration, dt): + """ + Converts the spike times to a binary representations + + Parameters + ---------- + spike_times : np.array + The spike times. + duration : float + The trial duration: + dt : float + The temporal resolution. + + Returns + ------- + binary : np.array + The binary representation of the spike train. + + """ + binary = np.zeros(int(np.round(duration / dt))) #create the binary array with the same length as potential + + spike_indices = np.asarray(np.round(spike_times / dt), dtype = int) # get the indices + binary[spike_indices] = 1 # put the indices into binary + return binary + +def firing_rate(binary_spikes, dt = 0.000025, box_width = 0.01): + box = np.ones(int(box_width // dt)) + box /= np.sum(box) * dt # normalisierung des box kernels to an integral of one + rate = np.convolve(binary_spikes, box, mode = 'same') + return rate + +def power_spectrum(rate, dt): + freq, power = welch(rate, fs = 1/dt, nperseg = 2**16, noverlap = 2**15) + return freq, power + +#find example data +datafolder = "../data" + +example_file = datafolder + "/" + "2024-10-16-ad-invivo-1.nix" + +#load dataset +dataset = rlx.Dataset(example_file) +# find all sams +sams = dataset.repro_runs('SAM') +sam = sams[2] # our example sam +potential,time = sam.trace_data("V-1") #membrane potential +spike_times, _ = sam.trace_data('Spikes-1') #spike times +df = sam.metadata['RePro-Info']['settings']['deltaf'][0][0] #find df in metadata +amp = sam.metadata['RePro-Info']['settings']['contrast'][0][0] * 100 #find amplitude in metadata + +#figure for a quick plot +fig = plt.figure(figsize = (5, 2.5)) +ax = fig.add_subplot() +ax.plot(time[time < 0.1], potential[time < 0.1]) # plot the membrane potential in 0.1s +ax.scatter(spike_times[spike_times < 0.1], + np.ones_like(spike_times[spike_times < 0.1]) * np.max(potential)) #plot teh spike times on top +plt.show() +plt.close() +# get all the stimuli +stims = sam.stimuli +# empty list for the spike times +spikes = [] +#spikes2 = np.array(range(len(stims))) +# loop over the stimuli +for stim in stims: + # get the spike times + spike, _ = stim.trace_data('Spikes-1') + # append the first 100ms to spikes + spikes.append(spike[spike < 0.1]) + # get stimulus duration + duration = stim.duration + ti = stim.trace_info("V-1") + dt = ti.sampling_interval # get the stimulus interval + bin_spikes = binary_spikes(spike, duration, dt) #binarize the spike_times + print(len(bin_spikes)) + pot,tim= stim.trace_data("V-1") #membrane potential + rate = firing_rate(bin_spikes, dt = dt) + print(np.mean(rate)) + fig, [ax1, ax2] = plt.subplots(1, 2,layout = 'constrained') + ax1.plot(tim,rate) + ax1.set_ylim(0,600) + ax1.set_xlim(0, 0.04) + freq, power = power_spectrum(rate, dt) + ax2.plot(freq,power) + ax2.set_xlim(0,1000) + +# make an eventplot +fig = plt.figure(figsize = (5, 3), layout = 'constrained') +ax = fig.add_subplot() +ax.eventplot(spikes, linelength = 0.8) +ax.set_xlabel('time [ms]') +ax.set_ylabel('loop no.') +plt.show() \ No newline at end of file From e8d4ca90fe7c13fed6fab75c2042d64801b48994 Mon Sep 17 00:00:00 2001 From: mbergmann Date: Tue, 22 Oct 2024 07:16:25 +0000 Subject: [PATCH 6/8] GP_code nach code verschoben --- GP_Code.py => code/GP_Code.py | 384 +++++++++++++++++----------------- 1 file changed, 192 insertions(+), 192 deletions(-) rename GP_Code.py => code/GP_Code.py (96%) diff --git a/GP_Code.py b/code/GP_Code.py similarity index 96% rename from GP_Code.py rename to code/GP_Code.py index 6ff4428..754715e 100644 --- a/GP_Code.py +++ b/code/GP_Code.py @@ -1,192 +1,192 @@ -# -*- coding: utf-8 -*- -""" -Created on Thu Oct 17 09:23:10 2024 - -@author: diana -""" -# -*- coding: utf-8 -*- - -import glob -import os -import rlxnix as rlx -import numpy as np -import matplotlib.pyplot as plt -import scipy.signal as sig -from scipy.integrate import quad - - -### FUNCTIONS ### -def binary_spikes(spike_times, duration, dt): - """ Converts the spike times to a binary representation. - Zeros when there is no spike, One when there is. - - Parameters - ---------- - spike_times : np.array - The spike times. - duration : float - The trial duration. - dt : float - the temporal resolution. - - Returns - ------- - binary : np.array - The binary representation of the spike times. - - """ - binary = np.zeros(int(np.round(duration / dt))) #Vektor, der genauso lang ist wie die stim time - spike_indices = np.asarray(np.round(spike_times / dt), dtype=int) - binary[spike_indices] = 1 - return binary - - -def firing_rate(binary_spikes, box_width, dt=0.000025): - """Calculate the firing rate from binary spike data. - - This function computes the firing rate using a boxcar (moving average) - filter of a specified width. - - Parameters - ---------- - binary_spikes : np.array - A binary array representing spike occurrences. - box_width : float - The width of the box filter in seconds. - dt : float, optional - The temporal resolution (time step) in seconds. Default is 0.000025 seconds. - - Returns - ------- - rate : np.array - An array representing the firing rate at each time step. - """ - box = np.ones(int(box_width // dt)) - box /= np.sum(box) * dt #Normalization of box kernel to an integral of 1 - rate = np.convolve(binary_spikes, box, mode="same") - return rate - - -def powerspectrum(rate, dt): - """Compute the power spectrum of a given firing rate. - - This function calculates the power spectrum using the Welch method. - - Parameters - ---------- - rate : np.array - An array of firing rates. - dt : float - The temporal resolution (time step) in seconds. - - Returns - ------- - frequency : np.array - An array of frequencies corresponding to the power values. - power : np.array - An array of power spectral density values. - """ - frequency, power = sig.welch(rate, fs=1/dt, nperseg=2**15, noverlap=2**14) - return frequency, power - - -def prepare_harmonics(frequencies, categories, num_harmonics, colors): - points_categories = {} - for idx, (freq, category) in enumerate(zip(frequencies, categories)): - points_categories[category] = [freq * (i + 1) for i in range(num_harmonics[idx])] - - points = [p for harmonics in points_categories.values() for p in harmonics] - color_mapping = {category: colors[idx] for idx, category in enumerate(categories)} - - return points, color_mapping, points_categories - - -def plot_power_spectrum_with_integrals(frequency, power, points, delta, color_mapping, points_categories): - """Create a figure of the power spectrum with integrals highlighted around specified points. - - This function creates a plot of the power spectrum and shades areas around - specified harmonic points to indicate the calculated integrals. - - Parameters - ---------- - frequency : np.array - An array of frequencies corresponding to the power values. - power : np.array - An array of power spectral density values. - points : list - A list of harmonic frequencies to highlight. - delta : float - Half-width of the range for integration around each point. - color_mapping : dict - A mapping of point categories to colors. - points_categories : dict - A mapping of categories to lists of points. - - Returns - ------- - fig : matplotlib.figure.Figure - The created figure object. - """ - fig, ax = plt.subplots() - ax.plot(frequency, power) - integrals = [] - - for point in points: - indices = (frequency >= point - delta) & (frequency <= point + delta) - integral = np.trapz(power[indices], frequency[indices]) - integrals.append(integral) - - # Get color based on point category - color = next((c for cat, c in color_mapping.items() if point in points_categories[cat]), 'gray') - ax.axvspan(point - delta, point + delta, color=color, alpha=0.3, label=f'{point:.2f} Hz') - print(f"Integral around {point:.2f} Hz: {integral:.5e}") - - ax.set_xlim([0, 1200]) - ax.set_xlabel('Frequency (Hz)') - ax.set_ylabel('Power') - ax.set_title('Power Spectrum with marked Integrals') - ax.legend() - - return fig - - - - -### Data retrieval ### -datafolder = "../data" # Geht in der Hierarchie einen Ordern nach oben (..) und dann in den Ordner 'data' -example_file = os.path.join("..", "data", "2024-10-16-ad-invivo-1.nix") -dataset = rlx.Dataset(example_file) -sams = dataset.repro_runs("SAM") -sam = sams[2] - -## Daten für Funktionen -df = sam.metadata["RePro-Info"]["settings"]["deltaf"][0][0] -stim = sam.stimuli[1] -potential, time = stim.trace_data("V-1") -spikes, _ = stim.trace_data("Spikes-1") -duration = stim.duration -dt = stim.trace_info("V-1").sampling_interval - - -### Anwendung Functionen ### -b = binary_spikes(spikes, duration, dt) -rate = firing_rate(b, box_width=0.05, dt=dt) -frequency, power = powerspectrum(b, dt) - -## Important stuff -eodf = stim.metadata[stim.name]["EODf"][0][0] -stimulus_frequency = eodf + df -AM = 50 # Hz -#print(f"EODf: {eodf}, Stimulus Frequency: {stimulus_frequency}, AM: {AM}") - -frequencies = [AM, eodf, stimulus_frequency] -categories = ["AM", "EODf", "Stimulus frequency"] -num_harmonics = [4, 2, 2] -colors = ["green", "orange", "red"] -delta = 2.5 - - -### Peaks im Powerspektrum finden ### -points, color_mapping, points_categories = prepare_harmonics(frequencies, categories, num_harmonics, colors) -fig = plot_power_spectrum_with_integrals(frequency, power, points, delta, color_mapping, points_categories) -plt.show() +# -*- coding: utf-8 -*- +""" +Created on Thu Oct 17 09:23:10 2024 + +@author: diana +""" +# -*- coding: utf-8 -*- + +import glob +import os +import rlxnix as rlx +import numpy as np +import matplotlib.pyplot as plt +import scipy.signal as sig +from scipy.integrate import quad + + +### FUNCTIONS ### +def binary_spikes(spike_times, duration, dt): + """ Converts the spike times to a binary representation. + Zeros when there is no spike, One when there is. + + Parameters + ---------- + spike_times : np.array + The spike times. + duration : float + The trial duration. + dt : float + the temporal resolution. + + Returns + ------- + binary : np.array + The binary representation of the spike times. + + """ + binary = np.zeros(int(np.round(duration / dt))) #Vektor, der genauso lang ist wie die stim time + spike_indices = np.asarray(np.round(spike_times / dt), dtype=int) + binary[spike_indices] = 1 + return binary + + +def firing_rate(binary_spikes, box_width, dt=0.000025): + """Calculate the firing rate from binary spike data. + + This function computes the firing rate using a boxcar (moving average) + filter of a specified width. + + Parameters + ---------- + binary_spikes : np.array + A binary array representing spike occurrences. + box_width : float + The width of the box filter in seconds. + dt : float, optional + The temporal resolution (time step) in seconds. Default is 0.000025 seconds. + + Returns + ------- + rate : np.array + An array representing the firing rate at each time step. + """ + box = np.ones(int(box_width // dt)) + box /= np.sum(box) * dt #Normalization of box kernel to an integral of 1 + rate = np.convolve(binary_spikes, box, mode="same") + return rate + + +def powerspectrum(rate, dt): + """Compute the power spectrum of a given firing rate. + + This function calculates the power spectrum using the Welch method. + + Parameters + ---------- + rate : np.array + An array of firing rates. + dt : float + The temporal resolution (time step) in seconds. + + Returns + ------- + frequency : np.array + An array of frequencies corresponding to the power values. + power : np.array + An array of power spectral density values. + """ + frequency, power = sig.welch(rate, fs=1/dt, nperseg=2**15, noverlap=2**14) + return frequency, power + + +def prepare_harmonics(frequencies, categories, num_harmonics, colors): + points_categories = {} + for idx, (freq, category) in enumerate(zip(frequencies, categories)): + points_categories[category] = [freq * (i + 1) for i in range(num_harmonics[idx])] + + points = [p for harmonics in points_categories.values() for p in harmonics] + color_mapping = {category: colors[idx] for idx, category in enumerate(categories)} + + return points, color_mapping, points_categories + + +def plot_power_spectrum_with_integrals(frequency, power, points, delta, color_mapping, points_categories): + """Create a figure of the power spectrum with integrals highlighted around specified points. + + This function creates a plot of the power spectrum and shades areas around + specified harmonic points to indicate the calculated integrals. + + Parameters + ---------- + frequency : np.array + An array of frequencies corresponding to the power values. + power : np.array + An array of power spectral density values. + points : list + A list of harmonic frequencies to highlight. + delta : float + Half-width of the range for integration around each point. + color_mapping : dict + A mapping of point categories to colors. + points_categories : dict + A mapping of categories to lists of points. + + Returns + ------- + fig : matplotlib.figure.Figure + The created figure object. + """ + fig, ax = plt.subplots() + ax.plot(frequency, power) + integrals = [] + + for point in points: + indices = (frequency >= point - delta) & (frequency <= point + delta) + integral = np.trapz(power[indices], frequency[indices]) + integrals.append(integral) + + # Get color based on point category + color = next((c for cat, c in color_mapping.items() if point in points_categories[cat]), 'gray') + ax.axvspan(point - delta, point + delta, color=color, alpha=0.3, label=f'{point:.2f} Hz') + print(f"Integral around {point:.2f} Hz: {integral:.5e}") + + ax.set_xlim([0, 1200]) + ax.set_xlabel('Frequency (Hz)') + ax.set_ylabel('Power') + ax.set_title('Power Spectrum with marked Integrals') + ax.legend() + + return fig + + + + +### Data retrieval ### +datafolder = "../data" # Geht in der Hierarchie einen Ordern nach oben (..) und dann in den Ordner 'data' +example_file = os.path.join("..", "data", "2024-10-16-ad-invivo-1.nix") +dataset = rlx.Dataset(example_file) +sams = dataset.repro_runs("SAM") +sam = sams[2] + +## Daten für Funktionen +df = sam.metadata["RePro-Info"]["settings"]["deltaf"][0][0] +stim = sam.stimuli[1] +potential, time = stim.trace_data("V-1") +spikes, _ = stim.trace_data("Spikes-1") +duration = stim.duration +dt = stim.trace_info("V-1").sampling_interval + + +### Anwendung Functionen ### +b = binary_spikes(spikes, duration, dt) +rate = firing_rate(b, box_width=0.05, dt=dt) +frequency, power = powerspectrum(b, dt) + +## Important stuff +eodf = stim.metadata[stim.name]["EODf"][0][0] +stimulus_frequency = eodf + df +AM = 50 # Hz +#print(f"EODf: {eodf}, Stimulus Frequency: {stimulus_frequency}, AM: {AM}") + +frequencies = [AM, eodf, stimulus_frequency] +categories = ["AM", "EODf", "Stimulus frequency"] +num_harmonics = [4, 2, 2] +colors = ["green", "orange", "red"] +delta = 2.5 + + +### Peaks im Powerspektrum finden ### +points, color_mapping, points_categories = prepare_harmonics(frequencies, categories, num_harmonics, colors) +fig = plot_power_spectrum_with_integrals(frequency, power, points, delta, color_mapping, points_categories) +plt.show() From 7e02490a89d17a96689c3f0c0ad4919df2e09b93 Mon Sep 17 00:00:00 2001 From: mbergmann Date: Tue, 22 Oct 2024 07:17:10 +0000 Subject: [PATCH 7/8] analysis_1.py nach code verschoben --- analysis_1.py => code/analysis_1.py | 306 ++++++++++++++-------------- 1 file changed, 153 insertions(+), 153 deletions(-) rename analysis_1.py => code/analysis_1.py (95%) diff --git a/analysis_1.py b/code/analysis_1.py similarity index 95% rename from analysis_1.py rename to code/analysis_1.py index b6d6497..17fb46b 100644 --- a/analysis_1.py +++ b/code/analysis_1.py @@ -1,154 +1,154 @@ -import rlxnix as rlx -import numpy as np -import matplotlib.pyplot as plt -import os -from scipy.signal import welch - -# close all currently open figures -plt.close('all') - -'''FUNCTIONS''' -def plot_vt_spikes(t, v, spike_t): - fig = plt.figure(figsize=(5, 2.5)) - # alternative to ax = axs[0] - ax = fig.add_subplot() - # plot vt diagram - ax.plot(t[t<0.1], v[t<0.1]) - # plot spikes into vt diagram, at max V - ax.scatter(spike_t[spike_t<0.1], np.ones_like(spike_t[spike_t<0.1]) * np.max(v)) - plt.show() - -def scatter_plot(colormap, stimuli_list, stimulus_count): - '''plot scatter plot for one sam with all 3 stims''' - fig = plt.figure() - ax = fig.add_subplot() - - ax.eventplot(stimuli_list, colors=colormap) - ax.set_xlabel('Spike Times [ms]') - ax.set_ylabel('Loop #') - ax.set_yticks(range(stimulus_count)) - ax.set_title('Spikes of SAM 3') - plt.show() - -# create binary array with ones for spike times -def binary_spikes(spike_times, duration , dt): - '''Converts spike times to binary representation - Params - ------ - spike_times: np.array - spike times - duration: float - trial duration - dt: float - temporal resolution - - Returns - -------- - binary: np.array - The binary representation of the spike times - ''' - binary = np.zeros(int(duration//dt)) # // is truncated division, returns number w/o decimals, same as np.round - spike_indices = np.asarray(np.round(spike_times//dt), dtype=int) - binary[spike_indices] = 1 - return binary - -# function to plot psth -def firing_rates(binary_spikes, box_width=0.01, dt=0.000025): - box = np.ones(int(box_width // dt)) - box /= np.sum(box * dt) # normalize box kernel w interal of 1 - rate = np.convolve(binary_spikes, box, mode='same') - return rate - -def power_spectrum(rate, dt): - f, p = welch(rate, fs = 1./dt, nperseg=2**16, noverlap=2**15) - # algorithm makes rounding mistakes, we want to calc many spectra and take mean of those - # nperseg: length of segments in # datapoints - # noverlap: # datapoints that overlap in segments - return f, p - -def power_spectrum_plot(f, p): - # plot power spectrum - fig = plt.figure() - ax = fig.add_subplot() - ax.plot(freq, power) - ax.set_xlabel('Frequency [Hz]') - ax.set_ylabel('Power [1/Hz]') - ax.set_xlim(0, 1000) - plt.show() - -'''IMPORT DATA''' -datafolder = '../data' #./ wo ich gerade bin; ../ eine ebene höher; ../../ zwei ebenen höher - -example_file = os.path.join('..', 'data', '2024-10-16-ac-invivo-1.nix') - -'''EXTRACT DATA''' -dataset = rlx.Dataset(example_file) - -# get sams -sams = dataset.repro_runs('SAM') -sam = sams[2] - -# get potetial over time (vt curve) -potential, time = sam.trace_data('V-1') - -# get spike times -spike_times, _ = sam.trace_data('Spikes-1') - -# get stim count -stim_count = sam.stimulus_count - -# extract spike times of all 3 loops of current sam -stimuli = [] -for i in range(stim_count): - # get stim i from sam - stim = sam.stimuli[i] - potential_stim, time_stim = stim.trace_data('V-1') - # get spike_times - spike_times_stim, _ = stim.trace_data('Spikes-1') - stimuli.append(spike_times_stim) - -eodf = stim.metadata[stim.name]['EODF'][0][0] -df = stim.metadata['RePro-Info']['settings']['deltaf'][0][0] -stimulus_freq = df + eodf - -'''PLOT''' -# create colormap -colors = plt.cm.prism(np.linspace(0, 1, stim_count)) - -# timeline of whole rec -dataset.plot_timeline() - -# voltage and spikes of current sam -plot_vt_spikes(time, potential, spike_times) - -# spike times of all loops -scatter_plot(colors, stimuli, stim_count) - - -'''POWER SPECTRUM''' -# define variables for binary spikes function -spikes, _ = stim.trace_data('Spikes-1') -ti = stim.trace_info('V-1') -dt = ti.sampling_interval -duration = stim.duration - -### spectrum -# vector with binary values for wholes length of stim -binary = binary_spikes(spikes, duration, dt) - -# calculate firing rate -rate = firing_rates(binary, 0.01, dt) # box width of 10 ms - -# plot psth or whatever -# plt.plot(time_stim, rate) -# plt.show() - -freq, power = power_spectrum(binary, dt) - -power_spectrum_plot(freq, power) - - -### TODO: - # then loop over sams/dfs, all stims, intensities - # when does stim start in eodf/ at which phase and how does that influence our signal --> alignment problem: egal wenn wir spectren haben +import rlxnix as rlx +import numpy as np +import matplotlib.pyplot as plt +import os +from scipy.signal import welch + +# close all currently open figures +plt.close('all') + +'''FUNCTIONS''' +def plot_vt_spikes(t, v, spike_t): + fig = plt.figure(figsize=(5, 2.5)) + # alternative to ax = axs[0] + ax = fig.add_subplot() + # plot vt diagram + ax.plot(t[t<0.1], v[t<0.1]) + # plot spikes into vt diagram, at max V + ax.scatter(spike_t[spike_t<0.1], np.ones_like(spike_t[spike_t<0.1]) * np.max(v)) + plt.show() + +def scatter_plot(colormap, stimuli_list, stimulus_count): + '''plot scatter plot for one sam with all 3 stims''' + fig = plt.figure() + ax = fig.add_subplot() + + ax.eventplot(stimuli_list, colors=colormap) + ax.set_xlabel('Spike Times [ms]') + ax.set_ylabel('Loop #') + ax.set_yticks(range(stimulus_count)) + ax.set_title('Spikes of SAM 3') + plt.show() + +# create binary array with ones for spike times +def binary_spikes(spike_times, duration , dt): + '''Converts spike times to binary representation + Params + ------ + spike_times: np.array + spike times + duration: float + trial duration + dt: float + temporal resolution + + Returns + -------- + binary: np.array + The binary representation of the spike times + ''' + binary = np.zeros(int(duration//dt)) # // is truncated division, returns number w/o decimals, same as np.round + spike_indices = np.asarray(np.round(spike_times//dt), dtype=int) + binary[spike_indices] = 1 + return binary + +# function to plot psth +def firing_rates(binary_spikes, box_width=0.01, dt=0.000025): + box = np.ones(int(box_width // dt)) + box /= np.sum(box * dt) # normalize box kernel w interal of 1 + rate = np.convolve(binary_spikes, box, mode='same') + return rate + +def power_spectrum(rate, dt): + f, p = welch(rate, fs = 1./dt, nperseg=2**16, noverlap=2**15) + # algorithm makes rounding mistakes, we want to calc many spectra and take mean of those + # nperseg: length of segments in # datapoints + # noverlap: # datapoints that overlap in segments + return f, p + +def power_spectrum_plot(f, p): + # plot power spectrum + fig = plt.figure() + ax = fig.add_subplot() + ax.plot(freq, power) + ax.set_xlabel('Frequency [Hz]') + ax.set_ylabel('Power [1/Hz]') + ax.set_xlim(0, 1000) + plt.show() + +'''IMPORT DATA''' +datafolder = '../data' #./ wo ich gerade bin; ../ eine ebene höher; ../../ zwei ebenen höher + +example_file = os.path.join('..', 'data', '2024-10-16-ac-invivo-1.nix') + +'''EXTRACT DATA''' +dataset = rlx.Dataset(example_file) + +# get sams +sams = dataset.repro_runs('SAM') +sam = sams[2] + +# get potetial over time (vt curve) +potential, time = sam.trace_data('V-1') + +# get spike times +spike_times, _ = sam.trace_data('Spikes-1') + +# get stim count +stim_count = sam.stimulus_count + +# extract spike times of all 3 loops of current sam +stimuli = [] +for i in range(stim_count): + # get stim i from sam + stim = sam.stimuli[i] + potential_stim, time_stim = stim.trace_data('V-1') + # get spike_times + spike_times_stim, _ = stim.trace_data('Spikes-1') + stimuli.append(spike_times_stim) + +eodf = stim.metadata[stim.name]['EODF'][0][0] +df = stim.metadata['RePro-Info']['settings']['deltaf'][0][0] +stimulus_freq = df + eodf + +'''PLOT''' +# create colormap +colors = plt.cm.prism(np.linspace(0, 1, stim_count)) + +# timeline of whole rec +dataset.plot_timeline() + +# voltage and spikes of current sam +plot_vt_spikes(time, potential, spike_times) + +# spike times of all loops +scatter_plot(colors, stimuli, stim_count) + + +'''POWER SPECTRUM''' +# define variables for binary spikes function +spikes, _ = stim.trace_data('Spikes-1') +ti = stim.trace_info('V-1') +dt = ti.sampling_interval +duration = stim.duration + +### spectrum +# vector with binary values for wholes length of stim +binary = binary_spikes(spikes, duration, dt) + +# calculate firing rate +rate = firing_rates(binary, 0.01, dt) # box width of 10 ms + +# plot psth or whatever +# plt.plot(time_stim, rate) +# plt.show() + +freq, power = power_spectrum(binary, dt) + +power_spectrum_plot(freq, power) + + +### TODO: + # then loop over sams/dfs, all stims, intensities + # when does stim start in eodf/ at which phase and how does that influence our signal --> alignment problem: egal wenn wir spectren haben # we want to see peaks at phase locking to own and stim frequency, and at amp modulation frequency \ No newline at end of file From 54d10789b4bee89bbe967c3013760586435aa17b Mon Sep 17 00:00:00 2001 From: mbergmann Date: Tue, 22 Oct 2024 07:21:37 +0000 Subject: [PATCH 8/8] test.py aktualisiert --- code/test.py | 230 +++++++++++++++++++++++++++++---------------------- 1 file changed, 130 insertions(+), 100 deletions(-) diff --git a/code/test.py b/code/test.py index f2b9a4e..9274e87 100644 --- a/code/test.py +++ b/code/test.py @@ -1,101 +1,131 @@ -import glob -import pathlib -import numpy as np -import matplotlib.pyplot as plt -import rlxnix as rlx -from IPython import embed -from scipy.signal import welch - -def binary_spikes(spike_times, duration, dt): - """ - Converts the spike times to a binary representations - - Parameters - ---------- - spike_times : np.array - The spike times. - duration : float - The trial duration: - dt : float - The temporal resolution. - - Returns - ------- - binary : np.array - The binary representation of the spike train. - - """ - binary = np.zeros(int(np.round(duration / dt))) #create the binary array with the same length as potential - - spike_indices = np.asarray(np.round(spike_times / dt), dtype = int) # get the indices - binary[spike_indices] = 1 # put the indices into binary - return binary - -def firing_rate(binary_spikes, dt = 0.000025, box_width = 0.01): - box = np.ones(int(box_width // dt)) - box /= np.sum(box) * dt # normalisierung des box kernels to an integral of one - rate = np.convolve(binary_spikes, box, mode = 'same') - return rate - -def power_spectrum(rate, dt): - freq, power = welch(rate, fs = 1/dt, nperseg = 2**16, noverlap = 2**15) - return freq, power - -#find example data -datafolder = "../data" - -example_file = datafolder + "/" + "2024-10-16-ad-invivo-1.nix" - -#load dataset -dataset = rlx.Dataset(example_file) -# find all sams -sams = dataset.repro_runs('SAM') -sam = sams[2] # our example sam -potential,time = sam.trace_data("V-1") #membrane potential -spike_times, _ = sam.trace_data('Spikes-1') #spike times -df = sam.metadata['RePro-Info']['settings']['deltaf'][0][0] #find df in metadata -amp = sam.metadata['RePro-Info']['settings']['contrast'][0][0] * 100 #find amplitude in metadata - -#figure for a quick plot -fig = plt.figure(figsize = (5, 2.5)) -ax = fig.add_subplot() -ax.plot(time[time < 0.1], potential[time < 0.1]) # plot the membrane potential in 0.1s -ax.scatter(spike_times[spike_times < 0.1], - np.ones_like(spike_times[spike_times < 0.1]) * np.max(potential)) #plot teh spike times on top -plt.show() -plt.close() -# get all the stimuli -stims = sam.stimuli -# empty list for the spike times -spikes = [] -#spikes2 = np.array(range(len(stims))) -# loop over the stimuli -for stim in stims: - # get the spike times - spike, _ = stim.trace_data('Spikes-1') - # append the first 100ms to spikes - spikes.append(spike[spike < 0.1]) - # get stimulus duration - duration = stim.duration - ti = stim.trace_info("V-1") - dt = ti.sampling_interval # get the stimulus interval - bin_spikes = binary_spikes(spike, duration, dt) #binarize the spike_times - print(len(bin_spikes)) - pot,tim= stim.trace_data("V-1") #membrane potential - rate = firing_rate(bin_spikes, dt = dt) - print(np.mean(rate)) - fig, [ax1, ax2] = plt.subplots(1, 2,layout = 'constrained') - ax1.plot(tim,rate) - ax1.set_ylim(0,600) - ax1.set_xlim(0, 0.04) - freq, power = power_spectrum(rate, dt) - ax2.plot(freq,power) - ax2.set_xlim(0,1000) - -# make an eventplot -fig = plt.figure(figsize = (5, 3), layout = 'constrained') -ax = fig.add_subplot() -ax.eventplot(spikes, linelength = 0.8) -ax.set_xlabel('time [ms]') -ax.set_ylabel('loop no.') +import glob +import pathlib +import numpy as np +import matplotlib.pyplot as plt +import rlxnix as rlx +from IPython import embed +from scipy.signal import welch + +def binary_spikes(spike_times, duration, dt): + """ + Converts the spike times to a binary representations + + Parameters + ---------- + spike_times : np.array + The spike times. + duration : float + The trial duration: + dt : float + The temporal resolution. + + Returns + ------- + binary : np.array + The binary representation of the spike train. + + """ + binary = np.zeros(int(np.round(duration / dt))) #create the binary array with the same length as potential + + spike_indices = np.asarray(np.round(spike_times / dt), dtype = int) # get the indices + binary[spike_indices] = 1 # put the indices into binary + return binary + +def firing_rate(binary_spikes, dt = 0.000025, box_width = 0.01): + box = np.ones(int(box_width // dt)) + box /= np.sum(box) * dt # normalisierung des box kernels to an integral of one + rate = np.convolve(binary_spikes, box, mode = 'same') + return rate + +def power_spectrum(rate, dt): + freq, power = welch(rate, fs = 1/dt, nperseg = 2**16, noverlap = 2**15) + return freq, power + +def extract_stim_data(stimulus): + ''' + extracts all necessary metadata for each stimulus + + Parameters + ---------- + stimulus : Stimulus object or rlxnix.base.repro module + The stimulus from which the data is needed. + + Returns + ------- + amplitude : float + The relative signal amplitude in percent. + df : float + Distance of the stimulus to the current EODf. + eodf : float + Current EODf. + stim_freq : float + The total stimulus frequency (EODF+df). + + ''' + # extract metadata + # the stim.name adjusts the first key as it changes with every stimulus + amplitude = stim.metadata[stim.name]['Contrast'][0][0] + df = stim.metadata[stim.name]['DeltaF'][0][0] + eodf = stim.metadata[stim.name]['EODf'][0][0] + stim_freq = stim.metadata[stim.name]['Frequency'][0][0] + return amplitude, df, eodf, stim_freq + + +#find example data +datafolder = "../data" + +example_file = datafolder + "/" + "2024-10-16-ad-invivo-1.nix" + +#load dataset +dataset = rlx.Dataset(example_file) +# find all sams +sams = dataset.repro_runs('SAM') +sam = sams[2] # our example sam +potential,time = sam.trace_data("V-1") #membrane potential +spike_times, _ = sam.trace_data('Spikes-1') #spike times +df = sam.metadata['RePro-Info']['settings']['deltaf'][0][0] #find df in metadata +amp = sam.metadata['RePro-Info']['settings']['contrast'][0][0] * 100 #find amplitude in metadata + +#figure for a quick plot +fig = plt.figure(figsize = (5, 2.5)) +ax = fig.add_subplot() +ax.plot(time[time < 0.1], potential[time < 0.1]) # plot the membrane potential in 0.1s +ax.scatter(spike_times[spike_times < 0.1], + np.ones_like(spike_times[spike_times < 0.1]) * np.max(potential)) #plot teh spike times on top +plt.show() +plt.close() +# get all the stimuli +stims = sam.stimuli +# empty list for the spike times +spikes = [] +#spikes2 = np.array(range(len(stims))) +# loop over the stimuli +for stim in stims: + # get the spike times + spike, _ = stim.trace_data('Spikes-1') + # append the first 100ms to spikes + spikes.append(spike[spike < 0.1]) + # get stimulus duration + duration = stim.duration + ti = stim.trace_info("V-1") + dt = ti.sampling_interval # get the stimulus interval + bin_spikes = binary_spikes(spike, duration, dt) #binarize the spike_times + print(len(bin_spikes)) + pot,tim= stim.trace_data("V-1") #membrane potential + rate = firing_rate(bin_spikes, dt = dt) + print(np.mean(rate)) + fig, [ax1, ax2] = plt.subplots(1, 2,layout = 'constrained') + ax1.plot(tim,rate) + ax1.set_ylim(0,600) + ax1.set_xlim(0, 0.04) + freq, power = power_spectrum(rate, dt) + ax2.plot(freq,power) + ax2.set_xlim(0,1000) + +# make an eventplot +fig = plt.figure(figsize = (5, 3), layout = 'constrained') +ax = fig.add_subplot() +ax.eventplot(spikes, linelength = 0.8) +ax.set_xlabel('time [ms]') +ax.set_ylabel('loop no.') plt.show() \ No newline at end of file