ContextLab / quail

A python toolbox for analyzing and plotting free recall data
http://cdl-quail.readthedocs.io/en/latest/
MIT License
20 stars 10 forks source link

generative part of reordering lists by fingerprint #65

Closed andrewheusser closed 5 years ago

andrewheusser commented 7 years ago

Currently, only implemented in javascript - need to pipe over to python so that we can run permutation analyses where we fix the temporal clustering score and see where feature clustering fall within that distribution

andrewheusser commented 6 years ago

I'm working on this now. I'm porting over all of the fingerprint logic from js to python. The way I'm planning to set this up is as a class within quail. Using it would be very similar to how its currently used in the js code, e.g.:

from quail import Fingerprint

# initialize fingerprint
fingerprint = Fingerprint(state='random', features=features, prior_weights=weights, alpha=4, tau=1)

# collect some data...

# compute some weights based on data
weights = fingerprint.compute_weights(pres_list, rec_list, feature_list)

# update the fingerprint class
fingerprint.update_weights(weights)

# reorder a list based on the fingerprint and the fingerprint state
reordered_list = fingerprint.reorder_list(next_list)

open to alternate APIs, @jeremymanning let me know what you think!

jeremymanning commented 6 years ago

A few questions/comments:

Do you want to take another stab at this given the above comments?

andrewheusser commented 6 years ago

What does the state='random' flag do? if the fingerprint object is in the random state, when you call fingerprint.reorder_list it will simply return a shuffled version of the original list. another possibility is that we could move to a 'stateless' architecture, where fingerprint.reorder_list would have to be called with a state parameter that determines its behavior.

Do we need to explicitly specify the features? Won't the features be computed from the stimuli directly? We could set it up this way, but specifying the features here would allow the user to store many features of the stimulus, and only reorder based on a subset of them.

Can you remind me what alpha and tau do? My memory is that they're used for re-ordering based on the fingerprint, not for computing the fingerprint itself...so the parameterization is a bit strange (e.g. one might want to use the same fingerprint to reorder lists with different alphas and taus, but with the current syntax that would require a new fingerprint object, which seems clunky). Alpha is a gain parameter for the feature stick and tau is a gain parameter for the stimulus stick i.e. ['size']*round(size_weight**alpha)*100 Instead, alpha and tau could be parameters of the reorder_list function. this would be more flexible

We should have a permute flag agree

_The computeweights function is really what computes the fingerprint, and the Fingerprint function is really what re-orders the list given the fingerprint. So the names are a bit off, I think. hmmm, not sure exactly what you mean. in my current setup, fingerprint is an instance of the Fingerprint class that stores the fingerprint state, past fingerprint weights, and some parameters. the class instance can compute weights and reorder lists by using the compute_weights and reorder_list methods, respectively.

jeremymanning commented 6 years ago

Ok, I think I understand how the current "fingerprint" API is designed. I'd recommend changing it a bit. First, I think it's worth drawing a distinction between two concepts that I think are conflated with the current naming system:

The reason these are distinct is that they can potentially operate independently. Some examples:

I think a more accurate name for the "Fingerprint" object would be something like an "OptimalPresenter" object. This could be initialized with the following flags:

In addition, there should be a Fingerprint object that stores and updates the fingerprint. This could be initialized using: fingerprint = Fingerprint(). The fingerprint object could have the following fields (any can be set when initializing the new object, using keyword arguments):

The OptimalPresenter object should also include

The fingerprint could be updated by calling fingerprint.update(presented, recalled), where presented is a list of presented stimuli and recalled is an ordered list of recalled stimuli. Each item in both of these lists should be a dictionary specifying the item's feature values. Updating the fingerprint does the following:

Finally, to re-order a list using the fingerprint, use:

presenter = OptimalPresenter()
fingerprint = presenter.get_params('fingerprint')

#repeat for each list:
fingerprint.update(next_presented, next_recalled) #this call should update the fingerprint object in presenter-- i.e. it's stored by reference, not by value

next_order = presenter.order(next_list)

In different conditions, we can use set_param to change the strategy (i.e. the way the fingerprint affects the ordering)-- e.g. presenter.set_strategy("forward"), etc.

andrewheusser commented 6 years ago

awesome, thanks for the feedback. i think this makes sense. i'll keep you posted if i run into any issues while implementing this