bycycle-tools / bycycle

Cycle-by-cycle analysis of neural oscillations.
https://bycycle-tools.github.io/
Apache License 2.0
82 stars 21 forks source link

Add MATLAB support #10

Closed srcole closed 5 years ago

lcnbeapp commented 5 years ago

I am attempting to write a wrapper function for this, but I'm having some trouble. I am trying to use this command: py.bycycle.features.compute_features() (inspired by the fooof wrapper, where py.fooof.FOOOF does the trick), but I'm getting the "Undefined variable "py" or class "py.bycycle.features.compute_features" error. Any insight? Thanks!

srcole commented 5 years ago

Thanks for working on it!

This was a conversation on the lab slack. See the screenshot below for Tom's advice.

image

lcnbeapp commented 5 years ago

Fixed it! I had the wrong python loaded in. Thank you, this was helpful.

mgm248 commented 5 years ago

I am working on integrating bycycle with BEAPP (https://github.com/lcnbeapp/beapp), so I did some work on this issue. It's a work in progress, and some of the variables might be confusing, but I wanted to share what I have in case it's helpful:

`src_dir = find_input_dir('bycycle',grp_proc_info_in.beapp_toggle_mods); max_n_chans = 256; for curr_file=1:length(grp_proc_info_in.beapp_fname_all)

cd(src_dir{1});

  if exist(strcat(src_dir{1},filesep,grp_proc_info_in.beapp_fname_all{curr_file}),'file')
     tic;
     % load eeg if module takes continuous input
    load(grp_proc_info_in.beapp_fname_all{curr_file});
    curr_eeg = eeg_w{1,1};
    if size(curr_eeg,3) > (grp_proc_info_in.bycyc_num_segs-1)
        segments_torun = randsample(size(curr_eeg,3),grp_proc_info_in.bycyc_num_segs);
        results_table = NaN(22,max_n_chans);
        results_table_burst = NaN(22,max_n_chans);
        results_table_nburst = NaN(22,max_n_chans);
        for chan = 1:max_n_chans
            curr_chan_results = NaN(500,22,size(segments_torun,1));
            if ismember(chan,file_proc_info.beapp_indx{1,1})
                for seg = 1:size(segments_torun,1) 
                    curr_seg = segments_torun(seg,1); 
                    signal = curr_eeg(chan,:,curr_seg); 
                    signal = py.numpy.array(signal);
                    f_range = py.list([12,20]);
                    fs = size(curr_eeg,2) / 10; %where 10 = segment length
                    fs = py.float(fs);
                    signal = py.bycycle.filt.lowpass_filter(signal, fs, py.float(35));
                    burst_kwargs = py.dict(pyargs('amplitude_fraction_threshold',.3,'amplitude_consistency_threshold',.4,'period_consistency_threshold',.5, 'monotonicity_threshold',.8,'N_cycles_min',3));
                    bycyc = py.bycycle.features.compute_features(signal, fs, f_range, py.str('P'), py.str('cycles'),burst_kwargs);
                    %py.bycycle.burst.plot_burst_detect_params(signal, fs, bycyc, burst_kwargs)
                    df = bycyc.to_dict;
                    %results_table = NaN(double(py.len(df{'sample_peak'})),double(py.len(keys(df))));
                    for row = 1:(double(py.len(df{'sample_peak'}))) %length sems to be 1 more than it actually is ...  maybe it counts the title?
                        curr_chan_results(row,1,seg) = df{'sample_peak'}{row-1};
                        curr_chan_results(row,2,seg) = df{'sample_zerox_decay'}{row-1};
                        curr_chan_results(row,3,seg)= df{'sample_zerox_rise'}{row-1};
                        curr_chan_results(row,4,seg)= df{'sample_last_trough'}{row-1};
                        curr_chan_results(row,5,seg) = df{'sample_next_trough'}{row-1};
                        curr_chan_results(row,6,seg) = df{'period'}{row-1};
                        curr_chan_results(row,7,seg) = df{'time_peak'}{row-1};
                        curr_chan_results(row,8,seg) = df{'time_trough'}{row-1};
                        curr_chan_results(row,9,seg) = df{'volt_peak'}{row-1};
                        curr_chan_results(row,10,seg) = df{'volt_trough'}{row-1};
                        curr_chan_results(row,11,seg) = df{'time_rise'}{row-1};
                        curr_chan_results(row,12,seg) = df{'volt_decay'}{row-1};
                        curr_chan_results(row,13,seg) = df{'volt_rise'}{row-1};
                        curr_chan_results(row,14,seg) = df{'volt_amp'}{row-1};
                        curr_chan_results(row,15,seg) = df{'time_rdsym'}{row-1};
                        curr_chan_results(row,16,seg) = df{'time_ptsym'}{row-1};
                        curr_chan_results(row,17,seg) = df{'band_amp'}{row-1};
                        curr_chan_results(row,18,seg) = df{'amp_fraction'}{row-1};
                        curr_chan_results(row,19,seg) = df{'amp_consistency'}{row-1};
                        curr_chan_results(row,20,seg) = df{'period_consistency'}{row-1};
                        curr_chan_results(row,21,seg) = df{'monotonicity'}{row-1};
                        curr_chan_results(row,22,seg) = df{'is_burst'}{row-1};
                    end
                   % avg_cyc_chan_results = squeeze(nanmean(curr_chan_results,1)); %avg over cycles
                end
                curr_chan_results_burst = NaN(size(curr_chan_results));
                curr_chan_results_nburst = NaN(size(curr_chan_results));
                for seg = 1:size(curr_chan_results,3)
                    for i=1:size(curr_chan_results,1)
                        if curr_chan_results(i,22,seg) == 1
                            curr_chan_results_burst(i,:,seg) = curr_chan_results(i,:,seg);
                        else
                            curr_chan_results_nburst(i,:,seg) = curr_chan_results(i,:,seg);
                        end
                    end  
                end

                results_table(:,chan) = squeeze(nanmean(nanmean(curr_chan_results,1),3)); %avg over cycles, then avg over segments
                results_table_burst(:,chan) = squeeze(nanmean(nanmean(curr_chan_results_burst,1),3));
                results_table_nburst(:,chan) = squeeze(nanmean(nanmean(curr_chan_results_nburst,1),3));
            end
        end

        %% save and update file history
        cd(grp_proc_info_in.beapp_toggle_mods{'bycycle','Module_Dir'}{1});
        filename = erase(file_proc_info.beapp_fname{1},'.mat');
        save(strcat(filename,'_bycycle_results','.mat'),'results_table','results_table_burst','results_table_nburst');
    end
  end
  clearvars -except grp_proc_info_in src_dir curr_file max_n_chans

end`

mgm248 commented 5 years ago

Also, re getting Matlab to find python, see this https://www.mathworks.com/matlabcentral/answers/427187-problem-with-python-numpy

TLDR: Matlab doesn't support anaconda python

StefanoBuccelli commented 5 years ago

Hi guys, inspired by mgm248's code I created a small repo. Take a look if you like :) and thanks for your great work with bycycle !

Stefano

srcole commented 5 years ago

Awesome. Thanks @mgm248 and @StefanoBuccelli!

irenevigueguix commented 4 years ago

Hey! I've got problems as well to launch Python into MATLAB, and I've successfully made it work (after spending the whole afternoon) by following your instructions together with other info that I found on the internet regarding the same problem with other Python packages. Yay! Also, I've created this tutorial (https://irenevigueguix.wordpress.com/2020/03/25/loading-python-into-matlab/) for those who might have the same issues in the future and probably end up on this very page. I hope you find the tutorial useful and clearly explained!