├── .conda └── meta.yaml ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── support-request.md └── workflows │ ├── publish.yml │ ├── style.yml │ └── testbuild.yml ├── .gitignore ├── .readthedocs.yml ├── AUTHORS ├── CITATION.cff ├── CONTRIBUTING.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── demos ├── 118_images_files │ ├── 118_animate_RDM.npy │ └── 118_animate_labels.npy ├── 92imageData │ ├── 92_behavRDMs.mat │ ├── 92_brainRDMs.mat │ ├── 92_modelRDMs.mat │ ├── Kriegeskorte_Neuron2008_supplementalData.mat │ ├── faceAnimateInaniClustersRDM.mat │ ├── rdm92_HMAXnatImPatch.mat │ ├── rdm92_V1model.mat │ └── simTruePatterns.mat ├── TemporalSampleData │ ├── meg_sample_data.pkl │ └── preproc_mne_sample_data.py ├── demo_annotated_rdm_plot.ipynb ├── demo_bootstrap.ipynb ├── demo_dataset.ipynb ├── demo_dissimilarities.ipynb ├── demo_eeg.ipynb ├── demo_eeg_data │ ├── Meadows_nsd100annotations_v_v1_devoted-caiman_2_annotations.csv │ ├── annotate.png │ ├── shared0140_nsd11797.png │ ├── shared0936_nsd67830.png │ └── shared0944_nsd68742.png ├── demo_flexible_models.ipynb ├── demo_fmri_custom.ipynb ├── demo_fmri_files │ ├── mur32_stims.png │ └── mur32_tasks.png ├── demo_fmri_nilearn.ipynb ├── demo_fmri_patterns.ipynb ├── demo_fmri_spm.ipynb ├── demo_meg_mne.ipynb ├── demo_model_family.ipynb ├── demo_model_map.ipynb ├── demo_rdm_comparison_scatterplot.ipynb ├── demo_rdm_visualisation_92images.ipynb ├── demo_riemannian.ipynb ├── demo_searchlight_volume.ipynb ├── demo_spikes.ipynb ├── demo_temporal.ipynb ├── demo_unbalanced.ipynb ├── paper_examples │ ├── sim_crossval.ipynb │ ├── sim_crossval.py │ └── sim_mahalanobis.ipynb ├── prepare_demo_fmri_patterns.py └── rdms_inferring │ ├── modelRDMs_A2020.mat │ └── noisyModelRDMs_A2020.mat ├── devops ├── gha.py ├── style_assistant.py └── typing_assistant.py ├── docs ├── Makefile ├── README.md ├── requirements.txt └── source │ ├── TemporalSampleData │ ├── meg_sample_data.pkl │ └── preproc_mne_sample_data.py │ ├── _static │ ├── SoftwareFlowChart.png │ ├── rsatoolbox_workflow.pdf │ └── rsatoolbox_workflow.png │ ├── comparing.rst │ ├── conf.py │ ├── datasets.rst │ ├── demo_annotated_rdm_plot.nblink │ ├── demo_bootstrap.nblink │ ├── demo_dataset.nblink │ ├── demo_dissimilarities.nblink │ ├── demo_eeg.nblink │ ├── demo_flexible_models.nblink │ ├── demo_fmri_files │ ├── mur32_stims.png │ └── mur32_tasks.png │ ├── demo_fmri_nilearn.nblink │ ├── demo_fmri_patterns.nblink │ ├── demo_fmri_spm.nblink │ ├── demo_meg_mne.nblink │ ├── demo_rdm_vis.nblink │ ├── demo_riemannian.nblink │ ├── demo_searchlight.nblink │ ├── demo_spikes.nblink │ ├── demo_temporal.nblink │ ├── demo_unbalanced.nblink │ ├── demos.rst │ ├── distances.rst │ ├── getting_started.rst │ ├── index.rst │ ├── inference.rst │ ├── literature_cited.rst │ ├── model_fitting.rst │ ├── model_specification.rst │ ├── operations.rst │ ├── overview.rst │ ├── rescale_partials.rst │ ├── rsatoolbox.data.base.rst │ ├── rsatoolbox.data.computations.rst │ ├── rsatoolbox.data.dataset.rst │ ├── rsatoolbox.data.noise.rst │ ├── rsatoolbox.data.ops.rst │ ├── rsatoolbox.data.rst │ ├── rsatoolbox.inference.boot_testset.rst │ ├── rsatoolbox.inference.bootstrap.rst │ ├── rsatoolbox.inference.crossvalsets.rst │ ├── rsatoolbox.inference.evaluate.rst │ ├── rsatoolbox.inference.noise_ceiling.rst │ ├── rsatoolbox.inference.result.rst │ ├── rsatoolbox.inference.rst │ ├── rsatoolbox.io.bids.rst │ ├── rsatoolbox.io.fmriprep.rst │ ├── rsatoolbox.io.hdf5.rst │ ├── rsatoolbox.io.meadows.rst │ ├── rsatoolbox.io.mne.rst │ ├── rsatoolbox.io.optional.rst │ ├── rsatoolbox.io.pandas.rst │ ├── rsatoolbox.io.pkl.rst │ ├── rsatoolbox.io.rst │ ├── rsatoolbox.io.spm.rst │ ├── rsatoolbox.model.fitter.rst │ ├── rsatoolbox.model.model.rst │ ├── rsatoolbox.model.rst │ ├── rsatoolbox.rdm.calc.rst │ ├── rsatoolbox.rdm.calc_unbalanced.rst │ ├── rsatoolbox.rdm.combine.rst │ ├── rsatoolbox.rdm.compare.rst │ ├── rsatoolbox.rdm.pairs.rst │ ├── rsatoolbox.rdm.rdms.rst │ ├── rsatoolbox.rdm.rst │ ├── rsatoolbox.rdm.transform.rst │ ├── rsatoolbox.rst │ ├── rsatoolbox.simulation.rst │ ├── rsatoolbox.simulation.sim.rst │ ├── rsatoolbox.util.data_utils.rst │ ├── rsatoolbox.util.descriptor_utils.rst │ ├── rsatoolbox.util.file_io.rst │ ├── rsatoolbox.util.inference_util.rst │ ├── rsatoolbox.util.matrix.rst │ ├── rsatoolbox.util.pooling.rst │ ├── rsatoolbox.util.rdm_utils.rst │ ├── rsatoolbox.util.rst │ ├── rsatoolbox.util.searchlight.rst │ ├── rsatoolbox.util.vis_utils.rst │ ├── rsatoolbox.vis.colors.rst │ ├── rsatoolbox.vis.icon.rst │ ├── rsatoolbox.vis.model_plot.rst │ ├── rsatoolbox.vis.rdm_plot.rst │ ├── rsatoolbox.vis.rst │ ├── rsatoolbox.vis.scatter_plot.rst │ ├── rsatoolbox.vis.timecourse.rst │ ├── temp_rdm.png │ └── visualization.rst ├── pyproject.toml ├── requirements.txt ├── ruff.toml ├── setup.py ├── src └── rsatoolbox │ ├── __init__.py │ ├── cengine │ ├── __init__.pxd │ ├── __init__.py │ └── similarity.pyx │ ├── data │ ├── __init__.py │ ├── base.py │ ├── computations.py │ ├── dataset.py │ ├── noise.py │ └── ops.py │ ├── inference │ ├── __init__.py │ ├── boot_testset.py │ ├── bootstrap.py │ ├── crossvalsets.py │ ├── evaluate.py │ ├── noise_ceiling.py │ └── result.py │ ├── io │ ├── __init__.py │ ├── bids.py │ ├── fmriprep.py │ ├── hdf5.py │ ├── hrf.py │ ├── meadows.py │ ├── mne.py │ ├── optional.py │ ├── pandas.py │ ├── petnames.py │ ├── pkl.py │ └── spm.py │ ├── model │ ├── __init__.py │ ├── fitter.py │ ├── model.py │ └── model_family.py │ ├── py.typed │ ├── rdm │ ├── __init__.py │ ├── calc.py │ ├── calc_unbalanced.py │ ├── combine.py │ ├── compare.py │ ├── pairs.py │ ├── rdms.py │ └── transform.py │ ├── resources.py │ ├── simulation │ ├── __init__.py │ └── sim.py │ ├── test.py │ ├── util │ ├── __init__.py │ ├── build_rdm.py │ ├── data_utils.py │ ├── descriptor_utils.py │ ├── file_io.py │ ├── inference_util.py │ ├── matrix.py │ ├── pooling.py │ ├── rdm_utils.py │ ├── searchlight.py │ └── vis_utils.py │ └── vis │ ├── __init__.py │ ├── colors.py │ ├── icon.py │ ├── model_map.py │ ├── model_plot.py │ ├── modelfamily_graph.py │ ├── rdm.mplstyle │ ├── rdm_comparison.py │ ├── rdm_plot.py │ ├── scatter_plot.py │ └── timecourse.py ├── tests ├── __init__.py ├── data │ ├── Meadows_myExp_v_v1_arrangement_1D.mat │ ├── Meadows_myExp_v_v1_cuddly-bunny_3_1D.mat │ ├── Meadows_myExp_v_v1_cuddly-bunny_3_tree.json │ ├── Meadows_twoMaTasks_v_v1_informed-mole_tree.json │ └── README.md ├── requirements.txt ├── test_c.py ├── test_calc_rdm.py ├── test_calc_unbalanced.py ├── test_colors.py ├── test_compare_rdm.py ├── test_crossval.py ├── test_data.py ├── test_data_ops_merge.py ├── test_data_utils.py ├── test_dataset_dataframe.py ├── test_demo.py ├── test_descriptor_utils.py ├── test_inference.py ├── test_io_bids.py ├── test_io_fmriprep.py ├── test_io_hdf5.py ├── test_io_meadows.py ├── test_io_mne.py ├── test_io_pkl.py ├── test_io_spm.py ├── test_model.py ├── test_noise_ceiling.py ├── test_pair_selection.py ├── test_rdm.py ├── test_rdms_combine.py ├── test_rdms_pandas.py ├── test_searchlight.py ├── test_simulation.py ├── test_util_matrix.py ├── test_util_rdm.py ├── test_vis.py ├── test_vis_model.py ├── test_vis_plot_rdm.py └── test_vis_rdm_plot_conf.py └── wiki ├── README.md └── SoftwareFlowChart.png /.conda/meta.yaml: -------------------------------------------------------------------------------- 1 | {% set name = "rsatoolbox" %} 2 | {% set version = "0.1.2" %} 3 | 4 | package: 5 | name: {{ name|lower }} 6 | version: {{ version }} 7 | 8 | source: 9 | url: https://pypi.io/packages/source/{{ name[0] }}/{{ name }}/rsatoolbox-{{ version }}.tar.gz 10 | sha256: fee6e0134c345f4d7e7f2d7bdde20b13724e9d0d32f89e8b22b7fc17ed21ef9a 11 | 12 | build: 13 | skip: true # [py<37 or py>310 or py2k] 14 | script: {{ PYTHON }} -m pip install . -vv 15 | number: 0 16 | 17 | requirements: 18 | build: 19 | - {{ compiler('c') }} 20 | host: 21 | - python 22 | - cython # >=3.0.0a11 23 | - numpy >=1.21.2 24 | - scipy 25 | - setuptools 26 | - setuptools-scm 27 | - pip 28 | run: 29 | - python 30 | - {{ pin_compatible('numpy') }} 31 | - {{ pin_compatible('scipy') }} 32 | - pandas 33 | - scikit-learn 34 | - scikit-image 35 | - matplotlib-base 36 | - h5py 37 | - joblib 38 | - tqdm 39 | - coverage 40 | 41 | 42 | test: 43 | imports: 44 | - rsatoolbox 45 | commands: 46 | - pip check 47 | - python -m unittest -v rsatoolbox.test 48 | requires: 49 | - pip 50 | 51 | about: 52 | home: https://github.com/rsagroup/rsatoolbox 53 | summary: Representational Similarity Analysis (RSA) in Python 54 | license: MIT 55 | license_file: LICENSE 56 | doc_url: https://rsatoolbox.readthedocs.io/ 57 | dev_url: https://github.com/rsagroup/rsatoolbox 58 | 59 | extra: 60 | recipe-maintainers: 61 | - ilogue 62 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us fix an issue 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Thanks for considering reporting an issue in `rsatoolbox` to us! Below are some tips that make it easier for us to understand what happened. 11 | 12 | **Describe the bug** 13 | A clear and concise description of what the bug is. 14 | 15 | **To Reproduce** 16 | Ideally you'd include the smallest code sample required to reproduce the error. 17 | 18 | **Expected behavior** 19 | A clear and concise description of what you expected to happen. 20 | 21 | **Plots** 22 | If the bug is related to plotting code, please attach the image(s). 23 | 24 | **Versions** 25 | Please report: 26 | - the version of *rsatoolbox* you used (`pip freeze | grep rsatoolbox`) 27 | - the version of python you used (`python --version`) 28 | 29 | **Additional context** 30 | Add any other context about the problem here. 31 | 32 | **stack trace** 33 | This is the error information that python printed. You can add this in the body of this issue, wrapped in triple backticks (` ``` `) or attach it in a text file 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Hi! Thanks for considering suggesting a change or new feature to `rsatoolbox`. Below you'll find some (optional) sections that may help to write a clear request. As `rsatoolbox` is still young, we are open to new ideas, discussions on methods, interface style and many other things. 11 | 12 | **Is your feature request related to a problem? Please describe.** 13 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 14 | 15 | **Describe the solution you'd like** 16 | A clear and concise description of what you want to happen. 17 | 18 | **Describe alternatives you've considered** 19 | A clear and concise description of any alternative solutions or features you may have considered. 20 | 21 | **volunteer** 22 | - do you have an (ideally anonymized) data sample that we could use for testing? 23 | - would you be willing to write code for the library with us? 24 | - if so, what do you need from us to get started? 25 | 26 | **Additional context** 27 | Add any other context or screenshots about the feature request here. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/support-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Support request 3 | about: Ask for help on how to do something 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Hi and thanks for using `rsatoolbox`. This category of issues is for when you're unsure how something is supposed to work. We welcome these as it may help us make the library easier to use. However, we are also just scientists doing this on the side, so please bear with us if it takes longer to get back to you. 11 | 12 | Where applicable, please tell us a bit about: 13 | - type of data 14 | - experimental design 15 | - what you are trying to accomplish 16 | - what have you tried but didn't work, or if you feel there's something lacking in the documentation 17 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | release: 8 | types: [published] 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | call-test-build: 15 | uses: ./.github/workflows/testbuild.yml 16 | 17 | publish-artifacts: 18 | environment: production 19 | needs: call-test-build 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Download all artifacts 23 | uses: actions/download-artifact@v4 24 | with: 25 | path: dist 26 | merge-multiple: true 27 | - name: List files 28 | run: ls -R dist 29 | - name: Publish package 30 | uses: pypa/gh-action-pypi-publish@v1.5.1 31 | with: 32 | verbose: true 33 | password: ${{ secrets.API_TOKEN_PYPI }} 34 | -------------------------------------------------------------------------------- /.github/workflows/style.yml: -------------------------------------------------------------------------------- 1 | name: Style and Typing checks 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize, reopened] 6 | 7 | permissions: 8 | contents: read 9 | statuses: write 10 | 11 | jobs: 12 | 13 | style: 14 | runs-on: ubuntu-22.04 15 | env: 16 | GH_TOKEN: ${{ github.token }} 17 | steps: 18 | - uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 0 21 | - uses: actions/setup-python@v5 22 | with: 23 | python-version: '3.11' 24 | - name: Install dependencies 25 | run: pip install ruff 26 | - name: Ruff output to file 27 | run: ruff check -ne -o ruff.log --output-format concise 28 | - name: Ruff output as annotations 29 | run: echo "$(ruff check -e --output-format github)" 30 | - name: Process Ruff output 31 | run: python devops/style_assistant.py ${{ github.run_id }} ${{ github.job }} ${{ github.event.pull_request.head.sha }} 32 | - name: Report status 33 | run: gh api ${{ env.DEVOPS_ASST_API_ARGS }} 34 | 35 | 36 | typing: 37 | runs-on: ubuntu-22.04 38 | env: 39 | GH_TOKEN: ${{ github.token }} 40 | steps: 41 | - uses: actions/setup-python@v5 42 | with: 43 | python-version: '3.11' 44 | - name: Python Version 45 | run: python --version 46 | - name: Update Pip 47 | run: python -m pip install --upgrade pip 48 | - name: Install dependencies 49 | run: pip install build setuptools pandas-stubs types-tqdm pyright 50 | - uses: actions/checkout@v4 51 | with: 52 | ref: main 53 | fetch-depth: 0 54 | - name: Build package 55 | run: python -m build --sdist 56 | - name: Install rsatoolbox 57 | run: pip install dist/* 58 | - name: Pyright (on main) 59 | continue-on-error: true 60 | run: pyright > pyright_main.log 61 | - name: Uninstall rsatoolbox 62 | run: pip uninstall -y rsatoolbox 63 | - name: Remove builds 64 | run: rm dist/* 65 | - uses: actions/checkout@v4 66 | with: 67 | clean: false 68 | fetch-depth: 0 69 | - name: Build package 70 | run: python -m build --sdist 71 | - name: Install rsatoolbox 72 | run: pip install dist/* 73 | - name: Pyright (on PR) 74 | continue-on-error: true 75 | run: pyright > pyright_pr.log 76 | - name: Process Pyright output 77 | run: python devops/typing_assistant.py ${{ github.run_id }} ${{ github.job }} ${{ github.event.pull_request.head.sha }} 78 | - name: Report status 79 | run: gh api ${{ env.DEVOPS_ASST_API_ARGS }} 80 | -------------------------------------------------------------------------------- /.github/workflows/testbuild.yml: -------------------------------------------------------------------------------- 1 | name: Test and Build 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize, reopened] 6 | workflow_call: 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | 13 | tests: 14 | runs-on: ubuntu-22.04 15 | steps: 16 | - uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 0 19 | - uses: actions/setup-python@v5 20 | with: 21 | python-version: '3.11' 22 | - name: Python Version 23 | run: python --version 24 | - name: Update Pip 25 | run: python -m pip install --upgrade pip 26 | - name: Install dependencies 27 | run: pip install -r requirements.txt 28 | - name: Install rsatoolbox 29 | run: pip install . 30 | - name: Install test dependencies 31 | run: pip install -r tests/requirements.txt 32 | - name: Skeleton tests 33 | run: python -m unittest -v rsatoolbox.test 34 | - name: Unit tests 35 | run: pytest 36 | 37 | source: 38 | needs: tests 39 | runs-on: ${{ matrix.os }} 40 | strategy: 41 | matrix: 42 | python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] 43 | os: [ubuntu-latest, macos-latest, windows-latest] 44 | steps: 45 | - uses: actions/checkout@v4 46 | with: 47 | fetch-depth: 0 48 | - uses: actions/setup-python@v5 49 | with: 50 | python-version: ${{ matrix.python-version }} 51 | - name: Python Version 52 | run: python --version 53 | - name: Update Pip 54 | run: python -m pip install --upgrade pip 55 | - name: Install Build 56 | run: pip install build setuptools 57 | - name: Build package 58 | run: python -m build --sdist 59 | - name: Install rsatoolbox (Linux, Mac) 60 | run: pip install dist/* 61 | if: matrix.os != 'windows-latest' 62 | - name: Install rsatoolbox (Windows) 63 | run: | 64 | $sdistfname = Get-ChildItem dist -Name 65 | pip install dist/$sdistfname 66 | if: matrix.os == 'windows-latest' 67 | - name: Install test dependencies 68 | run: pip install -r tests/requirements.txt 69 | - name: Skeleton tests 70 | run: python -m unittest -v rsatoolbox.test 71 | - name: Unit tests 72 | run: pytest 73 | - name: Check package compliance 74 | run: | 75 | pip install -q twine 76 | twine check dist/* 77 | - name: Store artifact 78 | uses: actions/upload-artifact@v4 79 | with: 80 | name: source-${{ matrix.os }}-${{ matrix.python-version }} 81 | path: dist/* 82 | if-no-files-found: error 83 | retention-days: 1 84 | 85 | binaries: 86 | needs: tests 87 | runs-on: ${{ matrix.os }} 88 | strategy: 89 | matrix: 90 | os: [ubuntu-latest, macos-latest, windows-latest] 91 | steps: 92 | - uses: actions/checkout@v4 93 | with: 94 | fetch-depth: 0 95 | - uses: actions/setup-python@v5 96 | with: 97 | python-version: '3.12' 98 | - name: Python Version 99 | run: python --version 100 | - name: Update Pip 101 | run: python -m pip install --upgrade pip 102 | - name: Install cibuildwheel 103 | run: python -m pip install cibuildwheel==2.19.2 104 | - name: Build wheels 105 | run: python -m cibuildwheel --output-dir dist 106 | - name: Check package compliance 107 | run: | 108 | pip install -q twine 109 | twine check dist/* 110 | - name: Store artifact 111 | uses: actions/upload-artifact@v4 112 | with: 113 | name: wheels-${{ matrix.os }} 114 | path: dist/*.whl 115 | if-no-files-found: error 116 | retention-days: 1 117 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Debug and profile files 2 | debug*.py 3 | *.Result 4 | 5 | # c files resulting from cython 6 | src/rsatoolbox/cengine/*.c 7 | src/rsatoolbox/cengine/*.html 8 | 9 | # data files from tutorials & demos 10 | cadena_ploscb_data.pkl 11 | fmri_data 12 | demos/allendata 13 | demos/temp_rdm.png 14 | docs/source/92imageData/ 15 | docs/source/rdms_inferring/ 16 | docs/source/demo_eeg_data 17 | 18 | # stats files 19 | stats/**/*.npz 20 | stats/**/*.hdf5 21 | stats/**/*.npy 22 | stats/**/*.txt 23 | stats/**/*.csv 24 | stats/boc 25 | stats/illustrations 26 | stats/figures 27 | 28 | # IDEs 29 | .vscode/ 30 | .idea/ 31 | 32 | # vscode jupyter extension garbage 33 | default-*.ipynb 34 | 35 | # MAC 36 | .DS_Store 37 | 38 | # Byte-compiled / optimized / DLL files 39 | __pycache__/ 40 | *.py[cod] 41 | *$py.class 42 | 43 | # C extensions 44 | *.so 45 | src/rsatoolbox/*.html 46 | 47 | # Distribution / packaging 48 | wheelhouse/ 49 | .Python 50 | build/ 51 | develop-eggs/ 52 | dist/ 53 | downloads/ 54 | eggs/ 55 | .eggs/ 56 | lib/ 57 | lib64/ 58 | parts/ 59 | sdist/ 60 | var/ 61 | wheels/ 62 | *.egg-info/ 63 | .installed.cfg 64 | *.egg 65 | MANIFEST 66 | 67 | # PyInstaller 68 | # Usually these files are written by a python script from a template 69 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 70 | *.manifest 71 | *.spec 72 | 73 | # Installer logs 74 | pip-log.txt 75 | pip-delete-this-directory.txt 76 | 77 | # Unit test / coverage reports 78 | htmlcov/ 79 | .tox/ 80 | .coverage 81 | .coverage.* 82 | .cache 83 | nosetests.xml 84 | coverage.xml 85 | *.cover 86 | .hypothesis/ 87 | .pytest_cache/ 88 | 89 | # Translations 90 | *.mo 91 | *.pot 92 | 93 | # Django stuff: 94 | *.log 95 | local_settings.py 96 | db.sqlite3 97 | 98 | # Flask stuff: 99 | instance/ 100 | .webassets-cache 101 | 102 | # Scrapy stuff: 103 | .scrapy 104 | 105 | # Sphinx documentation 106 | docs/build/ 107 | docs/source/_build/ 108 | 109 | # PyBuilder 110 | target/ 111 | 112 | # Jupyter Notebook 113 | .ipynb_checkpoints 114 | 115 | # pyenv 116 | .python-version 117 | 118 | # celery beat schedule file 119 | celerybeat-schedule 120 | 121 | # SageMath parsed files 122 | *.sage.py 123 | 124 | # Environments 125 | .env 126 | .venv 127 | env/ 128 | venv/ 129 | ENV/ 130 | env.bak/ 131 | venv.bak/ 132 | 133 | # Spyder project settings 134 | .spyderproject 135 | .spyproject 136 | 137 | # Rope project settings 138 | .ropeproject 139 | 140 | # mkdocs documentation 141 | /site 142 | 143 | # mypy 144 | .mypy_cache/ 145 | Icon[ 146 | ] 147 | 148 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | # Read the Docs configuration file 2 | 3 | # Required 4 | version: 2 5 | 6 | # Build documentation in the docs/ directory with Sphinx 7 | sphinx: 8 | configuration: docs/source/conf.py 9 | 10 | # Optionally build your docs in additional formats such as PDF 11 | formats: all 12 | 13 | build: 14 | os: ubuntu-22.04 15 | tools: 16 | python: "3.8" 17 | 18 | # Optionally set the version of Python and requirements required to build your docs 19 | python: 20 | install: 21 | - requirements: requirements.txt 22 | - requirements: docs/requirements.txt 23 | - method: pip 24 | path: . 25 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Alexander Kipnis 2 | Baihan Lin 3 | Cai Wingfield 4 | Heiko Schütt 5 | Ian Charest 6 | Jasper van den Bosch 7 | Johan Carlin 8 | Jörn Diedrichsen 9 | Mahdiyar Shahbazi 10 | Nikolaus Kriegeskorte 11 | Eva Berlot 12 | Giacomo Ariani 13 | Marieke Mur 14 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | date-released: "2024-08-24" 3 | title: "rsatoolbox" 4 | message: "If you use this software, please cite the pre-print." 5 | authors: 6 | - name: "RSA toolbox authors (See AUTHORS file)" 7 | repository-code: "https://github.com/rsagroup/rsatoolbox" 8 | type: software 9 | license: MIT 10 | version: 0.2 11 | url: "https://rsatoolbox.readthedocs.io/" 12 | preferred-citation: 13 | type: "article" 14 | publisher: 15 | name: "Cold Spring Harbor Laboratory" 16 | doi: "10.1101/2025.05.22.655542" 17 | date-released: "2025-05-22" 18 | journal: "bioRxiv" 19 | title: "A Python Toolbox for Representational Similarity Analysis" 20 | authors: 21 | - family-names: "van den Bosch" 22 | given-names: "Jasper JF" 23 | orcid: "https://orcid.org/0000-0001-9326-2090" 24 | - family-names: "Golan" 25 | given-names: "Tal" 26 | orcid: "https://orcid.org/0000-0002-7940-7473" 27 | - family-names: "Peters" 28 | given-names: "Benjamin" 29 | orcid: "https://orcid.org/0000-0002-0948-8976" 30 | - family-names: "Taylor" 31 | given-names: "JohnMark" 32 | orcid: "https://orcid.org/0000-0002-1034-6860" 33 | - family-names: "Shahbazi" 34 | given-names: "Mahdiyar" 35 | orcid: "https://orcid.org/0000-0002-4883-4376" 36 | - family-names: "Lin" 37 | given-names: "Baihan" 38 | orcid: "https://orcid.org/0000-0002-7979-5509" 39 | - family-names: "Charest" 40 | given-names: "Ian" 41 | orcid: "https://orcid.org/0000-0002-3939-3003" 42 | - family-names: "Diedrichsen" 43 | given-names: "Joern" 44 | orcid: "https://orcid.org/0000-0003-0264-8532" 45 | - family-names: "Kriegeskorte" 46 | given-names: "Nikolaus" 47 | orcid: "https://orcid.org/0000-0001-7433-9005" 48 | - family-names: "Mur" 49 | given-names: "Marieke" 50 | orcid: "https://orcid.org/0000-0003-1749-9058" 51 | - family-names: "Schuett" 52 | given-names: "Heiko" 53 | orcid: "https://orcid.org/0000-0002-2491-5710" 54 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | This is some guidance for RSAtoolbox contributors, but our approach evolves over time. Don't hesitate to contact @ilogue (Jasper) or @HeikoSchuett with any questions. 2 | 3 | 4 | Your cycle 5 | ========== 6 | 7 | 1. If you identify something that has to be fixed or done: create an issue. Discussions 8 | or questions about the theory or best practices should be filed as Github Discussions. 9 | 2. If you want to start coding or documenting something, the first step is to check if anyone else is working on this in the list of Pull Requests. If not, create a branch on your local machine, commit something small such as a note or empty file, and push the commit to the same branch on GitHub. Then you can then open a Pull Request. This is for two reasons: you're communicating to the team that you're working on this (so we're not doing things twice), and it gives you and the others an easy way to track your progress. 10 | 3. Commit regularly (typically every 10-30 minutes) and give your commits useful messages. "Changes to the data package" does not say anything about what you've done, "Added new feature model" does. If your commit is too large this makes it harder to write a short message. 11 | 4. Write unit-tests to cover your new code. This is easier when you recently wrote the code. Tip: try writing tests before you implement the code. The test should assert at least the most important outcomes of the functionality (typically the value returned). 12 | 5. Add Python type annotations where practical. 13 | 6. When you're done with the feature, ask for reviews from two team members or ask the maintainers for help. 14 | 15 | 16 | How-to 17 | ====== 18 | 19 | 1. `pip install -r requirements.txt` install rsatoolbox dependencies (and repeat when you make changes) 20 | 2. `pip install -r tests/requirements.txt` install test dependencies (and repeat when you make changes) 21 | 3. `pip uninstall rsatoolbox` uninstall rsatoolbox in your environment 22 | 4. `rm dist/*` remove any previously built packages if necessary 23 | 5. `python -m build` compile a new rsatoolbox package with your latest changes 24 | 6. `pip install dist/*` install the new package 25 | 7. `pytest` run the unit tests on the installed version of rsatoolbox 26 | 8. run linting tools such as `flake8`, `vulture` to discover any style issues 27 | 28 | 29 | Rules 30 | ===== 31 | 32 | 1. Only through Pull Requests can you submit changes or additions to the code. 33 | 2. Every Pull Request has to be reviewed by two team members. 34 | 3. New code should have useful unit tests. 35 | 4. Code should pass the `pylint` style check. 36 | 5. Functions, classes, methods should have a `Google-style docstring`. 37 | 6. Larger new features should come with narrative documentation and an example. 38 | 7. When you're ready for your Pull Request to be reviewed, in the top right corner you can suggest two reviewers, 39 | or alternatively, ping @ilogue or @HeikoSchuett and we will assign reviewers. 40 | 8. Consider how to handle NaNs in the user input. If your code can't handle them, you can throw an exception. 41 | 42 | 43 | Deployment 44 | ========== 45 | 46 | 47 | - when a PR is merged into the branch main, it is build as a pre-release (or "development") package and uploaded to pypi. The latest pre-release version can be installed using `pip install --pre rsatoolbox` 48 | - when a release tag is added to the branch main, the package is instead marked as a released (or "stable") version. 49 | 50 | 51 | Naming scheme 52 | ============= 53 | 54 | **Classes** 55 | 56 | - CamelCase 57 | - ends in noun 58 | 59 | *example: FancyModel* 60 | 61 | 62 | **Functions and methods** 63 | 64 | - lowercase with underscores 65 | - starts with verb 66 | 67 | *example: rdm.ranktransform(), transform_rank(rdm), calculate_gram_matrix, load_fmri_data* 68 | 69 | 70 | **Variables** 71 | 72 | - lowercase and underscore 73 | - typically nouns or concepts 74 | 75 | *example: contrast_matrix* 76 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 rsatoolbox authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include src/rsatoolbox *.mplstyle 2 | include src/rsatoolbox/py.typed 3 | include requirements.txt 4 | include tests/requirements.txt 5 | recursive-exclude *.yml 6 | prune .github 7 | prune demos 8 | prune docs 9 | prune tests 10 | prune wiki -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Representational Similarity Analysis 3.0 2 | 3 | [![Documentation Status](https://readthedocs.org/projects/rsatoolbox/badge/?version=latest)](https://rsatoolbox.readthedocs.io/en/latest/?badge=latest) 4 | [![PyPI version](https://badge.fury.io/py/rsatoolbox.svg)](https://badge.fury.io/py/rsatoolbox) 5 | [![Anaconda-Server Badge](https://anaconda.org/conda-forge/rsatoolbox/badges/version.svg)](https://anaconda.org/conda-forge/rsatoolbox) 6 | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/626ca9ec9f75485a9f73783c02710b1f)](https://www.codacy.com/gh/rsagroup/rsatoolbox?utm_source=github.com&utm_medium=referral&utm_content=rsagroup/rsatoolbox&utm_campaign=Badge_Grade) 7 | [![CodeFactor](https://www.codefactor.io/repository/github/rsagroup/rsatoolbox/badge)](https://www.codefactor.io/repository/github/rsagroup/rsatoolbox) 8 | 9 | 10 | Conceived during the RSA retreat 2019 in Blue Mountains. 11 | 12 | [Documentation](https://rsatoolbox.readthedocs.io/) 13 | 14 | 15 | #### Getting Started 16 | 17 | To install the latest stable version of rsatoolbox with pip: 18 | 19 | ```sh 20 | pip install rsatoolbox 21 | ``` 22 | 23 | or with conda: 24 | 25 | ```sh 26 | conda install -c conda-forge rsatoolbox 27 | ``` 28 | 29 | 30 | here is a simple code sample: 31 | 32 | ```python 33 | import numpy, rsatoolbox 34 | data = rsatoolbox.data.Dataset(numpy.random.rand(10, 5)) 35 | rdms = rsatoolbox.rdm.calc_rdm(data) 36 | rsatoolbox.vis.show_rdm(rdms) 37 | ``` -------------------------------------------------------------------------------- /demos/118_images_files/118_animate_RDM.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/demos/118_images_files/118_animate_RDM.npy -------------------------------------------------------------------------------- /demos/118_images_files/118_animate_labels.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/demos/118_images_files/118_animate_labels.npy -------------------------------------------------------------------------------- /demos/92imageData/92_behavRDMs.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/demos/92imageData/92_behavRDMs.mat -------------------------------------------------------------------------------- /demos/92imageData/92_brainRDMs.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/demos/92imageData/92_brainRDMs.mat -------------------------------------------------------------------------------- /demos/92imageData/92_modelRDMs.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/demos/92imageData/92_modelRDMs.mat -------------------------------------------------------------------------------- /demos/92imageData/Kriegeskorte_Neuron2008_supplementalData.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/demos/92imageData/Kriegeskorte_Neuron2008_supplementalData.mat -------------------------------------------------------------------------------- /demos/92imageData/faceAnimateInaniClustersRDM.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/demos/92imageData/faceAnimateInaniClustersRDM.mat -------------------------------------------------------------------------------- /demos/92imageData/rdm92_HMAXnatImPatch.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/demos/92imageData/rdm92_HMAXnatImPatch.mat -------------------------------------------------------------------------------- /demos/92imageData/rdm92_V1model.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/demos/92imageData/rdm92_V1model.mat -------------------------------------------------------------------------------- /demos/92imageData/simTruePatterns.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/demos/92imageData/simTruePatterns.mat -------------------------------------------------------------------------------- /demos/TemporalSampleData/meg_sample_data.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/demos/TemporalSampleData/meg_sample_data.pkl -------------------------------------------------------------------------------- /demos/TemporalSampleData/preproc_mne_sample_data.py: -------------------------------------------------------------------------------- 1 | """ Script to preprocess MEG sample data for Temporal RSA demo notebook 2 | 3 | Use of pickle is not recommended in multi-user environments 4 | """ 5 | import pickle #nosec - ignoring pickle security warning 6 | import mne 7 | from mne.datasets import sample 8 | 9 | data_path = sample.data_path() 10 | 11 | subjects_dir = data_path + '/subjects' 12 | raw_fname = data_path + '/MEG/sample/sample_audvis_raw.fif' 13 | tmin, tmax = -0.200, .7500 14 | event_id = {'Auditory/Left': 1, 'Auditory/Right': 2, 15 | 'Visual/Left': 3, 'Visual/Right': 4} 16 | 17 | raw = mne.io.read_raw_fif(raw_fname, preload=True) 18 | 19 | raw.filter(1, 20) 20 | events = mne.find_events(raw, 'STI 014') 21 | 22 | # Set up pick list: EEG + MEG - bad channels (modify to your needs) 23 | raw.info['bads'] += ['MEG 2443', 'EEG 053'] # bads + 2 more 24 | 25 | # Read epochs 26 | epochs = mne.Epochs(raw, events, event_id, tmin, tmax, proj=True, 27 | picks=('grad', 'eog'), baseline=(None, 0.), preload=True, 28 | reject=dict(grad=4000e-13, eog=150e-6), decim=10) 29 | epochs.pick_types(meg=True, exclude='bads') # remove stim and EOG 30 | 31 | epochs.apply_baseline((-.2,.0)) 32 | 33 | X = epochs.get_data() # MEG signals: n_epochs, n_meg_channels, n_times 34 | y = epochs.events[:, 2] 35 | 36 | pickle.dump({'data': X[:,:,:], 37 | 'times': epochs.times[:], 38 | 'cond_idx': epochs.events[:,2], 39 | 'cond_names': event_id, 40 | 'channel_names': epochs.ch_names}, 41 | open( "meg_sample_data.pkl", "wb" )) 42 | -------------------------------------------------------------------------------- /demos/demo_eeg_data/annotate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/demos/demo_eeg_data/annotate.png -------------------------------------------------------------------------------- /demos/demo_eeg_data/shared0140_nsd11797.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/demos/demo_eeg_data/shared0140_nsd11797.png -------------------------------------------------------------------------------- /demos/demo_eeg_data/shared0936_nsd67830.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/demos/demo_eeg_data/shared0936_nsd67830.png -------------------------------------------------------------------------------- /demos/demo_eeg_data/shared0944_nsd68742.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/demos/demo_eeg_data/shared0944_nsd68742.png -------------------------------------------------------------------------------- /demos/demo_fmri_files/mur32_stims.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/demos/demo_fmri_files/mur32_stims.png -------------------------------------------------------------------------------- /demos/demo_fmri_files/mur32_tasks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/demos/demo_fmri_files/mur32_tasks.png -------------------------------------------------------------------------------- /demos/paper_examples/sim_crossval.py: -------------------------------------------------------------------------------- 1 | import rsatoolbox as rsa 2 | import numpy as np 3 | import PcmPy as pcm 4 | import scipy.spatial.distance as sd 5 | import pandas as pd 6 | import seaborn as sb 7 | import matplotlib.pyplot as plt 8 | 9 | # Simulate data 10 | 11 | def crossval_sim(n_part=2,n_cond=4,n_sim=10,n_channel=200,sigma='iid'): 12 | """ Simulate data from a model with 4 conditions 13 | Use different noise covriances across trials for each 14 | Use square eucledian or cross-validated square Eucldian distancen 15 | Low numbers of partitions to emphasize increase in variance. 16 | """ 17 | 18 | cond_vec,part_vec = pcm.sim.make_design(n_cond,n_part) 19 | true_dist = np.array([2,1,0,3,2,1]) 20 | dist_type = np.array([1,2,3,4,5,6]) 21 | D = sd.squareform(true_dist) 22 | H = pcm.matrix.centering(n_cond) 23 | G = -0.5 * H @ D @ H 24 | M = pcm.model.FixedModel('fixed',G) 25 | if (sigma=='iid'): 26 | Sigma = np.kron(np.eye(n_part),np.eye(n_cond)) 27 | elif (sigma=='neigh'): 28 | A = [[1,0.8,0,0],[0.8,1,0,0.5],[0,0,1,0],[0,0.5,0,1]] 29 | Sigma = np.kron(np.eye(n_part),A) 30 | data = pcm.sim.make_dataset(M,[],cond_vec, 31 | n_sim=n_sim, 32 | noise=4, 33 | n_channel=n_channel, 34 | noise_cov_trial=Sigma) 35 | Z = pcm.matrix.indicator(cond_vec) 36 | 37 | D_simp = np.zeros((n_sim,n_cond*(n_cond-1)//2)) 38 | D_cross = np.zeros((n_sim,n_cond*(n_cond-1)//2)) 39 | for i in range(n_sim): 40 | mean_act = np.linalg.pinv(Z) @ data[i].measurements 41 | D_simp[i] = sd.pdist(mean_act)**2/n_channel 42 | G_cross,_ = pcm.est_G_crossval(data[i].measurements,cond_vec,part_vec) 43 | D_cross[i,:] = sd.squareform(pcm.G_to_dist(G_cross)) 44 | 45 | 46 | # model, theta, cond_vec, n_channel=30, n_sim=1, 47 | # signal=1, noise=1, signal_cov_channel=None, 48 | # noise_cov_channel=None, noise_cov_trial=None, 49 | # use_exact_signal=False, use_same_signal=False) 50 | T=pd.DataFrame({'Simp':D_simp.flatten(), 51 | 'Cross':D_cross.flatten(), 52 | 'True':np.tile(true_dist,n_sim), 53 | 'dist_type':np.tile(dist_type,n_sim)}) 54 | return(T) 55 | 56 | def plot_panel(T): 57 | sb.violinplot(data=T,x='True',y='Simp') 58 | sb.violinplot(data=T,x='True',y='Cross') 59 | sb.despine() 60 | plt.plot([0,3],[0,3],'k--') 61 | plt.xlabel('True distance') 62 | plt.ylabel('Estimated distance') 63 | ax=plt.gca() 64 | ax.set_ylim([-1,10]) 65 | 66 | 67 | if __name__=="__main__": 68 | T1 = crossval_sim(sigma='iid',n_sim=100) 69 | T2 = crossval_sim(sigma='neigh',n_sim=100) 70 | plt.figure() 71 | plt.subplot(1,2,1) 72 | plot_panel(T1) 73 | plt.subplot(1,2,2) 74 | plot_panel(T2) 75 | 76 | pass -------------------------------------------------------------------------------- /demos/rdms_inferring/modelRDMs_A2020.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/demos/rdms_inferring/modelRDMs_A2020.mat -------------------------------------------------------------------------------- /demos/rdms_inferring/noisyModelRDMs_A2020.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/demos/rdms_inferring/noisyModelRDMs_A2020.mat -------------------------------------------------------------------------------- /devops/gha.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Helper functions for Github Actions 3 | """ 4 | from typing import Tuple 5 | import sys 6 | import os 7 | ENV_VAR_NAME = 'DEVOPS_ASST_API_ARGS' 8 | 9 | 10 | def parse_github_args() -> Tuple[str, str, str]: 11 | assert len(sys.argv) == (1+3), 'Expected three input arguments' 12 | run_id = sys.argv[1] # github.run_id 13 | job_id = sys.argv[2] # github.job 14 | sha = sys.argv[3] # github.event.pull_request.head.sha 15 | return run_id, job_id, sha 16 | 17 | 18 | def count_log_issues(fpath: str) -> int: 19 | with open(fpath) as fhandle: 20 | lines = fhandle.readlines() 21 | if len(lines) < 2: 22 | return 0 23 | lines.reverse() 24 | for line in lines: 25 | if line.startswith('Found '): 26 | return int(line.split()[1]) 27 | elif 'pyright' in fpath: 28 | return int(line.split()[0]) 29 | else: 30 | raise ValueError('[devops.gha] Could not find summary line') 31 | 32 | 33 | def set_github_summary_api_envs(accept: bool, description: str, summary: str, context: str) -> None: 34 | run_id, job_id, sha = parse_github_args() 35 | target_url = (f'https://github.com/rsagroup/rsatoolbox/actions/runs/{ run_id }' 36 | f'/attempts/1#summary-{ job_id }') 37 | state = 'success' if accept else 'failure' # error, failure, pending, or success 38 | api_url = f'/repos/rsagroup/rsatoolbox/statuses/{ sha }' 39 | cmd = (f'--method POST {api_url} -f "state={state}" ' + 40 | f'-f "target_url={target_url}" -f "description={description}" -f "context={context}"') 41 | 42 | if 'GITHUB_STEP_SUMMARY' in os.environ: 43 | with open(os.environ['GITHUB_STEP_SUMMARY'], 'a') as fhandle: 44 | fhandle.write(summary) 45 | 46 | if 'GITHUB_ENV' in os.environ: 47 | with open(os.environ['GITHUB_ENV'], 'a') as fhandle: 48 | fhandle.write(f'{ENV_VAR_NAME}={cmd}') 49 | -------------------------------------------------------------------------------- /devops/style_assistant.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Helper for processing the Ruff log output 3 | """ 4 | from gha import count_log_issues, set_github_summary_api_envs 5 | 6 | THRESHOLD = 0 # How many errors are allowed 7 | CONTEXT = 'Ruff' 8 | 9 | n = count_log_issues('ruff.log') 10 | accept = n <= THRESHOLD 11 | 12 | summary_issues = """ 13 | ### Ruff: {} issues found :construction: 14 | 15 | See individual errors below under *Annotations*, or 16 | see them inline at the *Files Changed* tab of the Pull Request. 17 | 18 | You can also install ruff (`pip install ruff`) on your machine 19 | and then run `ruff check` locally. 20 | """ 21 | 22 | summary_ok = """ 23 | ### Ruff: No issues found :sparkles: 24 | """ 25 | 26 | summary = summary_ok if accept else summary_issues.format(n) 27 | description = 'No issues found' if accept else f'Found {n} issues' 28 | set_github_summary_api_envs(accept, description, summary, CONTEXT) 29 | -------------------------------------------------------------------------------- /devops/typing_assistant.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Helper for processing the Pyright log output 3 | """ 4 | from gha import count_log_issues, set_github_summary_api_envs 5 | 6 | THRESHOLD = 0 # How many errors are allowed 7 | CONTEXT = 'Pyright' 8 | 9 | old_n = count_log_issues('pyright_main.log') 10 | new_n = count_log_issues('pyright_pr.log') 11 | diff_n = new_n - old_n 12 | accept = diff_n <= THRESHOLD 13 | 14 | summary_issues = """ 15 | ### Pyright: {} new issues found :construction: 16 | 17 | Try to annotate your new code with type annotations. 18 | You can check for typing issues with the python plugin for your IDE, 19 | or on the terminal by installing pyright (`pip install pyright`) 20 | on your machine and then run `pyright` locally. 21 | """ 22 | 23 | summary_ok = """ 24 | ### Pyright: No new issues found :fireworks: 25 | 26 | {} fewer issues compared to the main branch! 27 | """ 28 | 29 | summary = summary_ok.format(diff_n) if accept else summary_issues.format(diff_n) 30 | description = f'Found {abs(diff_n)} fewer issues compared to main' 31 | if diff_n > 0: 32 | description = f'Found {diff_n} more issues compared to main' 33 | set_github_summary_api_envs(accept, description, summary, CONTEXT) 34 | 35 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SOURCEDIR = source 8 | BUILDDIR = build 9 | 10 | # Put it first so that "make" without argument is like "make help". 11 | help: 12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 13 | 14 | .PHONY: help Makefile 15 | 16 | # Catch-all target: route all unknown targets to Sphinx using the new 17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 18 | %: Makefile 19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | To build and check the documentation: 2 | 3 | 4 | 1. `pip install docs/requirements.txt` 5 | 2. `cd docs/` 6 | 3. `make html` 7 | 8 | When adding, removing or renaming a module, create / adapt a stub for autodoc. 9 | To build the docs, your system must also have pandoc installed, (`choco install pandoc` or `apt install pandoc` or `brew install pandoc`) 10 | Have a look at this RST cheatsheet: https://thomas-cokelaer.info/tutorials/sphinx/rest_syntax.html -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx 2 | nbsphinx 3 | nbsphinx-link 4 | ipykernel 5 | -------------------------------------------------------------------------------- /docs/source/TemporalSampleData/meg_sample_data.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/docs/source/TemporalSampleData/meg_sample_data.pkl -------------------------------------------------------------------------------- /docs/source/TemporalSampleData/preproc_mne_sample_data.py: -------------------------------------------------------------------------------- 1 | """ Script to preprocess MEG sample data for Temporal RSA demo notebook 2 | 3 | Use of pickle is not recommended in multi-user environments 4 | """ 5 | import pickle #nosec - ignoring pickle security warning 6 | import mne 7 | from mne.datasets import sample 8 | 9 | data_path = sample.data_path() 10 | 11 | subjects_dir = data_path + '/subjects' 12 | raw_fname = data_path + '/MEG/sample/sample_audvis_raw.fif' 13 | tmin, tmax = -0.200, .7500 14 | event_id = {'Auditory/Left': 1, 'Auditory/Right': 2, 15 | 'Visual/Left': 3, 'Visual/Right': 4} 16 | 17 | raw = mne.io.read_raw_fif(raw_fname, preload=True) 18 | 19 | raw.filter(1, 20) 20 | events = mne.find_events(raw, 'STI 014') 21 | 22 | # Set up pick list: EEG + MEG - bad channels (modify to your needs) 23 | raw.info['bads'] += ['MEG 2443', 'EEG 053'] # bads + 2 more 24 | 25 | # Read epochs 26 | epochs = mne.Epochs(raw, events, event_id, tmin, tmax, proj=True, 27 | picks=('grad', 'eog'), baseline=(None, 0.), preload=True, 28 | reject=dict(grad=4000e-13, eog=150e-6), decim=10) 29 | epochs.pick_types(meg=True, exclude='bads') # remove stim and EOG 30 | 31 | epochs.apply_baseline((-.2,.0)) 32 | 33 | X = epochs.get_data() # MEG signals: n_epochs, n_meg_channels, n_times 34 | y = epochs.events[:, 2] 35 | 36 | pickle.dump({'data': X[:,:,:], 37 | 'times': epochs.times[:], 38 | 'cond_idx': epochs.events[:,2], 39 | 'cond_names': event_id, 40 | 'channel_names': epochs.ch_names}, 41 | open( "meg_sample_data.pkl", "wb" )) 42 | -------------------------------------------------------------------------------- /docs/source/_static/SoftwareFlowChart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/docs/source/_static/SoftwareFlowChart.png -------------------------------------------------------------------------------- /docs/source/_static/rsatoolbox_workflow.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/docs/source/_static/rsatoolbox_workflow.pdf -------------------------------------------------------------------------------- /docs/source/_static/rsatoolbox_workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/docs/source/_static/rsatoolbox_workflow.png -------------------------------------------------------------------------------- /docs/source/datasets.rst: -------------------------------------------------------------------------------- 1 | .. _datasets: 2 | 3 | Defining the data set 4 | ===================== 5 | The first step in an RSA is to bring the data into the correct format. The RSA toolbox uses an own Class, ``rsatoolbox.data.Dataset``. 6 | The main content of such a dataset object is a measurement by channel matrix of measured data. Additionally it allows for descriptor variables 7 | for the measurements, channels and the whole data object, which are added as python dictionaries. 8 | 9 | The simplest method for generating a dataset object is based on a numpy array of data in the right format. Then you can simply call the 10 | `Dataset` constructor to generate the object. For example, the following code creates a dataset with 10 random observations of 6 channels: 11 | 12 | .. code-block:: python 13 | 14 | import numpy, rsatoolbox 15 | data = rsatoolbox.data.Dataset(numpy.random.rand(10, 6)) 16 | 17 | To add descriptors to the dataset, we need to define a dictionary of them with lists with one entry for each measurement of channel. 18 | As an example, the following variation of the code above adds a descriptor which says that the 10 measurements were taken from 5 stimuli 19 | and which ones correspond to which stimulus and adds a label 'l' vs. 'r' for left and right measurement channels: 20 | 21 | .. code-block:: python 22 | 23 | import numpy, rsatoolbox 24 | side = ['l', 'l', 'l', 'r', 'r', 'r'] 25 | stimulus = [0, 1, 2, 3, 4, 0, 1, 2, 3, 4] 26 | data = rsatoolbox.data.Dataset( 27 | numpy.random.rand(10, 6), 28 | channel_descriptors={'side': side}, 29 | obs_descriptors={'stimulus': stimulus}) 30 | 31 | These descriptors are used by donwnstream processing of the data to define how the measurements are combined into RDMs and can be used for 32 | manipulating the data before RDM creation as well. It is thus convenient to add all meta-information you might need to the dataset object. 33 | 34 | To manipulate the datasets, have a look at the functions of the dataset object 35 | ``sort_by``, ``split_channel``, ``split_obs``, ``subset_channel``, ``subset_obs``. 36 | 37 | Datasets can also be created (and converted to) DataFrame objects from the pandas library: 38 | 39 | .. code-block:: python 40 | 41 | df = data_in.to_DataFrame() 42 | data_out = Dataset.from_DataFrame(df) 43 | 44 | The dataset objects can also be saved to hdf5 files using their method ``save`` as in and loaded with the ``rsatoolbox.data.load_dataset`` function: 45 | 46 | .. code-block:: python 47 | 48 | data.save('test.hdf5') 49 | data_loaded = rsatoolbox.data.load_dataset('test.hdf5') 50 | 51 | 52 | .. _TemporalDatasets: 53 | 54 | Temporal data sets 55 | -------------------- 56 | 57 | Datasets with a temporal dimension are represented by the class ``rsatoolbox.data.TemporalDataset``. This class is a subclass of the 58 | ``rsatoolbox.data.Dataset`` class. The main difference is that the TemporalDataset expects ``measurements`` of shape 59 | ``(n_observations, n_channels, n_timepoints)`` and has descriptors for the temporal dimension (``time_descriptor``). 60 | 61 | As an example, we assume to have measured data from 10 trials, each with six EEG channels and a timecourse of 2s 62 | (from -.5 to 1.5 seconds, stimulus onset at 0 seconds). 63 | 64 | 65 | .. code-block:: python 66 | 67 | import numpy, rsatoolbox 68 | 69 | channel_names = ['Oz', 'O1', 'O2', 'PO3', 'PO4', 'POz'] # channel names 70 | stimulus = [0, 1, 2, 3, 4, 0, 1, 2, 3, 4] # stimulus idx, each stimulus was presented twice 71 | 72 | sampling_rate = 30 # in Hz 73 | t = numpy.arange(-.5, 1.5, 1/sampling_rate) # time vector 74 | 75 | n_observations = len(stimulus) 76 | n_channels = len(channel_names) 77 | n_timepoints = len(t) 78 | 79 | measurements = numpy.random.randn(n_observations, n_channels, n_timepoints) # random data 80 | 81 | data = rsatoolbox.data.TemporalDataset( 82 | measurements, 83 | channel_descriptors={'names': channel_names}, 84 | obs_descriptors={'stimulus': stimulus}, 85 | time_descriptors={'time': t} 86 | ) 87 | 88 | Beyond the functions to manipulate the data provided by ``rsatoolbox.data.Dataset``, the ``rsatoolbox.data.TemporalDataset`` class provides the following functions: 89 | ``split_time``, ``subset_time``, ``bin_time``, ``convert_to_dataset``. 90 | -------------------------------------------------------------------------------- /docs/source/demo_annotated_rdm_plot.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../demos/demo_annotated_rdm_plot.ipynb" 3 | } 4 | -------------------------------------------------------------------------------- /docs/source/demo_bootstrap.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../demos/demo_bootstrap.ipynb", 3 | "extra-media": [ 4 | "../../demos/92imageData", 5 | "../../demos/rdms_inferring" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /docs/source/demo_dataset.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../demos/demo_dataset.ipynb", 3 | "extra-media": [ 4 | "../../demos/92imageData" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /docs/source/demo_dissimilarities.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../demos/demo_dissimilarities.ipynb", 3 | "extra-media": [ 4 | "../../demos/92imageData" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /docs/source/demo_eeg.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../demos/demo_eeg.ipynb", 3 | "extra-media": [ 4 | "../../demos/demo_eeg_data" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /docs/source/demo_flexible_models.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../demos/demo_flexible_models.ipynb" 3 | } 4 | -------------------------------------------------------------------------------- /docs/source/demo_fmri_files/mur32_stims.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/docs/source/demo_fmri_files/mur32_stims.png -------------------------------------------------------------------------------- /docs/source/demo_fmri_files/mur32_tasks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/docs/source/demo_fmri_files/mur32_tasks.png -------------------------------------------------------------------------------- /docs/source/demo_fmri_nilearn.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../demos/demo_fmri_nilearn.ipynb", 3 | "extra-media": [ 4 | "../../demos/demo_fmri_files" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /docs/source/demo_fmri_patterns.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../demos/demo_fmri_patterns.ipynb", 3 | "extra-media": [ 4 | "../../demos/demo_fmri_files" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /docs/source/demo_fmri_spm.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../demos/demo_fmri_spm.ipynb", 3 | "extra-media": [ 4 | "../../demos/demo_fmri_files" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /docs/source/demo_meg_mne.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../demos/demo_meg_mne.ipynb" 3 | } 4 | -------------------------------------------------------------------------------- /docs/source/demo_rdm_vis.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../demos/demo_rdm_visualisation_92images.ipynb", 3 | "extra-media": [ 4 | "../../demos/92imageData" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /docs/source/demo_riemannian.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../demos/demo_riemannian.ipynb" 3 | } 4 | -------------------------------------------------------------------------------- /docs/source/demo_searchlight.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../demos/demo_searchlight_volume.ipynb" 3 | } 4 | -------------------------------------------------------------------------------- /docs/source/demo_spikes.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../demos/demo_spikes.ipynb" 3 | } 4 | -------------------------------------------------------------------------------- /docs/source/demo_temporal.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../demos/demo_temporal.ipynb", 3 | "extra-media": [ 4 | "../../demos/TemporalSampleData" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /docs/source/demo_unbalanced.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../demos/demo_unbalanced.ipynb" 3 | } 4 | -------------------------------------------------------------------------------- /docs/source/demos.rst: -------------------------------------------------------------------------------- 1 | .. _demos: 2 | 3 | Demos 4 | ===== 5 | 6 | 7 | Basics 8 | ****** 9 | 10 | * :doc:`/demo_bootstrap` 11 | * :doc:`/demo_dataset` 12 | * :doc:`/demo_dissimilarities` 13 | * :doc:`/demo_rdm_vis` 14 | * :doc:`/demo_annotated_rdm_plot` 15 | 16 | By Neuroimaging Modality 17 | ************************ 18 | 19 | * :doc:`/demo_temporal` 20 | * :doc:`/demo_meg_mne` 21 | * :doc:`/demo_eeg` 22 | * :doc:`/demo_fmri_spm` 23 | * :doc:`/demo_fmri_nilearn` 24 | * :doc:`/demo_fmri_patterns` 25 | * :doc:`/demo_searchlight` 26 | * :doc:`/demo_spikes` 27 | 28 | Miscellaneous 29 | ************* 30 | 31 | * :doc:`/rescale_partials` 32 | * :doc:`/demo_unbalanced` 33 | * :doc:`/demo_flexible_models` 34 | * :doc:`/demo_riemannian` 35 | -------------------------------------------------------------------------------- /docs/source/getting_started.rst: -------------------------------------------------------------------------------- 1 | .. _getting_started: 2 | 3 | Getting started 4 | =============== 5 | 6 | To install the latest release of rsatoolbox: 7 | 8 | .. code-block:: sh 9 | 10 | pip install rsatoolbox 11 | 12 | 13 | To get the bleeding edge or "pre-release" version: 14 | 15 | .. code-block:: sh 16 | 17 | pip install --pre rsatoolbox 18 | 19 | 20 | Or if you're using a Conda environment: 21 | 22 | .. code-block:: sh 23 | 24 | conda install -c conda-forge rsatoolbox 25 | 26 | 27 | To use rsatoolbox: 28 | 29 | .. code-block:: python 30 | 31 | import numpy, rsatoolbox 32 | data = rsatoolbox.data.Dataset(numpy.random.rand(10, 5)) 33 | rdms = rsatoolbox.rdm.calc_rdm(data) 34 | rsatoolbox.vis.show_rdm(rdms) 35 | 36 | Also make sure your setup meets the requirements to run the toolbox with the relevant toolboxes installed (see requirements.txt). 37 | 38 | As in introduction, we recommend having a look at the Jupyter notebooks in ``demos``. 39 | 40 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | **************************************************************** 2 | Python Representational Similarity Analysis (rsatoolbox) toolbox 3 | **************************************************************** 4 | 5 | The rsatoolbox is developed through a community effort by the labs of Nikolaus Kriegeskorte, Jörn Diedrichsen, Marieke Mur and Ian Charest. It was conceived during the RSA retreat 2019 in Blue Mountains, Ontario. The toolbox replaces the 2013 matlab version the toolbox of rsatoolbox previously at ilogue/rsatoolbox and reflects many of the new methodological developements. 6 | 7 | 8 | Documentation 9 | ============= 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | :caption: Contents: 14 | 15 | overview.rst 16 | getting_started.rst 17 | datasets.rst 18 | distances.rst 19 | operations.rst 20 | comparing.rst 21 | model_specification.rst 22 | model_fitting.rst 23 | inference.rst 24 | visualization.rst 25 | literature_cited.rst 26 | demos.rst 27 | rsatoolbox.rst 28 | 29 | Indices and tables 30 | ================== 31 | 32 | * :ref:`genindex` 33 | * :ref:`modindex` 34 | * :ref:`search` 35 | -------------------------------------------------------------------------------- /docs/source/literature_cited.rst: -------------------------------------------------------------------------------- 1 | .. _literature_cited: 2 | 3 | References 4 | ---------- 5 | 6 | * Cai, M.B., Schuck, N.W., Pillow, J., and Niv, Y. (2016). A Bayesian method for reducing bias in neural representational similarity analysis. In Advances in Neural Information Processing Systems, pp. 4952–4960. 7 | 8 | * Diedrichsen, J., Ridgway, G.R., Friston, K.J., and Wiestler, T. (2011). Comparing the similarity and spatial structure of neural representations: a pattern-component model. Neuroimage 55, 1665–1678. 9 | 10 | * Diedrichsen, J. (2019). Representational models and the feature fallacy. In The Cognitive Neurosciences, M.S. Gazzaniga, G.R. Mangun, and D. Poepple, eds. (Cambridge, MA: MIT Press), p. 11 | 12 | * Diedrichsen, J., and Kriegeskorte, N. (2017). Representational models: A common framework for understanding encoding, pattern-component, and representational-similarity analysis. PLOS Comput. Biol. 13, e1005508. 13 | 14 | * Diedrichsen, J., Yokoi, A., and Arbuckle, S.A. (2018). Pattern component modeling: A flexible approach for understanding the representational structure of brain activity patterns. Neuroimage 180, 119–133. 15 | 16 | * Ejaz, N., Hamada, M., and Diedrichsen, J. (2015). Hand use predicts the structure of representations in sensorimotor cortex. Nat Neurosci 18, 1034–1040. 17 | 18 | * Khaligh-Razavi, S.M., and Kriegeskorte, N. (2014). Deep supervised, but not unsupervised, models may explain IT cortical representation. PLoS Comput Biol 10, e1003915. 19 | 20 | * Kriegeskorte, N., and Diedrichsen, J. (2016). Inferring brain-computational mechanisms with models of activity measurements. Philos. Trans. R. Soc. B Biol. Sci. 371. 21 | 22 | * Kriegeskorte, N., and Diedrichsen, J. (2019). Peeling the Onion of Brain Representations. Annu. Rev. Neurosci. 42, 407–432. 23 | 24 | * Kriegeskorte, N., Mur, M., and Bandettini, P. (2008). Representational similarity analysis - connecting the branches of systems neuroscience. Front Syst Neurosci 2, 4. 25 | 26 | * Kriegeskorte, N., Mur, M., Ruff, D.A., Kiani, R., Bodurka, J., Esteky, H., Tanaka, K., and Bandettini, P.A. (2008). Matching categorical object representations in inferior temporal cortex of man and monkey. Neuron 60, 1126–1141. 27 | 28 | * Lin, B., & Kriegeskorte, N. (2023). The Topology and Geometry of Neural Representations. arXiv preprint arXiv:2309.11028. 29 | 30 | * Naselaris, T., Kay, K.N., Nishimoto, S., and Gallant, J.L. (2011). Encoding and decoding in fMRI. Neuroimage 56, 400–410. 31 | 32 | * Nili, H., Wingfield, C., Walther, A., Su, L., Marslen-Wilson, W., and Kriegeskorte, N. (2014). A toolbox for representational similarity analysis. PLoS Comput Biol 10, e1003553. 33 | 34 | * Walther, A., Nili, H., Ejaz, N., Alink, A., Kriegeskorte, N., and Diedrichsen, J. (2016). Reliability of dissimilarity measures for multi-voxel pattern analysis. Neuroimage 137, 188–200. 35 | 36 | * Yokoi, A., and Diedrichsen, J. (2019). Neural Organization of Hierarchical Motor Sequence Representations in the Human Neocortex. Neuron. 37 | 38 | * Yokoi, A., Arbuckle, S.A., and Diedrichsen, J. (2018). The Role of Human Primary Motor Cortex in the Production of Skilled Finger Sequences. J. Neurosci. 38, 1430–1442. 39 | -------------------------------------------------------------------------------- /docs/source/model_fitting.rst: -------------------------------------------------------------------------------- 1 | .. _model_fitting: 2 | 3 | Model Fitting 4 | ============= 5 | 6 | While fixed models (i.e. models that predict a fixed RDM) are important, many models have free parameters that can be fit to the data. All models come equipped with a default fitting function, 7 | which is called using the ``fit`` function of the model. This function takes a data RDMs object as input and returns a parameter value. 8 | 9 | Individual vs. group fits 10 | ------------------------- 11 | In the RSA toolbox, the models will always be fit to all RDMs that are passed to the fitting routine. That is, if the different RDMs are calculated on different subjects, 12 | the fit will be a group fit, with one set of parameters for the whole group. If you require individual fits, you need to loop over participants pass each RDM seperately to the fitting function. 13 | 14 | Continuous vs. rank-based evaluation metrics 15 | -------------------------------------------- 16 | Most model fitting techniques require the evaluation metric to be differentiable with regard to the model parameters and this is true for most 17 | RDM comparison metrics provided by the toolbox. However, rank based evaluations (i.e. Spearman $\rho$ and Kendall's $\tau$) are not differentiable 18 | and thus often lead to problems for optimization. Thus, we generally do not recommend fitting models using these criteria. 19 | 20 | Two model types with their special fitting methods are exempt from this recommendation: :ref:`Selection models ` and 21 | :ref:`Interpolation models `, which use a simple selection strategy and a derivative free bisection method for finding 22 | the best models respectively and can thus deal with discontinuous objective functions. 23 | 24 | Fitting algorithms 25 | ------------------ 26 | 27 | Different fitting methods can be found in ``rsatoolbox.model.fitter`` module. 28 | To use them you can either apply them manually you can set them as the default of a model by settings its ``default_fitter`` property. 29 | 30 | Unconstrained optimization 31 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ 32 | The function ``rsatoolbox.fitter.fit_optimize`` uses a general purpose optimization method. It simply maximizes the criterion using either positive or negative ``theta`` weights. 33 | 34 | Non-negative optimization 35 | ^^^^^^^^^^^^^^^^^^^^^^^^^ 36 | The function ``rsatoolbox.fitter.fit_optimize_positive`` maximizes the fit, but constraints the parameter values to be non-negative. 37 | This is the most appropriate fitting method for weighting RDMs, because weighting features in an underlying representation can only add to 38 | the distances between points not subtract from them. 39 | 40 | Regression-based methods 41 | ^^^^^^^^^^^^^^^^^^^^^^^^ 42 | For correlation and cosine distances and weighted models, ``rsatoolbox.fitter.fit_regress`` provides more reliable and faster solutions 43 | based on linear algebra. Being specialized this method works only for the 'cosine', 'corr', 'cosine_cov' and 'corr_cov' 44 | comparison methods. 45 | 46 | Analogously, ``rsatoolbox.fitter.fit_regress_nn`` provides a method for non-negative fits of such models. 47 | 48 | 49 | Calling the fitting function directly 50 | ------------------------------------- 51 | .. _modelfit: 52 | 53 | All fitting methods take the following inputs: A ``model`` object, ``data``, a data RDMs object to fit to, ``method``, a string that defines which similarity 54 | measure is optimized, and a ``pattern_idx``, ``pattern_descriptor`` combination that defines which subset of the RDM the provided 55 | data RDMs correspond to. Additionally, they might take a ``ridge_weight``, which adds a penalty on the squared norm of the weights vector to the objective 56 | and ``sigma_k``, which is required for adjusting the whitened correlation and cosine similarity measures to dependent measurements. 57 | 58 | For simple fitting of a single model simply apply the fit function to the model and data as shown in the following example: 59 | 60 | .. code-block:: python 61 | 62 | import rsatoolbox 63 | # generate 2 random model RDMs of 10 conditions 64 | model_features = [rsatoolbox.data.Dataset(np.random.rand(10, 7)) for i in range(2)] 65 | model_rdms = rsatoolbox.rdm.calc_rdm(model_features) 66 | model = rsatoolbox.model.ModelWeighted('test', model_rdms) 67 | 68 | # generate 5 random data RDMs of 10 conditions 69 | data = [rsatoolbox.data.Dataset(np.random.rand(10, 7)) for i in range(5)] 70 | data_rdms = rsatoolbox.rdm.calc_rdm(data) 71 | 72 | # fit model to group data to maximize cosine similarity using its default fitter 73 | theta = model.fit(data_rdms, method='cosine') 74 | 75 | # explicitly use the fit_optimize function to do the fit 76 | theta2 = rsatoolbox.model.fitter.fit_optimize(model, data_rdms, method='cosine') 77 | 78 | Using the Fitter object 79 | ----------------------- 80 | 81 | To provide an object, which fixes some parameters of the fitting function, rsatoolbox provides the fitter object. This type of object 82 | is defined as ``rsatoolbox.model.fitter.Fitter`` and takes a fitting function and additional keyword arguments as inputs. 83 | It then behaves as the original fitting function, with defaults changed to given keyword arguments. 84 | 85 | To create a fitting function which sets the ``ridge_weight`` to 1 by default you could use the following code for example: 86 | 87 | .. code-block:: python 88 | 89 | # fix some parameter of the function by using a fitter object: 90 | fitter = rsatoolbox.model.fitter.Fitter(rsatoolbox.model.fit_optimize, ridge_weight=1) 91 | theta3 = fitter(model, data_rdms, method='cosine') 92 | 93 | Observe that this does indeed slightly change the fitted parameters compared to ``theta`` and ``theta2`` 94 | 95 | Both the fitting functions themselves and the fitter objects can be used as inputs to the crossvalidation and bootstrap-crossvalidation 96 | methods to change how models are fit to data. 97 | -------------------------------------------------------------------------------- /docs/source/model_specification.rst: -------------------------------------------------------------------------------- 1 | .. _model: 2 | 3 | Model Specification 4 | =================== 5 | 6 | To run a RSA analysis, we need to define the models to be evaluated. For this the rsatoolbox provides a class with subclasses for different 7 | model types. Any type of model needs to define three functions ``predict``, ``predict_rdm``, and ``fit``. The ``predict`` and ``predict_rdm`` functions 8 | return the prediction of the model, taking a model parameter vector ``theta`` as input. ``predict`` returns a vectorized numpy array format for efficient computation, ``predict_rdm`` returns a RDMs object. For flexible models, the ``fit`` function estimates the parameter vector (``theta``) based on some data RDMs (see :ref:`model fitting `). 9 | 10 | Model types are defined in ``rsatoolbox.model``. Most of them can be initialized with a name and an RDMs object, which defines the RDM(s), which 11 | are combined into a prediction. 12 | 13 | Fixed models 14 | ------------ 15 | .. _ModelFixed: 16 | 17 | The simplest type of model are *fixed models*, which simply predict a single RDM and don not have any parameters to be fit. They are available 18 | as ``rsatoolbox.model.ModelFixed`` which can be created based on a single RDM. To generate a model for a RDM saved in rdm with name 'test' 19 | could use the following code: 20 | 21 | 22 | .. code-block:: python 23 | 24 | import rsatoolbox 25 | model = rsatoolbox.model.ModelFixed('test', rdm) 26 | 27 | 28 | To extract the prediction of this model, which will always be the RDM provided at its creation, you can use its ``predict`` and ``predict_rdm`` 29 | functions. The ``fit`` function dos nothing. 30 | 31 | .. code-block:: python 32 | 33 | pred = model.predict() # returns a numpy vectorized format 34 | pred_rdm = model.predict_rdm() # returns a RDMs object 35 | 36 | These methods also take a ``theta`` argument, which corresponds to the parameter vector of the model. For fixed models, this input is ignored however. 37 | 38 | Weighted models 39 | --------------- 40 | .. _ModelWeighted: 41 | 42 | The first type of flexible models we handle are *weighted models*, which are available as ``rsatoolbox.model.ModelWeighted``. These models 43 | predict the RDM as a weighted sum of a set of given RDMs. The typical use case for these models is feature weighting, i.e. when a theory 44 | contains multiple features or parts which contribute to the measured dissimilarities, whose relative weighting is not known a priori. 45 | Typical sources for the RDMs are the feature dimensions, sets of them like DNN layers, or the RDMs of completely separate model parts, 46 | which are thought to be mixed by the measurement. 47 | 48 | To generate a model for a set of RDMs saved in rdm with name 'test' could use the following code: 49 | 50 | .. code-block:: python 51 | 52 | import rsatoolbox 53 | model = rsatoolbox.model.ModelWeighted('test', rdms) 54 | 55 | The simplest method for fitting this kind of model is an unconstrained linear fit, which maximizes the chosen RDM similarity metric, 56 | allowing both negative and positive weights for the RDM. More correctly, the weights for each feature component should be constrained 57 | to be postive. See :ref:`model fitting ` for more information. 58 | 59 | Fitting this type of model generally works better with continuous RDM comparison measures than with the rank correlations. 60 | 61 | 62 | Selection models 63 | ---------------- 64 | .. _ModelSelect: 65 | 66 | *Selection models* are models which predict that the true RDM is one of a set of given RDMs. They are available as ``rsatoolbox.model.ModelSelect``. 67 | Fitting this model is simply done by choosing the RDM, which is closest to the training data RDM as implemented in ``rsatoolbox.model.fit_select``. 68 | 69 | If there are discrete different versions of the model, which represent the same theory this represents this uncertainty best. This model can also 70 | be used to represent any other uncertainty about the RDM approximately. To do so, sample the range of possible RDMs and let a selection model 71 | choose the best setting for you. 72 | 73 | Interpolation models 74 | -------------------- 75 | .. _ModelInterpolate: 76 | 77 | *Interpolation models* predict that the RDM is a linear interpolation between two consecutive RDMs in the list given to the model. They are available as ``rsatoolbox.model.ModelInterpolate``. 78 | Fitting this model is done by doing a bisection optimization on each line segment as implemented in ``rsatoolbox.model.fit_interpolate``. 79 | 80 | These models' primary use is to represent nonlinear effects of a single changed parameter. When the RDMs given to the model are generated 81 | by computing a model RDM under different settings of the parameter the interpolation effectively implements an approximation to the nonlinear 82 | 1D manifold of RDMs that the model can produce under arbitrary settings of the parameter. 83 | 84 | 85 | Noise ceiling models 86 | -------------------- 87 | .. _Model_nc: 88 | 89 | The computation of a noise ceiling is often conceptualized as evaluating a model, which can arbitrarily set all distances of the RDM. 90 | As the ``rsatoolbox`` currently computes the noise ceiling using analytic methods and does not explicitly create this model, it currently does not 91 | provide an implementation of this maximally flexible model. 92 | -------------------------------------------------------------------------------- /docs/source/operations.rst: -------------------------------------------------------------------------------- 1 | .. _operations: 2 | 3 | Operations on RDMs 4 | ================== 5 | 6 | The rsatoolbox provides various ways to manipulate RDMs objects. 7 | 8 | 9 | Matrices and Vectors 10 | -------------------- 11 | To access the underlying RDMs in vectorized or matrix form you can use 12 | the methods ``get_vectors`` and ``get_matrices``. The RDMs are returned 13 | as 2D or 3D numpy arrays respectively. 14 | 15 | Subset and Split 16 | ---------------- 17 | There are 2x2 methods for extracting parts of the RDMs object: 18 | ``subset``, ``subsample``, ``subset_pattern``, and ``subsample_pattern``. 19 | All four methods take the name of a descriptor and the values to be selected as input. 20 | 21 | ``subset`` and ``subsample`` select some of the RDMs, ``subset_pattern`` and ``subsample_pattern`` 22 | select some of the patterns/conditions. The ``subset`` variant ignores repetitions in the allowed set, 23 | the subsample function uses as many repetitions as provided in the set. 24 | 25 | For example ``rdms.subset('index', [0,2,2])`` selects only the rdms number 0 and 2 26 | and will thus contain 2 RDMs, while ``rdms.subsample('index', [0,2,2])`` repeats rdm number 2 and thus contains 3 RDMs. 27 | 28 | When patterns are repeated in ``subsample_pattern`` the resulting RDM will contain entries 29 | that correspond to the similarity of a condition to itself. These are set to ``NaN``. 30 | 31 | Concatenate 32 | ----------- 33 | For concatenating RDMs there is a function called ``rsatoolbox.rdm.concat``, 34 | which takes a list of RDMs as input and returns a RDMs object containing all RDMs. 35 | 36 | Also RDMs objects have a method ``append``, which allows appending a single other RDMs object. 37 | 38 | Only RDMs objects with an equal number of conditions can be concatenated. 39 | 40 | 41 | Missing data 42 | ------------ 43 | 44 | If you have several RDMs, but they don't all cover all conditions, 45 | you may want to expand them into larger RDMs with missing values, 46 | so that you can compare them or perform other operations on them 47 | that require them to have the same dimensions. This can be achieved 48 | with the :meth:`~rsatoolbox.rdm.combine.from_partials` function: 49 | 50 | .. code-block:: python 51 | 52 | from numpy import array 53 | from rsatoolbox.rdm.rdms import RDMs 54 | from rsatoolbox.rdm.combine import from_partials 55 | rdms1 = RDMs( 56 | array(1, 2, 3), 57 | pattern_descriptors={'conds': ['a', 'b', 'c']} 58 | ) 59 | rdms2 = RDMs( 60 | array(6, 7, 8), 61 | pattern_descriptors={'conds': ['b', 'c', 'd']} 62 | ) 63 | partial_rdms = from_partials([rdms1, rdms2]) 64 | partial_rdms.n_conds ## this is now 4 65 | 66 | Sort and reorder 67 | ---------------- 68 | To change the order of conditions/patterns in the RDMs object there are two functions 69 | ``reorder`` and ``sort_by``. 70 | 71 | ``reorder`` expects a new order of conditions/patterns, e.g. ``rdms.reorder([1,2,0])`` 72 | will change the order of conditions, moving the first condition to the end. 73 | 74 | ``sort_by`` sorts conditions according to a descriptor, e.g. ``rdms.sort_by(condition='alpha')`` 75 | sorts the conditions according to the 'condition' descriptor alphanumerically. 76 | 77 | **Caution:** Both ``reorder`` and ``sort_by`` operate in place, i.e. the RDMs object rdms is changed by calling them! 78 | 79 | 80 | Transformations 81 | --------------- 82 | To transform all RDM entries by a function ``rsatoolbox`` offers specific functions 83 | for the most common transformations and a general ``transform`` function, which takes the 84 | function to be applied as input. These functions are available in ``rsatoolbox.rdm`` 85 | 86 | The specific transformations are: ``rank_transform``, ``positive_transform``, ``sqrt_transform`` and ``minmax_transform``. 87 | They take only a RDMs object as input and compute a rank transform, set all negative values to 0, compute a square root of each value respectively, or normalize them into a range of 0 to 1. 88 | 89 | There are also and two important transformations as part of the Topological RSA framework, ``geotopological_transform`` and ``geodesic_transform``. These RDM transformations emphasize both the geometric and topological properties of the representations, in terms of neighborhood information, global structure, as well as graph theoretical measures. For more information regarding the Topological RSA framework, please refer to "The Topology and Geometry of Neural Representations" by Lin, B., & Kriegeskorte, N. (2023, arXiv:2309.11028). 90 | 91 | For example: 92 | 93 | .. code-block:: python 94 | 95 | rdms_rank = rsatoolbox.rdm.rank_transform(rdms) 96 | 97 | will produce a rank transformed version of the data in ``rdms`` 98 | 99 | The general ``rsatoolbox.rdm.transform`` function takes a function to be applied as an input and can thus 100 | implement any transform on the RDM. 101 | 102 | To compute the square of each RDM entry you could use the following code for example: 103 | 104 | .. code-block:: python 105 | 106 | def square(x): 107 | return x ** 2 108 | rdms_square = rsatoolbox.rdm.transform(rdms, square) 109 | 110 | The function you pass must take a 2D numpy array of vectorized RDMs as input and return an array of equal shape. 111 | -------------------------------------------------------------------------------- /docs/source/overview.rst: -------------------------------------------------------------------------------- 1 | .. _overview: 2 | 3 | Toolbox overview 4 | ================ 5 | 6 | 7 | .. figure:: _static/rsatoolbox_workflow.png 8 | 9 | *Overview over subpackages and work flow in rsatoolbox.* 10 | 11 | The Figure above shows the most important subpackages (blue), classes (gray), modules (yellow) and auxillary materials (orange) of the RSA toolbox. 12 | A common use of the RSA toolbox involves the following steps: 13 | 14 | * Extract the data that you want to analyzed. The data is stored in the format of a ``rsatoolbox.data.Dataset`` object, see :ref:`datasets`. 15 | * Use functions from the module ``rsatoolbox.rdm.calc`` to calculate a RDM from the data, with many options for different dissimilarity measures, see :ref:`distances`. 16 | * Define RSA models by defining objects of the ``rsatoolbox.model`` class. For information on the different model types, see :ref:`model`. 17 | * Models can be fitted to the data and then evaluated using the ``rsatoolbox.inference.evaluate`` module. Results of the evaluation is stored in a ``rsatoolbox.inference.results`` object. 18 | * Dataset, RDMs, Models, and results can be visualized using the ``rsatoolbox.vis`` subpackage. 19 | * For simulation of artificial data sets, you can used the ``rsatoolbox.sim.simulation`` module. 20 | 21 | For an example of a complete workflow, see the "getting started with RSAtoolbox" Notebook, :ref:`demos`. -------------------------------------------------------------------------------- /docs/source/rescale_partials.rst: -------------------------------------------------------------------------------- 1 | .. _rescale_partials: 2 | 3 | How to get an average RDM from a stack of partial RDMs 4 | ====================================================== 5 | 6 | Here's an example on how to combine RDMs that do not cover all conditions. 7 | One example of this is the trials of the Multiple Arrangements / inverse MDS task. 8 | Another is where each participant only takes part in a subset of conditions. 9 | 10 | .. code-block:: python 11 | 12 | from numpy import array 13 | from rsatoolbox.rdm.rdms import RDMs 14 | from rsatoolbox.rdm.combine import from_partials, rescale 15 | rdms1 = RDMs( 16 | array(1, 2, 3), 17 | pattern_descriptors={'conds': ['a', 'b', 'c']} 18 | ) 19 | rdms2 = RDMs( 20 | array(6, 7, 8), 21 | pattern_descriptors={'conds': ['b', 'c', 'd']} 22 | ) 23 | ## first, all rdms should have the same number of conditions, with NaNs for missing data 24 | partials = from_partials([rdms1, rdms2]) 25 | ## then we rescale/align these based on pairs in common (put them in the same space) 26 | rescaledPartials = rescale(partials, method='evidence') 27 | ## then we can take a weighted average: 28 | meanRDM = rescaledPartials.mean(weights='rescalingWeights') 29 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.data.base.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.data.base module 2 | =========================== 3 | 4 | .. automodule:: rsatoolbox.data.base 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.data.computations.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.data.computations module 2 | =================================== 3 | 4 | .. automodule:: rsatoolbox.data.computations 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.data.dataset.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.data.dataset module 2 | ============================== 3 | 4 | .. automodule:: rsatoolbox.data.dataset 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.data.noise.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.data.noise module 2 | ============================ 3 | 4 | .. automodule:: rsatoolbox.data.noise 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.data.ops.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.data.ops module 2 | ========================== 3 | 4 | .. automodule:: rsatoolbox.data.ops 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.data.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.data package 2 | ======================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | rsatoolbox.data.base 11 | rsatoolbox.data.computations 12 | rsatoolbox.data.dataset 13 | rsatoolbox.data.noise 14 | rsatoolbox.data.ops 15 | 16 | Module contents 17 | --------------- 18 | 19 | .. automodule:: rsatoolbox.data 20 | :members: 21 | :undoc-members: 22 | :show-inheritance: 23 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.inference.boot_testset.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.inference.boot\_testset module 2 | ========================================= 3 | 4 | .. automodule:: rsatoolbox.inference.boot_testset 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.inference.bootstrap.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.inference.bootstrap module 2 | ===================================== 3 | 4 | .. automodule:: rsatoolbox.inference.bootstrap 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.inference.crossvalsets.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.inference.crossvalsets module 2 | ======================================== 3 | 4 | .. automodule:: rsatoolbox.inference.crossvalsets 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.inference.evaluate.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.inference.evaluate module 2 | ==================================== 3 | 4 | .. automodule:: rsatoolbox.inference.evaluate 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.inference.noise_ceiling.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.inference.noise\_ceiling module 2 | ========================================== 3 | 4 | .. automodule:: rsatoolbox.inference.noise_ceiling 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.inference.result.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.inference.result module 2 | ================================== 3 | 4 | .. automodule:: rsatoolbox.inference.result 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.inference.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.inference package 2 | ============================ 3 | 4 | Submodules 5 | ---------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | rsatoolbox.inference.boot_testset 11 | rsatoolbox.inference.bootstrap 12 | rsatoolbox.inference.crossvalsets 13 | rsatoolbox.inference.evaluate 14 | rsatoolbox.inference.noise_ceiling 15 | rsatoolbox.inference.result 16 | 17 | Module contents 18 | --------------- 19 | 20 | .. automodule:: rsatoolbox.inference 21 | :members: 22 | :undoc-members: 23 | :show-inheritance: 24 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.io.bids.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.io.bids module 2 | ========================= 3 | 4 | .. automodule:: rsatoolbox.io.bids 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.io.fmriprep.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.io.fmriprep module 2 | ============================= 3 | 4 | .. automodule:: rsatoolbox.io.fmriprep 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.io.hdf5.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.io.hdf5 module 2 | ============================ 3 | 4 | .. automodule:: rsatoolbox.io.hdf5 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.io.meadows.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.io.meadows module 2 | ============================ 3 | 4 | .. automodule:: rsatoolbox.io.meadows 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.io.mne.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.io.mne module 2 | ======================== 3 | 4 | .. automodule:: rsatoolbox.io.mne 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.io.optional.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.io.optional module 2 | ============================= 3 | 4 | .. automodule:: rsatoolbox.io.optional 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.io.pandas.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.io.pandas module 2 | ============================ 3 | 4 | .. automodule:: rsatoolbox.io.pandas 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.io.pkl.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.io.pkl module 2 | ============================ 3 | 4 | .. automodule:: rsatoolbox.io.pkl 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.io.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.io package 2 | ===================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | rsatoolbox.io.bids 11 | rsatoolbox.io.fmriprep 12 | rsatoolbox.io.hdf5 13 | rsatoolbox.io.meadows 14 | rsatoolbox.io.mne 15 | rsatoolbox.io.optional 16 | rsatoolbox.io.pandas 17 | rsatoolbox.io.pkl 18 | rsatoolbox.io.spm 19 | 20 | Module contents 21 | --------------- 22 | 23 | .. automodule:: rsatoolbox.io 24 | :members: 25 | :undoc-members: 26 | :show-inheritance: 27 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.io.spm.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.io.spm module 2 | ======================== 3 | 4 | .. automodule:: rsatoolbox.io.spm 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.model.fitter.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.model.fitter module 2 | ============================== 3 | 4 | .. automodule:: rsatoolbox.model.fitter 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.model.model.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.model.model module 2 | ============================= 3 | 4 | .. automodule:: rsatoolbox.model.model 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.model.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.model package 2 | ======================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | rsatoolbox.model.fitter 11 | rsatoolbox.model.model 12 | 13 | Module contents 14 | --------------- 15 | 16 | .. automodule:: rsatoolbox.model 17 | :members: 18 | :undoc-members: 19 | :show-inheritance: 20 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.rdm.calc.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.rdm.calc module 2 | ========================== 3 | 4 | .. automodule:: rsatoolbox.rdm.calc 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.rdm.calc_unbalanced.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.rdm.calc\_unbalanced module 2 | ====================================== 3 | 4 | .. automodule:: rsatoolbox.rdm.calc_unbalanced 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.rdm.combine.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.rdm.combine module 2 | ============================= 3 | 4 | .. automodule:: rsatoolbox.rdm.combine 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.rdm.compare.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.rdm.compare module 2 | ============================= 3 | 4 | .. automodule:: rsatoolbox.rdm.compare 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.rdm.pairs.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.rdm.pairs module 2 | =========================== 3 | 4 | .. automodule:: rsatoolbox.rdm.pairs 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.rdm.rdms.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.rdm.rdms module 2 | ========================== 3 | 4 | .. automodule:: rsatoolbox.rdm.rdms 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.rdm.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.rdm package 2 | ====================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | rsatoolbox.rdm.calc 11 | rsatoolbox.rdm.calc_unbalanced 12 | rsatoolbox.rdm.combine 13 | rsatoolbox.rdm.compare 14 | rsatoolbox.rdm.rdms 15 | rsatoolbox.rdm.transform 16 | rsatoolbox.rdm.pairs 17 | 18 | Module contents 19 | --------------- 20 | 21 | .. automodule:: rsatoolbox.rdm 22 | :members: 23 | :undoc-members: 24 | :show-inheritance: 25 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.rdm.transform.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.rdm.transform module 2 | =============================== 3 | 4 | .. automodule:: rsatoolbox.rdm.transform 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox reference 2 | ==================== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | rsatoolbox.data 8 | rsatoolbox.inference 9 | rsatoolbox.io 10 | rsatoolbox.model 11 | rsatoolbox.rdm 12 | rsatoolbox.simulation 13 | rsatoolbox.util 14 | rsatoolbox.vis 15 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.simulation.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.simulation package 2 | ============================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | rsatoolbox.simulation.sim 11 | 12 | Module contents 13 | --------------- 14 | 15 | .. automodule:: rsatoolbox.simulation 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.simulation.sim.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.simulation.sim module 2 | ================================ 3 | 4 | .. automodule:: rsatoolbox.simulation.sim 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.util.data_utils.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.util.data\_utils module 2 | ================================== 3 | 4 | .. automodule:: rsatoolbox.util.data_utils 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.util.descriptor_utils.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.util.descriptor\_utils module 2 | ======================================== 3 | 4 | .. automodule:: rsatoolbox.util.descriptor_utils 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.util.file_io.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.util.file\_io module 2 | =============================== 3 | 4 | .. automodule:: rsatoolbox.util.file_io 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.util.inference_util.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.util.inference\_util module 2 | ====================================== 3 | 4 | .. automodule:: rsatoolbox.util.inference_util 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.util.matrix.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.util.matrix module 2 | ============================= 3 | 4 | .. automodule:: rsatoolbox.util.matrix 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.util.pooling.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.util.pooling module 2 | ============================== 3 | 4 | .. automodule:: rsatoolbox.util.pooling 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.util.rdm_utils.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.util.rdm\_utils module 2 | ================================= 3 | 4 | .. automodule:: rsatoolbox.util.rdm_utils 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.util.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.util package 2 | ======================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | rsatoolbox.util.data_utils 11 | rsatoolbox.util.descriptor_utils 12 | rsatoolbox.util.file_io 13 | rsatoolbox.util.inference_util 14 | rsatoolbox.util.matrix 15 | rsatoolbox.util.pooling 16 | rsatoolbox.util.rdm_utils 17 | rsatoolbox.util.searchlight 18 | rsatoolbox.util.vis_utils 19 | 20 | Module contents 21 | --------------- 22 | 23 | .. automodule:: rsatoolbox.util 24 | :members: 25 | :undoc-members: 26 | :show-inheritance: 27 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.util.searchlight.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.util.searchlight module 2 | ================================== 3 | 4 | .. automodule:: rsatoolbox.util.searchlight 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.util.vis_utils.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.util.vis\_utils module 2 | ================================= 3 | 4 | .. automodule:: rsatoolbox.util.vis_utils 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.vis.colors.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.vis.colors module 2 | ============================ 3 | 4 | .. automodule:: rsatoolbox.vis.colors 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.vis.icon.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.vis.icon module 2 | ========================== 3 | 4 | .. automodule:: rsatoolbox.vis.icon 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.vis.model_plot.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.vis.model\_plot module 2 | ================================= 3 | 4 | .. automodule:: rsatoolbox.vis.model_plot 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.vis.rdm_plot.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.vis.rdm\_plot module 2 | =============================== 3 | 4 | .. automodule:: rsatoolbox.vis.rdm_plot 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.vis.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.vis package 2 | ====================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | rsatoolbox.vis.colors 11 | rsatoolbox.vis.icon 12 | rsatoolbox.vis.model_plot 13 | rsatoolbox.vis.rdm_plot 14 | rsatoolbox.vis.scatter_plot 15 | rsatoolbox.vis.timecourse 16 | 17 | Module contents 18 | --------------- 19 | 20 | .. automodule:: rsatoolbox.vis 21 | :members: 22 | :undoc-members: 23 | :show-inheritance: 24 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.vis.scatter_plot.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.vis.scatter\_plot module 2 | =================================== 3 | 4 | .. automodule:: rsatoolbox.vis.scatter_plot 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/rsatoolbox.vis.timecourse.rst: -------------------------------------------------------------------------------- 1 | rsatoolbox.vis.timecourse module 2 | ================================ 3 | 4 | .. automodule:: rsatoolbox.vis.timecourse 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/temp_rdm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/docs/source/temp_rdm.png -------------------------------------------------------------------------------- /docs/source/visualization.rst: -------------------------------------------------------------------------------- 1 | .. _visualization: 2 | 3 | Visualization 4 | ============= 5 | 6 | 7 | Plotting RDMs 8 | ------------- 9 | 10 | The main function for showing RDMs is :func:`rsatoolbox.vis.rdm_plot.show_rdm`. 11 | It is illustrated in :doc:`demo_rdm_vis`. It allows relatively detailed 12 | plotting of both individual RDMs, as well as combined figures with 13 | multiple RDMs. For examples on adding overlays or contours to the plot, 14 | see :doc:`demo_annotated_rdm_plot`. 15 | 16 | 17 | Scatter plots 18 | ------------- 19 | 20 | Sometimes it may be helpful to display an RDM using a two-dimensional 21 | scatter plot. This requires that the multi-dimensional structure of the RDM 22 | is reduced. RSAtoolbox offers various functions for making such plots: 23 | 24 | - :func:`show_MDS ` 25 | - :func:`show_tSNE ` 26 | - :func:`show_iso ` 27 | 28 | .. _model plot: 29 | 30 | Ploting model evaluations 31 | ------------------------- 32 | 33 | Results objects can be plotted into the typical bar plot of model evaluations 34 | using ``rsatoolbox.vis.plot_model_comparison``. It takes 35 | a :ref:`Result object ` as input and does all necessary 36 | inferences based on the uncertainties stored in the results object. It 37 | provides many options for changing the layout and the inferences performed. 38 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "setuptools>=68.0", 4 | "setuptools-scm[toml]>=8.0", 5 | "wheel", 6 | "numpy>=1.21.2", 7 | "scipy", 8 | "cython~=3.0", 9 | "twine>=4.0.1" 10 | ] 11 | build-backend = "setuptools.build_meta" 12 | 13 | [project] 14 | name = "rsatoolbox" 15 | description = "Representational Similarity Analysis (RSA) in Python" 16 | requires-python = ">=3.8" 17 | authors = [ 18 | {name="rsatoolbox authors"}, 19 | ] 20 | keywords = ["neuroscience"] 21 | license = {file = "LICENSE"} 22 | classifiers = [ 23 | 'Programming Language :: Python', 24 | 'License :: OSI Approved :: MIT License', 25 | 'Operating System :: OS Independent', 26 | 'Development Status :: 4 - Beta', 27 | 'Topic :: Scientific/Engineering', 28 | 'Intended Audience :: Science/Research', 29 | 'Programming Language :: Python :: 3.8', 30 | 'Programming Language :: Python :: 3.9', 31 | 'Programming Language :: Python :: 3.10', 32 | 'Programming Language :: Python :: 3.11', 33 | 'Programming Language :: Python :: 3.12', 34 | ] 35 | dynamic = ["readme", "dependencies", "version"] 36 | [project.optional-dependencies] 37 | imaging = [ 38 | "mne~=1.5.1", 39 | "nibabel~=5.1.0", 40 | "neuroimagingtools~=1.1.4", 41 | ] 42 | 43 | [project.urls] 44 | homepage = "https://github.com/rsagroup/rsatoolbox" 45 | documentation = "https://rsatoolbox.readthedocs.io/" 46 | 47 | [tool.setuptools_scm] 48 | local_scheme = "no-local-version" 49 | 50 | [tool.setuptools.packages.find] 51 | where = ["src"] 52 | 53 | [tool.setuptools.dynamic] 54 | readme = {file = "README.md", content-type = "text/markdown"} 55 | dependencies = {file = "requirements.txt"} 56 | 57 | [tool.pytest.ini_options] 58 | testpaths = [ 59 | "tests" 60 | ] 61 | python_files = "*.py" 62 | addopts = "--assert=plain" 63 | 64 | [tool.cibuildwheel] 65 | test-requires = "pytest" 66 | test-command = "pytest {project}/tests" 67 | before-test = "pip install -r tests/requirements.txt" 68 | skip = ["*-win32", "*-manylinux_i686", "*-musllinux_*", "pp*"] 69 | 70 | [tool.pyright] 71 | include = ["src"] 72 | exclude = [] 73 | ignore = [] 74 | defineConstant = { DEBUG = true } 75 | stubPath = "" 76 | reportMissingImports = false 77 | pythonVersion = "3.11" 78 | executionEnvironments = [ 79 | { root = "src" } 80 | ] 81 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy>=1.21.2 2 | scipy>=1.10.1 3 | scikit-learn 4 | scikit-image 5 | pandas 6 | matplotlib 7 | h5py 8 | tqdm 9 | joblib 10 | importlib_resources>=5.12; python_version < "3.9" 11 | networkx>=3.0 -------------------------------------------------------------------------------- /ruff.toml: -------------------------------------------------------------------------------- 1 | line-length = 100 2 | extend-exclude = ["docs/source/conf.py"] 3 | 4 | [format] 5 | quote-style = "single" 6 | docstring-code-format = true 7 | 8 | [lint.per-file-ignores] 9 | "__init__.py" = ["F401"] 10 | 11 | [lint.pydocstyle] 12 | convention = "google" 13 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """Setup.py now only remains as a build script for the cython extensions. 2 | Using setup.py for other things is now deprecated: 3 | setup.py test -> pytest 4 | setup.py develop -> pip install -e 5 | """ 6 | from setuptools import setup, Extension 7 | import setuptools_scm # noqa # pylint: disable=unused-import 8 | from Cython.Build import build_ext 9 | import numpy 10 | 11 | 12 | setup( 13 | ext_modules=[ 14 | Extension( 15 | "rsatoolbox.cengine.similarity", 16 | ["src/rsatoolbox/cengine/similarity.pyx"], 17 | include_dirs=[numpy.get_include()])], 18 | cmdclass={'build_ext': build_ext} 19 | ) 20 | -------------------------------------------------------------------------------- /src/rsatoolbox/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ Top level package: Only imports and organisation 4 | """ 5 | 6 | from . import cengine 7 | from . import data 8 | from . import inference 9 | from . import model 10 | from . import rdm 11 | from . import simulation 12 | from . import util 13 | from . import vis 14 | -------------------------------------------------------------------------------- /src/rsatoolbox/cengine/__init__.pxd: -------------------------------------------------------------------------------- 1 | from .similarity cimport similarity 2 | -------------------------------------------------------------------------------- /src/rsatoolbox/cengine/__init__.py: -------------------------------------------------------------------------------- 1 | from .similarity import similarity, calc_one 2 | -------------------------------------------------------------------------------- /src/rsatoolbox/data/__init__.py: -------------------------------------------------------------------------------- 1 | from .dataset import Dataset 2 | from .dataset import TemporalDataset 3 | from .dataset import load_dataset 4 | from .dataset import dataset_from_dict 5 | from .computations import average_dataset 6 | from .computations import average_dataset_by 7 | from .noise import cov_from_residuals 8 | from .noise import prec_from_residuals 9 | from .noise import cov_from_measurements 10 | from .noise import prec_from_measurements 11 | from .noise import cov_from_unbalanced 12 | from .noise import prec_from_unbalanced 13 | -------------------------------------------------------------------------------- /src/rsatoolbox/data/computations.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Dataset computations 5 | """ 6 | 7 | import numpy as np 8 | from rsatoolbox.util.data_utils import get_unique_inverse 9 | 10 | 11 | def average_dataset(dataset): 12 | """ 13 | computes the average of a dataset 14 | 15 | Args: 16 | dataset(rsatoolbox.data.Dataset): the dataset to operate on 17 | 18 | Returns: 19 | numpy.ndarray: average: average activation vector 20 | """ 21 | return np.mean(dataset.measurements, axis=0) 22 | 23 | 24 | def average_dataset_by(dataset, by): 25 | """ 26 | computes the average of a dataset per value of a descriptor 27 | 28 | Args: 29 | dataset(rsatoolbox.data.Dataset): the dataset to operate on 30 | by(String): which obs_descriptor to split by 31 | 32 | Returns: 33 | numpy.ndarray: average: average activation vector 34 | """ 35 | unique_values, inverse = get_unique_inverse(dataset.obs_descriptors[by]) 36 | average = np.nan * np.empty( 37 | (len(unique_values), dataset.measurements.shape[1])) 38 | n_obs = np.nan * np.empty(len(unique_values)) 39 | for i_v, _ in enumerate(unique_values): 40 | measurements = dataset.measurements[inverse == i_v, :] 41 | average[i_v] = np.mean(measurements, axis=0) 42 | n_obs[i_v] = measurements.shape[0] 43 | return average, unique_values, n_obs 44 | -------------------------------------------------------------------------------- /src/rsatoolbox/data/ops.py: -------------------------------------------------------------------------------- 1 | """Operations on multiple Datasets 2 | """ 3 | from __future__ import annotations 4 | from typing import TYPE_CHECKING, Union, List, Set, overload 5 | from copy import deepcopy 6 | from warnings import warn 7 | try: 8 | from typing import Literal # pylint: disable=ungrouped-imports 9 | except ImportError: 10 | from typing_extensions import Literal 11 | from numpy import concatenate, repeat 12 | import rsatoolbox 13 | if TYPE_CHECKING: 14 | DESC_LEVEL = Union[Literal['obs'], Literal['set']] 15 | from rsatoolbox.data.dataset import Dataset, TemporalDataset 16 | 17 | 18 | @overload 19 | def merge_datasets(sets: List[TemporalDataset]) -> TemporalDataset: 20 | ... 21 | 22 | 23 | @overload 24 | def merge_datasets(sets: List[Dataset]) -> Dataset: 25 | ... 26 | 27 | 28 | def merge_datasets(sets: Union[List[Dataset], List[TemporalDataset]] 29 | ) -> Union[Dataset, TemporalDataset]: 30 | """Concatenate measurements to create one Dataset of the same type 31 | 32 | Only descriptors that exist on all subsets are assigned to the merged 33 | dataset. 34 | Dataset-level `descriptors` that are identical across subsets will be 35 | passed on, those that vary will become `obs_descriptors`. 36 | Channel and Time descriptors must be identical across subsets. 37 | 38 | Args: 39 | sets (Union[List[Dataset], List[TemporalDataset]]): List of Dataset 40 | or TemporalDataset objects. Must all be the same type. 41 | 42 | Returns: 43 | Union[Dataset, TemporalDataset]: The new dataset combining measurements 44 | and descriptors from the given subset datasets. 45 | """ 46 | if len(sets) == 0: 47 | warn('[merge_datasets] Received empty list, returning empty Dataset') 48 | return rsatoolbox.data.dataset.Dataset(measurements=[]) 49 | if len({type(s) for s in sets}) > 1: 50 | raise ValueError('All datasets must be of the same type') 51 | ds0 = sets[0] 52 | # numpy pre-allocates so this seems to be a performant solution: 53 | meas = concatenate([ds.measurements for ds in sets], axis=0) 54 | obs_descs = dict() 55 | # loop over obs descriptors that all subsets have in common: 56 | for k in _shared_descriptors(sets, 'obs'): 57 | obs_descs[k] = concatenate([ds.obs_descriptors[k] for ds in sets]) 58 | dat_decs = dict() 59 | for k in _shared_descriptors(sets): 60 | if len({s.descriptors[k] for s in sets}) == 1: 61 | # descriptor always has the same value 62 | dat_decs[k] = ds0.descriptors[k] 63 | else: 64 | # descriptor varies across subsets, so repeat it by observation 65 | obs_descs[k] = repeat( 66 | [ds.descriptors[k] for ds in sets], 67 | [ds.n_obs for ds in sets] 68 | ) 69 | # order is important as long as TemporalDataset inherits from Dataset 70 | if isinstance(ds0, rsatoolbox.data.dataset.TemporalDataset): 71 | return rsatoolbox.data.dataset.TemporalDataset( 72 | measurements=meas, 73 | descriptors=dat_decs, 74 | obs_descriptors=obs_descs, 75 | channel_descriptors=deepcopy(ds0.channel_descriptors), 76 | time_descriptors=deepcopy(ds0.time_descriptors), 77 | ) 78 | if isinstance(ds0, rsatoolbox.data.dataset.Dataset): 79 | return rsatoolbox.data.dataset.Dataset( 80 | measurements=meas, 81 | descriptors=dat_decs, 82 | obs_descriptors=obs_descs, 83 | channel_descriptors=deepcopy(ds0.channel_descriptors) 84 | ) 85 | raise ValueError('Unsupported Dataset type') 86 | 87 | 88 | def _shared_descriptors( 89 | datasets: Union[List[Dataset], List[TemporalDataset]], 90 | level: DESC_LEVEL = 'set') -> Set[str]: 91 | """Find descriptors that all datasets have in common 92 | """ 93 | if level == 'set': 94 | each_keys = [set(d.descriptors.keys()) for d in datasets] 95 | else: 96 | each_keys = [set(d.obs_descriptors.keys()) for d in datasets] 97 | return set.intersection(*each_keys) 98 | -------------------------------------------------------------------------------- /src/rsatoolbox/inference/__init__.py: -------------------------------------------------------------------------------- 1 | from .bootstrap import bootstrap_sample 2 | from .bootstrap import bootstrap_sample_rdm 3 | from .bootstrap import bootstrap_sample_pattern 4 | from .evaluate import eval_fixed 5 | from .evaluate import eval_bootstrap 6 | from .evaluate import eval_bootstrap_rdm 7 | from .evaluate import eval_bootstrap_pattern 8 | from .evaluate import eval_dual_bootstrap 9 | from .evaluate import bootstrap_crossval 10 | from .evaluate import eval_dual_bootstrap_random 11 | from .evaluate import crossval 12 | from .boot_testset import bootstrap_testset 13 | from .boot_testset import bootstrap_testset_pattern 14 | from .boot_testset import bootstrap_testset_rdm 15 | from .crossvalsets import sets_leave_one_out_pattern 16 | from .crossvalsets import sets_leave_one_out_rdm 17 | from .crossvalsets import sets_k_fold 18 | from .crossvalsets import sets_k_fold_pattern 19 | from .crossvalsets import sets_k_fold_rdm 20 | from .crossvalsets import sets_of_k_pattern 21 | from .noise_ceiling import cv_noise_ceiling 22 | from .noise_ceiling import boot_noise_ceiling 23 | from .result import load_results 24 | from .result import Result 25 | from .result import result_from_dict 26 | -------------------------------------------------------------------------------- /src/rsatoolbox/inference/bootstrap.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """inference module: bootstrapping 4 | """ 5 | 6 | import numpy as np 7 | from rsatoolbox.util.rdm_utils import add_pattern_index 8 | 9 | 10 | def bootstrap_sample(rdms, rdm_descriptor='index', pattern_descriptor='index'): 11 | """Draws a bootstrap_sample from the data. 12 | 13 | This function generates a bootstrap sample of RDMs resampled over 14 | measurements and patterns. By default every pattern and RDM sample is 15 | treated independently. If desired descriptor names can be passed in 16 | descriptors and in pattern_descriptors to group rdms instead. 17 | 18 | Args: 19 | rdms(rsatoolbox.rdm.rdms.RDMs): Data to be used 20 | 21 | rdm_descriptors(String): 22 | descriptor to group the samples by. For each unique value of 23 | the descriptor each sample will either contain all RDMs with 24 | this value or none 25 | 26 | pattern_descriptors(string): 27 | descriptor to group the patterns by. Each group of patterns will 28 | be in or out of the sample as a whole 29 | 30 | Returns: 31 | rsatoolbox.rdm.rdms.RDMs: rdms 32 | subsampled dataset with equal number of groups in both patterns 33 | and measurements of the rdms 34 | 35 | numpy.ndarray: rdm_idx 36 | sampled rdm indices 37 | 38 | numpy.ndarray: pattern_idx 39 | sampled pattern descriptor indices 40 | 41 | """ 42 | rdm_select = np.unique(rdms.rdm_descriptors[rdm_descriptor]) 43 | pattern_descriptor, pattern_select = \ 44 | add_pattern_index(rdms, pattern_descriptor) 45 | rdm_idx = np.random.randint(0, len(rdm_select), 46 | size=len(rdm_select)) 47 | rdm_idx = rdm_select[rdm_idx] 48 | rdms = rdms.subsample(rdm_descriptor, rdm_idx) 49 | pattern_idx = np.random.randint(0, len(pattern_select), 50 | size=len(pattern_select)) 51 | pattern_idx = pattern_select[pattern_idx] 52 | rdms = rdms.subsample_pattern(pattern_descriptor, 53 | pattern_idx) 54 | return rdms, rdm_idx, pattern_idx 55 | 56 | 57 | def bootstrap_sample_rdm(rdms, rdm_descriptor='index'): 58 | """Draws a bootstrap_sample from the data. 59 | 60 | This function generates a bootstrap sample of RDMs resampled over 61 | measurements. By default every RDM sample is treated independently. 62 | If desired a descriptor name can be passed inrdm_descriptor to group rdms. 63 | 64 | Args: 65 | rdms(rsatoolbox.rdm.rdms.RDMs): Data to be used 66 | 67 | rdm_descriptors(String): 68 | descriptor to group the samples by. For each unique value of 69 | the descriptor each sample will either contain all RDMs with 70 | this value or none 71 | 72 | Returns: 73 | rsatoolbox.rdm.rdms.RDMs: rdm_idx 74 | subsampled dataset with equal number of groups of rdms 75 | 76 | numpy.ndarray: rdm_idx 77 | sampled rdm indices 78 | 79 | numpy.ndarray: rdm_select 80 | rdm group descritor values 81 | 82 | """ 83 | rdm_select = np.unique(rdms.rdm_descriptors[rdm_descriptor]) 84 | rdm_sample = np.random.randint(0, len(rdm_select), 85 | size=len(rdm_select)) 86 | rdm_idx = rdm_select[rdm_sample] 87 | rdms = rdms.subsample(rdm_descriptor, rdm_idx) 88 | return rdms, rdm_idx 89 | 90 | 91 | def bootstrap_sample_pattern(rdms, pattern_descriptor='index'): 92 | """Draws a bootstrap_sample from the data. 93 | 94 | This function generates a bootstrap sample of RDMs resampled over 95 | patterns. By default every pattern is treated independently. If desired 96 | a descriptor name can be passed in pattern_descriptor to group patterns. 97 | 98 | Args: 99 | rdms(rsatoolbox.rdm.rdms.RDMs): Data to be used 100 | 101 | pattern_descriptors(string): 102 | descriptor to group the patterns by. Each group of patterns will 103 | be in or out of the sample as a whole 104 | 105 | Returns: 106 | rsatoolbox.rdm.rdms.RDMs: rdm_idx 107 | subsampled dataset with equal number of pattern groups 108 | 109 | numpy.ndarray: pattern_idx 110 | sampled pattern descriptor index values for subsampling other rdms 111 | """ 112 | pattern_descriptor, pattern_select = \ 113 | add_pattern_index(rdms, pattern_descriptor) 114 | pattern_idx = np.random.randint(0, len(pattern_select), 115 | size=len(pattern_select)) 116 | pattern_idx = pattern_select[pattern_idx] 117 | rdms = rdms.subsample_pattern(pattern_descriptor, 118 | pattern_idx) 119 | return rdms, pattern_idx 120 | -------------------------------------------------------------------------------- /src/rsatoolbox/inference/noise_ceiling.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | calculation of noise ceilings 5 | """ 6 | 7 | import numpy as np 8 | from rsatoolbox.util.inference_util import pool_rdm 9 | from rsatoolbox.rdm import compare 10 | from .crossvalsets import sets_leave_one_out_rdm 11 | 12 | 13 | def cv_noise_ceiling(rdms, ceil_set, test_set, method='cosine', 14 | pattern_descriptor='index'): 15 | """ calculates the noise ceiling for crossvalidation. 16 | The upper bound is calculated by pooling all rdms for the appropriate 17 | patterns in the testsets. 18 | the lower bound is calculated by using only the appropriate rdms 19 | from ceil_set for training. 20 | 21 | Args: 22 | rdms(rsatoolbox.rdm.RDMs): complete data 23 | ceil_set(list): a list of the training RDMs with 2-tuple entries: 24 | (RDMs, pattern_idx) 25 | test_set(list): a list of the test RDMs with 2-tuple entries: 26 | (RDMs, pattern_idx) 27 | method(string): comparison method to use 28 | pattern_descriptor(string): descriptor to group patterns 29 | 30 | Returns: 31 | list: lower nc-bound, upper nc-bound 32 | 33 | """ 34 | assert len(ceil_set) == len(test_set), \ 35 | 'train_set and test_set must have the same length' 36 | noise_min = [] 37 | noise_max = [] 38 | for i in range(len(ceil_set)): 39 | train = ceil_set[i] 40 | test = test_set[i] 41 | pred_train = pool_rdm(train[0], method=method) 42 | pred_train = pred_train.subsample_pattern(by=pattern_descriptor, 43 | value=test[1]) 44 | pred_test = pool_rdm(rdms, method=method) 45 | pred_test = pred_test.subsample_pattern(by=pattern_descriptor, 46 | value=test[1]) 47 | noise_min.append(np.mean(compare(pred_train, test[0], method))) 48 | noise_max.append(np.mean(compare(pred_test, test[0], method))) 49 | noise_min = np.mean(np.array(noise_min)) 50 | noise_max = np.mean(np.array(noise_max)) 51 | return noise_min, noise_max 52 | 53 | 54 | def boot_noise_ceiling(rdms, method='cosine', rdm_descriptor='index'): 55 | """ calculates a noise ceiling by leave one out & full set 56 | 57 | Args: 58 | rdms(rsatoolbox.rdm.RDMs): data to calculate noise ceiling 59 | method(string): comparison method to use 60 | rdm_descriptor(string): descriptor to group rdms 61 | 62 | Returns: 63 | list: [lower nc-bound, upper nc-bound] 64 | 65 | """ 66 | _, test_set, ceil_set = sets_leave_one_out_rdm(rdms, rdm_descriptor) 67 | pred_test = pool_rdm(rdms, method=method) 68 | noise_min = [] 69 | noise_max = [] 70 | for i in range(len(ceil_set)): 71 | train = ceil_set[i] 72 | test = test_set[i] 73 | pred_train = pool_rdm(train[0], method=method) 74 | noise_min.append(np.mean(compare(pred_train, test[0], method))) 75 | noise_max.append(np.mean(compare(pred_test, test[0], method))) 76 | noise_min = np.mean(np.array(noise_min)) 77 | noise_max = np.mean(np.array(noise_max)) 78 | return noise_min, noise_max 79 | -------------------------------------------------------------------------------- /src/rsatoolbox/io/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/src/rsatoolbox/io/__init__.py -------------------------------------------------------------------------------- /src/rsatoolbox/io/hdf5.py: -------------------------------------------------------------------------------- 1 | """ 2 | saving to and reading from HDF5 files 3 | """ 4 | from __future__ import annotations 5 | from typing import Union, Dict, List, IO 6 | import os 7 | from collections.abc import Iterable 8 | try: # drop:py37 (backport) 9 | from importlib.metadata import version 10 | except ModuleNotFoundError: 11 | from importlib_metadata import version 12 | from h5py import File, Group, Empty 13 | import numpy as np 14 | 15 | 16 | def write_dict_hdf5(fhandle: Union[str, IO], dictionary: Dict) -> None: 17 | """ writes a nested dictionary containing strings & arrays as data into 18 | a hdf5 file 19 | 20 | Args: 21 | file: a filename or opened writable file 22 | dictionary(dict): the dict to be saved 23 | 24 | """ 25 | if isinstance(fhandle, str): 26 | if os.path.exists(fhandle): 27 | raise ValueError('File already exists!') 28 | file = File(fhandle, 'a') 29 | file.attrs['rsatoolbox_version'] = version('rsatoolbox') 30 | _write_to_group(file, dictionary) 31 | 32 | 33 | def _write_to_group(group: Group, dictionary: Dict) -> None: 34 | """ writes a dictionary to a hdf5 group, which can recurse""" 35 | for key in dictionary.keys(): 36 | value = dictionary[key] 37 | if isinstance(value, str): 38 | # needs another conversion to string to catch weird subtypes 39 | # like numpy.str_ 40 | group.attrs[key] = str(value) 41 | elif isinstance(value, np.ndarray): 42 | if str(value.dtype)[:2] == ' None: 61 | """ 62 | writes a list to a hdf5 file. First tries conversion to np.array. 63 | If this fails the list is converted to a dict with integer keys. 64 | 65 | Parameters 66 | ---------- 67 | group : hdf5 group 68 | where to write. 69 | key : hdf5 key 70 | value : list 71 | list to be written 72 | """ 73 | try: 74 | value = np.array(value) 75 | if str(value.dtype)[:2] == ' Dict: 86 | """ writes a nested dictionary containing strings & arrays as data into 87 | a hdf5 file 88 | 89 | Args: 90 | file: a filename or opened readable file 91 | 92 | Returns: 93 | dictionary(dict): the loaded dict 94 | 95 | """ 96 | file = File(fhandle, 'r') 97 | return _read_group(file) 98 | 99 | 100 | def _read_group(group: Group) -> Dict: 101 | """ reads a group from a hdf5 file into a dict, which allows recursion""" 102 | dictionary = {} 103 | for key in group.keys(): 104 | sub_val = group[key] 105 | if isinstance(sub_val, Group): 106 | dictionary[key] = _read_group(sub_val) 107 | elif sub_val.shape is None: 108 | dictionary[key] = None 109 | else: 110 | dictionary[key] = np.array(sub_val) 111 | if dictionary[key].dtype.type is np.bytes_: 112 | dictionary[key] = np.array(sub_val).astype('unicode') 113 | # if (len(dictionary[key].shape) == 1 114 | # and dictionary[key].shape[0] == 1): 115 | # dictionary[key] = dictionary[key][0] 116 | for key in group.attrs.keys(): 117 | dictionary[key] = group.attrs[key] 118 | return dictionary 119 | -------------------------------------------------------------------------------- /src/rsatoolbox/io/mne.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from typing import Optional, Dict, TYPE_CHECKING 3 | from os.path import basename 4 | from rsatoolbox.data.dataset import TemporalDataset 5 | if TYPE_CHECKING: 6 | from mne.epochs import EpochsFIF 7 | 8 | 9 | def read_epochs(fpath: str) -> TemporalDataset: 10 | """Create TemporalDataset from epochs in mne FIF file 11 | 12 | Args: 13 | fpath (str): Full path to epochs file 14 | 15 | Returns: 16 | TemporalDataset: dataset with epochs 17 | """ 18 | # pylint: disable-next=import-outside-toplevel 19 | from mne import read_epochs as mne_read_epochs 20 | epo = mne_read_epochs(fpath, preload=True, verbose='error') 21 | fname = basename(fpath) 22 | descs = dict(filename=fname, **descriptors_from_bids_filename(fname)) 23 | return dataset_from_epochs(epo, descs) 24 | 25 | 26 | def dataset_from_epochs( 27 | epochs: EpochsFIF, 28 | descriptors: Optional[Dict] = None 29 | ) -> TemporalDataset: 30 | """Create TemporalDataset from MNE epochs object 31 | 32 | Args: 33 | fpath (str): Full path to epochs file 34 | 35 | Returns: 36 | TemporalDataset: dataset with epochs 37 | """ 38 | descriptors = descriptors or dict() 39 | return TemporalDataset( 40 | measurements=epochs.get_data(), 41 | descriptors=descriptors, 42 | obs_descriptors=dict(event=epochs.events[:, 2]), 43 | channel_descriptors=dict(name=epochs.ch_names), 44 | time_descriptors=dict(time=epochs.times) 45 | ) 46 | 47 | 48 | def descriptors_from_bids_filename(fname: str) -> Dict[str, str]: 49 | """parse a filename for BIDS-style entities 50 | 51 | Args: 52 | fname (str): filename 53 | 54 | Returns: 55 | Dict[str, str]: sub, run or task descriptors 56 | """ 57 | descs = dict() 58 | for dname in ['sub', 'run', 'task']: 59 | for segment in fname.split('_'): 60 | if segment.startswith(dname + '-'): 61 | descs[dname] = segment[len(dname)+1:] 62 | return descs 63 | -------------------------------------------------------------------------------- /src/rsatoolbox/io/optional.py: -------------------------------------------------------------------------------- 1 | """Just-in-time importing of optional dependencies 2 | 3 | These can be installed with rsatoolbox using the square brackets syntax; 4 | ``` 5 | pip install rsatoolbox[imaging] 6 | ``` 7 | """ 8 | 9 | 10 | def import_nibabel(mock=None): 11 | """Try to access the nibabel module or raise an exception. 12 | 13 | Nibabel is an open source python library for reading and writing 14 | MRI files. Used by the BIDS functionality. 15 | 16 | Args: 17 | mock (Mock, optional): When testing, a Mock object 18 | can be injected here. Defaults to None. 19 | 20 | Raises: 21 | OptionalImportMissingException: Raised when the dependency 22 | is not installed 23 | 24 | Returns: 25 | nibabel: nibabel main module 26 | """ 27 | if mock: 28 | return mock 29 | try: 30 | import nibabel 31 | except ImportError: 32 | raise OptionalImportMissingException('nibabel') 33 | return nibabel 34 | 35 | 36 | def import_nitools(mock=None): 37 | """Try to access the neuroimagingtools module or raise an exception. 38 | 39 | Neuroimagingtools is an open source python library for efficient 40 | access to Nifti, Gifti and Cifti files. Used by the SpmGlm class. 41 | 42 | Args: 43 | mock (Mock, optional): When testing, a Mock object 44 | can be injected here. Defaults to None. 45 | 46 | Raises: 47 | OptionalImportMissingException: Raised when the dependency 48 | is not installed 49 | 50 | Returns: 51 | nitools: neuroimagingtools main module 52 | """ 53 | if mock: 54 | return mock 55 | try: 56 | import nitools 57 | except ImportError: 58 | raise OptionalImportMissingException('nitools') 59 | return nitools 60 | 61 | 62 | class OptionalImportMissingException(Exception): 63 | 64 | def __init__(self, name: str): 65 | super().__init__(f'[rsatoolbox] Missing optional dependency: {name}') 66 | -------------------------------------------------------------------------------- /src/rsatoolbox/io/pandas.py: -------------------------------------------------------------------------------- 1 | """Conversions from rsatoolbox classes to pandas table objects 2 | """ 3 | from __future__ import annotations 4 | from typing import TYPE_CHECKING 5 | from pandas import DataFrame 6 | import numpy 7 | from numpy import asarray 8 | if TYPE_CHECKING: 9 | from rsatoolbox.rdm.rdms import RDMs 10 | 11 | 12 | def rdms_to_df(rdms: RDMs) -> DataFrame: 13 | """Create DataFrame representation of the RDMs object 14 | 15 | A column for: 16 | - dissimilarity 17 | - each rdm descriptor 18 | - two for each pattern descriptor, suffixed by _1 and _2 respectively 19 | 20 | Multiple RDMs are stacked row-wise. 21 | See also the `RDMs.to_df()` method which calls this function 22 | 23 | Args: 24 | rdms (RDMs): the object to convert 25 | 26 | Returns: 27 | DataFrame: long-form pandas DataFrame with 28 | dissimilarities and descriptors. 29 | """ 30 | n_rdms, n_pairs = rdms.dissimilarities.shape 31 | cols = dict(dissimilarity=rdms.dissimilarities.ravel()) 32 | for dname, dvals in rdms.rdm_descriptors.items(): 33 | # rename the default index desc as that has special meaning in df 34 | cname = 'rdm_index' if dname == 'index' else dname 35 | cols[cname] = numpy.repeat(dvals, n_pairs) 36 | for dname, dvals in rdms.pattern_descriptors.items(): 37 | ix = numpy.triu_indices(len(dvals), 1) 38 | # rename the default index desc as that has special meaning in df 39 | cname = 'pattern_index' if dname == 'index' else dname 40 | for p in (0, 1): 41 | cols[f'{cname}_{p+1}'] = numpy.tile(asarray(dvals)[ix[p]], n_rdms) 42 | return DataFrame(cols) 43 | -------------------------------------------------------------------------------- /src/rsatoolbox/io/pkl.py: -------------------------------------------------------------------------------- 1 | """ 2 | saving to and reading from pickle files 3 | """ 4 | from __future__ import annotations 5 | from typing import Union, Dict, IO 6 | try: # drop:py37 (backport) 7 | from importlib.metadata import version 8 | except ModuleNotFoundError: 9 | from importlib_metadata import version 10 | import pickle 11 | 12 | 13 | def write_dict_pkl(fhandle: Union[str, IO], dictionary: Dict) -> None: 14 | """ writes a nested dictionary containing strings & arrays as data into 15 | a pickle file 16 | 17 | Args: 18 | file: a filename or opened writable file 19 | dictionary(dict): the dict to be saved 20 | 21 | """ 22 | if isinstance(fhandle, str): 23 | fhandle = open(fhandle, 'wb') 24 | dictionary['rsatoolbox_version'] = version('rsatoolbox') 25 | pickle.dump(dictionary, fhandle, protocol=-1) 26 | 27 | 28 | def read_dict_pkl(fhandle: Union[str, IO]) -> Dict: 29 | """ writes a nested dictionary containing strings & arrays as data into 30 | a pickle file 31 | 32 | Args: 33 | file: a filename or opened readable file 34 | 35 | Returns: 36 | dictionary(dict): the loaded dict 37 | 38 | 39 | """ 40 | if isinstance(fhandle, str): 41 | fhandle = open(fhandle, 'rb') 42 | data = pickle.load(fhandle) 43 | return data 44 | -------------------------------------------------------------------------------- /src/rsatoolbox/model/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """Model definitions and handling 4 | """ 5 | from .model import Model, ModelFixed, ModelSelect, ModelWeighted 6 | from .model import ModelInterpolate 7 | from .model import model_from_dict 8 | from .model_family import ModelFamily 9 | from .fitter import fit_mock, fit_optimize, fit_select, fit_interpolate 10 | from .fitter import fit_regress, fit_regress_nn 11 | -------------------------------------------------------------------------------- /src/rsatoolbox/model/model_family.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Definition of RSA Model family 5 | """ 6 | import itertools 7 | import numpy as np 8 | from rsatoolbox.rdm import RDMs 9 | from .model import ModelWeighted 10 | 11 | 12 | class ModelFamily(): 13 | """Short summary. 14 | 15 | Parameters 16 | ---------- 17 | models : type 18 | Description of parameter `models`. 19 | 20 | Attributes 21 | ---------- 22 | num_models : int 23 | total number of fixed models. 24 | num_family_members : int 25 | total number of possible combinations of fixed models 2**num_models. 26 | family_list : list of array 27 | list of selected fixed model indices for all family members. 28 | model_indices : numpy array 29 | binary array indicating the selected fixed models for each family member 30 | __create_model_family : method 31 | generates family_list and model_indices 32 | models 33 | 34 | """ 35 | 36 | def __init__(self, models): 37 | """Class initialization. 38 | 39 | Parameters 40 | ---------- 41 | models : list of fixed models 42 | List of models 43 | Returns 44 | ------- 45 | None 46 | 47 | """ 48 | self.num_models = len(models) 49 | self.models = models 50 | self.num_family_members = 2**self.num_models-1 51 | self.family_list, self.model_indices = self.__create_model_family() 52 | 53 | def __create_model_family(self): 54 | """Creates model family. 55 | 56 | Returns 57 | ------- 58 | family_list : list of array 59 | list of selected fixed model indices for all family members. 60 | model_indices : numpy array 61 | binary array indicating the selected fixed models for each family member 62 | 63 | """ 64 | family_list = [] 65 | indices = np.zeros((self.num_family_members, self.num_models)) 66 | models_index_array = range(self.num_models) 67 | count = 0 68 | for subset_length in range(1, self.num_models+1): 69 | for subset in itertools.combinations(models_index_array, subset_length): 70 | selected_indices = [models_index_array.index(x) for x in subset] 71 | indices[count, selected_indices] = 1 72 | family_list.append(subset) 73 | count += 1 74 | return family_list, indices 75 | 76 | def get_family_member(self, family_index): 77 | """returns family member given an input index 78 | 79 | Parameters 80 | ---------- 81 | family_index : int 82 | Index corresponding to a family member 83 | 84 | Returns 85 | ------- 86 | weighted_model 87 | family member corresponding to input index 88 | 89 | """ 90 | 91 | member_indices = list(self.family_list[family_index]) 92 | family_member = [self.models[i] for i in member_indices] 93 | 94 | return family_member 95 | 96 | def get_all_family_members(self): 97 | """returns a list of weighted models. 98 | 99 | Returns 100 | ------- 101 | list 102 | list of weighted models. 103 | 104 | """ 105 | all_family_members = [] 106 | for family_index in range(self.num_family_members): 107 | family_member = self.get_family_member(family_index) 108 | member_rdms = [] 109 | member_name = "" 110 | for model in family_member: 111 | member_rdms.append(model.predict_rdm().get_vectors().ravel()) 112 | member_name = member_name + "_" + model.name 113 | member_rdms = np.array(member_rdms) 114 | member_rdms = RDMs(member_rdms) 115 | 116 | weighted_model = ModelWeighted(member_name, member_rdms) 117 | all_family_members.append(weighted_model) 118 | 119 | return all_family_members 120 | -------------------------------------------------------------------------------- /src/rsatoolbox/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/src/rsatoolbox/py.typed -------------------------------------------------------------------------------- /src/rsatoolbox/rdm/__init__.py: -------------------------------------------------------------------------------- 1 | from .rdms import RDMs 2 | from .rdms import concat 3 | from .rdms import get_categorical_rdm 4 | from .rdms import load_rdm 5 | from .rdms import rdms_from_dict 6 | from .transform import rank_transform 7 | from .transform import sqrt_transform 8 | from .transform import positive_transform 9 | from .transform import transform 10 | from .transform import minmax_transform 11 | from .transform import geotopological_transform 12 | from .transform import geodesic_transform 13 | from .calc import calc_rdm 14 | from .calc import calc_rdm_movie 15 | from .calc import calc_rdm_euclidean 16 | from .calc import calc_rdm_mahalanobis 17 | from .calc import calc_rdm_crossnobis 18 | from .calc import calc_rdm_correlation 19 | from .calc_unbalanced import calc_rdm_unbalanced 20 | from .compare import compare 21 | from .compare import compare_correlation 22 | from .compare import compare_cosine 23 | from .compare import compare_kendall_tau 24 | from .compare import compare_spearman 25 | from .compare import compare_rho_a 26 | from .compare import compare_correlation_cov_weighted 27 | from .compare import compare_cosine_cov_weighted 28 | from .compare import compare_neg_riemannian_distance 29 | -------------------------------------------------------------------------------- /src/rsatoolbox/rdm/pairs.py: -------------------------------------------------------------------------------- 1 | """Functions to select pairs 2 | """ 3 | # pylint: disable=redefined-builtin 4 | from __future__ import annotations 5 | from typing import TYPE_CHECKING 6 | from pandas import DataFrame 7 | import numpy 8 | from scipy.stats import rankdata 9 | if TYPE_CHECKING: 10 | from rsatoolbox.rdm.rdms import RDMs 11 | 12 | 13 | def pairs_by_percentile(rdms: RDMs, min: float = 0, max: float = 100, 14 | **kwargs) -> DataFrame: 15 | """Select pairs within a percentile range. 16 | 17 | Filter pairs first by providing the `with_pattern` argument. 18 | 19 | Args: 20 | rdms (RDMs): RDMs object 21 | min (float, optional): Lower percentile bound. Defaults to 0. 22 | max (float, optional): Upper percentile bound. Defaults to 100. 23 | kwargs: Pattern Descriptor value to match. 24 | 25 | Returns: 26 | DataFrame: Wide form DataFrame where each row represents a pair. 27 | """ 28 | (desc, val) = list(kwargs.items())[0] 29 | row_mask = rdms.pattern_descriptors[desc] == val 30 | mats = rdms.get_matrices() 31 | row = mats[0, row_mask, :].squeeze() 32 | pair_dissims = row[~row_mask] 33 | percs = rankdata(pair_dissims, 'average') / pair_dissims.size * 100 34 | matches = numpy.logical_and(percs >= min, percs <= max) 35 | matches_mask = numpy.full_like(row, False, dtype=bool) 36 | matches_mask[~row_mask] = matches 37 | columns = dict() 38 | columns[desc] = rdms.pattern_descriptors[desc][matches_mask] 39 | columns['dissim'] = row[matches_mask] 40 | return DataFrame(columns) 41 | -------------------------------------------------------------------------------- /src/rsatoolbox/resources.py: -------------------------------------------------------------------------------- 1 | """Provide access to resource files distributed with rsatoolbox 2 | """ 3 | from __future__ import annotations 4 | from typing import TYPE_CHECKING 5 | try: 6 | from importlib.resources import files, as_file 7 | except ImportError: 8 | from importlib_resources import files, as_file 9 | if TYPE_CHECKING: 10 | from pathlib import Path 11 | 12 | 13 | def get_style() -> Path: 14 | """Returns the location of the mplstyle file for rsatoolbox 15 | """ 16 | ref = files('rsatoolbox') / 'vis/rdm.mplstyle' 17 | with as_file(ref) as fpath: 18 | return fpath 19 | -------------------------------------------------------------------------------- /src/rsatoolbox/simulation/__init__.py: -------------------------------------------------------------------------------- 1 | # Imports all functions to be directly assessible under subpackage name 2 | from .sim import make_signal 3 | from .sim import make_dataset 4 | from .sim import make_design 5 | -------------------------------------------------------------------------------- /src/rsatoolbox/test.py: -------------------------------------------------------------------------------- 1 | """Skeleton test module 2 | 3 | The full collection of unit and acceptance tests for rsatoolbox is kept 4 | in a separate package that is not part of our distributables. It can be run 5 | by checking out the rsatoolbox git repository. 6 | 7 | The tests in this module are a limited number of basic so-called skeleton 8 | tests, which check that the library and all its dependencies are installed 9 | correctly. It is not exhaustive and assumes that unittests have passed 10 | for other most package formats. 11 | If rsatoolbox is installed, the tests can be run with: 12 | 13 | `python -m unittest rsatoolbox.test` 14 | 15 | These tests have to: 16 | 17 | - not have any dependencies outside of direct rsatoolbox runtime dependencies 18 | - be fast (a few seconds) 19 | - test interfaces that depend on external packages 20 | - test compiled code 21 | 22 | In other words they have to check that all the moving parts are there, 23 | without looking at very specific calculation outcomes. 24 | """ 25 | # pylint: disable=import-outside-toplevel 26 | from unittest import TestCase 27 | 28 | 29 | class SkeletonTests(TestCase): 30 | """Toolbox skeleton tests to ensure correct packaging and installation 31 | """ 32 | 33 | def setUp(self): 34 | """Create basic RDMs and Dataset objects for all tests 35 | """ 36 | from numpy import asarray, ones 37 | from numpy.testing import assert_almost_equal 38 | from rsatoolbox.data.dataset import Dataset 39 | from rsatoolbox.rdm.rdms import RDMs 40 | self.data = Dataset(asarray([[0, 0], [1, 1], [2.0, 2.0]])) 41 | self.rdms = RDMs(asarray([[1.0, 2, 3], [3, 4, 5]])) 42 | self.larger_rdms = RDMs(ones([3, 10])) 43 | self.array = asarray 44 | self.arrayAlmostEqual = assert_almost_equal 45 | 46 | def test_calc_compiled(self): 47 | """Covers similarity calculation with compiled code 48 | """ 49 | from rsatoolbox.rdm.calc_unbalanced import calc_rdm_unbalanced 50 | rdms = calc_rdm_unbalanced(self.data) 51 | self.arrayAlmostEqual(rdms.dissimilarities, self.array([[1, 4, 1]])) 52 | 53 | def test_model_fit(self): 54 | """Covers model fitting with scipy 55 | """ 56 | from rsatoolbox.model.model import ModelWeighted 57 | from rsatoolbox.model.fitter import fit_optimize 58 | theta = fit_optimize(ModelWeighted('F', self.rdms), self.rdms) 59 | self.arrayAlmostEqual(theta, [0.88, 0.47], decimal=2) 60 | 61 | def test_plotting_with_mpl(self): 62 | """Covers Matplotlib usage 63 | """ 64 | from rsatoolbox.vis.rdm_plot import show_rdm 65 | show_rdm(self.rdms) 66 | 67 | def test_mds(self): 68 | """Covers sklearn and Matplotlib usage 69 | """ 70 | from rsatoolbox.vis.scatter_plot import show_MDS 71 | show_MDS(self.rdms) 72 | 73 | def test_evaluate(self): 74 | """Covers tqdm usage and evaluate functionality 75 | """ 76 | from rsatoolbox.inference import eval_fixed 77 | from rsatoolbox.model import ModelFixed 78 | model = ModelFixed('G', self.array(list(range(10)))) 79 | result = eval_fixed(model, self.larger_rdms) 80 | self.assertAlmostEqual(result.test_zero()[0], 0) 81 | 82 | def test_pandas_io(self): 83 | """Covers pandas usage 84 | """ 85 | df = self.rdms.to_df() 86 | self.arrayAlmostEqual( 87 | self.array(df.loc[:, 'dissimilarity'].values), 88 | self.rdms.dissimilarities.ravel() 89 | ) 90 | 91 | def test_hdf_io(self): 92 | """Covers h5py library use 93 | """ 94 | from io import BytesIO 95 | from rsatoolbox.rdm.rdms import load_rdm 96 | fhandle = BytesIO() 97 | self.rdms.save(fhandle, file_type='hdf5') 98 | reconstituted_rdms = load_rdm(fhandle, file_type='hdf5') 99 | self.arrayAlmostEqual( 100 | self.rdms.dissimilarities, 101 | reconstituted_rdms.dissimilarities 102 | ) 103 | -------------------------------------------------------------------------------- /src/rsatoolbox/util/__init__.py: -------------------------------------------------------------------------------- 1 | from . import matrix 2 | -------------------------------------------------------------------------------- /src/rsatoolbox/util/build_rdm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """helper methods to create RDMs at the end of calculations""" 4 | 5 | from __future__ import annotations 6 | from typing import TYPE_CHECKING, Optional 7 | from copy import deepcopy 8 | import numpy as np 9 | from rsatoolbox.rdm.rdms import RDMs 10 | from rsatoolbox.data import average_dataset_by 11 | 12 | if TYPE_CHECKING: 13 | from rsatoolbox.data.base import DatasetBase 14 | from numpy.typing import NDArray 15 | 16 | 17 | def _build_rdms( 18 | utv: NDArray, 19 | ds: DatasetBase, 20 | method: str, 21 | obs_desc_name: str | None, 22 | obs_desc_vals: Optional[NDArray] = None, 23 | cv: Optional[NDArray] = None, 24 | noise: Optional[NDArray] = None 25 | ) -> RDMs: 26 | rdms = RDMs( 27 | dissimilarities=np.array([utv]), 28 | dissimilarity_measure=method, 29 | rdm_descriptors=deepcopy(ds.descriptors) 30 | ) 31 | if (obs_desc_vals is None) and (obs_desc_name is not None): 32 | # obtain the unique values in the target obs descriptor 33 | _, obs_desc_vals, _ = average_dataset_by(ds, obs_desc_name) 34 | 35 | if _averaging_occurred(ds, obs_desc_name, obs_desc_vals): 36 | orig_obs_desc_vals = np.asarray(ds.obs_descriptors[obs_desc_name]) 37 | for dname, dvals in ds.obs_descriptors.items(): 38 | dvals = np.asarray(dvals) 39 | avg_dvals = np.full_like(obs_desc_vals, np.nan, dtype=dvals.dtype) 40 | for i, v in enumerate(obs_desc_vals): 41 | subset = dvals[orig_obs_desc_vals == v] 42 | if len(set(subset)) > 1: 43 | break 44 | avg_dvals[i] = subset[0] 45 | else: 46 | rdms.pattern_descriptors[dname] = avg_dvals 47 | else: 48 | rdms.pattern_descriptors = deepcopy(ds.obs_descriptors) 49 | # Additional rdm_descriptors 50 | if noise is not None: 51 | rdms.descriptors['noise'] = noise 52 | if cv is not None: 53 | rdms.descriptors['cv_descriptor'] = cv 54 | return rdms 55 | 56 | 57 | def _averaging_occurred( 58 | ds: DatasetBase, 59 | obs_desc_name: str | None, 60 | obs_desc_vals: NDArray | None 61 | ) -> bool: 62 | if obs_desc_name is None: 63 | return False 64 | orig_obs_desc_vals = ds.obs_descriptors[obs_desc_name] 65 | return len(obs_desc_vals) != len(orig_obs_desc_vals) 66 | -------------------------------------------------------------------------------- /src/rsatoolbox/util/data_utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Collection of helper methods for data module 5 | 6 | @author: baihan 7 | """ 8 | 9 | from collections.abc import Iterable 10 | import numpy as np 11 | 12 | 13 | def extract_dict(dictionary, indices): 14 | """extract key-value pairs with values given indexes. 15 | """ 16 | extracted_dictionary = dictionary.copy() 17 | for k, v in dictionary.items(): 18 | if isinstance(indices, Iterable): 19 | extracted_dictionary[k] = [v[idx] for idx in indices] 20 | else: 21 | extracted_dictionary[k] = v[indices] 22 | return extracted_dictionary 23 | 24 | 25 | def get_unique_unsorted(array): 26 | """return a unique unsorted list 27 | """ 28 | u, indices = np.unique(array, return_index=True) 29 | temp = indices.argsort() 30 | return u[temp] 31 | 32 | 33 | def get_unique_inverse(array): 34 | """return a unique list in original order + inverse index to get 35 | which entries correspond to which unique value 36 | """ 37 | u, indices, inverse = np.unique(array, return_index=True, return_inverse=True) 38 | # sort indices to remove sorting of np.unique 39 | temp = indices.argsort() 40 | # invert sorting permutation 41 | s = np.empty(temp.size, temp.dtype) 42 | s[temp] = np.arange(temp.size) 43 | return u[temp], s[inverse] 44 | -------------------------------------------------------------------------------- /src/rsatoolbox/util/file_io.py: -------------------------------------------------------------------------------- 1 | """ 2 | saving to and reading from files 3 | """ 4 | from __future__ import annotations 5 | import os 6 | from pathlib import Path 7 | from typing import Union, IO 8 | 9 | 10 | def remove_file(file: Union[str, Path, IO]): 11 | """ Deletes file from OS if it exists 12 | 13 | Args: 14 | file (str, Path): 15 | a filename or opened readable file 16 | """ 17 | if isinstance(file, (str, Path)) and os.path.exists(file): 18 | os.remove(file) 19 | elif hasattr(file, 'name') and os.path.exists(file.name): 20 | file.truncate(0) 21 | -------------------------------------------------------------------------------- /src/rsatoolbox/util/pooling.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Tue Oct 20 23:58:28 2020 5 | 6 | @author: heiko 7 | """ 8 | 9 | import numpy as np 10 | import scipy.sparse 11 | from scipy.stats import rankdata 12 | from rsatoolbox.rdm import RDMs 13 | from rsatoolbox.util.matrix import get_v 14 | 15 | 16 | def pool_rdm(rdms, method='cosine', sigma_k=None): 17 | """pools multiple RDMs into the one with maximal performance under a given 18 | evaluation metric 19 | rdm_descriptors of the generated rdms are empty 20 | 21 | Args: 22 | rdms (pyrsa.rdm.RDMs): 23 | RDMs to be pooled 24 | method : String, optional 25 | Which comparison method to optimize for. The default is 'cosine'. 26 | 27 | Returns: 28 | pyrsa.rdm.RDMs: the pooled RDM, i.e. a RDM with maximal performance 29 | under the chosen method 30 | 31 | """ 32 | rdm_vec = rdms.get_vectors() 33 | if method == 'euclid': 34 | rdm_vec = _nan_mean(rdm_vec) 35 | elif method == 'cosine': 36 | rdm_vec = rdm_vec / np.sqrt(np.nanmean(rdm_vec ** 2, axis=1, 37 | keepdims=True)) 38 | rdm_vec = _nan_mean(rdm_vec) 39 | elif method == 'corr': 40 | rdm_vec = rdm_vec - np.nanmean(rdm_vec, axis=1, keepdims=True) 41 | rdm_vec = rdm_vec / np.nanstd(rdm_vec, axis=1, keepdims=True) 42 | rdm_vec = _nan_mean(rdm_vec) 43 | rdm_vec = rdm_vec - np.nanmin(rdm_vec) + 0.01 44 | elif method == 'cosine_cov': 45 | v = get_v(rdms.n_cond, sigma_k=sigma_k) 46 | ok_idx = np.all(np.isfinite(rdm_vec), axis=0) 47 | v = v[ok_idx][:, ok_idx] 48 | rdm_vec_nonan = rdm_vec[:, ok_idx] 49 | v_inv_x = np.array([scipy.sparse.linalg.cg(v, rdm_vec_nonan[i], 50 | atol=10 ** -9)[0] 51 | for i in range(rdms.n_rdm)]) 52 | rdm_norms = np.einsum('ij, ij->i', rdm_vec_nonan, v_inv_x).reshape( 53 | [rdms.n_rdm, 1]) 54 | rdm_vec = rdm_vec / np.sqrt(rdm_norms) 55 | rdm_vec = _nan_mean(rdm_vec) 56 | elif method == 'corr_cov': 57 | rdm_vec = rdm_vec - np.nanmean(rdm_vec, axis=1, keepdims=True) 58 | v = get_v(rdms.n_cond, sigma_k=sigma_k) 59 | ok_idx = np.all(np.isfinite(rdm_vec), axis=0) 60 | v = v[ok_idx][:, ok_idx] 61 | rdm_vec_nonan = rdm_vec[:, ok_idx] 62 | v_inv_x = np.array([scipy.sparse.linalg.cg(v, rdm_vec_nonan[i], 63 | atol=10 ** -9)[0] 64 | for i in range(rdms.n_rdm)]) 65 | rdm_norms = np.einsum('ij, ij->i', rdm_vec_nonan, v_inv_x).reshape( 66 | [rdms.n_rdm, 1]) 67 | rdm_vec = rdm_vec / np.sqrt(rdm_norms) 68 | rdm_vec = _nan_mean(rdm_vec) 69 | rdm_vec = rdm_vec - np.nanmin(rdm_vec) + 0.01 70 | elif method in ('spearman', 'rho-a'): 71 | rdm_vec = np.array([_nan_rank_data(v) for v in rdm_vec]) 72 | rdm_vec = _nan_mean(rdm_vec) 73 | elif method == 'rho-a': 74 | rdm_vec = np.array([_nan_rank_data(v) for v in rdm_vec]) 75 | rdm_vec = _nan_mean(rdm_vec) 76 | elif method in ('kendall', 'tau-b', 'tau-a'): 77 | Warning('Noise ceiling for tau based on averaged ranks!') 78 | rdm_vec = np.array([_nan_rank_data(v) for v in rdm_vec]) 79 | rdm_vec = _nan_mean(rdm_vec) 80 | else: 81 | raise ValueError('Unknown RDM comparison method requested!') 82 | return RDMs(rdm_vec, 83 | dissimilarity_measure=rdms.dissimilarity_measure, 84 | descriptors=rdms.descriptors, 85 | rdm_descriptors=None, 86 | pattern_descriptors=rdms.pattern_descriptors) 87 | 88 | 89 | def _nan_mean(rdm_vector): 90 | """ takes the average over a rdm_vector with nans for masked entries 91 | without a warning 92 | 93 | Args: 94 | rdm_vector(numpy.ndarray): set of rdm_vectors to be averaged 95 | 96 | Returns: 97 | rdm_mean(numpy.ndarray): the mean rdm 98 | 99 | """ 100 | nan_idx = ~np.isnan(rdm_vector[0]) 101 | mean_values = np.mean(rdm_vector[:, nan_idx], axis=0) 102 | rdm_mean = np.empty((1, rdm_vector.shape[1])) * np.nan 103 | rdm_mean[:, nan_idx] = mean_values 104 | return rdm_mean 105 | 106 | 107 | def _nan_rank_data(rdm_vector): 108 | """ rank_data for vectors with nan entries 109 | 110 | Args: 111 | rdm_vector(numpy.ndarray): the vector to be rank_transformed 112 | 113 | Returns: 114 | ranks(numpy.ndarray): the ranks with nans where the original vector 115 | had nans 116 | 117 | """ 118 | ranks_no_nan = rankdata(rdm_vector[~np.isnan(rdm_vector)]) 119 | ranks = np.ones_like(rdm_vector) * np.nan 120 | ranks[~np.isnan(rdm_vector)] = ranks_no_nan 121 | return ranks 122 | -------------------------------------------------------------------------------- /src/rsatoolbox/vis/__init__.py: -------------------------------------------------------------------------------- 1 | from .rdm_plot import show_rdm, show_rdm_panel 2 | from .scatter_plot import show_scatter, show_2d, show_MDS, show_tSNE, show_iso 3 | from .model_plot import plot_model_comparison 4 | from .modelfamily_graph import show_family_graph 5 | from .model_map import map_model_comparison 6 | from .icon import Icon 7 | from .icon import icons_from_folder 8 | from .rdm_comparison import rdm_comparison_scatterplot 9 | -------------------------------------------------------------------------------- /src/rsatoolbox/vis/colors.py: -------------------------------------------------------------------------------- 1 | """ 2 | Classic colormap ported from matlab rsatoolbox 3 | 4 | @author: iancharest 5 | """ 6 | from __future__ import annotations 7 | import numpy as np 8 | from skimage.color import rgb2hsv, hsv2rgb 9 | import matplotlib.pyplot as plt 10 | from matplotlib.colors import ListedColormap 11 | from scipy.interpolate import interp1d 12 | 13 | 14 | def color_scale(n_cols: int, anchor_cols=None, monitor=False): 15 | """ linearly interpolates between a set of given 16 | anchor colours to give n_cols and displays them 17 | if monitor is set 18 | 19 | Args: 20 | n_cols (int): number of colors for the colormap 21 | anchor_cols (numpy.ndarray, optional): what color space to 22 | interpolate. Defaults to None. 23 | monitor (boolean, optional): quick visualisation of the 24 | resulting colormap. Defaults to False. 25 | 26 | Returns: 27 | numpy.ndarray: n_cols x 3 RGB array. 28 | 29 | """ 30 | 31 | if anchor_cols is None: 32 | # if no anchor_cols provided, use red to blue 33 | anchor_cols = np.array([[1, 0, 0], [0, 0, 1]]) 34 | 35 | # define color scale 36 | n_anchors = anchor_cols.shape[0] 37 | 38 | # simple 1D interpolation 39 | fn = interp1d( 40 | range(n_anchors), 41 | anchor_cols.T, 42 | ) 43 | cols = fn(np.linspace(0, n_anchors - 1, n_cols)).T 44 | 45 | # optional visuals 46 | if monitor: 47 | reshaped_cols = cols.reshape((n_cols, 1, 3)) 48 | width = int(n_cols / 2) 49 | mapping = np.tile(reshaped_cols, (width, 1)) 50 | plt.imshow(mapping) 51 | plt.show() 52 | 53 | return cols 54 | 55 | 56 | def rdm_colormap_classic(n_cols: int = 256, monitor: bool = False): 57 | """this function provides a convenient colormap for visualizing 58 | dissimilarity matrices. it goes from blue to yellow and has grey for 59 | intermediate values. 60 | 61 | Args: 62 | n_cols (int, optional): precision of the colormap. 63 | Defaults to 256. 64 | 65 | Returns: 66 | [matplotlib ListedColormap]: this matplotlib color object can be 67 | used as a cmap in any plot. 68 | 69 | Example: 70 | .. code-block:: python 71 | 72 | import numpy as np 73 | import matplotlib.pyplot as plt 74 | from rsatoolbox.vis.colors import rdm_colormap_classic 75 | plt.imshow(np.random.rand(10,10),cmap=rdm_colormap_classic()) 76 | plt.colorbar() 77 | plt.show() 78 | 79 | (ported from Niko Kriegeskorte's RDMcolormap.m) 80 | """ 81 | 82 | # blue-cyan-gray-red-yellow with increasing V (BCGRYincV) 83 | anchor_cols = np.array([ 84 | [0, 0, 1], 85 | [0, 1, 1], 86 | [.5, .5, .5], 87 | [1, 0, 0], 88 | [1, 1, 0], 89 | ]) 90 | 91 | # skimage rgb2hsv is intended for 3d images (RGB) 92 | # here we add a new axis to our 2d anchorCols to satisfy 93 | # skimage, and then squeeze 94 | anchor_cols_hsv = rgb2hsv(anchor_cols[np.newaxis, :]).squeeze() 95 | 96 | inc_v_weight = 1 97 | anchor_cols_hsv[:, 2] = (1 - inc_v_weight) * anchor_cols_hsv[:, 2] + \ 98 | inc_v_weight * np.linspace(0.5, 1, anchor_cols.shape[0]).T 99 | 100 | # anchorCols = brightness(anchorCols) 101 | anchor_cols = hsv2rgb(anchor_cols_hsv[np.newaxis, :]).squeeze() 102 | 103 | cols = color_scale(n_cols, anchor_cols, monitor) 104 | 105 | cmap = ListedColormap(cols) 106 | cmap.set_bad('white') 107 | 108 | return cmap 109 | -------------------------------------------------------------------------------- /src/rsatoolbox/vis/rdm.mplstyle: -------------------------------------------------------------------------------- 1 | font.size: 9 2 | font.style: normal 3 | axes.grid: True 4 | axes.titlesize: 10 5 | axes.titleweight: bold 6 | axes.labelsize: 10 7 | axes.labelweight: normal 8 | grid.color: white 9 | xtick.major.size: 0 10 | xtick.labelsize: 9 11 | ytick.labelsize: 9 12 | ytick.major.size: 0 13 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/tests/__init__.py -------------------------------------------------------------------------------- /tests/data/Meadows_myExp_v_v1_arrangement_1D.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/tests/data/Meadows_myExp_v_v1_arrangement_1D.mat -------------------------------------------------------------------------------- /tests/data/Meadows_myExp_v_v1_cuddly-bunny_3_1D.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/tests/data/Meadows_myExp_v_v1_cuddly-bunny_3_1D.mat -------------------------------------------------------------------------------- /tests/data/README.md: -------------------------------------------------------------------------------- 1 | ## This directory holds sample data files to test i/o. -------------------------------------------------------------------------------- /tests/requirements.txt: -------------------------------------------------------------------------------- 1 | ## additional dependencies not listed in root requirements.txt 2 | pytest 3 | tqdm 4 | joblib 5 | parameterized 6 | mne 7 | -------------------------------------------------------------------------------- /tests/test_colors.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests the port of the matlab colormap 3 | 4 | @author: iancharest 5 | """ 6 | from unittest import TestCase 7 | import numpy as np 8 | 9 | 10 | class ColorTests(TestCase): 11 | 12 | def test_color_scale(self): 13 | from rsatoolbox.vis.colors import color_scale 14 | n_cols = 10 15 | cols = color_scale(n_cols) 16 | n_cols_returned, n_rgb = cols.shape 17 | self.assertEqual(n_cols_returned, n_cols) 18 | self.assertEqual(n_rgb, 3) 19 | 20 | def test_rdm_colormap(self): 21 | from rsatoolbox.vis.colors import rdm_colormap_classic 22 | n_cols = 10 23 | cols = rdm_colormap_classic(n_cols) 24 | n_cols_returned, n_rgb = cols.colors.shape 25 | last_color = [1., 1., 0] 26 | self.assertEqual(n_cols_returned, n_cols) 27 | self.assertEqual(n_rgb, 3) 28 | np.testing.assert_array_almost_equal(last_color, cols.colors[-1]) 29 | -------------------------------------------------------------------------------- /tests/test_data_ops_merge.py: -------------------------------------------------------------------------------- 1 | """Tests for the merge operation on Datasets 2 | """ 3 | from __future__ import annotations 4 | from unittest import TestCase 5 | from numpy.testing import assert_array_equal 6 | 7 | 8 | class MergeTests(TestCase): 9 | 10 | def test_merge_subsets(self): 11 | """Test the existing 'merge_subsets' interface 12 | """ 13 | import numpy as np 14 | import rsatoolbox.data.dataset as rsd 15 | measurements = np.random.rand(4, 10) 16 | des = {'session': 0, 'subj': 0} 17 | obs_des = {'conds': np.array([str(i) for i in range(1, 5)])} 18 | chn_des = {'rois': np.array([chr(r) for r in range(65, 75)])} 19 | test_data = rsd.Dataset( 20 | measurements=measurements, 21 | descriptors=des, 22 | obs_descriptors=obs_des, 23 | channel_descriptors=chn_des 24 | ) 25 | subsets = test_data.split_obs('conds') 26 | test_data_merged = rsd.merge_subsets(subsets) 27 | np.testing.assert_array_equal( 28 | test_data_merged.measurements, 29 | test_data.measurements) 30 | self.assertEqual(test_data_merged.descriptors, 31 | test_data.descriptors) 32 | np.testing.assert_array_equal( 33 | test_data_merged.obs_descriptors['conds'], 34 | test_data.obs_descriptors['conds']) 35 | np.testing.assert_array_equal( 36 | test_data_merged.channel_descriptors['rois'], 37 | test_data.channel_descriptors['rois']) 38 | 39 | def test_merge_datasets_standard(self): 40 | """Merge two standard datasets 41 | """ 42 | from numpy.random import rand 43 | import numpy 44 | from rsatoolbox.data.dataset import Dataset 45 | from rsatoolbox.data.ops import merge_datasets 46 | ds1 = Dataset( 47 | measurements=rand(3, 2), 48 | descriptors=dict(foo='bar', same='us'), 49 | obs_descriptors=dict(cond=numpy.array(['a', 'b', 'c'])), 50 | channel_descriptors=dict(name=numpy.array(['x', 'y'])) 51 | ) 52 | ds2 = Dataset( 53 | measurements=rand(3, 2)+1, 54 | descriptors=dict(foo='baz', same='us'), 55 | obs_descriptors=dict(cond=numpy.array(['b', 'c', 'd'])), 56 | channel_descriptors=dict(name=numpy.array(['x', 'y'])) 57 | ) 58 | ds = merge_datasets([ds1, ds2]) 59 | self.assertIsInstance(ds, Dataset) 60 | exp_meas = numpy.concatenate([ 61 | ds1.measurements, ds2.measurements], axis=0) 62 | assert_array_equal(ds.measurements, exp_meas) 63 | assert_array_equal( 64 | ds.obs_descriptors.get('cond', []), 65 | ['a', 'b', 'c', 'b', 'c', 'd']) 66 | # dataset descriptors that vary should become obs descriptor 67 | assert_array_equal( 68 | ds.obs_descriptors.get('foo', []), 69 | ['bar']*3 + ['baz']*3) 70 | # dataset descriptors that are identical should remain 71 | self.assertEqual(ds.descriptors.get('same'), 'us') 72 | 73 | def test_merge_datasets_temporal(self): 74 | from numpy.random import rand 75 | import numpy 76 | from rsatoolbox.data.dataset import TemporalDataset 77 | from rsatoolbox.data.ops import merge_datasets 78 | ds1 = TemporalDataset( 79 | measurements=rand(3, 2, 4), 80 | time_descriptors=dict(time=numpy.array([0, 1, 2, 3])/10), 81 | ) 82 | ds2 = TemporalDataset( 83 | measurements=rand(3, 2, 4)+1, 84 | time_descriptors=dict(time=numpy.array([0, 1, 2, 3])/10) 85 | ) 86 | ds = merge_datasets([ds1, ds2]) 87 | self.assertIsInstance(ds, TemporalDataset) 88 | exp_meas = numpy.concatenate([ 89 | ds1.measurements, ds2.measurements], axis=0) 90 | assert_array_equal(ds.measurements, exp_meas) 91 | assert_array_equal( 92 | ds.time_descriptors.get('time', []), 93 | [0.0, 0.1, 0.2, 0.3]) 94 | -------------------------------------------------------------------------------- /tests/test_data_utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | test_data_utils 5 | Test for Dataset utils 6 | @author: alex 7 | """ 8 | 9 | import unittest 10 | import numpy as np 11 | from rsatoolbox.util import data_utils as du 12 | import rsatoolbox.data as rsd 13 | 14 | 15 | class TestGetUniqueUnsorted(unittest.TestCase): 16 | def setUp(self): 17 | self.full_str = "slight not what is near through aiming at what is far" 18 | self.unique_str = "slight not what is near through aiming at far" 19 | self.full_ints = np.array([99, 4, 66, 4, 33, 99]) 20 | self.unique_ints = np.array([99, 4, 66, 33]) 21 | 22 | measurements = np.zeros((4, 5)) 23 | des = {'session': 0, 'subj': 0} 24 | obs_des = {'conds': np.array(['cond_foo', 'cond_bar', 'cond_foo', 'cond_bar'])} 25 | chn_des = {'rois': np.array(['V1', 'V1', 'IT', 'IT', 'V4'])} 26 | self.data = rsd.Dataset(measurements=measurements, 27 | descriptors=des, 28 | obs_descriptors=obs_des, 29 | channel_descriptors=chn_des 30 | ) 31 | 32 | def test_get_unique_unsorted_str(self): 33 | self.array = np.array([self.full_str.split(' ')]) 34 | self.unique_unsorted = du.get_unique_unsorted(self.array) 35 | assert np.all(self.unique_unsorted == self.unique_str.split(' ')) 36 | 37 | def test_get_unique_unsorted_ints(self): 38 | self.array = np.array([self.full_ints]) 39 | self.unique_unsorted = du.get_unique_unsorted(self.array) 40 | assert np.all(self.unique_unsorted == self.unique_ints) 41 | 42 | def test_get_unique_unsorted_ds(self): 43 | unique_values = du.get_unique_unsorted(self.data.obs_descriptors['conds']) 44 | assert np.all(np.array(['cond_foo', 'cond_bar']) == unique_values) 45 | 46 | if __name__ == '__main__': 47 | unittest.main() 48 | -------------------------------------------------------------------------------- /tests/test_dataset_dataframe.py: -------------------------------------------------------------------------------- 1 | """Various tests for converting Datasets to pandas.DataFrame and reverse 2 | """ 3 | # pylint: disable=C0415 ## allow imports on test level 4 | from unittest import TestCase 5 | import numpy 6 | import pandas 7 | from numpy.testing import assert_array_equal 8 | 9 | 10 | class DatasetToDataframeTests(TestCase): 11 | """Acceptance test for converting to a dataframe and back. 12 | """ 13 | 14 | def setUp(self) -> None: 15 | self.rng = numpy.random.default_rng(0) 16 | return super().setUp() 17 | 18 | def test_dataset_to_dataframe_and_back(self): 19 | """Converting a Dataset to a dataframe, and then loading that dataframe 20 | as a DataSet should preserve data and some metadata. 21 | """ 22 | from rsatoolbox.data.dataset import Dataset 23 | ds_in = Dataset( 24 | measurements=self.rng.random((3, 2)), 25 | descriptors=dict(foo='bar'), 26 | obs_descriptors=dict(participant=['a', 'b', 'c']), 27 | channel_descriptors=dict(name=['x', 'y']) 28 | ) 29 | df = ds_in.to_df() 30 | assert_array_equal(df.x.values, ds_in.measurements[:, 0]) 31 | self.assertEqual( 32 | df.columns.values.tolist(), 33 | ['x', 'y', 'participant', 'foo'] 34 | ) 35 | ds_out = Dataset.from_df(df) 36 | assert_array_equal(ds_out.measurements, ds_in.measurements) 37 | self.assertEqual(ds_out.descriptors, ds_in.descriptors) 38 | self.assertEqual(ds_out.obs_descriptors, ds_in.obs_descriptors) 39 | self.assertEqual(ds_out.channel_descriptors, ds_in.channel_descriptors) 40 | 41 | def test_dataset_to_dataframe_spec_channel_desc(self): 42 | """Converting a Dataset to a dataframe, and 43 | we specify the descriptors to use. 44 | """ 45 | from rsatoolbox.data.dataset import Dataset 46 | ds_in = Dataset( 47 | measurements=self.rng.random((3, 2)), 48 | descriptors=dict(foo='bar'), 49 | obs_descriptors=dict(participant=['a', 'b', 'c']), 50 | channel_descriptors=dict(foc=['x', 'y'], bac=[1, 2]) 51 | ) 52 | df = ds_in.to_df(channel_descriptor='bac') 53 | assert_array_equal(df[1].values, ds_in.measurements[:, 0]) 54 | self.assertEqual( 55 | df.columns.values.tolist(), 56 | [1, 2, 'participant', 'foo'] 57 | ) 58 | 59 | def test_dataframe_to_dataset_spec_columns(self): 60 | """Creating a Dataset from a dataframe, and 61 | we specify the column roles. 62 | """ 63 | from rsatoolbox.data.dataset import Dataset 64 | df = pandas.DataFrame([ 65 | {'a': 1.1, 'b': 1, 3: 1.11, 'd': 'one', 'e': 1.111, 'f': 'bla'}, 66 | {'a': 2.2, 'b': 2, 3: 2.22, 'd': 'two', 'e': 2.222, 'f': 'bla'}, 67 | {'a': 3.3, 'b': 3, 3: 3.33, 'd': 'thr', 'e': 3.333, 'f': 'bla'}, 68 | {'a': 4.4, 'b': 4, 3: 4.44, 'd': 'fou', 'e': 4.444, 'f': 'bla'}, 69 | ]) 70 | ds = Dataset.from_df( 71 | df, 72 | channels=['a', 'b', 3], 73 | channel_descriptor='foo' 74 | ) 75 | assert_array_equal(ds.measurements, df[['a', 'b', 3]].values) 76 | self.assertEqual(ds.descriptors, dict(f='bla')) 77 | self.assertEqual(ds.obs_descriptors, dict( 78 | d=['one', 'two', 'thr', 'fou'], 79 | e=[1.111, 2.222, 3.333, 4.444] 80 | )) 81 | self.assertEqual(ds.channel_descriptors, dict(foo=['a', 'b', 3])) 82 | -------------------------------------------------------------------------------- /tests/test_descriptor_utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | test_descriptor_utils 5 | Test for descriptor utils 6 | @author: adkipnis 7 | """ 8 | 9 | import unittest 10 | import numpy as np 11 | 12 | 13 | class TestDescriptorUtils(unittest.TestCase): 14 | 15 | def test_format_descriptor(self): 16 | from rsatoolbox.util.descriptor_utils import format_descriptor 17 | descriptors = {'foo': 'bar', 'foz': 12.3} 18 | self.assertEqual( 19 | format_descriptor(descriptors), 20 | 'foo = bar\nfoz = 12.3\n' 21 | ) 22 | 23 | def test_parse_input_descriptor(self): 24 | from rsatoolbox.util.descriptor_utils import parse_input_descriptor 25 | descriptors = {'foo': 'bar', 'foz': 12.3} 26 | self.assertEqual( 27 | parse_input_descriptor(descriptors), 28 | descriptors 29 | ) 30 | self.assertEqual( 31 | parse_input_descriptor(None), 32 | {} 33 | ) 34 | 35 | def test_append_descriptor(self): 36 | from rsatoolbox.util.descriptor_utils import append_descriptor 37 | desc = {'index': np.arange(3), 38 | 'test1': [1, 3, 2], 39 | 'test2': ['a', 'b', 'c']} 40 | desc_app = append_descriptor(desc, desc) 41 | assert np.all(desc_app['test1'] == np.array([1, 3, 2, 1, 3, 2])) 42 | assert np.all(desc_app['test2'] 43 | == np.array(['a', 'b', 'c', 'a', 'b', 'c'])) 44 | 45 | def test_check_descriptor_length(self): 46 | from rsatoolbox.util.descriptor_utils import check_descriptor_length 47 | descriptors = {'foo': ['bar', 'bar2']} 48 | assert check_descriptor_length(descriptors, 2) 49 | assert not check_descriptor_length(descriptors, 3) 50 | descriptors = {'foo': ['bar']} 51 | assert check_descriptor_length(descriptors, 1) 52 | 53 | def test_check_descriptor_length_0d(self): 54 | """numpy casts str to 0d arrays (arrays with empty shape). 55 | This breaks things.""" 56 | from rsatoolbox.util.descriptor_utils import check_descriptor_length 57 | descriptors = {'foo': 'bar'} 58 | assert check_descriptor_length(descriptors, 1) 59 | 60 | def test_subset_descriptor(self): 61 | from rsatoolbox.util.descriptor_utils import subset_descriptor 62 | descriptors = {'foo': ['bar', 'bar2']} 63 | self.assertEqual( 64 | subset_descriptor(descriptors, 0), 65 | {'foo': ['bar']} 66 | ) 67 | self.assertEqual( 68 | subset_descriptor(descriptors, (0, 1)), 69 | {'foo': ['bar', 'bar2']} 70 | ) 71 | 72 | def test_check_descriptor_length_error(self): 73 | from rsatoolbox.util.descriptor_utils import check_descriptor_length_error 74 | descriptors = {'foo': ['bar', 'bar2']} 75 | check_descriptor_length_error(descriptors, 'test', 2) 76 | 77 | def test_desc_eq(self): 78 | from rsatoolbox.util.descriptor_utils import desc_eq 79 | from numpy import array 80 | self.assertTrue(desc_eq( 81 | dict(abc=['a', 'b', 'c'], idx=array([1.1, 2.2])), 82 | dict(abc=['a', 'b', 'c'], idx=array([1.1, 2.2])), 83 | )) 84 | self.assertFalse(desc_eq( 85 | dict(abc=['a', 'b', 'c'], idx=array([1.1, 2.2])), 86 | dict(abc=['a', 'b', 'c'], idx=array([1.1, 3.3])), 87 | ), 'desc_eq should be False if an array element is different') 88 | self.assertFalse(desc_eq( 89 | dict(abc=['a', 'b', 'c'], idx=array([1.1, 2.2])), 90 | dict(abc=['a', 'b', 'D'], idx=array([1.1, 3.3])), 91 | ), 'desc_eq should be False if a list element is different') 92 | self.assertFalse(desc_eq( 93 | dict(abc=['a', 'b', 'c'], idx=array([1.1, 2.2])), 94 | dict(abc=['a', 'b', 'c'], idx=array([1.1, 2.2]), foo=[4, 5]), 95 | ), 'desc_eq should be False if the keys are different') 96 | -------------------------------------------------------------------------------- /tests/test_io_fmriprep.py: -------------------------------------------------------------------------------- 1 | """Test input/output for fmriprep 2 | """ 3 | from unittest import TestCase 4 | from unittest.mock import patch, Mock 5 | from numpy.testing import assert_array_equal 6 | import pandas 7 | import numpy 8 | 9 | 10 | class TestFindFmriprepRuns(TestCase): 11 | 12 | @patch('rsatoolbox.io.fmriprep.BidsLayout') 13 | @patch('rsatoolbox.io.fmriprep.FmriprepRun') 14 | def test_find_fmriprep_runs(self, FmriprepRun, BidsLayout): 15 | from rsatoolbox.io.fmriprep import find_fmriprep_runs 16 | BidsLayout().find_mri_derivative_files.return_value = ['a', 'b'] 17 | FmriprepRun.side_effect = lambda f: 'run-'+f 18 | out = find_fmriprep_runs('/path') 19 | BidsLayout.assert_called_with('/path') 20 | self.assertEqual(out, ['run-a', 'run-b']) 21 | 22 | 23 | class TestFmriprepRun(TestCase): 24 | 25 | def test_FmriprepRun_siblings(self): 26 | from rsatoolbox.io.fmriprep import FmriprepRun 27 | bidsFile = Mock() 28 | mask = Mock() 29 | parc = Mock() 30 | sibs = dict( 31 | brain=mask, 32 | aparcaseg=parc 33 | ) 34 | bidsFile.get_mri_sibling.side_effect = lambda desc, **kw: sibs[desc] 35 | run = FmriprepRun(bidsFile) 36 | self.assertIs(run.get_mask(), mask.get_data().astype(bool)) 37 | self.assertIs(run.get_parcellation(), parc.get_data().astype(int)) 38 | 39 | def test_FmriprepRun_dataset_descriptors(self): 40 | from rsatoolbox.io.fmriprep import FmriprepRun 41 | bidsFile = Mock() 42 | bidsFile.modality = 'moda' 43 | bidsFile.sub = '05' 44 | bidsFile.ses = '04' 45 | bidsFile.task = 'T1' 46 | bidsFile.run = '03' 47 | bidsFile.mod = 'mod' 48 | run = FmriprepRun(bidsFile) 49 | descs = run.get_dataset_descriptors() 50 | self.assertEqual(descs, dict( 51 | sub='05', ses='04', run='03', task='T1' 52 | )) 53 | 54 | def test_FmriprepRun_obs_descriptors(self): 55 | from rsatoolbox.io.fmriprep import FmriprepRun 56 | bidsFile = Mock() 57 | bidsFile.get_events.return_value = pandas.DataFrame([ 58 | dict(trial_type='s1'), 59 | dict(trial_type='s2'), 60 | dict(trial_type='s1'), 61 | dict(trial_type='s3'), 62 | ]) 63 | run = FmriprepRun(bidsFile) 64 | descs = run.get_obs_descriptors() 65 | self.assertIn('trial_type', descs) 66 | self.assertEqual( 67 | list(descs['trial_type']), 68 | ['s1', 's2', 's1', 's3'] 69 | ) 70 | 71 | def test_FmriprepRun_obs_descriptors_collapsed(self): 72 | """If we set collapse_by_trial_type=true, 73 | observations should be collapsed by trial_type. 74 | """ 75 | from rsatoolbox.io.fmriprep import FmriprepRun 76 | bidsFile = Mock() 77 | bidsFile.get_events.return_value = pandas.DataFrame([ 78 | dict(trial_type='s1'), 79 | dict(trial_type='s2'), 80 | dict(trial_type='s1'), 81 | dict(trial_type='s3'), 82 | ]) 83 | run = FmriprepRun(bidsFile) 84 | descs = run.get_obs_descriptors(collapse_by_trial_type=True) 85 | self.assertIn('trial_type', descs) 86 | self.assertEqual( 87 | list(descs['trial_type']), 88 | ['s1', 's2', 's3'] 89 | ) 90 | 91 | def test_FmriprepRun_channel_descriptors(self): 92 | from rsatoolbox.io.fmriprep import FmriprepRun 93 | bidsFile = Mock() 94 | parc = Mock() 95 | parc.get_data.return_value = numpy.array([[0, 2], [0, 4]]) 96 | df = pandas.DataFrame([ 97 | dict(index=0, name='nothing'), 98 | dict(index=2, name='foo'), 99 | dict(index=4, name='bar'), 100 | ]) 101 | parc.get_key().get_frame.return_value = df 102 | bidsFile.get_mri_sibling.return_value = parc 103 | run = FmriprepRun(bidsFile) 104 | descs = run.get_channel_descriptors() 105 | self.assertIn('aparcaseg', descs) 106 | assert_array_equal( 107 | descs['aparcaseg'], 108 | ['nothing', 'foo', 'nothing', 'bar'] 109 | ) 110 | 111 | 112 | class TestEventsDesignMatrix(TestCase): 113 | 114 | def test_make_design_matrix(self): 115 | from rsatoolbox.io.fmriprep import make_design_matrix 116 | events = pandas.DataFrame([ 117 | dict(onset=1, duration=0.5, trial_type='a'), 118 | dict(onset=2, duration=0.5, trial_type='b'), 119 | dict(onset=3, duration=0.5, trial_type='a'), 120 | dict(onset=4, duration=0.5, trial_type='b'), 121 | dict(onset=5, duration=0.5, trial_type='a'), 122 | dict(onset=6, duration=0.5, trial_type='b'), 123 | ]) 124 | confounds = pandas.DataFrame([ 125 | dict(sig1=1.0, sig2=0.5), 126 | dict(sig1=1.1, sig2=0.4), 127 | dict(sig1=1.2, sig2=0.3), 128 | dict(sig1=1.3, sig2=0.2), 129 | ]) 130 | dm, pred_mask, dof = make_design_matrix(events, tr=2.0, n_vols=4, 131 | confounds=confounds) 132 | self.assertEqual(dm.shape, (4, 2+2)) 133 | self.assertEqual(dof, 0) 134 | assert_array_equal(pred_mask, [True, True, False, False]) 135 | -------------------------------------------------------------------------------- /tests/test_io_hdf5.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from unittest.mock import patch 3 | from importlib.metadata import version 4 | 5 | 6 | class Hdf5IOTests(TestCase): 7 | 8 | @patch('rsatoolbox.io.hdf5.File') 9 | def test_write_dict_hdf5_version(self, h5pyFile): 10 | """Check version tag matches current version 11 | """ 12 | from rsatoolbox.io.hdf5 import write_dict_hdf5 13 | h5pyFile().attrs = dict() 14 | write_dict_hdf5('a file path', dict()) 15 | self.assertEqual( 16 | h5pyFile().attrs.get('rsatoolbox_version'), 17 | version('rsatoolbox') 18 | ) 19 | -------------------------------------------------------------------------------- /tests/test_io_mne.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from os.path import join 3 | from tempfile import TemporaryDirectory 4 | from numpy.testing import assert_almost_equal, assert_array_equal 5 | import numpy 6 | 7 | 8 | class MneIOTests(TestCase): 9 | """Acceptance and unit tests for loading MNE data. 10 | """ 11 | 12 | def setUp(self) -> None: 13 | """Assemble MNE Epochs object from toy data 14 | """ 15 | import mne # should be installed as a test dependency 16 | self.test_dir = TemporaryDirectory() 17 | self.events = numpy.array([ 18 | [200, 0, 11], 19 | [1200, 0, 12], 20 | [2000, 0, 13], 21 | [2200, 0, 12], 22 | ]) 23 | t = numpy.arange(12).reshape([4, 3]) 24 | data = numpy.array([numpy.sin(t), numpy.cos(t)]) 25 | self.data = numpy.moveaxis(data, 0, 1) 26 | info = mne.create_info( 27 | ch_names=['A1', 'X32'], 28 | ch_types='eeg', 29 | sfreq=20 30 | ) 31 | self.epochs = mne.EpochsArray(self.data, info, self.events) 32 | return super().setUp() 33 | 34 | def store_test_epochs(self, fname: str) -> str: 35 | """Save the epochs object with this filename and return the full path 36 | """ 37 | fpath = join(self.test_dir.name, fname) 38 | self.epochs.save(fpath, verbose='error') 39 | return fpath 40 | 41 | def tearDown(self) -> None: 42 | """Delete any files created 43 | """ 44 | self.test_dir.cleanup() 45 | return super().tearDown() 46 | 47 | def test_read_epochs(self): 48 | """Acceptance test for loading a single MNE _epo.fif 49 | file as TemporalDataset 50 | """ 51 | from rsatoolbox.io.mne import read_epochs 52 | ds = read_epochs(self.store_test_epochs('test_epo.fif')) 53 | self.assertEqual(ds.measurements.shape, (4, 2, 3)) 54 | assert_almost_equal(ds.measurements, self.data) 55 | self.assertEqual(ds.descriptors.get('filename'), 'test_epo.fif') 56 | assert_array_equal( 57 | ds.obs_descriptors.get('event', []), 58 | [11, 12, 13, 12] 59 | ) 60 | self.assertEqual( 61 | ds.channel_descriptors.get('name'), 62 | ['A1', 'X32'] 63 | ) 64 | assert_almost_equal( 65 | ds.time_descriptors.get('time', []), 66 | [0, 0.05, 0.1] 67 | ) 68 | 69 | def test_load_descriptors_from_bids_filename(self): 70 | """read BIDS-style tab-separated events file as additional 71 | obs-descriptor 72 | """ 73 | from rsatoolbox.io.mne import read_epochs 74 | fpath = self.store_test_epochs('sub-01_run-02_task-abc_epo.fif') 75 | ds = read_epochs(fpath) 76 | self.assertEqual(ds.descriptors.get('sub'), '01') 77 | self.assertEqual(ds.descriptors.get('run'), '02') 78 | self.assertEqual(ds.descriptors.get('task'), 'abc') 79 | -------------------------------------------------------------------------------- /tests/test_io_pkl.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from unittest.mock import patch, sentinel 3 | from importlib.metadata import version 4 | 5 | 6 | class PickleIOTests(TestCase): 7 | 8 | @patch('rsatoolbox.io.pkl.pickle') 9 | def test_write_dict_pkl_version(self, pickle): 10 | """Check version tag matches current version 11 | """ 12 | from rsatoolbox.io.pkl import write_dict_pkl 13 | write_dict_pkl(sentinel.filepath, dict()) 14 | self.assertEqual( 15 | pickle.dump.call_args[0][0], 16 | dict(rsatoolbox_version=version('rsatoolbox')), 17 | ) 18 | -------------------------------------------------------------------------------- /tests/test_io_spm.py: -------------------------------------------------------------------------------- 1 | """Tests for SPM I/O functions 2 | """ 3 | from __future__ import annotations 4 | from typing import Dict 5 | from unittest import TestCase 6 | from unittest.mock import Mock, patch 7 | import numpy as np 8 | from os.path import join 9 | 10 | 11 | class TestIoSPM(TestCase): 12 | 13 | def setUp(self) -> None: 14 | self.nitools = Mock() 15 | 16 | def stub_spm_mat(self) -> Dict: 17 | return {'SPM': 18 | { 19 | 'nscan': [1, 2, 3], 20 | 'Vbeta': [dict(fname='a')], 21 | 'xY': { 22 | 'P': [ 23 | '/Users/jdoe/DoeLab Dropbox/the_proj/func/uas01_run01.nii,1 ', 24 | ], 25 | }, 26 | 'xX': { 27 | 'name': ['00012 b'], 28 | 'K': [dict(X0=None)], 29 | 'iC': np.array([1]), 30 | 'xKXs': dict(X=None), 31 | 'erdf': None, 32 | 'W': None, 33 | 'pKX': None 34 | } 35 | } 36 | } 37 | 38 | @patch('rsatoolbox.io.spm.loadmat') 39 | def test_basic_spmglm_usage(self, loadmat): 40 | loadmat.return_value = self.stub_spm_mat() 41 | self.nitools.sample_images.return_value = np.array([[4, 5, 6]]) 42 | from rsatoolbox.io.spm import SpmGlm 43 | spm = SpmGlm('/path', self.nitools) 44 | spm.get_info_from_spm_mat() 45 | [beta, _, info] = spm.get_betas('/pth/anat/M1_L.nii') 46 | self.assertEqual(beta.shape, (0, 3)) 47 | self.assertEqual(info['reg_name'][0], 'b') 48 | self.assertEqual(info['run_number'][0], 1) 49 | 50 | @patch('rsatoolbox.io.spm.loadmat') 51 | def test_adapt_spm_paths(self, loadmat): 52 | loadmat.return_value = self.stub_spm_mat() 53 | from rsatoolbox.io.spm import SpmGlm 54 | spm = SpmGlm('/path/glm_firstlevel', self.nitools) 55 | spm.get_info_from_spm_mat() 56 | self.assertEqual(spm.rawdata_files, [ 57 | '/path/func/uas01_run01.nii,1 ', 58 | ]) 59 | 60 | def test_relocate_file(self): 61 | from rsatoolbox.io.spm import SpmGlm 62 | spm = SpmGlm(join('/path', 'leaf'), self.nitools) 63 | self.assertEqual( 64 | spm.relocate_file('/bla/dip/func/abc.nii,1 '), 65 | '/path/func/abc.nii,1 ' 66 | ) 67 | self.assertEqual( 68 | spm.relocate_file('c:\\bla\\dip\\func\\abc.nii,2 '), 69 | '/path/func/abc.nii,2 ' 70 | ) 71 | -------------------------------------------------------------------------------- /tests/test_noise_ceiling.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Tue Feb 18 16:59:33 2020 5 | 6 | @author: heiko 7 | """ 8 | 9 | import unittest 10 | import numpy as np 11 | from parameterized import parameterized 12 | 13 | 14 | class TestNoiseCeiling(unittest.TestCase): 15 | 16 | def setUp(self) -> None: 17 | from rsatoolbox.rdm import RDMs 18 | self.rng = np.random.default_rng(0) 19 | dis = self.rng.random((11, 10)) # 11 5x5 rdms 20 | mes = "Euclidean" 21 | des = {'subj': 0} 22 | rdm_des = {'session': np.array([1, 1, 2, 2, 4, 5, 6, 7, 7, 7, 7])} 23 | pattern_des = {'type': np.array([0, 1, 2, 2, 4])} 24 | self.rdms = RDMs( 25 | dissimilarities=dis, 26 | rdm_descriptors=rdm_des, 27 | pattern_descriptors=pattern_des, 28 | dissimilarity_measure=mes, 29 | descriptors=des 30 | ) 31 | return super().setUp() 32 | 33 | def test_cv_noise_ceiling(self): 34 | from rsatoolbox.inference import cv_noise_ceiling 35 | from rsatoolbox.inference import sets_k_fold_rdm 36 | _, test_set, ceil_set = sets_k_fold_rdm( 37 | self.rdms, k_rdm=3, random=False) 38 | _, _ = cv_noise_ceiling(self.rdms, ceil_set, test_set, method='cosine') 39 | 40 | @parameterized.expand([ 41 | ['cosine'], 42 | ['rho-a'], 43 | ['tau-a'], 44 | ['spearman'], 45 | ['corr'], 46 | ]) 47 | def test_boot_noise_ceiling_runs_for_method(self, method): 48 | from rsatoolbox.inference import boot_noise_ceiling 49 | _, _ = boot_noise_ceiling(self.rdms, method=method) 50 | -------------------------------------------------------------------------------- /tests/test_pair_selection.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from unittest import TestCase 3 | from pandas.testing import assert_frame_equal 4 | from pandas import DataFrame 5 | from numpy import array 6 | from scipy.spatial.distance import squareform 7 | 8 | 9 | class PairSelectionTests(TestCase): 10 | 11 | def test_percentile_with_target(self): 12 | """From pairs which include the target pattern, 13 | return those that fall between the given percentiles. 14 | """ 15 | from rsatoolbox.rdm.rdms import RDMs 16 | from rsatoolbox.rdm.pairs import pairs_by_percentile 17 | rdms = RDMs( 18 | dissimilarities=squareform(array([ 19 | [0, 7, 5, 4, 3], 20 | [7, 0, 6, 5, 4], 21 | [5, 6, 0, 9, 9], 22 | [4, 5, 9, 0, 9], 23 | [3, 4, 9, 9, 0], 24 | ])), 25 | pattern_descriptors=dict(cond=array(['a', 'b', 'c', 'd', 'e'])) 26 | ) 27 | ## 25% lowest dissimilarities 28 | out = pairs_by_percentile(rdms, max=25, cond='a') 29 | assert_frame_equal(out, DataFrame([ 30 | dict(cond='e', dissim=3.0), 31 | ])) 32 | ## 40% - 80% mid range dissimilarities 33 | out = pairs_by_percentile(rdms, min=40, max=80, cond='b') 34 | assert_frame_equal(out, DataFrame([ 35 | dict(cond='c', dissim=6.0), 36 | dict(cond='d', dissim=5.0), 37 | ])) 38 | -------------------------------------------------------------------------------- /tests/test_rdms_pandas.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from unittest import TestCase 3 | from typing import TYPE_CHECKING, Union, List 4 | from numpy.testing import assert_array_equal 5 | import numpy 6 | from pandas import Series, DataFrame 7 | if TYPE_CHECKING: 8 | from numpy.typing import NDArray 9 | 10 | 11 | class RdmsToPandasTests(TestCase): 12 | 13 | def setUp(self) -> None: 14 | self.rng = numpy.random.default_rng(0) 15 | return super().setUp() 16 | 17 | def assertValuesEqual(self, 18 | actual: Series, 19 | expected: Union[NDArray, List]): 20 | assert_array_equal(numpy.asarray(actual.values), expected) 21 | 22 | def test_to_df(self): 23 | """Convert an RDMs object to a pandas DataFrame 24 | 25 | Default is long form; multiple rdms are stacked row-wise. 26 | """ 27 | from rsatoolbox.rdm.rdms import RDMs 28 | dissimilarities = self.rng.random((2, 6)) 29 | rdms = RDMs( 30 | dissimilarities, 31 | rdm_descriptors=dict(xy=[c for c in 'xy']), 32 | pattern_descriptors=dict(abcd=numpy.asarray([c for c in 'abcd'])) 33 | ) 34 | df = rdms.to_df() 35 | self.assertIsInstance(df, DataFrame) 36 | self.assertEqual(len(df.columns), 7) 37 | self.assertValuesEqual(df.dissimilarity, dissimilarities.ravel()) 38 | self.assertValuesEqual(df['rdm_index'], ([0] * 6) + ([1] * 6)) 39 | self.assertValuesEqual(df['xy'], (['x'] * 6) + (['y'] * 6)) 40 | self.assertValuesEqual(df['pattern_index_1'], 41 | ([0] * 3 + [1] * 2 + [2] * 1) * 2) 42 | self.assertValuesEqual(df['pattern_index_2'], [1, 2, 3, 2, 3, 3] * 2) 43 | self.assertValuesEqual(df['abcd_1'], [c for c in 'aaabbc'] * 2) 44 | self.assertValuesEqual(df['abcd_2'], [c for c in 'bcdcdd'] * 2) 45 | -------------------------------------------------------------------------------- /tests/test_searchlight.py: -------------------------------------------------------------------------------- 1 | """ 2 | searchlight tests 3 | @author: Daniel Lindh 4 | """ 5 | # pylint: disable=import-outside-toplevel, no-self-use 6 | import unittest 7 | import numpy as np 8 | 9 | 10 | class TestSearchlight(unittest.TestCase): 11 | def test__get_searchlight_neighbors(self): 12 | from rsatoolbox.util.searchlight import _get_searchlight_neighbors 13 | 14 | mask = np.zeros((5, 5, 5)) 15 | center = [2, 2, 2] 16 | mask[2, 2, 2] = 10 17 | radius = 2 18 | # a radius of 2 will give us 19 | neighbors = _get_searchlight_neighbors(mask, center, radius=radius) 20 | 21 | assert np.array(neighbors).shape == (3, 27) 22 | assert np.mean(mask[tuple(neighbors)]) == 10 / 27 23 | 24 | def test_get_volume_searchlight(self): 25 | from rsatoolbox.util.searchlight import get_volume_searchlight 26 | 27 | mask = np.array( 28 | [[[False, False, False], 29 | [False, True, False], 30 | [False, False, False]], 31 | 32 | [[False, True, False], 33 | [True, True, True], 34 | [False, True, False]], 35 | 36 | [[False, False, False], 37 | [False, True, False], 38 | [False, False, False]]], dtype=int) 39 | 40 | centers, neighbors = get_volume_searchlight( 41 | mask, radius=1, threshold=1.0) 42 | assert len(centers) == 7 43 | assert len(neighbors) == 7 44 | 45 | def test_get_searchlight_RDMs(self): 46 | from rsatoolbox.util.searchlight import get_searchlight_RDMs 47 | 48 | n_observations = 5 49 | n_voxels = 5 50 | rng = np.random.default_rng(0) 51 | data_2d = rng.random((n_observations, n_voxels)) 52 | centers = np.array([1, 3]) 53 | neighbors = [[0, 1, 2], [2, 3, 4]] 54 | events = np.arange(n_observations) 55 | 56 | sl_RDMs = get_searchlight_RDMs(data_2d, centers, neighbors, events) 57 | 58 | assert sl_RDMs.dissimilarities.shape == (2, 10) 59 | -------------------------------------------------------------------------------- /tests/test_simulation.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """Tests for the simulation subpackage 4 | """ 5 | #pylint: disable=import-outside-toplevel, no-self-use 6 | import unittest 7 | import numpy as np 8 | from scipy.spatial.distance import squareform 9 | import rsatoolbox 10 | import rsatoolbox.model as model 11 | 12 | 13 | class TestSimulation(unittest.TestCase): 14 | 15 | def test_make_design(self): 16 | import rsatoolbox.simulation.sim as sim 17 | # Test for make_design 18 | cond_vec, _ = sim.make_design(4, 8) 19 | self.assertEqual(cond_vec.size, 32) 20 | 21 | def test_make_signal(self): 22 | # Test make signal 23 | import rsatoolbox.simulation.sim as sim 24 | M = model.ModelFixed("test", np.array([2, 2, 2, 1, 1, 1])) 25 | RDM = M.predict(None) 26 | D = squareform(RDM) 27 | H = rsatoolbox.util.matrix.centering(D.shape[0]) 28 | G = -0.5 * (H @ D @ H) 29 | S = sim.make_signal(G, 40, make_exact=True) 30 | Diff = S@S.T/40 - G 31 | self.assertTrue(np.all(np.abs(Diff) < 1e-7)) 32 | 33 | def test_make_data(self): 34 | # Test for make_data 35 | import rsatoolbox.simulation.sim as sim 36 | cond_vec, _ = sim.make_design(4, 8) 37 | M = model.ModelFixed("test", np.array([2, 3, 4, 1, 1.1, 0.9])) 38 | D = sim.make_dataset(M, None, cond_vec, n_channel=40) 39 | self.assertEqual(D[0].n_obs, 32) 40 | self.assertEqual(D[0].n_channel, 40) 41 | 42 | 43 | if __name__ == '__main__': 44 | unittest.main() 45 | -------------------------------------------------------------------------------- /tests/test_util_matrix.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | test_util_matrix 5 | 6 | @author: jdiedrichsen 7 | """ 8 | 9 | import unittest 10 | import rsatoolbox.util as rsu 11 | import numpy as np 12 | 13 | 14 | class TestIndicator(unittest.TestCase): 15 | 16 | def test_indicator(self): 17 | a = np.array(range(0, 5)) 18 | a = np.concatenate((a, a)) 19 | X = rsu.matrix.indicator(a) 20 | n_row, n_col = X.shape 21 | self.assertEqual(n_row, 10) 22 | self.assertEqual(n_col, 5) 23 | self.assertEqual(X[0, 0], 1.0) 24 | 25 | def test_indicator_pos(self): 26 | a = np.array(range(0, 5)) 27 | a = np.concatenate((a, a)) 28 | X = rsu.matrix.indicator(a, positive=True) 29 | n_row, n_col = X.shape 30 | self.assertEqual(n_row, 10) 31 | self.assertEqual(n_col, 4) 32 | self.assertEqual(X[0, 0], 0.0) 33 | 34 | def test_pairwise(self): 35 | a = np.array(range(0, 5)) 36 | X = rsu.matrix.pairwise_contrast(a) 37 | n_row, n_col = X.shape 38 | self.assertEqual(n_row, 10) 39 | self.assertEqual(n_col, 5) 40 | self.assertEqual(X[0, 0], 1.0) 41 | 42 | def test_centering(self): 43 | X = rsu.matrix.centering(10) 44 | n_row, n_col = X.shape 45 | self.assertEqual(n_row, 10) 46 | self.assertEqual(n_col, 10) 47 | 48 | 49 | if __name__ == '__main__': 50 | unittest.main() 51 | -------------------------------------------------------------------------------- /tests/test_util_rdm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Tue Mar 17 10:17:51 2020 5 | 6 | @author: heiko 7 | """ 8 | 9 | import unittest 10 | import numpy as np 11 | 12 | 13 | class TestRdmUtils(unittest.TestCase): 14 | 15 | def test_batch_to_vectors(self): 16 | from rsatoolbox.util.rdm_utils import batch_to_vectors 17 | dis = np.zeros((8, 5, 5)) 18 | y, n_rdm, n_cond = batch_to_vectors(dis) 19 | assert y.shape[0] == 8 20 | assert y.shape[1] == 10 21 | assert n_rdm == 8 22 | assert n_cond == 5 23 | 24 | def test_batch_to_matrices(self): 25 | from rsatoolbox.util.rdm_utils import batch_to_matrices 26 | dis = np.zeros((8, 5, 5)) 27 | y, n_rdm, n_cond = batch_to_matrices(dis) 28 | assert y.shape[0] == 8 29 | assert y.shape[1] == 5 30 | assert y.shape[2] == 5 31 | assert n_rdm == 8 32 | assert n_cond == 5 33 | 34 | 35 | class TestPoolRDM(unittest.TestCase): 36 | 37 | def setUp(self) -> None: 38 | self.rng = np.random.default_rng(0) 39 | return super().setUp() 40 | 41 | def test_pool_standard(self): 42 | from rsatoolbox.rdm import RDMs 43 | from rsatoolbox.util.pooling import pool_rdm 44 | dissimilarities = self.rng.random((5, 10)) 45 | rdms = RDMs(dissimilarities) 46 | for method in ['euclid', 'cosine', 'corr', 'cosine_cov', 'corr_cov', 47 | 'spearman', 'rho-a', 'tau-b', 'tau-a']: 48 | pooled_rdm = pool_rdm(rdms, method=method) 49 | self.assertEqual(pooled_rdm.n_cond, rdms.n_cond) 50 | self.assertEqual(pooled_rdm.n_rdm, 1) 51 | 52 | def test_pool_nan(self): 53 | from rsatoolbox.rdm import RDMs 54 | from rsatoolbox.util.pooling import pool_rdm 55 | dissimilarities = self.rng.random((5, 10)) 56 | dissimilarities[:, 3] = np.nan 57 | rdms = RDMs(dissimilarities) 58 | for method in ['euclid', 'cosine', 'corr', 'cosine_cov', 'corr_cov', 59 | 'spearman', 'rho-a', 'tau-b', 'tau-a']: 60 | pooled_rdm = pool_rdm(rdms, method=method) 61 | self.assertEqual(pooled_rdm.n_cond, rdms.n_cond) 62 | self.assertEqual(pooled_rdm.n_rdm, 1) 63 | self.assertTrue(np.isnan(pooled_rdm.dissimilarities[0, 3]), 64 | 'nan got removed while pooling for %s' % method) 65 | self.assertFalse(np.isnan(pooled_rdm.dissimilarities[0, 4]), 66 | 'too many nans while pooling for %s' % method) 67 | 68 | def test_category_condition_idxs_2_categories_by_name(self): 69 | from rsatoolbox.util.rdm_utils import category_condition_idxs 70 | from rsatoolbox.rdm.rdms import RDMs 71 | 72 | n_rdm, n_cond = 4, 8 73 | dis = np.zeros((n_rdm, n_cond, n_cond)) 74 | rdms = RDMs(dissimilarities=dis, 75 | # 2 categories, P and Q 76 | pattern_descriptors={'type': list('PPPPQQQQ')}, 77 | dissimilarity_measure='Euclidean', 78 | descriptors={'subj': range(n_rdm)}) 79 | categories = category_condition_idxs(rdms, 'type') 80 | 81 | self.assertEqual(categories, { 82 | 'P': [0, 1, 2, 3], 83 | 'Q': [4, 5, 6, 7], 84 | }) 85 | 86 | def test_category_condition_idxs_2_categories_by_ints(self): 87 | from rsatoolbox.util.rdm_utils import category_condition_idxs 88 | from rsatoolbox.rdm.rdms import RDMs 89 | 90 | n_rdm, n_cond = 4, 8 91 | dis = np.zeros((n_rdm, n_cond, n_cond)) 92 | rdms = RDMs(dissimilarities=dis, 93 | dissimilarity_measure='Euclidean', 94 | descriptors={'subj': range(n_rdm)}) 95 | categories = category_condition_idxs(rdms, [1, 2, 1, 2, 1, 2, 1, 2]) 96 | 97 | self.assertEqual(categories, { 98 | 'Category 1': [0, 2, 4, 6], 99 | 'Category 2': [1, 3, 5, 7], 100 | }) 101 | -------------------------------------------------------------------------------- /tests/test_vis.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | test_data 5 | Test for visualization class 6 | @author: baihan 7 | """ 8 | from unittest import TestCase 9 | from unittest.mock import patch 10 | import numpy as np 11 | import rsatoolbox.rdm as rsr 12 | import rsatoolbox.vis as rsv 13 | from scipy.spatial.distance import pdist 14 | 15 | 16 | class TestMDS(TestCase): 17 | 18 | def setUp(self) -> None: 19 | self.rng = np.random.default_rng(0) 20 | return super().setUp() 21 | 22 | @patch('rsatoolbox.vis.scatter_plot.show_scatter') 23 | def test_vis_mds_output_shape_corresponds_to_inputs(self, show_scatter): 24 | from rsatoolbox.vis.scatter_plot import show_MDS 25 | dis = self.rng.random((8, 10)) 26 | mes = "Euclidean" 27 | des = {"session": 0, "subj": 0} 28 | rdms = rsr.RDMs(dissimilarities=dis, 29 | dissimilarity_measure=mes, descriptors=des) 30 | show_MDS(rdms) 31 | coords = show_scatter.call_args[0][1] 32 | self.assertEqual(coords.shape, (8, 5, 2)) 33 | 34 | @patch('rsatoolbox.vis.scatter_plot.show_scatter') 35 | def test_vis_weighted_mds_output_shape_corresponds_to_inputs(self, show_scatter): 36 | from rsatoolbox.vis.scatter_plot import show_MDS 37 | dis = self.rng.random((8, 10)) 38 | wes = self.rng.random((8, 10)) 39 | mes = "Euclidean" 40 | des = {"session": 0, "subj": 0} 41 | rdms = rsr.RDMs(dissimilarities=dis, 42 | dissimilarity_measure=mes, descriptors=des) 43 | show_MDS(rdms, weights=wes) 44 | coords = show_scatter.call_args[0][1] 45 | self.assertEqual(coords.shape, (8, 5, 2)) 46 | 47 | @patch('rsatoolbox.vis.scatter_plot.show_scatter') 48 | def test_vis_weighted_mds_output_behaves_like_mds(self, show_scatter): 49 | """ 50 | Fails stochastically as MDS solution not deterministic 51 | """ 52 | from rsatoolbox.vis.scatter_plot import show_MDS 53 | dis = self.rng.random((8, 10)) 54 | wes = np.ones((8, 10)) 55 | mes = "Euclidean" 56 | des = {"session": 0, "subj": 0} 57 | rdms = rsr.RDMs(dissimilarities=dis, 58 | dissimilarity_measure=mes, descriptors=des) 59 | show_MDS(rdms) 60 | mds_coords = show_scatter.call_args[0][1] 61 | show_MDS(rdms, weights=wes) 62 | wmds_coords = show_scatter.call_args[0][1] 63 | diff = pdist(mds_coords[0]) - pdist(wmds_coords[0]) 64 | self.assertLess(abs(diff.mean()), 0.02) 65 | 66 | 67 | class Test_Icon(TestCase): 68 | 69 | def setUp(self) -> None: 70 | self.rng = np.random.default_rng(0) 71 | return super().setUp() 72 | 73 | def test_Icon_no_error(self): 74 | import PIL 75 | from rsatoolbox.vis import Icon 76 | import matplotlib.pyplot as plt 77 | 78 | test_im = PIL.Image.fromarray(255 * self.rng.random((50, 100))) 79 | ic5 = Icon( 80 | image=test_im, color="red", border_width=5, make_square=True, resolution=20 81 | ) 82 | ic5.plot(0.8, 0.8) 83 | ic = Icon(image=255 * self.rng.random((50, 100)), cmap="Blues") 84 | ax = plt.axes(label="test") 85 | ic.plot(0.5, 0.5, ax=ax) 86 | ic2 = Icon(image=test_im, color="black", 87 | border_width=15, string="test") 88 | ic2.plot(0.8, 0.2, ax=ax, size=0.4) 89 | ic2.x_tick_label(0.5, 0.15, offset=7) 90 | ic2.y_tick_label(0.5, 0.25, offset=7) 91 | ic3 = Icon(image=test_im, color="red", 92 | border_width=5, make_square=True) 93 | ic3.plot(0.2, 0.2, size=0.4) 94 | ic4 = Icon(string="test") 95 | ic4.plot(0.2, 0.8, size=0.4) 96 | ic4.x_tick_label(0.75, 0.15, offset=7) 97 | ic4.y_tick_label(0.75, 0.25, offset=17) 98 | self.assertEqual(ic2.image, test_im) 99 | 100 | def test_Icon_from_rdm(self): 101 | from rsatoolbox.vis import Icon 102 | from rsatoolbox.rdm import RDMs 103 | rdm = RDMs(self.rng.random((1, 190))) 104 | ic = Icon(rdm) 105 | self.assertEqual(ic.final_image.size[0], 100) 106 | 107 | 108 | def _dummy_rdm(): 109 | import PIL 110 | import matplotlib.markers 111 | from collections import defaultdict 112 | 113 | rng = np.random.default_rng(0) 114 | 115 | markers = list(matplotlib.markers.MarkerStyle('').markers.keys()) 116 | images = np.meshgrid( 117 | np.linspace(0.5, 1.0, 50), np.linspace( 118 | 0.5, 1.0, 30), np.linspace(0.5, 1.0, 3) 119 | ) 120 | images = [ 121 | this_image * this_ind / 4.0 for this_ind in range(4) for this_image in images 122 | ] 123 | images = [PIL.Image.fromarray(255 * this_image, "RGB") 124 | for this_image in images] 125 | names = [ 126 | this_class + this_ex 127 | for this_class in ("a", "b", "c", "d") 128 | for this_ex in ("1", "2", "3") 129 | ] 130 | n_con = len(names) 131 | icons = defaultdict(list) 132 | for this_marker, this_image, this_name in zip(markers, images, names): 133 | icons["image"].append(rsv.Icon(image=this_image)) 134 | icons["marker"].append(rsv.Icon(marker=this_marker, color=[0, 0, 0])) 135 | icons["string"].append(rsv.Icon(string=this_name)) 136 | icons["text"].append(this_name) 137 | ROIs = ["X", "Y", "Z"] 138 | return rsr.concat( 139 | [ 140 | rsr.RDMs( 141 | dissimilarities=rng.random( 142 | (1, int((n_con - 1) * n_con / 2))), 143 | dissimilarity_measure="1-rho", 144 | rdm_descriptors=dict(ROI=this_roi), 145 | pattern_descriptors=icons, 146 | ) 147 | for this_roi in ROIs 148 | ] 149 | ) 150 | -------------------------------------------------------------------------------- /tests/test_vis_model.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from unittest import TestCase 3 | 4 | 5 | class Test_model_plot(TestCase): 6 | 7 | methods_supported = [ 8 | "corr", "cosine", "cosine_cov", "spearman", "corr_cov", 9 | "tau-b", "tau-a", "neg_riem_dist", "rho-a" 10 | ] 11 | 12 | def test_y_label(self): 13 | from rsatoolbox.vis.model_plot import _get_y_label 14 | for this_method in self.methods_supported: 15 | with self.subTest(msg=f"Testing {this_method}..."): 16 | y_label = _get_y_label(this_method) 17 | self.assertIsInstance(y_label, str) 18 | 19 | def test_descr(self): 20 | from rsatoolbox.vis.model_plot import _get_model_comp_descr 21 | descr = _get_model_comp_descr( 22 | "t-test", 23 | 5, 24 | "fwer", 25 | 0.05, 26 | 1000, 27 | "boostrap_rdm", 28 | "ci56", 29 | "droplets", 30 | "icicles", 31 | ) 32 | EXPECTED = ( 33 | 'Model comparisons: two-tailed t-test, p < 0.05, ' 34 | 'Bonferroni-corrected for 10 model-pair comparisonsError bars ' 35 | 'indicate the 56.0% confidence interval.\nOne-sided comparisons ' 36 | 'of each model performance against 0 and against the lower-bound ' 37 | 'estimate of the noise ceiling are Bonferroni-corrected for ' 38 | '5 models.') 39 | self.assertEqual(EXPECTED, descr) 40 | -------------------------------------------------------------------------------- /tests/test_vis_plot_rdm.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from tests.test_vis import _dummy_rdm 3 | import numpy 4 | 5 | 6 | class Test_rdm_plot(TestCase): 7 | """"" 8 | "No Error" tests 9 | """ 10 | 11 | @classmethod 12 | def setUpClass(cls): 13 | cls.rdm = _dummy_rdm() 14 | 15 | def setUp(self) -> None: 16 | import matplotlib.pyplot 17 | matplotlib.pyplot.close('all') 18 | return super().setUp() 19 | 20 | def test_show_rdm_no_arg_no_error(self): 21 | """regression test for crashes when gridlines is None (and needs to be set to [] 22 | internally to avoid breaking mpl""" 23 | from rsatoolbox.vis.rdm_plot import show_rdm 24 | show_rdm(self.rdm[0]) 25 | 26 | def test_show_rdm_text_label_no_error(self): 27 | """test RDM visualisation with vanilla Matplotlib text labels.""" 28 | from rsatoolbox.vis.rdm_plot import show_rdm 29 | show_rdm(self.rdm[0], pattern_descriptor="index") 30 | 31 | def test_show_rdm_icon_image_no_error(self): 32 | from rsatoolbox.vis.rdm_plot import show_rdm 33 | show_rdm(self.rdm[0], pattern_descriptor="image") 34 | 35 | def test_show_rdm_icon_image_groups_no_error(self): 36 | from rsatoolbox.vis.rdm_plot import show_rdm 37 | show_rdm(self.rdm[0], pattern_descriptor="image", num_pattern_groups=4) 38 | 39 | def test_show_rdm_icon_marker_no_error(self): 40 | from rsatoolbox.vis.rdm_plot import show_rdm 41 | show_rdm(self.rdm[0], pattern_descriptor="marker") 42 | 43 | def test_show_rdm_icon_string_no_error(self): 44 | from rsatoolbox.vis.rdm_plot import show_rdm 45 | show_rdm(self.rdm[0], pattern_descriptor="string") 46 | 47 | def test_contour_coords(self): 48 | """Helper function determines edge coordinates for a 2d mask 49 | """ 50 | from rsatoolbox.vis.rdm_plot import _contour_coords 51 | mask = numpy.array([ 52 | [0, 0, 0, 0, 0], 53 | [0, 1, 1, 0, 0], 54 | [0, 0, 1, 1, 0], 55 | [0, 0, 1, 1, 0], 56 | [0, 0, 0, 0, 0], 57 | ]) 58 | OFFSET = 0 59 | def with_offset(*args): 60 | return tuple([v+OFFSET for v in args]) 61 | expected = [ 62 | with_offset(1, 1, 2, 1), 63 | with_offset(2, 2, 1, 2), 64 | with_offset(1, 2, 1, 1), 65 | 66 | with_offset(2, 1, 3, 1), 67 | with_offset(3, 1, 3, 2), 68 | 69 | with_offset(2, 3, 2, 2), # 3rd pix, left side 70 | 71 | with_offset(3, 4, 2, 4), # pixel below that one, bottom 72 | with_offset(2, 4, 2, 3), # left 73 | 74 | with_offset(3, 2, 4, 2), 75 | with_offset(4, 2, 4, 3), 76 | 77 | with_offset(4, 3, 4, 4), 78 | with_offset(4, 4, 3, 4), 79 | ] 80 | actual = list(_contour_coords(mask, offset=OFFSET)) 81 | self.assertEqual(actual, expected) 82 | -------------------------------------------------------------------------------- /tests/test_vis_rdm_plot_conf.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from unittest.mock import Mock 3 | from numpy.testing import assert_array_equal 4 | import numpy 5 | 6 | 7 | class TestRdmPlot(TestCase): 8 | 9 | def test_from_show_rdm_args__nrow_ncolumn(self): 10 | from rsatoolbox.vis.rdm_plot import MultiRdmPlot, Symmetry 11 | rdms = Mock() 12 | rdms.n_cond = 3 13 | rdms.n_rdm = 10 14 | rdms.dissimilarities.shape = (10, 3) 15 | rdms.get_matrices.return_value = numpy.zeros([10, 3, 3]) 16 | conf = MultiRdmPlot.from_show_rdm_args( 17 | rdms, 18 | pattern_descriptor = None, 19 | cmap = 'bone', 20 | rdm_descriptor=None, 21 | n_column = None, 22 | n_row = None, 23 | show_colorbar = 'figure', 24 | gridlines = None, 25 | num_pattern_groups = None, 26 | figsize = None, 27 | nanmask = "diagonal", 28 | style = None, 29 | vmin = None, 30 | vmax = None, 31 | icon_spacing = 1.0, 32 | linewidth = 0.5, 33 | overlay = None, 34 | overlay_color='#00ff0050', 35 | overlay_symmetry=Symmetry.UPPER, 36 | contour = None, 37 | contour_color = 'red', 38 | contour_symmetry=Symmetry.BOTH 39 | ) 40 | self.assertEqual(conf.n_column, 4) 41 | self.assertEqual(conf.n_row, 3) 42 | 43 | def test_from_show_rdm_args__multi_contour_overlay(self): 44 | from rsatoolbox.vis.rdm_plot import MultiRdmPlot, Symmetry 45 | rdms = Mock() 46 | rdms.n_cond = 3 47 | rdms.n_rdm = 10 48 | rdms.get_matrices.return_value = numpy.zeros([10, 3, 3]) 49 | mask_rdm = Mock() 50 | mask_rdm.dissimilarities = numpy.array([[0, 1, 0, 1, 0, 1]]) 51 | rdms.subset.return_value = mask_rdm 52 | rdms.dissimilarities = numpy.zeros([10, 6]) 53 | conf = MultiRdmPlot.from_show_rdm_args( 54 | rdms, 55 | pattern_descriptor = None, 56 | cmap = 'bone', 57 | rdm_descriptor=None, 58 | n_column = None, 59 | n_row = None, 60 | show_colorbar = 'figure', 61 | gridlines = None, 62 | num_pattern_groups = None, 63 | figsize = None, 64 | nanmask = "diagonal", 65 | style = None, 66 | vmin = None, 67 | vmax = None, 68 | icon_spacing = 1.0, 69 | linewidth = 0.5, 70 | overlay = numpy.array([0,0,0,1,1,1]), 71 | overlay_color='#00ff0050', 72 | overlay_symmetry=Symmetry.BOTH, 73 | contour = ('name', 'foo'), 74 | contour_color = 'red', 75 | contour_symmetry=Symmetry.LOWER 76 | ) 77 | assert_array_equal(conf.overlay, numpy.array([0, 0, 0, 1, 1, 1])) 78 | assert_array_equal(conf.contour, numpy.array([0, 1, 0, 1, 0, 1])) 79 | 80 | def test_single_from_show_rdm_panel_args(self): 81 | from rsatoolbox.vis.rdm_plot import SingleRdmPlot, Symmetry 82 | rdms = Mock() 83 | rdms.n_cond = 3 84 | rdms.n_rdm = 1 85 | rdms.get_matrices.return_value = numpy.zeros([1, 3, 3]) 86 | rdms.rdm_descriptors = dict() 87 | mask_rdm = Mock() 88 | mask_rdm.dissimilarities = numpy.array([[0, 1, 0, 1, 0, 1]]) 89 | rdms.dissimilarities = numpy.zeros([10, 6]) 90 | conf = SingleRdmPlot.from_show_rdm_panel_args( 91 | rdms = rdms, 92 | cmap = 'bone', 93 | nanmask = None, 94 | rdm_descriptor = None, 95 | gridlines = None, 96 | vmin = None, 97 | vmax = None, 98 | overlay = numpy.array([0, 0, 0, 1, 1, 1]), 99 | overlay_color='#00ff0050', 100 | overlay_symmetry=Symmetry.UPPER, 101 | contour = numpy.array([0, 1, 0, 1, 0, 1]), 102 | contour_color = 'red', 103 | contour_symmetry=Symmetry.LOWER 104 | ) 105 | assert_array_equal(conf.overlay, numpy.array([0, 0, 0, 1, 1, 1])) 106 | assert_array_equal(conf.contour, numpy.array([0, 1, 0, 1, 0, 1])) 107 | 108 | def test_overlay_contour_mask_single_from_show_rdm_panel_args(self): 109 | from rsatoolbox.vis.rdm_plot import SingleRdmPlot, Symmetry 110 | rdms = Mock() 111 | rdms.n_cond = 3 112 | rdms.n_rdm = 1 113 | rdms.get_matrices.return_value = numpy.zeros([1, 3, 3]) 114 | rdms.rdm_descriptors = dict() 115 | mask_rdm = Mock() 116 | mask_rdm.dissimilarities = numpy.array([[0, 1, 0, 1, 0, 1]]) 117 | rdms.dissimilarities = numpy.zeros([10, 6]) 118 | conf = SingleRdmPlot.from_show_rdm_panel_args( 119 | rdms = rdms, 120 | cmap = 'bone', 121 | nanmask = None, 122 | rdm_descriptor = None, 123 | gridlines = None, 124 | vmin = None, 125 | vmax = None, 126 | overlay = numpy.array([0, 0, 0, 1, 1, 1]), 127 | overlay_color='#00ff0050', 128 | overlay_symmetry=Symmetry.UPPER, 129 | contour = numpy.array([0, 0, 0, 1, 1, 1]), 130 | contour_color = 'red', 131 | contour_symmetry=Symmetry.LOWER 132 | ) 133 | assert_array_equal(conf.overlay_mask, numpy.array([ 134 | [0, 0, 0, 0], 135 | [0, 0, 1, 1], 136 | [0, 0, 0, 1], 137 | [0, 0, 0, 0] 138 | ])) 139 | assert_array_equal(conf.contour_mask, numpy.array([ 140 | [0, 0, 0, 0], 141 | [0, 0, 0, 0], 142 | [0, 1, 0, 0], 143 | [0, 1, 1, 0] 144 | ])) 145 | -------------------------------------------------------------------------------- /wiki/README.md: -------------------------------------------------------------------------------- 1 | # wiki 2 | 3 | This directory contains resources to be linked to from the [Github wiki](https://github.com/rsagroup/rsatoolbox/wiki). 4 | -------------------------------------------------------------------------------- /wiki/SoftwareFlowChart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsagroup/rsatoolbox/c61590560c271419b660e96f91190f3a5d7b60d7/wiki/SoftwareFlowChart.png --------------------------------------------------------------------------------