Revision 86d380144b3f85c8951923de873893583bd25edf authored by narendramukherjee on 01 October 2020, 17:54:51 UTC, committed by GitHub on 01 October 2020, 17:54:51 UTC
Change marking of trial start time
2 parent s c98da81 + f1007b5
Raw File
GFP_frequency_band_extraction.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Oct  3 08:19:17 2018

@author: bradly
"""
# import Libraries
# Built-in Python libraries
import os # functions for interacting w operating system
import sys # access to functions/variables at the level of the interpreter

#import operator tools for list manipulations
from itertools import groupby
from operator import itemgetter

# 3rd-party libraries
import numpy as np # module for low-level scientific computing
import scipy as sp # library for working with NumPy arrays
import scipy.io as sio # read/write data to various formats
from scipy import signal # signal processing module
from scipy.signal import hilbert #Hilbert transform to determine the amplitude envelope and instantaneous frequency of an amplitude-modulated signal
from scipy.signal import butter
from scipy.signal import filtfilt
from scipy.signal import argrelextrema #for peak amp coverage
import matplotlib.pyplot as plt # makes matplotlib work like MATLAB. ’pyplot’ functions.
import easygui
import tables
import pickle #for data storage and retreival

#Get name of directory where you want to save output files to
save_name = easygui.diropenbox(msg = 'Choose directory you want output files sent to (and/or where ".dir" files are)',default = '/home/bradly/drive2/data/Affective_State_Protocol/LiCl/Combined_Passive_Data/LiCl_Saline/freq_bands/LiCl')

#Ask user if they have ran set-up before
msg   = "Have you performed directory set-up before (ie. do you have '.dir' files in output folder) ?"
dir_check = easygui.buttonbox(msg,choices = ["Yes","No"])

if dir_check == "No":
    #Get data_saving name
    msg   = "What condition are you analyzing first?"
    subplot_check1 = easygui.buttonbox(msg,choices = ["Saline","LiCl","Other"])
    
    # Ask the user for the hdf5 files that need to be plotted together (fist condition)
    dirs_1 = []
    while True:
    	dir_name = easygui.diropenbox(msg = 'Choose first condition directory with a hdf5 file, hit cancel to stop choosing')
    	try:
    		if len(dir_name) > 0:	
    			dirs_1.append(dir_name)
    	except:
    		break   
    
    # Ask the user for the hdf5 files that need to be plotted together (second condition)
    #Get data_saving name
    msg   = "What condition are you analyzing second?"
    subplot_check2 = easygui.buttonbox(msg,choices = ["Saline","LiCl","Other"])
    
    dirs_2 = []
    while True:
    	dir_name = easygui.diropenbox(msg = 'Choose second condition directory with a hdf5 file, hit cancel to stop choosing')
    	try:
    		if len(dir_name) > 0:	
    			dirs_2.append(dir_name)
    	except:
    		break
    
    #Dump the directory names into chosen output location for each condition
    #condition 1
    completeName_1 = os.path.join(save_name, 'dirs_cond1.dir') 
    f_1 = open(completeName_1, 'w')
    for item in dirs_1:
        f_1.write("%s\n" % item)
    f_1.close()
    
    #condition 2
    completeName_2 = os.path.join(save_name, 'dirs_cond2.dir') 
    f_2 = open(completeName_2, 'w')
    for item in dirs_2:
        f_2.write("%s\n" % item)
    f_2.close()

if dir_check == "Yes":
    
    #Get data_saving name
    msg   = "What condition are you analyzing first?"
    subplot_check1 = easygui.buttonbox(msg,choices = ["Saline","LiCl","Other"])
    
    #establish directories to flip through
    #condition 1
    dirs_1_path = os.path.join(save_name, 'dirs_cond1.dir')
    dirs_1_file = open(dirs_1_path,'r')
    dirs_1 = dirs_1_file.read().splitlines()
    dirs_1_file.close()
    
    #Get data_saving name
    msg   = "What condition are you analyzing second?"
    subplot_check2 = easygui.buttonbox(msg,choices = ["Saline","LiCl","Other"])
    
    #condition 2
    dirs_2_path = os.path.join(save_name, 'dirs_cond2.dir')
    dirs_2_file = open(dirs_2_path,'r')
    dirs_2 = dirs_2_file.read().splitlines()
    dirs_2_file.close()

#modify save names
if subplot_check1==subplot_check2:
    subplot_check2=subplot_check2+'_2'    

#Define functions used in loop
#define bandpass filter parameters to parse out frequencies
def butter_bandpass_filter(data, lowcut, highcut, fs, order=2):
    nyq = 0.5 * fs
    low = lowcut /nyq
    high = highcut/nyq
    b, a = butter(order, [low, high], btype='bandpass')
    y = filtfilt(b, a, data)
    return y

#define joining parameters
def inner_join(a, b):
    L = a + b
    L.sort(key=itemgetter(0)) # sort by the first column
    for _, group in groupby(L, itemgetter(0)):
        row_a, row_b = next(group), next(group, None)
        if row_b is not None: # join
            yield row_a + row_b[1:] # cut 1st column from 2nd row
            
#Get name of directory where the data files and hdf5 file sits, and change to that directory for processing
#dir_name = easygui.diropenbox()
#os.chdir(dir_name)

combined_tuples1 = list(); combined_tuples2 = list()
            
for dir_name in dirs_1:
	#Change to the directory
	os.chdir(dir_name)
	#Locate the hdf5 file
	file_list = os.listdir('./')
	hdf5_name = ''
	for files in file_list:
		if files[-2:] == 'h5':
			hdf5_name = files

	#Open the hdf5 file
	hf5 = tables.open_file(hdf5_name, 'r')
    
    #Establish child dig_in number/name for LFP data
	dig_name = hf5.root.Parsed_LFP._f_iter_nodes()
	this_dig_in = next(dig_name)
	lfp_signal = np.squeeze(this_dig_in[:],axis=1) # The signal itself. This is a dataframe of electrodes x shape times

    #Close the hdf5 file
	hf5.close()
    
    #processing variables
	sfreq=1000 # Sampling frequency
	tmin = 0  #In seconds
	tmax = 1200 #In seconds
	samples = int(sfreq*np.size(lfp_signal,axis=1)/1000)
	times = np.arange(samples) / sfreq
    
    #frequency bands
	iter_freqs = [
        ('Theta', 4, 7),
        ('Alpha', 8, 12),
        ('Beta', 13, 25),
        ('Gamma', 30, 45)]
    
    #detrend the signal to remove linear trend
	#lfp_signal_detrend = signal.detrend(lfp_signal)    
	lfp_signal_detrend = lfp_signal
    
    #create list to store outputs into
	frequency_map = list(); envelope_map = list();  extrema_map = list()
    
    #run through frequency bands and compile list using hilbert transform
	for band, fmin, fmax in iter_freqs:
        
        #apply filter to compute specified bands 
		band_filt_sig = butter_bandpass_filter(np.mean(lfp_signal_detrend,axis=0), fmin, fmax, 1000)
         
        # bandpass filter and compute Hilbert
		analytic_signal = hilbert(band_filt_sig) 
        
        #complex vector created, so must perform list creation to extract only real parts of number
		analytic_signal = np.asarray([num.real for num in analytic_signal])
		amplitude_envelope = np.abs(analytic_signal)
		extrema = argrelextrema(amplitude_envelope, np.greater)
		instantaneous_phase = np.unwrap(np.angle(analytic_signal)) #first (angle) in radians, then unwrap radian phase
		instantaneous_frequency = (np.diff(instantaneous_phase) /(2.0*np.pi) * sfreq)
    
        #store and move on
		frequency_map.append(((band, fmin, fmax), analytic_signal))    
		envelope_map.append(((band, fmin, fmax), amplitude_envelope)) 
		extrema_map.append(((band, fmin, fmax), np.squeeze(np.asarray(extrema),axis=0))) 
    
    #plot the results (by frequency band and a 2.0s time window for visualization)
	fig, axes = plt.subplots(4, 1, figsize=(10, 7), sharex=True, sharey=True)
	colors = plt.get_cmap('winter_r')(np.linspace(0, 1, 4))
	for ((freq_name, fmin, fmax), average), color, ax in zip(
            frequency_map, colors, axes.ravel()[::-1]):
    
		gfp = average
		ax.plot(times, gfp, label=freq_name, color=color, linewidth=2.5)
		ax.axhline(0, linestyle='--', color='grey', linewidth=2)
		ax.set_ylabel('GFP')
		ax.annotate('%s (%d-%dHz)' % (freq_name, fmin, fmax),
                    xy=(0.95, 0.8),
                    horizontalalignment='right',
                    xycoords='axes fraction')
		ax.set_xlim(0, 2)
		ax.set_ylim(-60,60)
    
	axes.ravel()[-1].set_xlabel('Time [s]')
	plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=None, hspace=0.00)
	plt.suptitle('Global Field Potential by Frequency Band: 2sec',size=20)
	file_name = hdf5_name[:4]+ '_' + subplot_check1 + '_smaller_passive_bands.png'
	fig_save = os.path.join(save_name, file_name)
	fig.savefig(fig_save)
	plt.close()
    
    #plot the results (full 20-minutes)
	fig, axes = plt.subplots(4, 1, figsize=(10, 7), sharex=True, sharey=True)
	colors = plt.get_cmap('winter_r')(np.linspace(0, 1, 4))
	for ((freq_name, fmin, fmax), average), color, ax in zip(
            frequency_map, colors, axes.ravel()[::-1]):
        
		gfp = average
		ax.plot(times, gfp, label=freq_name, color=color, linewidth=2.5)
		ax.axhline(0, linestyle='--', color='grey', linewidth=2)
		ax.set_ylabel('GFP')
		ax.annotate('%s (%d-%dHz)' % (freq_name, fmin, fmax),
                    xy=(0.95, 0.8),
                    horizontalalignment='right',
                    xycoords='axes fraction')
		ax.set_xlim(0, 1200)
        #ax.set_ylim(-50,40)
    
	axes.ravel()[-1].set_xlabel('Time [s]')
	plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=None, hspace=0.00)
	plt.suptitle('Global Field Potential by Frequency Band: Extended',size=20)
	file_name = hdf5_name[:4]+ '_' + subplot_check1 +'_full_passive_bands.png'
	fig_save = os.path.join(save_name, file_name)
	fig.savefig(fig_save)
	plt.close()
    
	fig, axes = plt.subplots(4, 1, figsize=(10, 7), sharex=True, sharey=True)
	colors = plt.get_cmap('winter_r')(np.linspace(0, 1, 4))
	for ((freq_name, fmin, fmax), average), color, ax in zip(
            envelope_map, colors, axes.ravel()[::-1]):
    
		gfp = average
		ax.plot(times, gfp, label=freq_name, color=color, linewidth=2.5)
		ax.axhline(0, linestyle='--', color='grey', linewidth=2)
		ax.set_ylabel('GFP')
		ax.annotate('%s (%d-%dHz)' % (freq_name, fmin, fmax),
                    xy=(0.95, 0.8),
                    horizontalalignment='right',
                    xycoords='axes fraction')
		ax.set_xlim(0, 1)
		ax.set_ylim(-1,40)
    
	axes.ravel()[-1].set_xlabel('Time [s]')
       
    #combine all items of the 3 lists based on key ids
	result = list(inner_join(frequency_map, envelope_map));result2= list(inner_join(result, extrema_map))
    
    # take second element for sorting list based on frequency
	def takeSecond(elem):
		return elem[:][0][1]
    
    # sort list with key based on the frequency
	result2.sort(key=takeSecond)
    
	fig, axes = plt.subplots(4, 1, figsize=(10, 7), sharex=True, sharey=True)
	colors = plt.get_cmap('winter_r')(np.linspace(0, 1, 4))
	for ((freq_name, fmin, fmax), average1,average2,average3), color, ax in zip(
            result2, colors, axes.ravel()[::-1]):
        
		hilb_signal = average1; env_signal = average2; maxpeak_signal = average3
        
		ax.plot(times[maxpeak_signal], np.abs(hilb_signal[maxpeak_signal]), label=freq_name, color=color, linewidth=2.5)
		ax.axhline(0, linestyle='--', color='grey', linewidth=2)
		ax.set_ylabel('GFP')
		ax.annotate('%s (%d-%dHz)' % (freq_name, fmin, fmax),
                    xy=(0.95, 0.8),
                    horizontalalignment='right',
                    xycoords='axes fraction')
		ax.set_xlim(0, 1200)
        #ax.set_ylim(-1,40)
    
	axes.ravel()[-1].set_xlabel('Time [s]')
    
    
	fig, axes = plt.subplots(4, 1, figsize=(10, 7), sharex=True, sharey=True)
	colors = plt.get_cmap('winter_r')(np.linspace(0, 1, 4))
	for ((freq_name, fmin, fmax), average1,average2,average3), color, ax in zip(
            result2, colors, axes.ravel()[::-1]):
    
		hilb_signal = average1; env_signal = average2; maxpeak_signal = average3
         
        #apply a Savitzky-Golay Filter to smooth/eliminate noise from dataset
		power_smoothed = signal.savgol_filter(np.abs(hilb_signal[maxpeak_signal]), 351, 1)
         
		ax.plot(times[maxpeak_signal], power_smoothed, label=freq_name, color=color, linewidth=2.5)
		ax.axhline(np.mean(power_smoothed), linestyle='--', color='grey', linewidth=2)
		ax.set_ylabel('GFP')
		ax.annotate('%s (%d-%dHz)' % (freq_name, fmin, fmax),
                    xy=(0.95, 0.8),
                    horizontalalignment='right',
                    xycoords='axes fraction')
		ax.set_xlim(0, 1200)
		#ax.set_ylim(-3,40)
    
	axes.ravel()[-1].set_xlabel('Time [s]')
	plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=None, hspace=0.00)
	plt.suptitle('Global Field Potential by Frequency Band: Savitzky-Golay Enveloped',size=20)
	file_name = hdf5_name[:4]+ '_' + subplot_check1 + '_full_passive_eveloped.png'
	fig_save = os.path.join(save_name, file_name)
	fig.savefig(fig_save)
	plt.close()
    
    #store animal's output into group list
	combined_tuples1.append((hdf5_name[:4], result2)) 
    
#Save output to .dump file for easy access plotting later
tuple_save = subplot_check1 + '_tuple.dump'
output_name =   os.path.join(save_name, tuple_save)
pickle.dump(combined_tuples1, open(output_name, 'wb'))  

#flip through next condition
for dir_name in dirs_2:
	#Change to the directory
	os.chdir(dir_name)
	#Locate the hdf5 file
	file_list = os.listdir('./')
	hdf5_name = ''
	for files in file_list:
		if files[-2:] == 'h5':
			hdf5_name = files

	#Open the hdf5 file
	hf5 = tables.open_file(hdf5_name, 'r')
    
    #Establish child dig_in number/name for LFP data
	dig_name = hf5.root.Parsed_LFP._f_iter_nodes()
	this_dig_in = next(dig_name)
	lfp_signal = np.squeeze(this_dig_in[:],axis=1) # The signal itself. This is a dataframe of electrodes x shape times

    #Close the hdf5 file
	hf5.close()
    
    #processing variables
	sfreq=1000 # Sampling frequency
	tmin = 0  #In seconds
	tmax = 1200 #In seconds
	samples = int(sfreq*np.size(lfp_signal,axis=1)/1000)
	times = np.arange(samples) / sfreq
    
    #frequency bands
	iter_freqs = [
        ('Theta', 4, 7),
        ('Alpha', 8, 12),
        ('Beta', 13, 25),
        ('Gamma', 30, 45)]
    
    #detrend the signal to remove linear trend
	lfp_signal_detrend = signal.detrend(lfp_signal)    
    
    #create list to store outputs into
	frequency_map = list(); envelope_map = list();  extrema_map = list()
    
    #run through frequency bands and compile list using hilbert transform
	for band, fmin, fmax in iter_freqs:
        
        #apply filter to compute specified bands 
		band_filt_sig = butter_bandpass_filter(np.mean(lfp_signal_detrend,axis=0), fmin, fmax, 1000)
         
        # bandpass filter and compute Hilbert
		analytic_signal = hilbert(band_filt_sig) 
        
        #complex vector created, so must perform list creation to extract only real parts of number
		analytic_signal = np.asarray([num.real for num in analytic_signal])
		amplitude_envelope = np.abs(analytic_signal)
		extrema = argrelextrema(amplitude_envelope, np.greater)
		instantaneous_phase = np.unwrap(np.angle(analytic_signal)) #first (angle) in radians, then unwrap radian phase
		instantaneous_frequency = (np.diff(instantaneous_phase) /(2.0*np.pi) * sfreq)
    
        #store and move on
		frequency_map.append(((band, fmin, fmax), analytic_signal))    
		envelope_map.append(((band, fmin, fmax), amplitude_envelope)) 
		extrema_map.append(((band, fmin, fmax), np.squeeze(np.asarray(extrema),axis=0))) 
    
    #plot the results (by frequency band and a 2.0s time window for visualization)
	fig, axes = plt.subplots(4, 1, figsize=(10, 7), sharex=True, sharey=True)
	colors = plt.get_cmap('winter_r')(np.linspace(0, 1, 4))
	for ((freq_name, fmin, fmax), average), color, ax in zip(
            frequency_map, colors, axes.ravel()[::-1]):
    
		gfp = average
		ax.plot(times, gfp, label=freq_name, color=color, linewidth=2.5)
		ax.axhline(0, linestyle='--', color='grey', linewidth=2)
		ax.set_ylabel('GFP')
		ax.annotate('%s (%d-%dHz)' % (freq_name, fmin, fmax),
                    xy=(0.95, 0.8),
                    horizontalalignment='right',
                    xycoords='axes fraction')
		ax.set_xlim(0, 2)
		ax.set_ylim(-60,60)
    
	axes.ravel()[-1].set_xlabel('Time [s]')
	plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=None, hspace=0.00)
	plt.suptitle('Global Field Potential by Frequency Band: 2sec',size=20)
	file_name = hdf5_name[:4]+ '_' + subplot_check2 + '_smaller_passive_bands.png'
	fig_save = os.path.join(save_name, file_name)
	fig.savefig(fig_save)
	plt.close()
    
    #plot the results (full 20-minutes)
	fig, axes = plt.subplots(4, 1, figsize=(10, 7), sharex=True, sharey=True)
	colors = plt.get_cmap('winter_r')(np.linspace(0, 1, 4))
	for ((freq_name, fmin, fmax), average), color, ax in zip(
            frequency_map, colors, axes.ravel()[::-1]):
        
		gfp = average
		ax.plot(times, gfp, label=freq_name, color=color, linewidth=2.5)
		ax.axhline(0, linestyle='--', color='grey', linewidth=2)
		ax.set_ylabel('GFP')
		ax.annotate('%s (%d-%dHz)' % (freq_name, fmin, fmax),
                    xy=(0.95, 0.8),
                    horizontalalignment='right',
                    xycoords='axes fraction')
		ax.set_xlim(0, 1200)
        #ax.set_ylim(-50,40)
    
	axes.ravel()[-1].set_xlabel('Time [s]')
	plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=None, hspace=0.00)
	plt.suptitle('Global Field Potential by Frequency Band: Extended',size=20)
	file_name = hdf5_name[:4]+ '_' + subplot_check2 +'_full_passive_bands.png'
	fig_save = os.path.join(save_name, file_name)
	fig.savefig(fig_save)
	plt.close()
    
	fig, axes = plt.subplots(4, 1, figsize=(10, 7), sharex=True, sharey=True)
	colors = plt.get_cmap('winter_r')(np.linspace(0, 1, 4))
	for ((freq_name, fmin, fmax), average), color, ax in zip(
            envelope_map, colors, axes.ravel()[::-1]):
    
		gfp = average
		ax.plot(times, gfp, label=freq_name, color=color, linewidth=2.5)
		ax.axhline(0, linestyle='--', color='grey', linewidth=2)
		ax.set_ylabel('GFP')
		ax.annotate('%s (%d-%dHz)' % (freq_name, fmin, fmax),
                    xy=(0.95, 0.8),
                    horizontalalignment='right',
                    xycoords='axes fraction')
		ax.set_xlim(0, 1)
		ax.set_ylim(-1,40)
    
	axes.ravel()[-1].set_xlabel('Time [s]')
       
    #combine all items of the 3 lists based on key ids
	result = list(inner_join(frequency_map, envelope_map));result2= list(inner_join(result, extrema_map))
    
    # take second element for sorting list based on frequency
	def takeSecond(elem):
		return elem[:][0][1]
    
    # sort list with key based on the frequency
	result2.sort(key=takeSecond)
    
	fig, axes = plt.subplots(4, 1, figsize=(10, 7), sharex=True, sharey=True)
	colors = plt.get_cmap('winter_r')(np.linspace(0, 1, 4))
	for ((freq_name, fmin, fmax), average1,average2,average3), color, ax in zip(
            result2, colors, axes.ravel()[::-1]):
        
		hilb_signal = average1; env_signal = average2; maxpeak_signal = average3
        
		ax.plot(times[maxpeak_signal], np.abs(hilb_signal[maxpeak_signal]), label=freq_name, color=color, linewidth=2.5)
		ax.axhline(0, linestyle='--', color='grey', linewidth=2)
		ax.set_ylabel('GFP')
		ax.annotate('%s (%d-%dHz)' % (freq_name, fmin, fmax),
                    xy=(0.95, 0.8),
                    horizontalalignment='right',
                    xycoords='axes fraction')
		ax.set_xlim(0, 1200)
        #ax.set_ylim(-1,40)
    
	axes.ravel()[-1].set_xlabel('Time [s]')
    
    
	fig, axes = plt.subplots(4, 1, figsize=(10, 7), sharex=True, sharey=True)
	colors = plt.get_cmap('winter_r')(np.linspace(0, 1, 4))
	for ((freq_name, fmin, fmax), average1,average2,average3), color, ax in zip(
            result2, colors, axes.ravel()[::-1]):
    
		hilb_signal = average1; env_signal = average2; maxpeak_signal = average3
         
        #apply a Savitzky-Golay Filter to smooth/eliminate noise from dataset
		power_smoothed = signal.savgol_filter(np.abs(hilb_signal[maxpeak_signal]), 351, 1)
         
		ax.plot(times[maxpeak_signal], power_smoothed, label=freq_name, color=color, linewidth=2.5)
		ax.axhline(np.mean(power_smoothed), linestyle='--', color='grey', linewidth=2)
		ax.set_ylabel('GFP')
		ax.annotate('%s (%d-%dHz)' % (freq_name, fmin, fmax),
                    xy=(0.95, 0.8),
                    horizontalalignment='right',
                    xycoords='axes fraction')
		ax.set_xlim(0, 1200)
		#ax.set_ylim(-3,40)
    
	axes.ravel()[-1].set_xlabel('Time [s]')
	plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=None, hspace=0.00)
	plt.suptitle('Global Field Potential by Frequency Band: Savitzky-Golay Enveloped',size=20)
	file_name = hdf5_name[:4]+ '_' + subplot_check2 + '_full_passive_eveloped.png'
	fig_save = os.path.join(save_name, file_name)
	fig.savefig(fig_save)
	plt.close()
    
    #store animal's output into group list
	combined_tuples2.append((hdf5_name[:4], result2)) 
    
#Save output to .dump file for easy access plotting later
tuple_save = subplot_check2 + '_tuple.dump'
output_name =   os.path.join(save_name, tuple_save)
pickle.dump(combined_tuples2, open(output_name, 'wb'))  
back to top