"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Introduction\n",
"\n",
"\n",
"**Welcome to MoSeq2 Analysis Visualization Notebook!**\n",
"\n",
"This notebook contains a collection of analyses we typically perform on behavioral recordings to better understand mouse behavior. Examples of such analyses are:\n",
"- descriptive labeling of individual syllables\n",
"- comparing syllable usage across different experimental groups\n",
"- comparing syllable transition structure across different experimental groups\n",
"\n",
"The goal of this notebook is to provide you with a basic summary of syllable usage and sequencing statistics across experimental conditions and to help generate more specific questions related to observations you may identify here.\n",
"If you want to perform more specific analyses, you can export the MoSeq data (see below) to use with the tool (i.e., Excel, Python, R, Matlab) you're most comfortable with.\n",
"\n",
"In this notebook, the **Markdown** above each cell describes the purpose of the cell(s), cell output, and the instructions for running the cell(s) and/or interacting with the widget. The **inline code comments** in the code block provides contextual information about the function, code structure, and parameters.\n",
"\n",
"***"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Project setup\n",
"\n",
"## Files and Directory Structure\n",
"To run this notebook, you need the following files and folders in your data directory:\n",
"- `progress.yaml` - this file stores all the required MoSeq paths used throughout the notebooks\n",
"- `config.yaml` - configuration file that stores parameters used throughout the MoSeq pipeline\n",
"- `aggregate_results/` - folder that contains aggregated and extracted session data\n",
"- `moseq2-index.yaml` - contains paths to extracted sessions and is used to generate syllable crowd movies\n",
"- `_pca/` - folder that contains data generated from the PCA section of the extraction notebook or CLI\n",
"- `base_model_path` - the folder that keeps all the trained models or all the output model objects after applying pre-trained model(s) to data.\n",
"- `model.p` - the trained AR-HMM or the output model object after applying a pre-trained model. There should be at least one trained AR-HMM or one output applied model object in the `base_model_path`.\n",
"\n",
"At this stage, your base directory should look something like what's shown below:\n",
"```\n",
".\n",
"└── /\n",
" ├── config.yaml\n",
" ├── aggregate_results/\n",
" ├── moseq2-index.yaml\n",
" ├── _pca/\n",
" ├── base_model_path/\n",
" ├ └── model.p\n",
" ...\n",
" └── progress.yaml\n",
"\n",
"```\n",
"For more information about how MoSeq organizes data, check out our [wiki](https://github.com/dattalab/moseq2-app/wiki/Directory-Structures-and-yaml-Files-in-MoSeq-Pipeline).\n",
"\n",
"**Note: this notebook uses the `progress.yaml` file to keep track of all the necessary paths.** Please ensure you run the Load Progress cell below before running any analysis modules."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Load Progress\n",
"\n",
"**[IMPORTANT] ALWAYS run this cell when you open this notebook.** If you don't, the analysis functions won't work.\n",
"\n",
"MoSeq uses a [progress.yaml](https://github.com/dattalab/moseq2-app/wiki/Directory-Structures-and-yaml-Files-in-MoSeq-Pipeline#progressyaml) file to keep track of the progress. \n",
"\n",
"The following cell generates a `progress.yaml` file or loads your progress from the last saved checkpoint. This cell checks if the `progress_paths` dictionary contains the necessary paths to use the features in the rest of the notebook and displays the content in `progress_paths`.\n",
"\n",
"- **Specify the path to the `progress.yaml` file**. No change is needed if the notebooks are in the base directory.\n",
"- **Run the following cell**.\n",
"\n",
"**Note:** If the Extraction, PCA, and/or modeling steps are done using the Command Line Interface, set `from_CLI=True`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from os.path import join, dirname, abspath\n",
"from moseq2_app.gui.progress import update_progress, restore_progress_vars, progress_path_sanity_check\n",
"\n",
"progress_filepath = './progress.yaml' # Add the path to your progress.yaml here.\n",
"\n",
"# set to True if you are coming from the CLI and did not use the extraction and modeling notebook.\n",
"# keep False if you came from the extraction and modeling notebook. Most users will keep this False\n",
"from_CLI = False\n",
"# keep False if this is not a model object from applying a pre-trained model to new data. Most users will keep this False\n",
"pretrained_model_used = False\n",
"\n",
"progress_paths = restore_progress_vars(progress_file=progress_filepath, init=from_CLI, overwrite=False)\n",
"\n",
"# Find the best model when coming from extraction and modeling from the CLI.\n",
"if from_CLI and not pretrained_model_used:\n",
" # Find the best model given the objective\n",
" from moseq2_viz.gui import get_best_fit_model\n",
" output_file = join(progress_paths['base_dir'], 'model_vs_pc_changepoints')\n",
" objective = 'median_loglikelihood' # objective could be either duration, jsd or median_loglikelihood\n",
" best_model_fit = get_best_fit_model(progress_paths, output_file, plot_all=True, objective=objective)\n",
" progress_paths = update_progress(progress_filepath, 'model_path', best_model_fit[f'best model - {objective}'])\n",
"\n",
"progress_path_sanity_check(progress_paths, progress_filepath) # check all necessary paths exist\n",
"\n",
"progress_paths"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Setup Directory Structure for Analyzing Model(s)\n",
"This step reorganizes trained models so that they have their own folder, which makes it easy to associate the analyses below with specific models (the analyses are saved in the same folder as the model).\n",
"\n",
"The following step creates a folder for each model and copies the model to the respective folder.\n",
"Refer to the [wiki](https://github.com/dattalab/moseq2-app/wiki/Directory-Structures-and-yaml-Files-in-MoSeq-Pipeline#after-training-the-ar-hmm-models) for more details about how this step reorganizes models.\n",
"\n",
"- **Run the following cell**."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from moseq2_app.util import setup_model_folders, update_model_paths\n",
"from os.path import basename\n",
"\n",
"best_model = basename(progress_paths['model_path'])\n",
"\n",
"model_dict = setup_model_folders(progress_paths)\n",
"progress_paths = update_model_paths(best_model, model_dict, progress_filepath)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## [OPTIONAL] Override Paths to Specific Model\n",
"NOTE: only run this section if you want to skip finding the best model and manually select a model to analyze.\n",
"\n",
"**Instructions:**\n",
"- **Specify the file name of the model in the `desired_model` field.** The model name must be a key of the `model_dict` dictionary.\n",
"- **Run the following cell**."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"pycharm": {
"name": "#%%\n"
},
"scrolled": true
},
"outputs": [],
"source": [
"from moseq2_app.util import update_model_paths\n",
"print('Select a model from the following models:', [key for key in model_dict.keys()])\n",
"desired_model = best_model # override this with the file name to the desired model, eg. \"model1.p\"\n",
"\n",
"# Update progress file\n",
"progress_paths = update_model_paths(desired_model, model_dict, progress_filepath)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Compute Syllable Statistics\n",
"MoSeq extracts scalar values (eg. 2D velocity, width, height, etc) from the depth video while the models identify behavioral motifs (syllables).\n",
"The steps below combine these data streams into a `DataFrame`. Learn more about `DataFrames` on [pandas's website](https://pandas.pydata.org/pandas-docs/stable/user_guide/dsintro.html).\n",
"\n",
"`DataFrames` are both useful for the analysis and visualization cells below, and are a great way to organize data.\n",
"This means you can save and export them in multiple formats to run custom analyses in the software of your choosing.\n",
"You can find more information about how we use `DataFrames` in the wiki [here](https://github.com/dattalab/moseq2-app/wiki/MoSeq2-Analysis-Visualization-Notebook-Instructions#compute-syllable-statistics)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Compute `moseq_df`\n",
"The following cell generates a `DataFrame` of scalar values computed during the extraction step aligned to the model labels. All sessions are concatenated to form this `DataFrame`. Each row is one \"frame\" which corresponds to one sample from the original depth video. Each column is a different entry.\n",
"To view the entries of this `DataFrame`, run `print(moseq_df.columns)`. This `DataFrame` can be used to plot the scalar feature values for any session over time.\n",
"[Visit our wiki](https://github.com/dattalab/moseq2-app/wiki/MoSeq2-Analysis-Visualization-Notebook-Instructions#moseq_df) for more information about the contents of each column.\n",
"\n",
"You will notice multiple columns with syllable labels. Two of the columns relabels syllables by \"usage\" or \"frames\". [See here](https://github.com/dattalab/moseq2-app/wiki/MoSeq2-Analysis-Visualization-Notebook-Instructions#sorting-syllable-labels) for information about how this relabeling is performed.\n",
"\n",
"**Note:** the rows in the `labels` columns contain `-5` for the first 3 frames of each session's recordings.\n",
"This is because we use the first 3 frames to initialize the AR-HMM (`nlags = 3` in the modeling step), and thus cannot give a label to them.\n",
"We generally remove these frames from analysis.\n",
"\n",
"**Instructions:**\n",
"- **Run the following cell**."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"from moseq2_viz.util import parse_index\n",
"from moseq2_viz.scalars.util import scalars_to_dataframe\n",
"\n",
"_, sorted_index = parse_index(progress_paths['index_file'])\n",
"# compute session scalar data\n",
"moseq_df = scalars_to_dataframe(sorted_index, model_path=progress_paths['model_path'])\n",
"\n",
"print('moseq_df size: ', moseq_df.shape[0], 'rows;', moseq_df.shape[1], 'columns')\n",
"\n",
"# this line prints out the first 5 rows of the dataframe in table format. It is used to\n",
"# get a sense of what is contained in the DataFrame\n",
"moseq_df.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Export `moseq_df`\n",
"\n",
"You can export the `moseq_df` to a csv file (or other alternative file types) for further analysis using the following cell. [See here](https://pandas.pydata.org/pandas-docs/stable/user_guide/io.html) for a list of alternative file formats to save `moseq_df`.\n",
"\n",
"**Note:** it can take quite a while to export the `moseq_df`, especially if it is saved as a `.csv` file. Check out [pandas's user guide](https://pandas.pydata.org/pandas-docs/stable/user_guide/io.html) to see if other formats will work better for you.\n",
"\n",
"**Instructions:**\n",
"- **Specify the folder** you want to save the dataframe in `save_path`. By default, the file will be saved to `base_dir`.\n",
"- **Run the following cell** to save `moseq_df` as a CSV file."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Save `moseq_df` as a csv file\n",
"from os.path import exists, join\n",
"\n",
"# Specify the place you want to save the dataframe in `save_path`\n",
"save_path = progress_paths['model_session_path'] # save the dataframe in the model specific folder\n",
"\n",
"# exports the dataframe\n",
"filename = 'moseq_df.csv'\n",
"# here we use .to_csv to export the dataframe, but you can change it to try other ways of saving your data\n",
"moseq_df.to_csv(join(save_path, filename), index=False)\n",
"\n",
"print('DataFrame is saved:', join(save_path, filename))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Compute `stats_df`\n",
"`stats_df` is a `DataFrame` that contains statistical summaries (i.e., min, max, mean, std) of scalar values associated with each syllable, as well as the frequency each syllable is expressed. By default, it is computed using the features included in `moseq_df` for each session independently. This dataframe will be used to plot syllable statistics and perform hypothesis testing.\n",
"\n",
"The function can group data into whichever categories you supply into the `groupby` parameter.\n",
"By default, we group by \"group\" (the experimental cohort) and \"uuid\" (each unique recording session).\n",
"An alternative is to groupby by experimental cohort and mouse: `groupby = ['group', 'SubjectName']`.\n",
"Under the hood, we use `pandas` to perform the `groupby` calculation.\n",
"To learn more about `pandas`'s `groupby` capabilities, check out [their documentation](https://pandas.pydata.org/docs/user_guide/groupby.html#groupby) on the subject.\n",
"\n",
"By default, each row of the `stats_df` contains the average syllable usage for one syllable for one group (experimental cohort) within one uuid (session). Changing the contents of the `groupby` variable will change the contents of the `stats_df`.\n",
"\n",
"**Instructions:**\n",
"- **Run the following cell**."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"# compute syllable usage and scalar statistics\n",
"from moseq2_viz.model.util import compute_behavioral_statistics\n",
"\n",
"count = 'usage' # can either be usage or frames\n",
"syllable_key = 'labels (usage sort)' # either 'labels (usage sort)', 'labels (original)', or 'labels (frames sort)'\n",
"groupby = ['group', 'uuid'] # can be any categorical variables\n",
"usage_normalization = True # turn syllable usage into a probability distribution from raw counts\n",
"\n",
"stats_df = compute_behavioral_statistics(moseq_df, count=count, groupby=groupby, usage_normalization=usage_normalization, syllable_key=syllable_key)\n",
"print('The shape of stats_df', stats_df.shape)\n",
"stats_df.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Export `stats_df`\n",
"\n",
"You can export the `stats_df` to a csv file for further analysis using the following cell.\n",
"\n",
"**Instructions:**\n",
"- **Specify the place** you want to save the dataframe in `save_path`. By default, the file will be saved to `base_dir`.\n",
"- **Run the following cell** to save `stats_df` as a CSV file."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Save `stats_df` as a csv file\n",
"from os.path import exists, join\n",
"\n",
"# Specify the place you want to save the dataframe in `save_path`\n",
"save_path = progress_paths['model_session_path'] # save the dataframe in the model specific folder\n",
"\n",
"# exports the dataframe\n",
"filename = 'stats_df.csv'\n",
"stats_df.to_csv(join(save_path, filename), index=False)\n",
"\n",
"print('DataFrame is saved:', join(save_path, filename))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Generate Behavioral Summary (Fingerprints)\n",
"Fingerprints summarize behavior by showing distributions of scalars (eg. position, velocity, height, and length) and syllables.\n",
"These plots can be used as a useful diagnostic tool.\n",
"Sessions where the mouse wasn't extracted properly or moves too much or little can be identified in these plots.\n",
"You can find more information in the wiki [here](https://github.com/dattalab/moseq2-app/wiki/MoSeq2-Analysis-Visualization-Notebook-Instructions#fingerprint-plots).\n",
"\n",
"The following cell generates the summary dataframe and plots the behavioral summary. The fingerprint plot will be automatically saved as png and pdf in the `plots` folder in the model directory.\n",
"\n",
"**Instructions:**\n",
"- **Set `n_bins` variable** to an integer to specify the number of bins for the MoSeq scalar values. Set `n_bins` variable to `None` if you want the number of bins to match the number of syllables. `n_bins` does not bin syllables.\n",
"- **Set `range_type` variable** to 'robust' to include data ranging from 1 percentile top 99 percentile. **Set `range_type` variable** to 'full' to include all the data.\n",
"- **Assign an `sklearn.preprocessing` object to `preprocessor` variable** if you want to scale the values by session. `preprocessor` variable is set to `None`, the figure will show the proportion of data filling each bin.\n",
"- **Run the following cell.**\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from moseq2_viz.model.fingerprint_classifier import create_fingerprint_dataframe, plotting_fingerprint\n",
"from sklearn.preprocessing import MinMaxScaler, StandardScaler\n",
"\n",
"stat_type = 'mean'\n",
"n_bins = 100 # resolution of distribution \n",
"range_type = 'robust' # robust or full\n",
"preprocessor = MinMaxScaler()\n",
"\n",
"summary, range_dict = create_fingerprint_dataframe(moseq_df, stats_df, stat_type=stat_type, n_bins=n_bins, range_type=range_type)\n",
"plotting_fingerprint(summary, progress_paths['plot_path'], range_dict, preprocessor=preprocessor)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Crowd Movie Generation and Interactive Syllable Labelling Tool\n",
"Syllable crowd movies are movies that superimpose examples of mice in each syllable labeled by MoSeq.\n",
"Crowd movies are a tool for learning which behaviors are captured by each syllable and you can assign behavioral labels and short descriptions to syllables by observing the crowd movies for further analysis.\n",
"When a red dot appears over the mouse's body, it means that they are performing the specific syllable in the crowd movie.\n",
"You can find more information in the [wiki](https://github.com/dattalab/moseq2-app/wiki/MoSeq2-Analysis-Visualization-Notebook-Instructions#interactive-syllable-labelling).\n",
"\n",
"After running the syllable labeler tool, syllable crowd movies based on the model of interest will be generated in its model-specific folder, as well as a [syll_info.yaml file](https://github.com/dattalab/moseq2-app/wiki/Directory-Structures-and-yaml-Files-in-MoSeq-Pipeline#syll_infoyaml).\n",
"The `syll_info.yaml` file will contain entries for all syllables that explain `explained_variance`% of frames.\n",
"For example, if 6 syllables explained 50% of all frames, then `syll_info.yaml` will contain entries for those 6 syllables.\n",
"At this stage, syllables are relabeled by usage.\n",
"You can find more information about the file structure after running the interactive syllable labeler tool [here](https://github.com/dattalab/moseq2-app/wiki/Directory-Structures-and-yaml-Files-in-MoSeq-Pipeline/#after-running-syllable-labeller).\n",
"\n",
"Run the following cell to start the interactive widget and assign labels and short descriptions (both optional) to syllables by observing the crowd movies and syllable statistics.\n",
"The explained variance plot will be automatically saved to the `plots` folder in the model directory.\n",
"\n",
"**Note:** sometimes this widget can take a while to load or switch between syllables. Please be patient.\n",
"\n",
"**Instructions:**\n",
"- **Run the following cell.**\n",
"- **Select a syllable** from the `Syllable` dropdown menu to view the associated crowd movie and syllable info.\n",
"- **Adjust the crowd movie playback speed using the `Playback Speed` slider** to better observe the behavior associated with short/fast syllables.\n",
"- **Input the syllable behavioral label and short description** in the text fields.\n",
"- Click `Save Setting` to save the syllable label and description for later analysis.\n",
"- Use `Next` and `Previous` to navigate between syllables and the syllable label and description will be automatically saved when using these buttons."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from os.path import join\n",
"from moseq2_app.main import label_syllables\n",
"from moseq2_app.gui.progress import update_progress\n",
"\n",
"# Path to generate crowd movies in\n",
"crowd_dir = join(progress_paths['model_session_path'], 'crowd_movies/')\n",
"\n",
"# Path to file containing Syllable label information\n",
"syll_infopath = join(progress_paths['model_session_path'], 'syll_info.yaml')\n",
"\n",
"# Select number of syllables based on an explained variance percentage\n",
"explained_variance = 99\n",
"\n",
"# To instead label a fixed number of syllables, set max_syllables <= nstates\n",
"max_syllables = None\n",
"\n",
"# To specify number of mouse examples in the syallable crowd movies\n",
"max_examples = 20\n",
"\n",
"# To generate crowd movies selecting syllable instances that are close to syllable duration median\n",
"# set select_median_duration_instances = True and delete the existing crowd_movie folder\n",
"select_median_duration_instances = False\n",
"\n",
"update_progress(progress_filepath, 'crowd_dir', crowd_dir)\n",
"progress_paths = update_progress(progress_filepath, 'syll_info', syll_infopath)\n",
"\n",
"# max syllables is re-defined here\n",
"max_syllables = label_syllables(progress_paths, max_syllables=max_syllables, n_explained=explained_variance, select_median_duration_instances=select_median_duration_instances, max_examples=max_examples)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Generate Syllable Dendrogram\n",
"The similarity dendrogram represents the hierarchically sorted pairwise distances between the given model's autoregressive matrices representing the syllables. The cell uses information from `syll_info.yaml` [file](https://github.com/dattalab/moseq2-app/wiki/Directory-Structures-and-yaml-Files-in-MoSeq-Pipeline#syll_infoyaml) generated in the previous cell. The dendrogram is automatically saved to saved as png and pdf in the `plots` folder in the model directory.\n",
"\n",
"**Instructions:**\n",
"- **Run the following cell.**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from moseq2_app.main import show_dendrogram\n",
"\n",
"# max_syllables is defined above. Uncomment and change the line below if you want to manually set syllable number.\n",
"# max_syllables = 100\n",
"# Set color_by_cluster to True to color the syllables by similarity clusters\n",
"show_dendrogram(progress_paths, max_syllable=max_syllables, color_by_cluster=False)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Interactive Syllable Statistics Graphing\n",
"Syllable statistics provide information about the behavioral patterns and you can find more information in the [wiki](https://github.com/dattalab/moseq2-app/wiki/MoSeq2-Analysis-Visualization-Notebook-Instructions#interactive-syllable-statistics-graphing).\n",
"\n",
"Run the following cell to start the interactive widget to plot different syllable statistics and their differences in the modeled groups.\n",
"This cell is dependent upon the previous cell to generate crowd movies and the `syll_info.yaml` [file](https://github.com/dattalab/moseq2-app/wiki/Directory-Structures-and-yaml-Files-in-MoSeq-Pipeline#syll_infoyaml).\n",
"\n",
"**NOTE**: this widget can take a while to render after changing parameters within the widget, so hang tight.\n",
"\n",
"**Instructions:**\n",
"- **Run the following cell.**\n",
"- **Select the parameter(s) from the dropdown menus** parameters to control the graph. \n",
"- To plot multiple sessions and subjects, **select multiple sessions from `SessionName` or `SubjectName` while holding down the [Ctrl]/[Command]/[Shift] key**.\n",
"- **Hover over the data points** to display syllable info.\n",
"- **Click the save icon in the toolbar on the side of the figures** to save the figures as SVG in your default web image download folder."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"from moseq2_app.main import interactive_syllable_stats\n",
"\n",
"# max_syllables is defined above. Uncomment and change the line below if you want to manually set syllable number.\n",
"# max_syllables = 100\n",
"interactive_syllable_stats(progress_paths, max_syllable=max_syllables)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Compute Syllable Transition Matrices\n",
"Transition matrices compactly represent the frequency any syllable transitions into any other syllable and is one way to describe structure in behavior.\n",
"The row of the transition matrix represents an incoming syllable, while the column represents the outgoing syllable and the value at a specific row and column position represent the frequency the incoming syllable transitions into the outgoing syllable.\n",
"\n",
"These plots can help visualize gross changes in the structure of behavior between two experimental groups. For example, certain syllables that frequently transition into one set of syllables in one experimental condition might transition into a completely different set in another experimental condition.\n",
"\n",
"The following cell computes syllable transition matrices for each group. The figures will be automatically saved as png and pdf in the `plots` folder in the model directory.\n",
"\n",
"**Note**: This code block is loading the model from the `model_path` set in the `progress_paths` config.\n",
"\n",
"**Instructions:**\n",
"- **Run the following cell to plot the transition matrices.**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"import os\n",
"import matplotlib.pyplot as plt\n",
"from moseq2_viz.model.util import parse_model_results, relabel_by_usage\n",
"from moseq2_viz.model.trans_graph import get_trans_graph_groups, get_group_trans_mats\n",
"\n",
"# syllable threshold defined above. Uncomment if you want to manually set\n",
"# max_syllables = 40\n",
"\n",
"# select a transition matrix normalization method\n",
"normalize = 'bigram' # options: bigram, columns, rows\n",
"\n",
"# load your model\n",
"model_path = progress_paths['model_path']\n",
"model_data = parse_model_results(model_path)\n",
"model_data['labels'] = relabel_by_usage(model_data['labels'], count='usage')[0]\n",
"\n",
"# Get modeled session uuids to compute group-mean transition graph for each group\n",
"label_group, uuids = get_trans_graph_groups(model_data)\n",
"group = list(set(label_group))\n",
"# compute transition matrices and usages for each group\n",
"print('Group(s):', ', '.join(group))\n",
"trans_mats, usages = get_group_trans_mats(model_data['labels'], label_group, group, max_syllables, normalize=normalize)\n",
"\n",
"# squeeze=False keeps ax a row of axes even when there is only one group\n",
"fig, ax = plt.subplots(1, len(group), figsize=(12, 8), sharex=False, sharey=True, squeeze=False)\n",
"ax = ax[0]\n",
"title_map = dict(bigram='Bigram', columns='Incoming', rows='Outgoing')\n",
"\n",
"# max color threshold for graphs - set to any value \n",
"color_lim = max([x.max() for x in trans_mats])\n",
"\n",
"for i, g in enumerate(group):\n",
" h = ax[i].imshow(trans_mats[i][:max_syllables,:max_syllables], cmap='cubehelix', vmax=color_lim)\n",
" if i == 0:\n",
" ax[i].set_ylabel('Incoming syllable')\n",
" cb = fig.colorbar(h, ax=ax[i], fraction=0.046, pad=0.04)\n",
" cb.set_label(f'{title_map[normalize]} transition probability')\n",
" ax[i].set_xlabel('Outgoing syllable')\n",
" ax[i].set_title(g)\n",
"\n",
"# save transition matrix figure\n",
"os.makedirs(progress_paths['plot_path'], exist_ok=True)\n",
"for ext in ('pdf', 'png'):\n",
" fig.savefig(join(progress_paths['plot_path'], f'{normalize}_transition_matrix.{ext}'))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Export Transition Matrices and Usages\n",
"\n",
"You can export the transition matrices and usages to csv files for further analysis using the following cell.\n",
"\n",
"**Instructions:**\n",
"- **Specify the path** to save the data in `save_path`. By default, it will save in the model folder.\n",
"- **Run the cell** if you want to export the transition matrices and syllable usages as CSV files for analysis outside of this notebook."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"from os.path import join\n",
"\n",
"save_path = progress_paths['model_session_path']\n",
"\n",
"for group_name, tm, usage in zip(group, trans_mats, usages):\n",
" out = join(save_path, f'{group_name}_{normalize}_transition_matrix.csv')\n",
" np.savetxt(out, tm, delimiter=',') # save TM as csv file\n",
" out = join(save_path, f'{group_name}_syllable_counts.csv')\n",
" np.savetxt(out, np.array(list(usage.items())), delimiter=',', header='syllable id,counts') # save usage mapping as csv"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Interactive Syllable Transition Graph Tool\n",
"Transition matrices can also be visualized as directed graphs, where each node of the graph represents one syllable, and the directional edges represent transitions between syllables.\n",
"The **node size** represents **syllable usage** and **edge thickness** represents the **syllable to syllable transition frequency**.\n",
"This widget can also highlight the differences in transition frequencies between multiple experimental groups.\n",
"You can find more information in the [wiki](https://github.com/dattalab/moseq2-app/wiki/MoSeq2-Analysis-Visualization-Notebook-Instructions#using-the-syllable-transition-graph-tool).\n",
"\n",
"**Instructions:**\n",
"- **Run the following 2 cells**. The first cell changes how the widget is displayed (by removing scroll bars).\n",
"- **Select the parameter(s)** from the dropdown menus to control the graph.\n",
"- **Hover over the edges and nodes** to display syllable info. Edge colors represent the type of syllable-to-syllable transitions.\n",
"**Note: Nodes outside the threshold will be hidden. The widget may take some time (5-10 seconds) to reload after changing the parameters. Be patient as the data loads.**\n",
"- **Click the save icon in the toolbar on top of the figures** to save the figures as SVG in your default web image download folder."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"from moseq2_app.main import interactive_transition_graph\n",
"\n",
"# max_syllables is defined above\n",
"interactive_transition_graph(progress_paths, max_syllables=max_syllables, plot_vertically=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"***\n",
"\n",
"# Notebook End \n",
"\n",
"# User Survey\n",
"\n",
"Please take some time to tell us your thoughts about this notebook:\n",
"**[user feedback survey](https://forms.gle/S88jptAEs41mQjff7)**"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.9"
},
"moseq2": {
"version": "1.4"
},
"pycharm": {
"stem_cell": {
"cell_type": "raw",
"metadata": {
"collapsed": false
},
"source": []
}
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": true,
"toc_position": {
"height": "calc(100% - 180px)",
"left": "10px",
"top": "150px",
"width": "383.965px"
},
"toc_section_display": true,
"toc_window_display": true
},
"varInspector": {
"cols": {
"lenName": 16,
"lenType": 16,
"lenVar": 40
},
"kernels_config": {
"python": {
"delete_cmd_postfix": "",
"delete_cmd_prefix": "del ",
"library": "var_list.py",
"varRefreshCmd": "print(var_dic_list())"
},
"r": {
"delete_cmd_postfix": ") ",
"delete_cmd_prefix": "rm(",
"library": "var_list.r",
"varRefreshCmd": "cat(var_dic_list()) "
}
},
"types_to_exclude": [
"module",
"function",
"builtin_function_or_method",
"instance",
"_Feature"
],
"window_display": false
},
"vscode": {
"interpreter": {
"hash": "b4020b50b3b3d2ea6961348cfb7ebb5d335bc252ea84e439b63e9046e0c3f674"
}
}
},
"nbformat": 4,
"nbformat_minor": 4
}