lposani / decodanda

Decoding and geometrical analysis of neural activity with built-in best practices
GNU General Public License v3.0
29 stars 3 forks source link

Reasons why CCGP would be significantly below chance? #3

Closed qsimeon closed 7 months ago

qsimeon commented 1 year ago

I have a binary variable and when I compute the CCGP I get that it is significantly below chance (0.5). But this is strange to me because I figure that the worst case scenario would be that CCGP is at chance. Are there scenarios with a binary variable where one can get CCGP significantly below chance or could this be pointing to a potential bug in my code?

lposani commented 1 year ago

It can definitely happen and is an indication that the coding directions of the same variable cross-condition are anti-parallel. I've seen this happening when there is another strongly encoded variable that is anti-correlated to the decoded one and is not being balanced in the analysis (a sort of anti-confound).

qsimeon commented 1 year ago

I see. Right now I am decoding the context, action and reinforcer variables from the Bernardi et al. task, but not doing stimulus since it is quarternary. So I have:

#@title Applying `Decodanda` for analysis

# we need the trial vector
trial = np.array(np.array([trial_data["trial"] for trial_data in test_info])) 

# the raster is the neural data
raster = neural_data  # n x n_units := num_trials x hidden_size
print("data matrix:", raster.shape, end="\n\n")

# organize into decodanda format
data = {
    'raster': raster,   # <TxN array>, neural activations
    'context': context, # <Tx1 array>, labels
    'stimulus': stimulus, # <Tx1 array>, labels
    'action': action,    # <Tx1 array>, labels
    'reinforcer': reinforcer,    # <Tx1 array>, labels
    'trial': trial, # <Tx1 array>, trial number
}

conditions = {
    'context': np.unique(context).tolist(),
    # 'stimulus': np.random.choice(np.unique(stimulus), 2, replace=False).tolist(), # pick 2 out of the 4
    'action': np.unique(action).tolist(),
    'reinforcer': np.unique(reinforcer).tolist(),
}
print("conditions:\n", conditions, end="\n\n")

# Decodanda object
verbose = False # <--- set to True to log all the operations happening under the hood
dec = Decodanda(data=data, conditions=conditions, neural_attr='raster',
                trial_attr='trial', verbose=verbose)

My CCGP (using the last timestep of the hidden states of an RNN trained on the task from the paper) plot looks like:

Screenshot 2023-10-11 at 11 04 24 AM

Are you saying a reason for the significantly below chance CCGP is because I am not balancing for the stimulus (since I have not included it as a variable at all) and the stimulus may be a strongly encoded variable that may be anti-correlated with the decoded ones? @lposani

lposani commented 7 months ago

That is one possibility. In short, unlike a below-chance decoding accuracy, which has no interpretation and could be symptomatic of a bug or strong artifacts, a below-chance CCGP is usually an indication of anti-parallelism of coding direction.