a
    g                     @   s  d Z ddlZejZddlmZ ddlZddlm	Z
 ddlmZ ddlmZ ddlZddlZddlmZmZmZ ddlmZ ddlmZ ddlmZmZ dd	lm Z  dd
l!m"Z" ddlm#Z# ddl$m%Z% ddl&m'Z'm(Z(m)Z)m*Z*m+Z+m,Z, ddl-Z-e-j.de/d ddl0m1Z1 ddl2m3Z3 ddl4Z4ddlZe4j56dej7 ddl8m9Z9m:Z: doddZ;dpddZ<dqddZ=dd Z>drddZ?dsd"d#Z@dtd&d'ZAd(d) ZBd*d+ ZCd,d- ZDdud.d/ZEdvd1d2ZFdwd3d4ZGdxd6d7ZHdyd8d9ZIdzd;d<ZJd{d=d>ZKd|d@dAZLdBdC ZMdDdE ZNdFdG ZOdHdI ZPd}dJdKZQd~dLdMZRddQdRZSddSdTZTdUdV ZUddYdZZVdd d[d\d]d^ifd_d`ZWdd ejXd[d0dd\d5daddbd0ddcdddfdedfZYdgdg dhd[fdidjZZdkdlgddfdmdnZ[dS )z
Created on Thu Sep 30 19:37:30 2021

@author: Albert

Various useful functions for processing Negar's data.
Might separate this script into preprocessing, processing, postprocessing

    N)partial)Line2D)KFold
GroupKFoldLeaveOneGroupOut)LogisticRegression)SVC)r2_scoref1_score)CCA)decomposition)interpolate)gaussian_filter)WienerFilterRegressionWienerCascadeRegressionKalmanFilterRegressionSVRegressionNaiveBayesRegressionXGBoostRegressionignore)actioncategory)Line3DCollection)LineCollection)compute_structure_index
draw_graphc                 C   s    t | j}|||< t| |dS )zR
    Applies a gaussian filter of given size and std along the specified axis
    )sigma)npzerosndimr   )datastdaxisr    r#   VD:\MPI_Brain\Codes\aversive_scripts_2025\aversive_scripts_2025\processing_functions.py	smoothing5   s    r%   c                 C   s   d|kr|| j  }| j}|| | }|dkrdt| j df}|| || d< tj| |tddd} | |d| d|f ||d d  }|j|d d}|S )	a   Sums every X elements of array along specified axis, with X being chunk_size
    
        Parameters:
        ----------
        array: numpy array, any dimensions
        chunk_size: number of elements from array that will be summed
        axis: axis of array along which the sum will be performed
        pro
        Returns:
        ------------
        array_out: array of dimensions same as input array, except for "axis" which will be its original size divided by chunk_size
        
        eg: 
            array = [[0 0 1 0 0 1],
                     [1 1 0 1 1 1]]
            axis = -1 (or, equivalently, 1)
            chunk_size = 2
            
            array_out = [[0, 1, 1],
                         [2, 1, 2]]
        
    r         constant)modeconstant_valuesNr&   r"   )	r   shaper   r   padastypeintreshapesum)arraybin_sizer"   r-   axis_residual	pad_widtharray_reshaped	array_outr#   r#   r$   sum_array_by_chunks?   s    
*r9   Fc                 C   s   t | tttjfv r| g} t |tttjfv r4|g}t| } t|}| jdkr\|jdks`J t|| ||| f}||  }tjt	|dd}||t
t| f }|dkrt	|}|S )a   Computes the difference between a prediction and its objective but taking periodicity into account
        Assumes they are all 1D array of same size
        If "get_sign" is True, the sign of the difference is kept.
            Example: for period=1500, 20-50 = -30; 1250-50 = -300; 50-20 = 30, 50-1250 = 300
            Explanation: the shortest path from 50 to 1250 is to move 300 units left. The shortest path from 1250 to 50 is 300 units to the right.
    r(   r   r,   F)typer0   floatr   float64r3   r   stackargminabsrangelen)
prediction	objectiveperiodget_signextended_objectiveraw_diffextended_diff_argextended_diffr#   r#   r$   get_periodic_differenceg   s    
rJ   c                 C   s    t | }t ||kd }|S )a$   
        Returns number of dimensions needed to explain a given pca variance.
    
        variance_explained should be the output of pca_instance.explained_variance_ratio_ (from sklearn)
        variance_to_explain must be a float between 0 and 1, representing % of variance to explain
    r(   )r   cumsumargmax)variance_explainedvariance_to_explainvariance_explained_cumdimensions_for_xr#   r#   r$   dimensions_to_explain_variance   s    
rQ     c           
         s   t | }tt || k d d }t ||k}|rtt ||kd d }t| }t j|td |D ]} |d  d7  < ql|D ]} |d  d8  < qt  t  }	 fddt	d|	d D }|S )a   Get round endtimes. Assumes position goes from its maximum value to 0 after a round end.
        Accounts for reverse movement 
            e.g. 1495, 1499, 3, 1497, 6, 10... counts as 1 single round change
        position is 1-D array
        diff_thresh is how much the position value must change in a single step to consider it a round end.
            Recommend to take 1/2 or 2/3 of maximum position
    r   r(   )dtypeNc                    s   g | ]}  |qS r#   )index).0	round_idxround_counterr#   r$   
<listcomp>       z&get_round_endtimes.<locals>.<listcomp>)
r   difflistwhereanyrA   r   r0   maxr@   )
positiondiff_threshpos_diff	overroundany_round_back_jumpsoverround_backnum_timepoints	round_end	jump_back
num_roundsr#   rW   r$   get_round_endtimes   s    

rj        c                    s   |du rd| }t | |d}t| }dg| |g   fddtt d D }|dkr||k rjtd	 | d |kr||d= | d
 || k r|d
= |S )a]   Returns a list of pairs with the start and end points of each trial.
        Convenience function to avoid problems with whether or not to include the zero
        pos_thresh: if full_rounds_only is chosen, any round that starts or ends at a 
        position further away from the end by more than "pos_thresh" will be taken as incomplete
        NQ?ra   r   c                    s    g | ]} |  |d   fqS r(   r#   rU   iround_ends_fullr#   r$   rY      rZ   z,get_round_endtimes_pairs.<locals>.<listcomp>r(   TzVWARNING: pos_thresh is larger than round size, every round will be taken as incompleter&   )rj   rA   r@   print)r`   max_posra   full_rounds_only
pos_thresh
round_ends	last_timepairsr#   rr   r$   get_round_endtimes_pairs   s    r{   interpolationtimec           %   	      sD  t t| t| |dd}t|} jd }	t || }
t |	t|
f}g }|dkrd}ttj|d| } fddt	|	D }t
|D ]p\}}|\}}|t ||d g \}}| ||  d	d	||f }t t}|d
krt |||  | }n|dkrt || | }zfdd|D }W nb ty   tfddt
|D }fdd|d	| D }||d gt||   }Y n0 |dkr|t | }|||
||< t	|	D ]}|| |||||f< qڐn|dkr |d
kr&t||g }|}n|dkrBt||g }}dg}t	t|d D ]} ||  }!|| d  }"t||  }#t |!|k||"k }$t |$st |}$t ||$ || < ||  |
|#< t j|d	d	|$f dd|d	d	|#f< |$}qX|t | }|| q|dkr:t|
|}
t||}tt ||}|
||fS )a   Function that splits the data into trials, and warps each trial into an equal amount of bins
        position: array of shape "timesteps"
        data: array of shape "num features" X "timesteps"
        trial_bin_num: number of bins the trial is split on
        max_pos: maximum value of the "position" vector
        warp_sampling_type: how is the sampling performed
            interpolation - data is linearly interpolated, each bin sampled at a specific location
            averaging - data is averaged for all points that fall within each bin
        warp_based_on: which variable is used to determine start and end of bins. Must be 'time' or 'position'.
        return_flattened: if True, data is outputed with the last axis being "num bins * num trials"
        
        
        RETURNS:
        position_warped: shape "num bins" X "num trials"
        data_warped: shape "num features" X "num bins" X "num trials"
        sampling_warped: 
    T)ru   rv   r   r|   r(   )kc                    s"   g | ]} |d d f qS Nr#   rU   dim)r    interpolatorttr#   r$   rY      rZ   zwarping.<locals>.<listcomp>Nr}   r`   c                    s&   g | ] t  fd dtD qS )c                 3   s   | ]\}}| kr|V  qd S r   r#   )rU   t_visit	pos_visitpos_binr#   r$   	<genexpr>   rZ   %warping.<locals>.<listcomp>.<genexpr>next	enumeraterU   position_roundr   r$   rY      rZ   c                 3   s"   | ]\}}| d  kr|V  qdS )r&   Nr#   )rU   bin_idxbin_posr   r#   r$   r      rZ   zwarping.<locals>.<genexpr>c                    s&   g | ] t  fd dtD qS )c                 3   s"   | ]}|d   kr|d V  qdS )r(   r   Nr#   )rU   xposr#   r$   r      rZ   r   r   r   r   r   r$   rY      rZ   r&   	averagingr,   F)r   arangerA   r{   r-   r   r   r   InterpolatedUnivariateSpliner@   r   r3   StopIterationr   r\   r0   bitwise_andr^   copyaverageextendunflatten_warped_data)%r`   r    trial_bin_numru   warp_sampling_typewarp_based_onreturn_flattenedendtime_pairs
num_trialsnum_data_dimposition_warpeddata_warpedsampling_warpedspline_orderposition_interpdata_interp_listrV   
round_pair	start_idxend_idxstart_warped_idxend_warped_idx
data_roundtt_roundtwarp_roundposwarplast_visited_bintwarpr   sampling_binsreference_for_binprev_idxs_in_binbin_start_idx	bin_startbin_endbin_warped_idxidxs_in_binr#   )r    r   r   r   r$   warping   sv    



 
 



&


r   c                 C   sh   | j d | dkr&t| j d | }ntd tdtdd | j dd D ||g }| j|ddS )	a   "array" must be an array with the last axis having size "num_trial_bins" * "num trials"
        This function unflattens this last axis into "num_trial_bins" X "num trials"
        array can be of any dimension, only the last axis is assumed to be the warped time one
    r&   r   z:Incorrect num trials, array is not a multple of num_trialsc                 S   s   g | ]}|qS r#   r#   r   r#   r#   r$   rY   6  rZ   z)unflatten_warped_data.<locals>.<listcomp>NForder)r-   r0   rt   
ValueErrortupler1   )r3   r   r   	new_shaper#   r#   r$   r   ,  s    $r   c                 C   s@   t dd | jdd D | jd | jd  g }| j|ddS )z Flattens the last two dimensions of an array of the form ... X "num_trial_bins" X "num_trials"
        array can be of any dimensions (larger than 1), only the last two axis are assumed to be number of bins and trials
    c                 S   s   g | ]}|qS r#   r#   r   r#   r#   r$   rY   >  rZ   z'flatten_warped_data.<locals>.<listcomp>Nr&   r   r   )r   r-   r1   )r3   r   r#   r#   r$   flatten_warped_data:  s    2r   c                 C   sX   t | } ||k r(t | |k| |k }n,| | |k   |7  < t | |k| || k }|S )z values: array with a single dimension of size S
        left_value: start of interval (included)
        right_value: end of interval (excluded included)
        period: max value of "values" before they go to zero
    )r   r   r   )values
left_valueright_valuerD   idxsr#   r#   r$   get_idxs_in_periodic_intervalA  s    
r   c                 C   s2  | d d ||f }t | | }| | d d ||f  }t |}| | d d ||f  d d }| | d d ||f  d d }| d d ||f }	| |	 d }
| | d d ||f  d	 d }| | d d
 ||f  dddf }|j\}}t|t|fdkrd}|ddd|f }|ddd|f }|ddd|f }|d| }|d| }|
d| }
|d| }|j\}}|dur|\}}t|| }t|| }|dd||f }|dd||f }||| }||| }|
|| }
||| }t|}|||||
|||||t| |d}|S )a  
    Returns relevant variables from the data cluster
    mouse_num: between 0 and 7, 0-3 are V-D and 4-7 are D-D
    session_num: between 0 and 17
        - 0-2 are baseline trials (B1-B3)
        - 3-6 are airpuff trials at 50cm (T1, T2, Tn-1, Tn)
        - 7-8 are extinction trials (P1-P2)
        - 9-11 are 2nd round of baseline trials (B1-B2)
        - 12-13 are airpuff trials at 100cm (T1, T2)
        - 14 is extinction trial (P1)
        
    trim_data_selection: if not None, it should be a two-element tuple with the start and end of the timepoints to select. Must be floats between 0 and 1.
                        Example: for 5000 timepoints, (0.1,0.2) would select the data from timepint 500 to 1000        
    CAIMSSRawbehavetsscnr   distanceAPrunningcclust   N)r(   r(   i.'  )spikes
amplitudestimesr   r   r   num_neuronsrf   	mouse_numsession_numsession_nameskaggs)r   	transposer-   r0   rA   SESSION_NAMES)data_clusterr   r   trim_data_selection
spikes_refr   r   r   r   airpuff_refairpuff_boolrunning_boolskaggs_infor   rf   thr
start_propend_propstartend	data_dictr#   r#   r$   	read_CAIMV  sL    
   $




r   Tc                    s  t | ||fd|i|}|d }d|gg}d|v rH|d }|d|g t|D ]<\}\}}t|t|dd}t||d| }||| d< qP|d }t|dd |dd  }|d	   d
  t |d|   | |  d|v r t|d t|d| }tj	|ddt
}||d< |dkrnt|D ]*\}\}}|dd|f }||| d< q2 |  || }|dkrt }t|D ].\}\}}t|||
d\ }}||| d< qtt | }|D ]D\}}t|dd\}}}|d ||d < |||d < |||d < qt }dgtt|dk d d  td } fddt|D }||d<  |d	< |d< ||d< ||d< |S )a   Apply read_CAIM function and a bunch of preprocessing, convenience function
        data_cluster: the CAIM .mat file, opened with h5py
        mouse_num: int, should be 0-7
        session_num: int, should be 0-17
        gaussian_size: size (in bin elements) of the gaussian filter
        time_bin_size: size (in bin elements) that are averaged together for analysis
        distance_bin_size: size (* in mm *) of the distance bin. 
            e.g. if "10", all elements 5-15 will become 10, 15-25 to 20, etc.
        trim_data_selection: if not None, it should be a two-element tuple with the start and end of the timepoints to select
        **kwargs are the keyword arguments for the "read_CAIM" function
        
        
        
        r   r   r   r(   r,   r   Nr&   r   rk   r   r   )decimalsT)pos_max_binned_normalized_mean_stdic                    s$   g | ]} | |d    qS ro   r#   rp   r   rc   r#   r$   rY     rZ   z,read_and_preprocess_data.<locals>.<listcomp>dtrc   r   distance_by_trial)r   appendr   r%   r/   r;   r9   r   r   aroundboolr   $compute_velocity_and_eliminate_zerosr   rA   normalize_datar[   r\   r]   r@   )r   r   r   gaussian_sizetime_bin_sizedistance_bin_sizer   only_runningeliminate_v_zerosdistance_limitsr   kwargsr   r   output_datar   idx	data_namer    r   r   r   _data_cutdistance_originaldata_normalized	data_meandata_std	dist_diffr   r   r#   r   r$   read_and_preprocess_data  s^    





 r  c                 C   s  t | dd | dd |d }t | d g| d g|}t | d g| d g|}t|t| t| }t|dk }t|rt|dkt}t|dkd }t|dkd d }	t	|}
t	|	}|dkrD|
dkr|dks|d |	d krD|	d }tj
|ddd|d f dd|dd|f< |	dd }	|d }|
dkr|
dkrb|dkst|d |	d kr|d }tj
|dd|df dd|dd|f< |dd }|
d }
|
dkrLt||	D ]|\}}tt||| d  }tj
|dd||f dd|dd|f< tj
|dd||d f dd|dd|f< qt|}| | } |dd|f }tt|| }| ||fS )	a   Given a 1D array "position" of size "timepoints", return the velocity at each point. "position" is assumed periodic in the range [0, pos_max]
        Additionally eliminate all the points where it is zero, by collapsing the position elements (e.g. 1,3,5,5,5,6 becomes 1,3,5,5,6)
        "Data" is a related 2D matrix of size "features X timepoints", the collapsed points are averaged accordingly so no information is ignored
    r'   Nr   r(   r   r&   g|=r,   )rJ   r\   r   r3   r^   r[   r/   r0   r]   rA   meanzipceilinvert)r`   r    r   vv0vendzero_v_booldiff_for_zeroszero_vel_interval_startszero_vel_interval_ends
starts_numends_numr   r   midnon_zero_v_boolr#   r#   r$   r     s<     ,.0*
*2
r   r(   c                 C   s>   t j| |dd}t j| |dd}d||dk< | | | ||fS )ze Elements along given axis have their average subtracted and are divided by their standard deviation T)r"   keepdimsr(   r   )r   r  r!   )r    r"   r	  r
  r#   r#   r$   r   1  s    r   c                 C   s   | d }| d }| d }| d }| d }| d }	|j d }
|dkrNt|}n*|dkrbt|}n|dkrxt|| }|d	ur|\}}t||k ||k }|d	d	|f }|| }|	| }	|||	fS )
a   Convenience function that returns the relevant data from the data_dict
        data_dict: output of "read_CAIM" function
        data_used: spikes, amplitudes, or scaled spikes
        running: if True, only running datapoints are used
        eliminate_zero_v: if True, all the points were v=0 (using centered differences) are averaged out, so that distance increases almost everywhere
            NOTE: this is different from "running". "running" is a boolean from the original dataset which sometimes includes points with no detected distance increase, v=0 is applied on top of this
        
        distance_limits: if not None, must be a two element array with the minimum and maximum distance to analyze
        
    
    r   spikes_binned_normalizedr   amplitudes_binned_normalizedr   r   r   zscaled spikesN)r-   r   r   r   )r   	data_usedr   r   r   r  r   r  r   r   r   r  dmindmax	dlim_boolr#   r#   r$   get_data_from_datadict8  s$    
r#  dombeckc                 C   sL   ddl m} |dkrtj}n|dkr*tj}||d }t||| f }|S )Nr   )loadmatr$  losonczyplace_cell_bool)scipy.ior%  pparamPLACE_CELL_PATH_DOMBECKPLACE_CELL_PATH_LOSONCZYr   squeeze)mnumsnumcriteriar%  place_cell_pathplace_cell_bool_datasetr'  r#   r#   r$   load_place_cell_boolean\  s    r2  c                 C   s   | j \}}||d  }|dur*|| | }t|}t|}t||d d}t|}	t||f}
t||f}t|D ]Z\}}| dd||kf }tj|dd|
dd|f< tj|ddt	|	 |dd|f< qx||
|fS )a   Given a dataset, calculate its average value for each observed position (using the given bin size)
        Input:
            data: matrix of size "pca dim" X "timepoints"
            position: array of size "timepoints"
            position_bin_size: if None the values from "position" are used to average. If "int", the position values are approximated to the nearest mcm with the bin size
            max_pos: maximum value of the position
            
        Returns:
            position_bins: array of size "num of position bins", contains the position values used to average the data
                e.g.: if bin_size = 20 and the first element is 0, the first data_average element will contain the average of the PCA of all the times the position was between 0 and 20
            data_average: array of size "pca dim" X "num of unique positions", contains the average PCA at each corresponding position bin
        
    r(   Nrm   rn   r,   )
r-   r   uniquerA   rj   r   r   r  r!   sqrt)r    r`   position_bin_sizeru   num_dimensionsrf   position_binsnum_binsrc   ri   data_averager
  r  ddata_filteredr#   r#   r$    compute_average_data_by_positionm  s    

&r<  allc           	      C   s   | j d }|du r*tj|d}|| j |du s:|dkr@|}n|t|tttj	fv r^t|}n^t|t
fv r|j}d|v rt|d|d }t||d }qt|}t||}ntd|jd|ddf }||  }|dkr|S ||fS dS )	ar   Projects spikes to PCA space.
        pca_input_data: spikes, shape "num features (e.g. neurons)" X "num samples (e.g. timepoints)"
        pca_instance: if None, a pca instance will be created and trained. If not None, it must be a trained instace of sklearn's PCA. 
        num_components: how many pca dimensions to take.
            *if None or 'all', take all
            *if int, take that many
            *if 'X%', where "X" is a scalar, take dimensions that explain X% of the variance
            *if 'X', where "X" is a float between 0 and 1, interpret it as taking X*100% of the variance
        
            
    r   N)n_componentsr=  %d   z&Unknown data type for "num_components"F)r-   r   PCAfitTr:   r0   r;   r   int32strexplained_variance_ratio_findrQ   	TypeErrorcomponents_)	pca_input_datapca_instancenum_componentsreturn_pca_instancenum_featuresrM   rN   transform_mspikes_projectedr#   r#   r$   project_spikes_PCA  s*    

rQ  c                 C   s   g }g }g }g }t |D ]d\}}	| | }
|| }t|
jd | }|| |t|
| |t|| ||	g|  qt|}t|}t	|}t
|}|||fS )Nr(   )r   r0   r-   r   r   r   r2   r   dstackhstackr=   )pca_listpos_listr8  session_listpos_aligned_by_trial_listpca_aligned_by_trial_listnum_trials_listsnum_by_trial_listsidxr.  pcar   ntrialsr   pca_by_trialpos_by_trialsnum_by_trialr#   r#   r$   reshape_pca_list_by_trial  s"    



ra  c                 C   sH   | | ||  d t j t j }t |}t |}t ||fj}|S )z Takes 1D position data and transforms to circular.
        pmin and pmax are the minimum and maximum position, respectively.
        Returns data in the form "samples" X 2
    r'   )r   pisincosvstackrC  )r`   pminpmaxangle_dsin_dcos_d	angle_posr#   r#   r$    from_linear_to_circular_position  s
     

rl  c                 C   s   | dkrt  }nv| dkr$tdd}nb| dkr8tdd}nN| dkrNtd	d
d}n8| dkrdtddd}n"| dkr~td	ddd
d}ntd|S )NWienerzWiener Cascade   )degreeKalmanr(   )CSVR   r&   )rq  max_iterzNaive Bayes	quadraticr@  )encoding_modelresXGBoosti,  g333333?)	max_depth	num_roundetagpuz4Predictor name for position predictor is not allowed)r   r   r   r   r   r   	NameError)predictor_name	predictorr#   r#   r$   initiate_predictor  s    r  c                 C   s8   t | t t t t fv r*| ||}n
| |}|S )ay   Convenience function that handles the Kalman filter case differently, X is data set to predict, Y is the actual labels.
        Note that Y is only ever called in the Kalman prediction but only the array's shape is used
        "predictor" must be scikit type trained object (or from "decoders.py")
        X of shape "samples X features"
        Y of shape "samples"
        )r:   r   r   predict)r  XYpredr#   r#   r$   get_prediction  s    

r  c                 C   s6   |du r| | }nt | ||}t|d | j }|S )zh Computes the average squared sum of error. prediction and objective are assumed to be 1d and same size Nr'   )rJ   r   r2   size)rB   rC   rD   r[   sser#   r#   r$   get_sse  s
    
r  c           
      C   s   t || |d}t|d }d}|d ur<t|| |dd }n||  d }t|}|d }tt|}t| |}||||||d}	|	|fS )NrD   
   r   r'   )r  r[   diff_stddiff_avgr2
best_delay)r  r   r4  rJ   r!   r  r?   r2_score_fun)
r`   position_predrD   r  r  r[   r  r  r  
error_dictr#   r#   r$   get_error_dict!  s    


r     rm  r  c
                 C   s\  | j }
|rt|||}n
t|}|du r|dkrt||d}t|j}||
D ]P\}}|
| |
|  }}|| ||  }}t|}|	|| t
|||||< qTqt|}|	|
| t
||
|}n|}t
||
|}|r*t|dddf |dddf }|tj dtj  ||  | }|}nd}t|}t|||d\}}||	 }|||fS )a   
    1D position data is converted to a 2D circle so the quantity to predict is periodic
    
    data: shape "features" X "samples"
    position: 1D array of size "samples" with real values to predict
    n_splits: number of CV folds. If 1 or less no cross-validation is performed (so no test sets)
    shuffle: if True, data is shuffled [DOESN'T WORK AS OF NOW]
    periodic: if True, data is converted                                        
    dmin, dmax: min and max position to do the periodicity
    predictor name: 'Wiener', 'Kalman', 'SVR'
    predictor_default: if None, a type of predictor specified by "predictor_name" is trained on the data. If one is given, that is used instead (we assume it has been trained)
    return_error: must be a error name. 
    Nr(   )n_splitsshuffler   r'   r  )rC  rl  r   re  r   r   r-   splitr  rB  r  arctan2rb  r,  r  )r    r`   r  r  periodicrf  rg  r~  predictor_defaultreturn_errorr  r  kfY_predtrain_index
test_indexX_trainX_testY_trainY_testr  angle_d_predr  rD   r  errorr#   r#   r$   predict_position_CV6  s8    
$ 
r  c                 C   s   | j }|rt|||}n
t|}t|||}	|r|t|	d d df |	d d df }
|
tj dtj  ||  | }|}nd }t|	}t|||d\}}|| }||fS )Nr   r(   r'   r  )	rC  rl  r   re  r  r  rb  r,  r  )r    r`   r  r  rf  rg  r  r  r  r  r  r  rD   r  r  r#   r#   r$   &predict_position_from_predictor_objectx  s    
$ 
r  c           	      C   sN   t | }t |j}t|D ]*\}}| |k}||k}t||}|||< q|S )z Returns F1 measures in a multiclass problem. Each class has its own F1.
        Output: array of size "num_unique_labels"
    )r   r3  r   r-   r   r
   )	true_labelspredicted_labelsunique_labelsf1_listr  label	true_bool	pred_boolf1r#   r#   r$   multiclass_f1  s    


r  viridisr'   c                 C   s  t |}||}t| ||gj}tj|dd |dd fddddd}|durlt | t  }n.t	|t
u rt |}|jddd	}n| }|du rt t|t|}nt |d |d }t|||d
}|| || ||}|r||}|	du r2|t| t| g n
||	 |
du rb|t|t|g n
||
 |du r|t|t|g n
|| |||fS )a   Efficiently plots a 2d colored line using LineCollection
        x: dimension N
        y: dimension N
        z: dimension N
        color_scalar: dimension N, each element is a scalar that defines its color
        cmap: string, indicates matplotlib cmap to use for the colorbar
        figure: which figure. Can be int for its number or a figure instance from matplotlib
        ax: if not None, takes precedence over "figure"
        lw: linewidth, thickness of drawing       
        color_norm: if None, the color will use the min and max of color scalar as limits.
                    If two-element list of the sort [min, max], it will use those instead
        
        Outputs the figure, axis, and colorbar objects
        
    Nr&   r(   r,   r   r'   o   3d
projection)cmapnorm)pltget_cmapr   r3   rC  r=   r   scagcfr:   r0   figureadd_subplotgca	Normalizeminr_   r   	set_arrayset_linewidthadd_collectioncolorbarset_xlimset_ylimset_zlim)r   yzcolor_scalar	cmap_namefigaxlwcbarxlimylimzlimcolor_norm_limitsr  colorpointssegmentsr  lcliner#   r#   r$   plot_colored_line_3d  s<    
,












r  r   zPosition (mm)fractiong{Gz?c           	      C   s\   t jj||d}| jt jj||dfddi|}|jj||d |jjdd|d d	 |S )
N)vminvmax)r  r  orientationverticalfontsizebothmajorrs  )r"   which	labelsize)	mplcolorsr  r  cmScalarMappabler  	set_titletick_params)	r  r  r  r  fs
cbar_labelcbar_kwargsr  r  r#   r#   r$   add_distance_cbar  s
    "r  2   offr  rs  c                 C   s  |du rt jddddd}t  }t |}| }| }| }| dd \}}}|dkr|j|||||| |d|	d n*t|||||||d	|||d
|gd\}}}|du rd}|dkr|j	|d |d |j
|d |d |j|d |d |j|
|d |dkr t||d
|||d |tt||d
 tt||d g |tt||d
 tt||d g |tt||d
 tt||d g |dkr|  n8|dkr|d	 |jd |jd |jd |S )a  
    Plots the first three dimensions of a PCA.
    
    Parameters
    ----------
    pca : array of size "num features" X "num samples"
    position : array of size "num samples"
        Indicates the position of the animal per sample.
    ax : TYPE, optional
        DESCRIPTION. The default is None.
    max_pos : int, optional
        Maximum value of the position value. The default is 1500.
    cmap_name : string, optional
        Name of the colormap to use. The default is 'hsv'.
    fs : int, optional
        Fontsize. The default is 15.
    scatter : bool, optional
        If True, PCA is plotted using the scatter function. Otherwise lines are drawn. The default is True.
    cbar : bool, optional
        if True, a colorbar is plotted
    alpha : float, optional
        Transparency of the plot. The default is 1.
    angle : int, optional
        Main 3d angle. The default is 50.
    angle_azim : int, optional
        Azimuthal angle. The default is None.
    axis : string, optional
        'on' or 'off'. The default is 'off'.
    show_axis_labels : bool, optional
        Whether or not to label the axis. The default is True.
    axis_label : str, optional
        If None, each axis is labelled as a PCA axis. Otherwise this label is used. The default is None.
    ms : int, optional
        Marker size for the scatter plots. The default is 10.
    lw : int, optional
        Line width for the average PCA plot. The default is 3.

    Returns
    -------
    ax : matplotlib axis object. If None, a new one is created.

    Nr(   r  r  rs  To)r  smarkeralphaFr   )r  r  r  r  r  r  r  r  rA  z D1r  z D2z D3)elevazim)r  r  r  r  r  on)      ?r  r  g        )r  subplotr  r  get_xlimget_ylimget_zlimscatterr  
set_xlabel
set_ylabel
set_zlabel	view_initr  r  r   minimumr  maximumr_   r  r  set_axis_offgridw_xaxisset_pane_colorw_yaxisw_zaxis)r\  r`   r  ru   r  r  r  r  r  r  angle
angle_azimr"   show_axis_labels
axis_labelmsr  line_effectr  r  r  r  r  r   r  r  r  r#   r#   r$   plot_pca_with_position  s>    -
"
222



r  left)g?g{Gzt?gMb@?c                 C   sV  |dv r|dkr@|}|| }|}|}|| | }|| d | }|dkrt|}|| }|}|}|| }|| d | }||||g}||||g}n|dv r|dkr|}|}|}|| }|| d | }|| }|dkr|}|}|}|| }|| d | }|| }||||g}||||g}| j ||ddd	d
 t||	ddd}| j||||
dd dS )a   
        ax is the axis plot
        pval is the pvalue to plot
        orientation: left, right, top, bottom
        label_padding: distance from the line center to the text label (in order to be centered, we might want it smaller)
        
        
        For left, the values are as follows:
            
              d0
            |----|
         
            ------ (p00, p11)
            |
            |
            |
            |
            |
            |
            |
            |            
            ------ (p00, p10)
        
        Other orientations simply rotate this. 
        p00 is the coordinate that doesn't change along the line, p10 and p11 are the coordinates indicating start/end
        d0 is the width from that position to the actual line (can be 0)
        dp is the distance from the line to the text
        fs is fontsize
        
        
        
    )r  rightr  r'   r  )topbottomr  r  r~   gffffff?)r  r  T)asterisknsitalic)r  styleN)plotget_significance_labeltext)r  pvalp00p10p11d0dpr  label_padding
thresholdsr  x0x1y0y1xpypxxyy
pval_labelr#   r#   r$   draw_significanceH  s:    "

r,  gMbP?g-C6?c                 C   sv   t |d d d }t | t |k }|dkrB|dkr<dS dS n0|dkrjdd||d	   d
d S d| S d S )Nr&   r   Tr   Fzp<z%fr(   0.*)r   sortr2   r3   rstrip)r  r"  r  r  signumr#   r#   r$   r    s     r  )r   )r&   )F)rR   )rk   NFrl   )rk   r|   r}   F)N)NTFNrk   )rk   )r(   )Nrk   )r$  )Nrk   )Nr=  F)N)rk   )r  FTr   rk   rm  Nr  )Tr   rk   r  )	r  r(   Nr'   TNNNN)\__doc__project_parametersr)  r   	functoolsr   
matplotlibr  matplotlib.pyplotpyplotr  matplotlib.linesr   matplotlib.patheffectspatheffectspenumpyr   scipysklearn.model_selectionr   r   r   sklearn.linear_modelr   Zsklearn.svmr   sklearn.metricsr	   r  r
   sklearn.cross_decompositionr   sklearnr   r   scipy.ndimager   ZNeural_Decoding.decodersr   r   r   r   r   r   warningsfilterwarningsUserWarningZmpl_toolkits.mplot3d.art3dr   matplotlib.collectionsr   syspathinsertSTRUCTURE_INDEX_PATHstructure_indexr   r   r%   r9   rJ   rQ   rj   r{   r   r   r   r   r   r  r   r   r#  r2  r<  rQ  ra  rl  r  r  r  r  r  r  r  r  r  PCA_CMAPr  r,  r  r#   r#   r#   r$   <module>   s    


(



j
P  
Y
2

$

'
.

  
B
  
F
XN