├── .prettierrc ├── .coveragerc ├── .eslintignore ├── .prettierignore ├── lineup_widget.json ├── tsconfig.eslint.json ├── .npmignore ├── pytest.ini ├── docs ├── source │ ├── introduction.rst │ ├── _static │ │ └── helper.js │ ├── examples │ │ └── index.rst │ ├── index.rst │ ├── develop-install.rst │ ├── installing.rst │ └── conf.py ├── environment.yml ├── Makefile └── make.bat ├── pyproject.toml ├── css └── widget.css ├── src ├── index.ts ├── version.ts ├── extension.ts ├── utils.ts ├── plugin.ts └── widget.ts ├── readthedocs.yml ├── lineup_widget ├── _version.py ├── _frontend.py ├── nbextension │ └── extension.js ├── __init__.py └── lineup.py ├── install.json ├── babel.config.js ├── codecov.yml ├── jest.config.js ├── .vscode └── settings.json ├── tsconfig.json ├── MANIFEST.in ├── .eslintrc.js ├── setup.py ├── .github └── workflows │ └── ci.yml ├── setup.cfg ├── LICENSE.txt ├── .gitattributes ├── webpack.config.js ├── .gitignore ├── package.json ├── README.md └── examples └── introduction.ipynb /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = lineup_widget/tests/* 3 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | **/*.d.ts 5 | tests -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | **/node_modules 3 | **/lib 4 | **/package.json -------------------------------------------------------------------------------- /lineup_widget.json: -------------------------------------------------------------------------------- 1 | { 2 | "load_extensions": { 3 | "lineup_widget/extension": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src/**/*.ts", "src/**/*.tsx"], 4 | "exclude": [] 5 | } -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | tests/ 4 | .jshintrc 5 | # Ignore any build output from python: 6 | dist/*.tar.gz 7 | dist/*.wheel 8 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | testpaths = lineup_widget/tests examples 3 | norecursedirs = node_modules .ipynb_checkpoints 4 | addopts = --nbval --current-env 5 | -------------------------------------------------------------------------------- /docs/source/introduction.rst: -------------------------------------------------------------------------------- 1 | ============= 2 | Introduction 3 | ============= 4 | 5 | .. todo:: 6 | 7 | add prose explaining project purpose and usage here 8 | -------------------------------------------------------------------------------- /docs/source/_static/helper.js: -------------------------------------------------------------------------------- 1 | var cache_require = window.require; 2 | 3 | window.addEventListener('load', function() { 4 | window.require = cache_require; 5 | }); 6 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["jupyter_packaging==0.7.9", "jupyterlab==3.*", "setuptools>=40.8.0", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | -------------------------------------------------------------------------------- /css/widget.css: -------------------------------------------------------------------------------- 1 | @import '~lineupjs/build/LineUpJS.css'; 2 | 3 | .lineup-engine > main > article { 4 | /* 5 | * override jupyter style 6 | */ 7 | line-height: 100%; 8 | } -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Samuel Gratzl 2 | // Distributed under the terms of the Modified BSD License. 3 | 4 | export * from './version'; 5 | export * from './widget'; 6 | -------------------------------------------------------------------------------- /readthedocs.yml: -------------------------------------------------------------------------------- 1 | type: sphinx 2 | python: 3 | version: 3.5 4 | pip_install: true 5 | extra_requirements: 6 | - examples 7 | - docs 8 | conda: 9 | file: docs/environment.yml 10 | -------------------------------------------------------------------------------- /lineup_widget/_version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # Copyright (c) Samuel Gratzl. 5 | # Distributed under the terms of the Modified BSD License. 6 | 7 | __version__ = "4.6.0dev" 8 | -------------------------------------------------------------------------------- /install.json: -------------------------------------------------------------------------------- 1 | { 2 | "packageManager": "python", 3 | "packageName": "lineup_widget", 4 | "uninstallInstructions": "Use your Python package manager (pip, conda, etc.) to uninstall the package lineup_widget" 5 | } 6 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | sourceMap: 'inline', 3 | presets: [ 4 | [ 5 | '@babel/preset-env', 6 | { 7 | targets: { 8 | node: 'current', 9 | }, 10 | }, 11 | ], 12 | ], 13 | }; 14 | -------------------------------------------------------------------------------- /docs/environment.yml: -------------------------------------------------------------------------------- 1 | 2 | name: lineup_widget_docs 3 | channels: 4 | - conda-forge 5 | dependencies: 6 | - python=3.* 7 | - nodejs 8 | - pandas 9 | - numpy 10 | - jupyter_sphinx 11 | - sphinx 12 | - sphinx_rtd_theme 13 | - nbsphinx 14 | - nbsphinx-link 15 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: off 2 | # show coverage in CI status, but never consider it a failure 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | target: 0% 8 | patch: 9 | default: 10 | target: 0% 11 | ignore: 12 | - "lineup_widget/tests" 13 | -------------------------------------------------------------------------------- /lineup_widget/_frontend.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # Copyright (c) Samuel Gratzl. 5 | # Distributed under the terms of the Modified BSD License. 6 | 7 | """ 8 | Information about the frontend package of the widgets. 9 | """ 10 | 11 | module_name = "lineup_widget" 12 | module_version = "^4.6.0" 13 | -------------------------------------------------------------------------------- /docs/source/examples/index.rst: -------------------------------------------------------------------------------- 1 | 2 | Examples 3 | ======== 4 | 5 | This section contains several examples generated from Jupyter notebooks. 6 | The widgets have been embedded into the page for demonstrative purposes. 7 | 8 | .. todo:: 9 | 10 | Add links to notebooks in examples folder similar to the initial 11 | one. This is a manual step to ensure only those examples that 12 | are suited for inclusion are used. 13 | 14 | 15 | .. toctree:: 16 | :glob: 17 | 18 | * 19 | -------------------------------------------------------------------------------- /lineup_widget/nbextension/extension.js: -------------------------------------------------------------------------------- 1 | // Entry point for the notebook bundle containing custom model definitions. 2 | // 3 | define(function() { 4 | "use strict"; 5 | 6 | window['requirejs'].config({ 7 | map: { 8 | '*': { 9 | 'lineup_widget': 'nbextensions/lineup_widget/index', 10 | }, 11 | } 12 | }); 13 | // Export the required load_ipython_extension function 14 | return { 15 | load_ipython_extension : function() {} 16 | }; 17 | }); -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | automock: false, 3 | moduleNameMapper: { 4 | '\\.(css|less|sass|scss)$': 'identity-obj-proxy', 5 | }, 6 | preset: 'ts-jest/presets/js-with-babel', 7 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 8 | testPathIgnorePatterns: ['/lib/', '/node_modules/'], 9 | testRegex: '/__tests__/.*.spec.ts[x]?$', 10 | transformIgnorePatterns: ['/node_modules/(?!(@jupyter(lab|-widgets)/.*)/)'], 11 | globals: { 12 | 'ts-jest': { 13 | tsconfig: '/tsconfig.json', 14 | }, 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "**/.git": true, 4 | "**/.svn": true, 5 | "**/.hg": true, 6 | "**/CVS": true, 7 | "**/.DS_Store": true, 8 | "**/*.pyc": true, 9 | "js/lib/**/*.d.ts": true, 10 | "js/lib/**/*.js": true, 11 | "**/*.map": true 12 | }, 13 | "javascript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": false, 14 | "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": false, 15 | "python.linting.flake8Enabled": false, 16 | "python.linting.enabled": true, 17 | "python.formatting.provider": "black" 18 | } 19 | -------------------------------------------------------------------------------- /src/version.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Samuel Gratzl 2 | // Distributed under the terms of the Modified BSD License. 3 | 4 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 5 | // @ts-ignore 6 | // eslint-disable-next-line @typescript-eslint/no-var-requires 7 | const data = require('../package.json'); 8 | 9 | /** 10 | * The _model_module_version/_view_module_version this package implements. 11 | * 12 | * The html widget manager assumes that this is the same as the npm package 13 | * version number. 14 | */ 15 | export const MODULE_VERSION = data.version; 16 | 17 | /* 18 | * The current package name. 19 | */ 20 | export const MODULE_NAME = data.name; 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "esModuleInterop":true, 5 | "lib": ["es2015", "dom"], 6 | "module": "commonjs", 7 | "moduleResolution": "node", 8 | "noEmitOnError": true, 9 | "noUnusedLocals": true, 10 | "outDir": "lib", 11 | "resolveJsonModule": true, 12 | "rootDir": "src", 13 | "skipLibCheck": true, 14 | "sourceMap": true, 15 | "strict": true, 16 | "strictPropertyInitialization": false, 17 | "target": "es2015", 18 | "types": ["jest"] 19 | }, 20 | "include": [ 21 | "src/**/*.ts", 22 | "src/**/*.tsx", 23 | ], 24 | "exclude": ["src/**/__tests__"] 25 | } 26 | -------------------------------------------------------------------------------- /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 | SPHINXPROJ = lineup_widget 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Jupyter Development Team. 2 | // Distributed under the terms of the Modified BSD License. 3 | 4 | // Entry point for the notebook bundle containing custom model definitions. 5 | // 6 | // Setup notebook base URL 7 | // 8 | // Some static assets may be required by the custom widget javascript. The base 9 | // url for the notebook is not known at build time and is therefore computed 10 | // dynamically. 11 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 12 | (window as any).__webpack_public_path__ = 13 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 14 | document.querySelector('body')!.getAttribute('data-base-url') + 15 | 'nbextensions/lineup_widget'; 16 | 17 | export * from './index'; 18 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Samuel Gratzl. 2 | // Distributed under the terms of the MIT License. 3 | 4 | import { buildRanking, LocalDataProvider, Ranking } from 'lineupjs'; 5 | 6 | export interface ILineUpRanking { 7 | group_by: string[]; 8 | sort_by: string[]; 9 | columns: string[]; 10 | } 11 | 12 | export function pushRanking( 13 | data: LocalDataProvider, 14 | ranking: ILineUpRanking 15 | ): Ranking { 16 | const r = buildRanking(); 17 | (ranking.columns || ['_*', '*']).forEach((col) => r.column(col)); 18 | (typeof ranking.sort_by === 'string' 19 | ? [ranking.sort_by] 20 | : ranking.sort_by || [] 21 | ).forEach((s) => r.sortBy(s)); 22 | (typeof ranking.group_by === 'string' 23 | ? [ranking.group_by] 24 | : ranking.group_by || [] 25 | ).forEach((s) => r.groupBy(s)); 26 | return r.build(data); 27 | } 28 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE.txt 2 | include README.md 3 | 4 | include setup.cfg 5 | include setup.py 6 | include pyproject.toml 7 | include pytest.ini 8 | include .coverage.rc 9 | 10 | include tsconfig.json 11 | include package.json 12 | include webpack.config.js 13 | include lineup_widget/labextension/*.tgz 14 | 15 | # Documentation 16 | graft docs 17 | exclude docs/\#* 18 | prune docs/build 19 | prune docs/gh-pages 20 | prune docs/dist 21 | 22 | # Examples 23 | graft examples 24 | 25 | # Tests 26 | graft tests 27 | prune tests/build 28 | 29 | # Javascript files 30 | graft lineup_widget/nbextension 31 | graft src 32 | graft css 33 | prune **/node_modules 34 | prune coverage 35 | prune lib 36 | 37 | # Patterns to exclude from any directory 38 | global-exclude *~ 39 | global-exclude *.pyc 40 | global-exclude *.pyo 41 | global-exclude .git 42 | global-exclude .ipynb_checkpoints 43 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | 2 | lineup_widget 3 | ===================================== 4 | 5 | Version: |release| 6 | 7 | A Custom Jupyter Widget Library 8 | 9 | 10 | Quickstart 11 | ---------- 12 | 13 | To get started with lineup_widget, install with pip:: 14 | 15 | pip install lineup_widget 16 | 17 | or with conda:: 18 | 19 | conda install lineup_widget 20 | 21 | 22 | Contents 23 | -------- 24 | 25 | .. toctree:: 26 | :maxdepth: 2 27 | :caption: Installation and usage 28 | 29 | installing 30 | introduction 31 | 32 | .. toctree:: 33 | :maxdepth: 1 34 | 35 | examples/index 36 | 37 | 38 | .. toctree:: 39 | :maxdepth: 2 40 | :caption: Development 41 | 42 | develop-install 43 | 44 | 45 | .. links 46 | 47 | .. _`Jupyter widgets`: https://jupyter.org/widgets.html 48 | 49 | .. _`notebook`: https://jupyter-notebook.readthedocs.io/en/latest/ 50 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | 'eslint:recommended', 4 | 'plugin:@typescript-eslint/eslint-recommended', 5 | 'plugin:@typescript-eslint/recommended', 6 | 'plugin:prettier/recommended' 7 | ], 8 | parser: '@typescript-eslint/parser', 9 | parserOptions: { 10 | project: 'tsconfig.eslint.json', 11 | sourceType: 'module' 12 | }, 13 | plugins: ['@typescript-eslint'], 14 | rules: { 15 | '@typescript-eslint/no-unused-vars': ['warn', { args: 'none' }], 16 | '@typescript-eslint/no-explicit-any': 'off', 17 | '@typescript-eslint/no-namespace': 'off', 18 | '@typescript-eslint/no-use-before-define': 'off', 19 | '@typescript-eslint/quotes': [ 20 | 'error', 21 | 'single', 22 | { avoidEscape: true, allowTemplateLiterals: false } 23 | ], 24 | curly: ['error', 'all'], 25 | eqeqeq: 'error', 26 | 'prefer-arrow-callback': 'error' 27 | } 28 | }; -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | set SPHINXPROJ=lineup_widget 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 20 | echo.installed, then set the SPHINXBUILD environment variable to point 21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 22 | echo.may add the Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /docs/source/develop-install.rst: -------------------------------------------------------------------------------- 1 | 2 | Developer install 3 | ================= 4 | 5 | 6 | To install a developer version of lineup_widget, you will first need to clone 7 | the repository:: 8 | 9 | git clone https://github.com/lineupjs/lineup_widget 10 | cd lineup_widget 11 | 12 | Next, install it with a develop install using pip:: 13 | 14 | pip install -e . 15 | 16 | 17 | If you are planning on working on the JS/frontend code, you should also do 18 | a link installation of the extension:: 19 | 20 | jupyter nbextension install [--sys-prefix / --user / --system] --symlink --py lineup_widget 21 | 22 | jupyter nbextension enable [--sys-prefix / --user / --system] --py lineup_widget 23 | 24 | with the `appropriate flag`_. Or, if you are using Jupyterlab:: 25 | 26 | jupyter labextension install . 27 | 28 | 29 | .. links 30 | 31 | .. _`appropriate flag`: https://jupyter-notebook.readthedocs.io/en/stable/extending/frontend_extensions.html#installing-and-enabling-extensions 32 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | from pathlib import Path 3 | 4 | from jupyter_packaging import ( 5 | create_cmdclass, 6 | install_npm, 7 | ensure_targets, 8 | combine_commands, 9 | ) 10 | 11 | JS_DIR = Path(__file__).resolve().parent / 'js' 12 | 13 | # Representative files that should exist after a successful build 14 | jstargets = [JS_DIR / 'dist' / 'index.js'] 15 | 16 | data_files_spec = [ 17 | ('share/jupyter/nbextensions/lineup_widget', 'lineup_widget/nbextension', '*.*'), 18 | ('share/jupyter/labextensions/lineup_widget', 'lineup_widget/labextension', '**'), 19 | ('share/jupyter/labextensions/lineup_widget', '.', 'install.json'), 20 | ('etc/jupyter/nbconfig/notebook.d', '.', 'lineup_widget.json'), 21 | ] 22 | 23 | cmdclass = create_cmdclass('jsdeps', data_files_spec=data_files_spec) 24 | cmdclass['jsdeps'] = combine_commands( 25 | install_npm(JS_DIR, npm=['yarn'], build_cmd='build:prod'), 26 | ensure_targets(jstargets), 27 | ) 28 | 29 | # See setup.cfg for other parameters 30 | setup(cmdclass=cmdclass) 31 | -------------------------------------------------------------------------------- /docs/source/installing.rst: -------------------------------------------------------------------------------- 1 | 2 | .. _installation: 3 | 4 | Installation 5 | ============ 6 | 7 | 8 | The simplest way to install lineup_widget is via pip:: 9 | 10 | pip install lineup_widget 11 | 12 | or via conda:: 13 | 14 | conda install lineup_widget 15 | 16 | 17 | If you installed via pip, and notebook version < 5.3, you will also have to 18 | install / configure the front-end extension as well. If you are using classic 19 | notebook (as opposed to Jupyterlab), run:: 20 | 21 | jupyter nbextension install [--sys-prefix / --user / --system] --py lineup_widget 22 | 23 | jupyter nbextension enable [--sys-prefix / --user / --system] --py lineup_widget 24 | 25 | with the `appropriate flag`_. If you are using Jupyterlab, install the extension 26 | with:: 27 | 28 | jupyter labextension install lineup_widget 29 | 30 | If you are installing using conda, these commands should be unnecessary, but If 31 | you need to run them the commands should be the same (just make sure you choose the 32 | `--sys-prefix` flag). 33 | 34 | 35 | .. links 36 | 37 | .. _`appropriate flag`: https://jupyter-notebook.readthedocs.io/en/stable/extending/frontend_extensions.html#installing-and-enabling-extensions 38 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: [push] 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Checkout 9 | uses: actions/checkout@v2 10 | - name: Install node 11 | uses: actions/setup-node@v3 12 | with: 13 | node-version: 16 14 | cache: 'npm' 15 | - name: Install Python 16 | uses: actions/setup-python@v4 17 | with: 18 | python-version: "3.10" 19 | 20 | - name: Setup pip cache 21 | uses: actions/cache@v3 22 | with: 23 | path: ~/.cache/pip 24 | key: pip-3.10-${{ hashFiles('package.json') }} 25 | restore-keys: | 26 | pip-3.10- 27 | pip- 28 | 29 | - name: Install dependencies 30 | run: | 31 | python -m pip install -U pip setuptools codecov 32 | npm install -g codecov 33 | - name: Test the extension 34 | run: | 35 | python -m pip install --upgrade -v -e ".[test, examples, docs]" 36 | yarn run lint:check 37 | 38 | # pytest 39 | yarn run test 40 | 41 | - name: Check docs can be build + links 42 | run: | 43 | sudo apt install -y pandoc 44 | pushd docs 45 | make html 46 | python -m pytest --check-links 47 | popd 48 | -------------------------------------------------------------------------------- /src/plugin.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Samuel Gratzl 2 | // Distributed under the terms of the Modified BSD License. 3 | 4 | import { Application, IPlugin } from '@phosphor/application'; 5 | 6 | import { Widget } from '@phosphor/widgets'; 7 | 8 | import { IJupyterWidgetRegistry } from '@jupyter-widgets/base'; 9 | 10 | import { LineUpModel, LineUpView, TaggleModel, TaggleView } from './widget'; 11 | 12 | import { MODULE_NAME, MODULE_VERSION } from './version'; 13 | 14 | const EXTENSION_ID = 'lineup_widget:plugin'; 15 | 16 | /** 17 | * The example plugin. 18 | */ 19 | const lineupPlugin: IPlugin, void> = { 20 | id: EXTENSION_ID, 21 | requires: [IJupyterWidgetRegistry], 22 | activate: activateWidgetExtension, 23 | autoStart: true, 24 | } as unknown as IPlugin, void>; 25 | // the "as unknown as ..." typecast above is solely to support JupyterLab 1 26 | // and 2 in the same codebase and should be removed when we migrate to Lumino. 27 | 28 | export default lineupPlugin; 29 | 30 | /** 31 | * Activate the widget extension. 32 | */ 33 | function activateWidgetExtension( 34 | app: Application, 35 | registry: IJupyterWidgetRegistry 36 | ): void { 37 | registry.registerWidget({ 38 | name: MODULE_NAME, 39 | version: MODULE_VERSION, 40 | exports: { 41 | LineUpModel, 42 | LineUpView, 43 | TaggleModel, 44 | TaggleView, 45 | }, 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal=1 3 | 4 | [metadata] 5 | long_description = file: README.md 6 | long_description_content_type = text/markdown 7 | license_file = LICENSE.txt 8 | 9 | [metadata] 10 | name = lineup_widget 11 | author = Samuel Gratzl 12 | author_email= sam@sgratzl.com 13 | version = attr: lineup_widget._version.__version__ 14 | description = Wrapper around the LineUp.js library for multi attribute rankings 15 | long_description = file: README.md 16 | long_description_content_type = text/markdown 17 | url = https://github.com/lineupjs/lineup_widget 18 | keywords = ipython, widgets, Jupyter, JupyterLab, JupyterLab3 19 | license = BSD 3-Clause License 20 | classifiers = 21 | License :: OSI Approved :: BSD License 22 | Development Status :: 4 - Beta 23 | Framework :: IPython 24 | Intended Audience :: Developers 25 | Intended Audience :: Science/Research 26 | Programming Language :: Python 27 | Programming Language :: Python :: 3.8 28 | Programming Language :: Python :: 3.9 29 | Programming Language :: Python :: 3.10 30 | Framework :: Jupyter 31 | Framework :: Jupyter :: JupyterLab 32 | Framework :: Jupyter :: JupyterLab :: 3 33 | Framework :: Jupyter :: JupyterLab :: Extensions 34 | Framework :: Jupyter :: JupyterLab :: Extensions :: Prebuilt 35 | 36 | [options] 37 | zip_safe = False 38 | include_package_data = True 39 | packages = find: 40 | install_requires = 41 | ipywidgets>=7.6.0,<9 42 | python_requires = >=3.8 43 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 Samuel Gratzl 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /lineup_widget/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # Copyright (c) Samuel Gratzl. 5 | # Distributed under the terms of the Modified BSD License. 6 | 7 | from .lineup import LineUpWidget, TaggleWidget 8 | 9 | from ._version import __version__ 10 | 11 | def _jupyter_labextension_paths(): 12 | """Called by Jupyter Lab Server to detect if it is a valid labextension and 13 | to install the widget 14 | Returns 15 | ======= 16 | src: Source directory name to copy files from. Webpack outputs generated files 17 | into this directory and Jupyter Lab copies from this directory during 18 | widget installation 19 | dest: Destination directory name to install widget files to. Jupyter Lab copies 20 | from `src` directory into /labextensions/ directory 21 | during widget installation 22 | """ 23 | return [{ 24 | 'src': 'labextension', 25 | 'dest': 'lineup_widget', 26 | }] 27 | 28 | 29 | def _jupyter_nbextension_paths(): 30 | """Called by Jupyter Notebook Server to detect if it is a valid nbextension and 31 | to install the widget 32 | Returns 33 | ======= 34 | section: The section of the Jupyter Notebook Server to change. 35 | Must be 'notebook' for widget extensions 36 | src: Source directory name to copy files from. Webpack outputs generated files 37 | into this directory and Jupyter Notebook copies from this directory during 38 | widget installation 39 | dest: Destination directory name to install widget files to. Jupyter Notebook copies 40 | from `src` directory into /nbextensions/ directory 41 | during widget installation 42 | require: Path to importable AMD Javascript module inside the 43 | /nbextensions/ directory 44 | """ 45 | return [{ 46 | 'section': 'notebook', 47 | 'src': 'nbextension', 48 | 'dest': 'lineup_widget', 49 | 'require': 'lineup_widget/extension' 50 | }] 51 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # These settings are for any web project 2 | 3 | # Handle line endings automatically for files detected as text 4 | # and leave all files detected as binary untouched. 5 | * text=auto eol=lf 6 | 7 | # 8 | # The above will handle all files NOT found below 9 | # 10 | 11 | # 12 | ## These files are text and should be normalized (Convert crlf => lf) 13 | # 14 | 15 | # source code 16 | *.php text 17 | *.css text 18 | *.sass text 19 | *.scss text 20 | *.less text 21 | *.styl text 22 | *.js text 23 | *.ts text 24 | *.coffee text 25 | *.json text 26 | *.htm text 27 | *.html text 28 | *.xml text 29 | *.txt text 30 | *.ini text 31 | *.inc text 32 | *.pl text 33 | *.rb text 34 | *.py text 35 | *.scm text 36 | *.sql text 37 | *.sh text eof=LF 38 | *.bat text 39 | 40 | # templates 41 | *.hbt text 42 | *.jade text 43 | *.haml text 44 | *.hbs text 45 | *.dot text 46 | *.tmpl text 47 | *.phtml text 48 | 49 | # server config 50 | .htaccess text 51 | 52 | # git config 53 | .gitattributes text 54 | .gitignore text 55 | 56 | # code analysis config 57 | .jshintrc text 58 | .jscsrc text 59 | .jshintignore text 60 | .csslintrc text 61 | 62 | # misc config 63 | *.yaml text 64 | *.yml text 65 | .editorconfig text 66 | 67 | # build config 68 | *.npmignore text 69 | *.bowerrc text 70 | Dockerfile text eof=LF 71 | 72 | # Heroku 73 | Procfile text 74 | .slugignore text 75 | 76 | # Documentation 77 | *.md text 78 | LICENSE text 79 | AUTHORS text 80 | 81 | 82 | # 83 | ## These files are binary and should be left untouched 84 | # 85 | 86 | # (binary is a macro for -text -diff) 87 | *.png binary 88 | *.jpg binary 89 | *.jpeg binary 90 | *.gif binary 91 | *.ico binary 92 | *.mov binary 93 | *.mp4 binary 94 | *.mp3 binary 95 | *.flv binary 96 | *.fla binary 97 | *.swf binary 98 | *.gz binary 99 | *.zip binary 100 | *.7z binary 101 | *.ttf binary 102 | *.pyc binary 103 | *.pdf binary 104 | 105 | # Source files 106 | # ============ 107 | *.pxd text 108 | *.py text 109 | *.py3 text 110 | *.pyw text 111 | *.pyx text 112 | *.sh text eol=lf 113 | *.json text 114 | 115 | # Binary files 116 | # ============ 117 | *.db binary 118 | *.p binary 119 | *.pkl binary 120 | *.pyc binary 121 | *.pyd binary 122 | *.pyo binary 123 | 124 | # Note: .db, .p, and .pkl files are associated 125 | # with the python modules ``pickle``, ``dbm.*``, 126 | # ``shelve``, ``marshal``, ``anydbm``, & ``bsddb`` 127 | # (among others). 128 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const version = require('./package.json').version; 3 | 4 | // Custom webpack rules 5 | const rules = [ 6 | { test: /\.ts$/, loader: 'ts-loader' }, 7 | { test: /\.js$/, loader: 'source-map-loader' }, 8 | { test: /\.css$/, use: ['style-loader', 'css-loader']} 9 | ]; 10 | 11 | // Packages that shouldn't be bundled but loaded at runtime 12 | const externals = ['@jupyter-widgets/base']; 13 | 14 | const resolve = { 15 | // Add '.ts' and '.tsx' as resolvable extensions. 16 | extensions: [".webpack.js", ".web.js", ".ts", ".js"] 17 | }; 18 | 19 | module.exports = [ 20 | /** 21 | * Notebook extension 22 | * 23 | * This bundle only contains the part of the JavaScript that is run on load of 24 | * the notebook. 25 | */ 26 | { 27 | entry: './src/extension.ts', 28 | output: { 29 | filename: 'index.js', 30 | path: path.resolve(__dirname, 'lineup_widget', 'nbextension'), 31 | libraryTarget: 'amd', 32 | publicPath: '', 33 | }, 34 | module: { 35 | rules: rules 36 | }, 37 | devtool: 'source-map', 38 | externals, 39 | resolve, 40 | }, 41 | 42 | /** 43 | * Embeddable lineup_widget bundle 44 | * 45 | * This bundle is almost identical to the notebook extension bundle. The only 46 | * difference is in the configuration of the webpack public path for the 47 | * static assets. 48 | * 49 | * The target bundle is always `dist/index.js`, which is the path required by 50 | * the custom widget embedder. 51 | */ 52 | { 53 | entry: './src/index.ts', 54 | output: { 55 | filename: 'index.js', 56 | path: path.resolve(__dirname, 'dist'), 57 | libraryTarget: 'amd', 58 | library: "lineup_widget", 59 | publicPath: 'https://unpkg.com/lineup_widget@' + version + '/dist/' 60 | }, 61 | devtool: 'source-map', 62 | module: { 63 | rules: rules 64 | }, 65 | externals, 66 | resolve, 67 | }, 68 | 69 | 70 | /** 71 | * Documentation widget bundle 72 | * 73 | * This bundle is used to embed widgets in the package documentation. 74 | */ 75 | { 76 | entry: './src/index.ts', 77 | output: { 78 | filename: 'embed-bundle.js', 79 | path: path.resolve(__dirname, 'docs', 'source', '_static'), 80 | library: "lineup_widget", 81 | libraryTarget: 'amd' 82 | }, 83 | module: { 84 | rules: rules 85 | }, 86 | devtool: 'source-map', 87 | externals, 88 | resolve, 89 | } 90 | 91 | ]; 92 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask instance folder 57 | instance/ 58 | 59 | # Scrapy stuff: 60 | .scrapy 61 | 62 | # Sphinx documentation 63 | docs/_build/ 64 | docs/source/_static/embed-bundle.js 65 | docs/source/_static/embed-bundle.js.map 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # IPython Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # dotenv 80 | .env 81 | 82 | # virtualenv 83 | venv/ 84 | ENV/ 85 | 86 | # Spyder project settings 87 | .spyderproject 88 | 89 | # Rope project settings 90 | .ropeproject 91 | 92 | # ========================= 93 | # Operating System Files 94 | # ========================= 95 | 96 | # OSX 97 | # ========================= 98 | 99 | .DS_Store 100 | .AppleDouble 101 | .LSOverride 102 | 103 | # Thumbnails 104 | ._* 105 | 106 | # Files that might appear in the root of a volume 107 | .DocumentRevisions-V100 108 | .fseventsd 109 | .Spotlight-V100 110 | .TemporaryItems 111 | .Trashes 112 | .VolumeIcon.icns 113 | 114 | # Directories potentially created on remote AFP share 115 | .AppleDB 116 | .AppleDesktop 117 | Network Trash Folder 118 | Temporary Items 119 | .apdisk 120 | 121 | # Windows 122 | # ========================= 123 | 124 | # Windows image file caches 125 | Thumbs.db 126 | ehthumbs.db 127 | 128 | # Folder config file 129 | Desktop.ini 130 | 131 | # Recycle Bin used on file shares 132 | $RECYCLE.BIN/ 133 | 134 | # Windows Installer files 135 | *.cab 136 | *.msi 137 | *.msm 138 | *.msp 139 | 140 | # Windows shortcuts 141 | *.lnk 142 | 143 | 144 | # NPM 145 | # ---- 146 | 147 | **/node_modules/ 148 | lineup_widget/nbextension/index.* 149 | *.eot 150 | *.ttf 151 | *.svg 152 | *.woff 153 | lineup_widget/labextension/*.tgz 154 | 155 | # Coverage data 156 | # ------------- 157 | **/coverage/ 158 | 159 | # Packed lab extensions 160 | lineup_widget/labextension 161 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lineup_widget", 3 | "version": "4.6.0", 4 | "description": "Wrapper around the LineUp.js library for multi attribute rankings", 5 | "keywords": [ 6 | "jupyter", 7 | "jupyterlab", 8 | "jupyterlab-extension", 9 | "widgets", 10 | "lineupjs" 11 | ], 12 | "files": [ 13 | "lib/**/*.js", 14 | "dist/*.js", 15 | "css/*.css" 16 | ], 17 | "homepage": "https://github.com/lineupjs/lineup_widget", 18 | "bugs": { 19 | "url": "https://github.com/lineupjs/lineup_widget/issues" 20 | }, 21 | "license": "MIT", 22 | "author": { 23 | "name": "Samuel Gratzl", 24 | "email": "sam@sgratzl.com", 25 | "url": "https://www.sgratzl.com" 26 | }, 27 | "main": "lib/index.js", 28 | "types": "./lib/index.d.ts", 29 | "repository": { 30 | "type": "git", 31 | "url": "https://github.com/lineupjs/lineup_widget" 32 | }, 33 | "scripts": { 34 | "build": "yarn run build:lib && yarn run build:nbextension && yarn run build:labextension:dev", 35 | "build:prod": "yarn run build:lib && yarn run build:nbextension && yarn run build:labextension", 36 | "build:labextension": "jupyter labextension build .", 37 | "build:labextension:dev": "jupyter labextension build --development True .", 38 | "build:lib": "tsc", 39 | "build:nbextension": "webpack", 40 | "clean": "yarn run clean:lib && yarn run clean:nbextension && yarn run clean:labextension", 41 | "clean:lib": "rimraf lib", 42 | "clean:labextension": "rimraf lineup_widget/labextension", 43 | "clean:nbextension": "rimraf lineup_widget/nbextension/static/index.js", 44 | "lint": "eslint . --ext .ts,.tsx --fix", 45 | "lint:check": "eslint . --ext .ts,.tsx", 46 | "prepack": "yarn run build:lib", 47 | "test": "jest --passWithNoTests", 48 | "watch": "npm-run-all -p watch:*", 49 | "watch:lib": "tsc -w", 50 | "watch:nbextension": "webpack --watch --mode=development", 51 | "watch:labextension": "jupyter labextension watch ." 52 | }, 53 | "dependencies": { 54 | "@jupyter-widgets/base": "^1.1.10 || ^2.0.0 || ^3.0.0 || ^4.0.0 || ^6.0.0", 55 | "lineupjs": "^4.6.2" 56 | }, 57 | "devDependencies": { 58 | "@babel/core": "^7.18.10", 59 | "@babel/preset-env": "^7.18.10", 60 | "@jupyterlab/builder": "^3.4.4", 61 | "@phosphor/application": "^1.7.3", 62 | "@phosphor/widgets": "^1.9.3", 63 | "@types/jest": "^28.1.6", 64 | "@types/webpack-env": "^1.17.0", 65 | "@typescript-eslint/eslint-plugin": "^5.33.0", 66 | "@typescript-eslint/parser": "^5.33.0", 67 | "acorn": "^8.8.0", 68 | "css-loader": "^6.7.1", 69 | "eslint": "^8.21.0", 70 | "eslint-config-prettier": "^8.5.0", 71 | "eslint-plugin-prettier": "^4.2.1", 72 | "fs-extra": "^10.1.0", 73 | "identity-obj-proxy": "^3.0.0", 74 | "jest": "^28.1.3", 75 | "mkdirp": "^1.0.4", 76 | "npm-run-all": "^4.1.5", 77 | "prettier": "^2.7.1", 78 | "rimraf": "^3.0.2", 79 | "source-map-loader": "^4.0.0", 80 | "style-loader": "^3.3.1", 81 | "ts-jest": "^28.0.7", 82 | "ts-loader": "^9.3.1", 83 | "typescript": "~4.7.4", 84 | "webpack": "^5.74.0", 85 | "webpack-cli": "^4.10.0" 86 | }, 87 | "jupyterlab": { 88 | "extension": "lib/plugin", 89 | "outputDir": "lineup_widget/labextension/", 90 | "sharedPackages": { 91 | "@jupyter-widgets/base": { 92 | "bundled": false, 93 | "singleton": true 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /lineup_widget/lineup.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (c) Samuel Gratzl. 4 | # Distributed under the terms of the MIT License. 5 | 6 | from ipywidgets import DOMWidget, Layout, ValueWidget, register 7 | from traitlets import default, Unicode, List, Dict, Bool, Enum, Union, Int 8 | import pandas as pd 9 | 10 | from lineup_widget._frontend import module_name, module_version 11 | 12 | class ALineUpWidget(ValueWidget, DOMWidget): 13 | _model_module = Unicode(module_name).tag(sync=True) 14 | _model_module_version = Unicode(module_version).tag(sync=True) 15 | _view_module = Unicode(module_name).tag(sync=True) 16 | _view_module_version = Unicode(module_version).tag(sync=True) 17 | 18 | _data = List(trait=Dict(), default_value=[]).tag(sync=True) 19 | _columns = List(trait=Dict(), default_value=[]).tag(sync=True) 20 | options = Dict(traits=dict(filterGlobally=Bool(), singleSelection=Bool(), animated=Bool(), 21 | sidePanel=Enum((True, False, 'collapsed')), summaryHeader=Bool(), overviewMode=Bool(), 22 | hierarchyIndicator=Bool(), labelRotation=Int(), ignoreUnsupportedBrowser=Bool(), 23 | rowHeight=Int(), rowPadding=Int(), groupHeight=Int(), groupPadding=Int(), 24 | expandLineOnHover=Bool(), defaultSlopeGraphMode=Enum(('item', 'band'))), 25 | default_value=dict(filterGlobally=True, singleSelection=False, animated=True, 26 | sidePanel='collapsed', summaryHeader=True, overviewMode=False, 27 | hierarchyIndicator=True, labelRotation=0, ignoreUnsupportedBrowser=False, 28 | rowHeight=18, rowPadding=2, groupHeight=40, groupPadding=5, 29 | expandLineOnHover=False, defaultSlopeGraphMode='item' 30 | )).tag(sync=True) 31 | rankings = List(trait=Dict(traits=dict(columns=List(trait=Union((Unicode(), Dict()))), sort_by=List(trait=Unicode()), 32 | group_by=List(trait=Unicode())), 33 | default_value=dict(columns=['_*', '*'], sort_by=[], group_by=[])), default_value=[]).tag( 34 | sync=True) 35 | value = List(trait=Int(), default_value=[]).tag(sync=True) 36 | 37 | def __init__(self, data=None, **kwargs): 38 | super(ALineUpWidget, self).__init__(**kwargs) 39 | if data is not None: 40 | self.data = data 41 | 42 | def add_ranking(self, columns=['_*', '*'], sort_by=[], group_by=[]): 43 | self.rankings = self.rankings + [dict(columns=columns, sort_by=sort_by, group_by=group_by)] 44 | 45 | @property 46 | def selection(self): 47 | return self.value 48 | 49 | @selection.setter 50 | def selection(self, value): 51 | self.value = value 52 | 53 | @property 54 | def data(self): 55 | return pd.DataFrame.from_dict(self._data) 56 | 57 | @data.setter 58 | def data(self, value): 59 | self._data = value.to_dict(orient='records') 60 | 61 | def to_desc(d): 62 | col = value[d] 63 | name = str(col.dtype) 64 | base = dict(type='string', column=d) 65 | if name.startswith('int') or name.startswith('float'): 66 | base['type'] = 'number' 67 | base['domain'] = [col.min(), col.max()] 68 | elif name == 'bool': 69 | base['type'] = 'boolean' 70 | elif name == 'category': 71 | base['type'] = 'categorical' 72 | base['categories'] = col.cat.categories 73 | return base 74 | 75 | self._columns = [to_desc(col) for col in value] 76 | 77 | @default('layout') 78 | def _default_layout(self): 79 | return Layout(height='600px', align_self='stretch') 80 | 81 | def on_selection_changed(self, callback): 82 | self.observe(lambda evt: callback(evt.new), 'value') 83 | 84 | 85 | @register 86 | class LineUpWidget(ALineUpWidget): 87 | """builds a LineUp widget wrapper 88 | """ 89 | _model_name = Unicode('LineUpModel').tag(sync=True) 90 | _view_name = Unicode('LineUpView').tag(sync=True) 91 | description = Unicode('', help="LineUp").tag(sync=True) 92 | 93 | 94 | @register 95 | class TaggleWidget(ALineUpWidget): 96 | """builds a Taggle widget wrapper 97 | """ 98 | _model_name = Unicode('TaggleModel').tag(sync=True) 99 | _view_name = Unicode('TaggleView').tag(sync=True) 100 | description = Unicode('', help="Taggle").tag(sync=True) 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | LineUp.js as Jupyter Widget 3 | ============================ 4 | 5 | [![License: MIT][mit-image]][mit-url] [![Github Actions][github-actions-image]][github-actions-url] 6 | 7 | 8 | LineUp is an interactive technique designed to create, visualize and explore rankings of items based on a set of heterogeneous attributes. 9 | This is a [Jupyter Widget](https://jupyter.org/widgets.html) wrapper around the JavaScript library [LineUp.js](https://github.com/lineupjs/lineupjs). Details about the LineUp visualization technique can be found at [http://lineup.caleydo.org](http://lineup.caleydo.org). 10 | 11 | Installation 12 | ------------ 13 | 14 | You can install using `pip`: 15 | 16 | ```bash 17 | pip install lineup_widget 18 | ``` 19 | 20 | If you are using Jupyter Notebook 5.2 or earlier, you may also need to enable 21 | the nbextension: 22 | ```bash 23 | jupyter nbextension enable --py [--sys-prefix|--user|--system] lineup_widget 24 | ``` 25 | 26 | Examples 27 | -------- 28 | 29 | [![Launch Binder][binder-image]][binder-url] 30 | 31 | ```python 32 | import lineup_widget 33 | import pandas as pd 34 | import numpy as np 35 | 36 | df = pd.DataFrame(np.random.randint(0,100,size=(100, 4)), columns=list('ABCD')) 37 | 38 | w = lineup_widget.LineUpWidget(df) 39 | w.on_selection_changed(lambda selection: print(selection)) 40 | w 41 | ``` 42 | 43 | ![simple usage](https://user-images.githubusercontent.com/4129778/35321859-7925d3a6-00e8-11e8-9884-bcbc76ae51c9.png) 44 | 45 | ```python 46 | from __future__ import print_function 47 | from ipywidgets import interact, interactive, interact_manual 48 | 49 | def selection_changed(selection): 50 | return df.iloc[selection] 51 | 52 | interact(selection_changed, selection=lineup_widget.LineUpWidget(df)); 53 | ``` 54 | 55 | ![interact example](https://user-images.githubusercontent.com/4129778/35321846-6c5b07cc-00e8-11e8-9388-0acb65cbb509.png) 56 | 57 | **Hint**: 58 | 59 | In case you see scrollbars in each cell it is because of the font the cells are too narrow, you can specify a larger row height using 60 | ```python 61 | w = lineup_widget.LineUpWidget(df, options=dict(rowHeight=20)) 62 | ``` 63 | 64 | 65 | Development Installation 66 | ------------------------ 67 | 68 | Create a dev environment: 69 | ```bash 70 | conda create -n lineup_widget-dev -c conda-forge nodejs yarn python jupyterlab jupyter-packaging numpy pandas 71 | conda activate lineup_widget-dev 72 | ``` 73 | 74 | Install the python. This will also build the TS package. 75 | ```bash 76 | pip install -e ".[test, examples]" 77 | ``` 78 | 79 | When developing your extensions, you need to manually enable your extensions with the 80 | notebook / lab frontend. For lab, this is done by the command: 81 | 82 | ``` 83 | jupyter labextension develop --overwrite . 84 | yarn run build 85 | ``` 86 | 87 | For classic notebook, you need to run: 88 | 89 | ``` 90 | jupyter nbextension install --sys-prefix --symlink --overwrite --py lineup_widget 91 | jupyter nbextension enable --sys-prefix --py lineup_widget 92 | ``` 93 | 94 | Note that the `--symlink` flag doesn't work on Windows, so you will here have to run 95 | the `install` command every time that you rebuild your extension. For certain installations 96 | you might also need another flag instead of `--sys-prefix`, but we won't cover the meaning 97 | of those flags here. 98 | 99 | ### How to see your changes 100 | #### Typescript: 101 | If you use JupyterLab to develop then you can watch the source directory and run JupyterLab at the same time in different 102 | terminals to watch for changes in the extension's source and automatically rebuild the widget. 103 | 104 | ```bash 105 | # Watch the source directory in one terminal, automatically rebuilding when needed 106 | yarn run watch 107 | # Run JupyterLab in another terminal 108 | jupyter lab 109 | ``` 110 | 111 | After a change wait for the build to finish and then refresh your browser and the changes should take effect. 112 | 113 | #### Python: 114 | If you make a change to the python code then you will need to restart the notebook kernel to have it take effect. 115 | 116 | Authors 117 | ------- 118 | 119 | * Samuel Gratzl (@sgratzl) 120 | 121 | 122 | [mit-image]: https://img.shields.io/badge/License-MIT-yellow.svg 123 | [mit-url]: https://opensource.org/licenses/MIT 124 | [binder-image]: https://camo.githubusercontent.com/70c5b4d050d4019f4f20b170d75679a9316ac5e5/687474703a2f2f6d7962696e6465722e6f72672f62616467652e737667 125 | [binder-url]: http://mybinder.org/repo/datavisyn/lineup_widget/examples 126 | [github-actions-image]: https://github.com/lineupjs/lineup_widget/workflows/ci/badge.svg 127 | [github-actions-url]: https://github.com/lineupjs/lineup_widget/actions 128 | -------------------------------------------------------------------------------- /src/widget.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Samuel Gratzl 2 | // Distributed under the terms of the Modified BSD License. 3 | 4 | import { DOMWidgetModel, DOMWidgetView } from '@jupyter-widgets/base'; 5 | 6 | import { MODULE_NAME, MODULE_VERSION } from './version'; 7 | 8 | // Import the CSS 9 | import '../css/widget.css'; 10 | import LineUp, { 11 | ITaggleOptions, 12 | Taggle, 13 | LocalDataProvider, 14 | IColumnDesc, 15 | deriveColors, 16 | } from 'lineupjs'; 17 | import { ILineUpRanking, pushRanking } from './utils'; 18 | 19 | const fields = { 20 | _data: [], 21 | _columns: [], 22 | options: {}, 23 | rankings: [], 24 | value: [], 25 | }; 26 | 27 | export class LineUpModel extends DOMWidgetModel { 28 | defaults(): any { 29 | return { 30 | ...super.defaults(), 31 | _model_name: LineUpModel.model_name, 32 | _model_module: LineUpModel.model_module, 33 | _model_module_version: LineUpModel.model_module_version, 34 | _view_name: LineUpModel.view_name, 35 | _view_module: LineUpModel.view_module, 36 | _view_module_version: LineUpModel.view_module_version, 37 | ...fields, 38 | }; 39 | } 40 | 41 | static serializers = { 42 | ...DOMWidgetModel.serializers, 43 | // Add any extra serializers here 44 | }; 45 | 46 | static model_name = 'LineUpModel'; 47 | static model_module = MODULE_NAME; 48 | static model_module_version = MODULE_VERSION; 49 | static view_name = 'LineUpView'; // Set to null if no view 50 | static view_module = MODULE_NAME; 51 | static view_module_version = MODULE_VERSION; 52 | } 53 | 54 | export class TaggleModel extends DOMWidgetModel { 55 | defaults(): any { 56 | return { 57 | ...super.defaults(), 58 | _model_name: TaggleModel.model_name, 59 | _model_module: TaggleModel.model_module, 60 | _model_module_version: TaggleModel.model_module_version, 61 | _view_name: TaggleModel.view_name, 62 | _view_module: TaggleModel.view_module, 63 | _view_module_version: TaggleModel.view_module_version, 64 | ...fields, 65 | }; 66 | } 67 | 68 | static serializers = { 69 | ...DOMWidgetModel.serializers, 70 | // Add any extra serializers here 71 | }; 72 | 73 | static model_name = 'TaggleModel'; 74 | static model_module = MODULE_NAME; 75 | static model_module_version = MODULE_VERSION; 76 | static view_name = 'TaggleView'; // Set to null if no view 77 | static view_module = MODULE_NAME; // Set to null if no view 78 | static view_module_version = MODULE_VERSION; 79 | } 80 | 81 | export abstract class ALineUpView extends DOMWidgetView { 82 | protected lineup: LineUp | Taggle; 83 | protected data: LocalDataProvider; 84 | 85 | render(): void { 86 | this.data = this.createData(); 87 | this.model.on('change:_data', this.dataChanged, this); 88 | 89 | this.data.setSelection(this.model.get('value')); 90 | this.model.on('change:value', this.selectionChanged, this); 91 | this.data.on('selectionChanged', (selection: number[]) => { 92 | this.model.set('value', selection, { updated_view: this }); 93 | this.touch(); 94 | }); 95 | 96 | this.createRankings(); 97 | 98 | const options = this.model.get('options'); 99 | this.lineup = this.createLineUp({ 100 | ...options, 101 | panel: options.sidePanel !== false, 102 | panelCollapsed: options.sidePanel === 'collapsed', 103 | }); 104 | } 105 | 106 | protected abstract createLineUp( 107 | options: Partial 108 | ): LineUp | Taggle; 109 | 110 | private createData() { 111 | const options = this.model.get('options'); 112 | const rows = this.model.get('_data'); 113 | const columns = this.model.get('_columns'); 114 | 115 | return new LocalDataProvider(rows, deriveColors(columns), { 116 | filterGlobally: options.filterGlobally, 117 | singleSelection: options.singleSelection, 118 | }); 119 | } 120 | 121 | private dataChanged() { 122 | this.data.clearColumns(); 123 | 124 | const rows = this.model.get('_data'); 125 | const columns = this.model.get('_columns'); 126 | 127 | deriveColors(columns).forEach((desc: IColumnDesc) => 128 | this.data.pushDesc(desc) 129 | ); 130 | 131 | this.data.setData(rows); 132 | this.createRankings(); 133 | } 134 | 135 | private createRankings() { 136 | this.data.clearRankings(); 137 | const rankings = this.model.get('rankings'); 138 | if (rankings.length === 0) { 139 | this.data.deriveDefault(); 140 | } else { 141 | rankings.forEach((ranking) => pushRanking(this.data, ranking)); 142 | } 143 | } 144 | 145 | private selectionChanged() { 146 | this.data.setSelection(this.model.get('value')); 147 | } 148 | } 149 | 150 | export class LineUpView extends ALineUpView { 151 | protected createLineUp(options: Partial): LineUp { 152 | return new LineUp(this.el, this.data, options); 153 | } 154 | } 155 | 156 | export class TaggleView extends ALineUpView { 157 | protected createLineUp(options: Partial): Taggle { 158 | return new Taggle(this.el, this.data, options); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # lineup_widget documentation build configuration file 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | 16 | # -- General configuration ------------------------------------------------ 17 | 18 | # If your documentation needs a minimal Sphinx version, state it here. 19 | # 20 | # needs_sphinx = '1.0' 21 | 22 | # Add any Sphinx extension module names here, as strings. They can be 23 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 24 | # ones. 25 | extensions = [ 26 | 'sphinx.ext.autodoc', 27 | 'sphinx.ext.viewcode', 28 | 'sphinx.ext.intersphinx', 29 | 'sphinx.ext.napoleon', 30 | 'sphinx.ext.todo', 31 | 'nbsphinx', 32 | 'jupyter_sphinx', 33 | 'nbsphinx_link', 34 | ] 35 | 36 | # Set the nbsphinx JS path to empty to avoid showing twice of the widgets 37 | nbsphinx_requirejs_path = "" 38 | nbsphinx_widgets_path = "" 39 | 40 | # Ensure our extension is available: 41 | import sys 42 | from os.path import dirname, join as pjoin 43 | docs = dirname(dirname(__file__)) 44 | root = dirname(docs) 45 | sys.path.insert(0, root) 46 | sys.path.insert(0, pjoin(docs, 'sphinxext')) 47 | 48 | # Add any paths that contain templates here, relative to this directory. 49 | templates_path = ['_templates'] 50 | 51 | # The suffix(es) of source filenames. 52 | # You can specify multiple suffix as a list of string: 53 | # 54 | # source_suffix = ['.rst', '.md'] 55 | source_suffix = '.rst' 56 | 57 | # The master toctree document. 58 | master_doc = 'index' 59 | 60 | # General information about the project. 61 | project = 'lineup_widget' 62 | copyright = '2022, Samuel Gratzl' 63 | author = 'Samuel Gratzl' 64 | 65 | # The version info for the project you're documenting, acts as replacement for 66 | # |version| and |release|, also used in various other places throughout the 67 | # built documents. 68 | # 69 | # The short X.Y version. 70 | 71 | 72 | # get version from python package: 73 | import os 74 | here = os.path.dirname(__file__) 75 | repo = os.path.join(here, '..', '..') 76 | _version_py = os.path.join(repo, 'lineup_widget', '_version.py') 77 | version_ns = {} 78 | with open(_version_py) as f: 79 | exec(f.read(), version_ns) 80 | 81 | # The short X.Y version. 82 | version = '%i.%i' % version_ns['version_info'][:2] 83 | # The full version, including alpha/beta/rc tags. 84 | release = version_ns['__version__'] 85 | 86 | # The language for content autogenerated by Sphinx. Refer to documentation 87 | # for a list of supported languages. 88 | # 89 | # This is also used if you do content translation via gettext catalogs. 90 | # Usually you set "language" from the command line for these cases. 91 | language = None 92 | 93 | # List of patterns, relative to source directory, that match files and 94 | # directories to ignore when looking for source files. 95 | # This patterns also effect to html_static_path and html_extra_path 96 | exclude_patterns = ['**.ipynb_checkpoints'] 97 | 98 | # The name of the Pygments (syntax highlighting) style to use. 99 | pygments_style = 'sphinx' 100 | 101 | # If true, `todo` and `todoList` produce output, else they produce nothing. 102 | todo_include_todos = False 103 | 104 | 105 | # -- Options for HTML output ---------------------------------------------- 106 | 107 | 108 | # Theme options are theme-specific and customize the look and feel of a theme 109 | # further. For a list of options available for each theme, see the 110 | # documentation. 111 | # 112 | # html_theme_options = {} 113 | 114 | # Add any paths that contain custom static files (such as style sheets) here, 115 | # relative to this directory. They are copied after the builtin static files, 116 | # so a file named "default.css" will overwrite the builtin "default.css". 117 | html_static_path = ['_static'] 118 | 119 | 120 | # -- Options for HTMLHelp output ------------------------------------------ 121 | 122 | # Output file base name for HTML help builder. 123 | htmlhelp_basename = 'lineup_widgetdoc' 124 | 125 | 126 | # -- Options for LaTeX output --------------------------------------------- 127 | 128 | latex_elements = { 129 | # The paper size ('letterpaper' or 'a4paper'). 130 | # 131 | # 'papersize': 'letterpaper', 132 | 133 | # The font size ('10pt', '11pt' or '12pt'). 134 | # 135 | # 'pointsize': '10pt', 136 | 137 | # Additional stuff for the LaTeX preamble. 138 | # 139 | # 'preamble': '', 140 | 141 | # Latex figure (float) alignment 142 | # 143 | # 'figure_align': 'htbp', 144 | } 145 | 146 | # Grouping the document tree into LaTeX files. List of tuples 147 | # (source start file, target name, title, 148 | # author, documentclass [howto, manual, or own class]). 149 | latex_documents = [ 150 | (master_doc, 'lineup_widget.tex', 'lineup_widget Documentation', 151 | 'Samuel Gratzl', 'manual'), 152 | ] 153 | 154 | 155 | # -- Options for manual page output --------------------------------------- 156 | 157 | # One entry per manual page. List of tuples 158 | # (source start file, name, description, authors, manual section). 159 | man_pages = [ 160 | (master_doc, 161 | 'lineup_widget', 162 | 'lineup_widget Documentation', 163 | [author], 1) 164 | ] 165 | 166 | 167 | # -- Options for Texinfo output ------------------------------------------- 168 | 169 | # Grouping the document tree into Texinfo files. List of tuples 170 | # (source start file, target name, title, author, 171 | # dir menu entry, description, category) 172 | texinfo_documents = [ 173 | (master_doc, 174 | 'lineup_widget', 175 | 'lineup_widget Documentation', 176 | author, 177 | 'lineup_widget', 178 | 'A Custom Jupyter Widget Library', 179 | 'Miscellaneous'), 180 | ] 181 | 182 | 183 | # Example configuration for intersphinx: refer to the Python standard library. 184 | intersphinx_mapping = {'https://docs.python.org/': None} 185 | 186 | # Read The Docs 187 | # on_rtd is whether we are on readthedocs.org, this line of code grabbed from 188 | # docs.readthedocs.org 189 | on_rtd = os.environ.get('READTHEDOCS', None) == 'True' 190 | 191 | if not on_rtd: # only import and set the theme if we're building docs locally 192 | import sphinx_rtd_theme 193 | html_theme = 'sphinx_rtd_theme' 194 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 195 | 196 | # otherwise, readthedocs.org uses their theme by default, so no need to specify it 197 | 198 | 199 | # Uncomment this line if you have know exceptions in your included notebooks 200 | # that nbsphinx complains about: 201 | # 202 | nbsphinx_allow_errors = True # exception ipstruct.py ipython_genutils 203 | 204 | from sphinx.util import logging 205 | logger = logging.getLogger(__name__) 206 | 207 | def setup(app): 208 | def add_scripts(app): 209 | for fname in ['helper.js', 'embed-bundle.js']: 210 | if not os.path.exists(os.path.join(here, '_static', fname)): 211 | logger.warning('missing javascript file: %s' % fname) 212 | app.add_js_file(fname) 213 | app.connect('builder-inited', add_scripts) 214 | -------------------------------------------------------------------------------- /examples/introduction.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import lineup_widget\n", 10 | "import numpy as np\n", 11 | "import pandas as pd" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": null, 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [ 20 | "df = pd.DataFrame(np.random.randint(0,100,size=(100, 4)), columns=list('ABCD'))" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": null, 26 | "metadata": {}, 27 | "outputs": [], 28 | "source": [ 29 | "w = lineup_widget.LineUpWidget(df, options=dict(rowHeight=20))\n", 30 | "w.on_selection_changed(lambda selection: print(selection))\n", 31 | "w" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": null, 37 | "metadata": {}, 38 | "outputs": [], 39 | "source": [ 40 | "from __future__ import print_function\n", 41 | "from ipywidgets import interact, interactive, interact_manual\n", 42 | "\n", 43 | "def f(selection):\n", 44 | " return df.iloc[selection]\n", 45 | "\n", 46 | "interact(f, selection=lineup_widget.LineUpWidget(df, options=dict(rowHeight=20)));" 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": null, 52 | "metadata": {}, 53 | "outputs": [], 54 | "source": [ 55 | "lineup_widget.TaggleWidget(df, \n", 56 | " rankings=[dict(sort_by=['A:desc']), dict(sort_by=['B:desc'])],\n", 57 | " options=dict(rowHeight=20, overviewMode=True))" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": null, 63 | "metadata": {}, 64 | "outputs": [], 65 | "source": [] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": null, 70 | "metadata": {}, 71 | "outputs": [], 72 | "source": [] 73 | } 74 | ], 75 | "metadata": { 76 | "kernelspec": { 77 | "display_name": "Python 3 (ipykernel)", 78 | "language": "python", 79 | "name": "python3" 80 | }, 81 | "language_info": { 82 | "codemirror_mode": { 83 | "name": "ipython", 84 | "version": 3 85 | }, 86 | "file_extension": ".py", 87 | "mimetype": "text/x-python", 88 | "name": "python", 89 | "nbconvert_exporter": "python", 90 | "pygments_lexer": "ipython3", 91 | "version": "3.10.5" 92 | }, 93 | "vscode": { 94 | "interpreter": { 95 | "hash": "43191785d8a93b130981be1a5526ce802037d0ba38916cb9ee8bd4fd71b51feb" 96 | } 97 | }, 98 | "widgets": { 99 | "application/vnd.jupyter.widget-state+json": { 100 | "state": { 101 | "076acfb1b75e48f4bd6d3200a91a11d4": { 102 | "model_module": "lineup_widget", 103 | "model_module_version": "4.6.0", 104 | "model_name": "LineUpModel", 105 | "state": { 106 | "_columns": [ 107 | { 108 | "colorMapping": "#1f77b4", 109 | "column": "A", 110 | "domain": [ 111 | 0, 112 | 99 113 | ], 114 | "groupRenderer": "boxplot", 115 | "label": "A", 116 | "summaryRenderer": "histogram", 117 | "type": "number" 118 | }, 119 | { 120 | "colorMapping": "#ff7f0e", 121 | "column": "B", 122 | "domain": [ 123 | 0, 124 | 99 125 | ], 126 | "groupRenderer": "boxplot", 127 | "label": "B", 128 | "summaryRenderer": "histogram", 129 | "type": "number" 130 | }, 131 | { 132 | "colorMapping": "#2ca02c", 133 | "column": "C", 134 | "domain": [ 135 | 1, 136 | 99 137 | ], 138 | "groupRenderer": "boxplot", 139 | "label": "C", 140 | "summaryRenderer": "histogram", 141 | "type": "number" 142 | }, 143 | { 144 | "colorMapping": "#d62728", 145 | "column": "D", 146 | "domain": [ 147 | 4, 148 | 99 149 | ], 150 | "groupRenderer": "boxplot", 151 | "label": "D", 152 | "summaryRenderer": "histogram", 153 | "type": "number" 154 | } 155 | ], 156 | "_data": [ 157 | { 158 | "A": 15, 159 | "B": 40, 160 | "C": 14, 161 | "D": 51 162 | }, 163 | { 164 | "A": 80, 165 | "B": 19, 166 | "C": 58, 167 | "D": 12 168 | }, 169 | { 170 | "A": 35, 171 | "B": 89, 172 | "C": 94, 173 | "D": 46 174 | }, 175 | { 176 | "A": 1, 177 | "B": 36, 178 | "C": 99, 179 | "D": 25 180 | }, 181 | { 182 | "A": 78, 183 | "B": 19, 184 | "C": 38, 185 | "D": 16 186 | }, 187 | { 188 | "A": 96, 189 | "B": 41, 190 | "C": 48, 191 | "D": 78 192 | }, 193 | { 194 | "A": 10, 195 | "B": 84, 196 | "C": 11, 197 | "D": 33 198 | }, 199 | { 200 | "A": 87, 201 | "B": 67, 202 | "C": 6, 203 | "D": 36 204 | }, 205 | { 206 | "A": 12, 207 | "B": 19, 208 | "C": 41, 209 | "D": 86 210 | }, 211 | { 212 | "A": 88, 213 | "B": 73, 214 | "C": 42, 215 | "D": 24 216 | }, 217 | { 218 | "A": 13, 219 | "B": 87, 220 | "C": 62, 221 | "D": 75 222 | }, 223 | { 224 | "A": 0, 225 | "B": 95, 226 | "C": 44, 227 | "D": 69 228 | }, 229 | { 230 | "A": 93, 231 | "B": 94, 232 | "C": 91, 233 | "D": 12 234 | }, 235 | { 236 | "A": 93, 237 | "B": 99, 238 | "C": 76, 239 | "D": 75 240 | }, 241 | { 242 | "A": 41, 243 | "B": 51, 244 | "C": 73, 245 | "D": 39 246 | }, 247 | { 248 | "A": 46, 249 | "B": 66, 250 | "C": 32, 251 | "D": 4 252 | }, 253 | { 254 | "A": 50, 255 | "B": 15, 256 | "C": 59, 257 | "D": 58 258 | }, 259 | { 260 | "A": 34, 261 | "B": 53, 262 | "C": 20, 263 | "D": 94 264 | }, 265 | { 266 | "A": 69, 267 | "B": 66, 268 | "C": 91, 269 | "D": 82 270 | }, 271 | { 272 | "A": 14, 273 | "B": 11, 274 | "C": 3, 275 | "D": 50 276 | }, 277 | { 278 | "A": 85, 279 | "B": 68, 280 | "C": 22, 281 | "D": 81 282 | }, 283 | { 284 | "A": 6, 285 | "B": 97, 286 | "C": 78, 287 | "D": 72 288 | }, 289 | { 290 | "A": 63, 291 | "B": 95, 292 | "C": 64, 293 | "D": 71 294 | }, 295 | { 296 | "A": 43, 297 | "B": 93, 298 | "C": 85, 299 | "D": 82 300 | }, 301 | { 302 | "A": 76, 303 | "B": 95, 304 | "C": 3, 305 | "D": 30 306 | }, 307 | { 308 | "A": 53, 309 | "B": 40, 310 | "C": 54, 311 | "D": 48 312 | }, 313 | { 314 | "A": 79, 315 | "B": 59, 316 | "C": 74, 317 | "D": 49 318 | }, 319 | { 320 | "A": 94, 321 | "B": 24, 322 | "C": 41, 323 | "D": 32 324 | }, 325 | { 326 | "A": 28, 327 | "B": 28, 328 | "C": 51, 329 | "D": 14 330 | }, 331 | { 332 | "A": 3, 333 | "B": 78, 334 | "C": 8, 335 | "D": 59 336 | }, 337 | { 338 | "A": 92, 339 | "B": 10, 340 | "C": 61, 341 | "D": 55 342 | }, 343 | { 344 | "A": 26, 345 | "B": 9, 346 | "C": 22, 347 | "D": 62 348 | }, 349 | { 350 | "A": 12, 351 | "B": 83, 352 | "C": 15, 353 | "D": 41 354 | }, 355 | { 356 | "A": 17, 357 | "B": 38, 358 | "C": 62, 359 | "D": 22 360 | }, 361 | { 362 | "A": 35, 363 | "B": 37, 364 | "C": 29, 365 | "D": 8 366 | }, 367 | { 368 | "A": 90, 369 | "B": 69, 370 | "C": 80, 371 | "D": 81 372 | }, 373 | { 374 | "A": 76, 375 | "B": 29, 376 | "C": 46, 377 | "D": 35 378 | }, 379 | { 380 | "A": 43, 381 | "B": 51, 382 | "C": 69, 383 | "D": 17 384 | }, 385 | { 386 | "A": 25, 387 | "B": 68, 388 | "C": 76, 389 | "D": 61 390 | }, 391 | { 392 | "A": 58, 393 | "B": 74, 394 | "C": 48, 395 | "D": 38 396 | }, 397 | { 398 | "A": 13, 399 | "B": 30, 400 | "C": 14, 401 | "D": 37 402 | }, 403 | { 404 | "A": 76, 405 | "B": 10, 406 | "C": 86, 407 | "D": 39 408 | }, 409 | { 410 | "A": 39, 411 | "B": 14, 412 | "C": 86, 413 | "D": 94 414 | }, 415 | { 416 | "A": 26, 417 | "B": 90, 418 | "C": 48, 419 | "D": 80 420 | }, 421 | { 422 | "A": 89, 423 | "B": 13, 424 | "C": 62, 425 | "D": 48 426 | }, 427 | { 428 | "A": 32, 429 | "B": 66, 430 | "C": 22, 431 | "D": 30 432 | }, 433 | { 434 | "A": 76, 435 | "B": 0, 436 | "C": 19, 437 | "D": 51 438 | }, 439 | { 440 | "A": 32, 441 | "B": 60, 442 | "C": 41, 443 | "D": 37 444 | }, 445 | { 446 | "A": 59, 447 | "B": 52, 448 | "C": 14, 449 | "D": 45 450 | }, 451 | { 452 | "A": 22, 453 | "B": 59, 454 | "C": 87, 455 | "D": 81 456 | }, 457 | { 458 | "A": 84, 459 | "B": 68, 460 | "C": 48, 461 | "D": 76 462 | }, 463 | { 464 | "A": 93, 465 | "B": 87, 466 | "C": 24, 467 | "D": 44 468 | }, 469 | { 470 | "A": 85, 471 | "B": 37, 472 | "C": 61, 473 | "D": 35 474 | }, 475 | { 476 | "A": 40, 477 | "B": 44, 478 | "C": 5, 479 | "D": 8 480 | }, 481 | { 482 | "A": 6, 483 | "B": 77, 484 | "C": 59, 485 | "D": 78 486 | }, 487 | { 488 | "A": 75, 489 | "B": 67, 490 | "C": 15, 491 | "D": 51 492 | }, 493 | { 494 | "A": 35, 495 | "B": 0, 496 | "C": 45, 497 | "D": 9 498 | }, 499 | { 500 | "A": 18, 501 | "B": 49, 502 | "C": 24, 503 | "D": 23 504 | }, 505 | { 506 | "A": 53, 507 | "B": 9, 508 | "C": 96, 509 | "D": 22 510 | }, 511 | { 512 | "A": 59, 513 | "B": 98, 514 | "C": 48, 515 | "D": 33 516 | }, 517 | { 518 | "A": 19, 519 | "B": 15, 520 | "C": 98, 521 | "D": 48 522 | }, 523 | { 524 | "A": 76, 525 | "B": 43, 526 | "C": 44, 527 | "D": 41 528 | }, 529 | { 530 | "A": 80, 531 | "B": 64, 532 | "C": 97, 533 | "D": 57 534 | }, 535 | { 536 | "A": 97, 537 | "B": 87, 538 | "C": 35, 539 | "D": 70 540 | }, 541 | { 542 | "A": 98, 543 | "B": 19, 544 | "C": 66, 545 | "D": 36 546 | }, 547 | { 548 | "A": 94, 549 | "B": 84, 550 | "C": 45, 551 | "D": 85 552 | }, 553 | { 554 | "A": 86, 555 | "B": 93, 556 | "C": 45, 557 | "D": 56 558 | }, 559 | { 560 | "A": 71, 561 | "B": 60, 562 | "C": 55, 563 | "D": 98 564 | }, 565 | { 566 | "A": 32, 567 | "B": 30, 568 | "C": 71, 569 | "D": 5 570 | }, 571 | { 572 | "A": 71, 573 | "B": 44, 574 | "C": 87, 575 | "D": 16 576 | }, 577 | { 578 | "A": 42, 579 | "B": 39, 580 | "C": 91, 581 | "D": 86 582 | }, 583 | { 584 | "A": 41, 585 | "B": 89, 586 | "C": 86, 587 | "D": 21 588 | }, 589 | { 590 | "A": 51, 591 | "B": 80, 592 | "C": 13, 593 | "D": 91 594 | }, 595 | { 596 | "A": 5, 597 | "B": 54, 598 | "C": 19, 599 | "D": 58 600 | }, 601 | { 602 | "A": 4, 603 | "B": 20, 604 | "C": 20, 605 | "D": 35 606 | }, 607 | { 608 | "A": 37, 609 | "B": 44, 610 | "C": 58, 611 | "D": 51 612 | }, 613 | { 614 | "A": 44, 615 | "B": 88, 616 | "C": 21, 617 | "D": 99 618 | }, 619 | { 620 | "A": 82, 621 | "B": 21, 622 | "C": 1, 623 | "D": 98 624 | }, 625 | { 626 | "A": 92, 627 | "B": 34, 628 | "C": 24, 629 | "D": 32 630 | }, 631 | { 632 | "A": 28, 633 | "B": 49, 634 | "C": 67, 635 | "D": 92 636 | }, 637 | { 638 | "A": 11, 639 | "B": 83, 640 | "C": 32, 641 | "D": 25 642 | }, 643 | { 644 | "A": 63, 645 | "B": 93, 646 | "C": 36, 647 | "D": 27 648 | }, 649 | { 650 | "A": 29, 651 | "B": 46, 652 | "C": 77, 653 | "D": 75 654 | }, 655 | { 656 | "A": 93, 657 | "B": 45, 658 | "C": 79, 659 | "D": 38 660 | }, 661 | { 662 | "A": 69, 663 | "B": 84, 664 | "C": 14, 665 | "D": 7 666 | }, 667 | { 668 | "A": 1, 669 | "B": 89, 670 | "C": 96, 671 | "D": 39 672 | }, 673 | { 674 | "A": 31, 675 | "B": 58, 676 | "C": 22, 677 | "D": 89 678 | }, 679 | { 680 | "A": 26, 681 | "B": 64, 682 | "C": 79, 683 | "D": 98 684 | }, 685 | { 686 | "A": 3, 687 | "B": 94, 688 | "C": 66, 689 | "D": 60 690 | }, 691 | { 692 | "A": 43, 693 | "B": 80, 694 | "C": 28, 695 | "D": 57 696 | }, 697 | { 698 | "A": 7, 699 | "B": 73, 700 | "C": 49, 701 | "D": 23 702 | }, 703 | { 704 | "A": 16, 705 | "B": 70, 706 | "C": 70, 707 | "D": 24 708 | }, 709 | { 710 | "A": 33, 711 | "B": 25, 712 | "C": 50, 713 | "D": 75 714 | }, 715 | { 716 | "A": 58, 717 | "B": 9, 718 | "C": 13, 719 | "D": 61 720 | }, 721 | { 722 | "A": 39, 723 | "B": 71, 724 | "C": 75, 725 | "D": 50 726 | }, 727 | { 728 | "A": 64, 729 | "B": 45, 730 | "C": 5, 731 | "D": 39 732 | }, 733 | { 734 | "A": 72, 735 | "B": 23, 736 | "C": 14, 737 | "D": 18 738 | }, 739 | { 740 | "A": 16, 741 | "B": 29, 742 | "C": 58, 743 | "D": 62 744 | }, 745 | { 746 | "A": 99, 747 | "B": 22, 748 | "C": 23, 749 | "D": 52 750 | }, 751 | { 752 | "A": 39, 753 | "B": 86, 754 | "C": 44, 755 | "D": 16 756 | } 757 | ], 758 | "description": "selection", 759 | "layout": "IPY_MODEL_1a5d65a06b734d3f97eeee274ded3856", 760 | "options": { 761 | "rowHeight": 20 762 | } 763 | } 764 | }, 765 | "1a5d65a06b734d3f97eeee274ded3856": { 766 | "model_module": "@jupyter-widgets/base", 767 | "model_module_version": "1.2.0", 768 | "model_name": "LayoutModel", 769 | "state": { 770 | "align_self": "stretch", 771 | "height": "600px" 772 | } 773 | }, 774 | "23f9d6dd3e9548a7a2c2c186dad4eb39": { 775 | "model_module": "@jupyter-widgets/controls", 776 | "model_module_version": "1.5.0", 777 | "model_name": "VBoxModel", 778 | "state": { 779 | "_dom_classes": [ 780 | "widget-interact" 781 | ], 782 | "children": [ 783 | "IPY_MODEL_076acfb1b75e48f4bd6d3200a91a11d4", 784 | "IPY_MODEL_e36cccb2b16249bb94c667832ab875c8" 785 | ], 786 | "layout": "IPY_MODEL_2aa4ba0f6c7b4f1d9696fd733e68de6e" 787 | } 788 | }, 789 | "2aa4ba0f6c7b4f1d9696fd733e68de6e": { 790 | "model_module": "@jupyter-widgets/base", 791 | "model_module_version": "1.2.0", 792 | "model_name": "LayoutModel", 793 | "state": {} 794 | }, 795 | "4ecfd70665764b59b4073bf2841c440f": { 796 | "model_module": "lineup_widget", 797 | "model_module_version": "4.6.0", 798 | "model_name": "TaggleModel", 799 | "state": { 800 | "_columns": [ 801 | { 802 | "colorMapping": "#1f77b4", 803 | "column": "A", 804 | "domain": [ 805 | 0, 806 | 99 807 | ], 808 | "groupRenderer": "boxplot", 809 | "label": "A", 810 | "summaryRenderer": "histogram", 811 | "type": "number" 812 | }, 813 | { 814 | "colorMapping": "#ff7f0e", 815 | "column": "B", 816 | "domain": [ 817 | 0, 818 | 99 819 | ], 820 | "groupRenderer": "boxplot", 821 | "label": "B", 822 | "summaryRenderer": "histogram", 823 | "type": "number" 824 | }, 825 | { 826 | "colorMapping": "#2ca02c", 827 | "column": "C", 828 | "domain": [ 829 | 1, 830 | 99 831 | ], 832 | "groupRenderer": "boxplot", 833 | "label": "C", 834 | "summaryRenderer": "histogram", 835 | "type": "number" 836 | }, 837 | { 838 | "colorMapping": "#d62728", 839 | "column": "D", 840 | "domain": [ 841 | 4, 842 | 99 843 | ], 844 | "groupRenderer": "boxplot", 845 | "label": "D", 846 | "summaryRenderer": "histogram", 847 | "type": "number" 848 | } 849 | ], 850 | "_data": [ 851 | { 852 | "A": 15, 853 | "B": 40, 854 | "C": 14, 855 | "D": 51 856 | }, 857 | { 858 | "A": 80, 859 | "B": 19, 860 | "C": 58, 861 | "D": 12 862 | }, 863 | { 864 | "A": 35, 865 | "B": 89, 866 | "C": 94, 867 | "D": 46 868 | }, 869 | { 870 | "A": 1, 871 | "B": 36, 872 | "C": 99, 873 | "D": 25 874 | }, 875 | { 876 | "A": 78, 877 | "B": 19, 878 | "C": 38, 879 | "D": 16 880 | }, 881 | { 882 | "A": 96, 883 | "B": 41, 884 | "C": 48, 885 | "D": 78 886 | }, 887 | { 888 | "A": 10, 889 | "B": 84, 890 | "C": 11, 891 | "D": 33 892 | }, 893 | { 894 | "A": 87, 895 | "B": 67, 896 | "C": 6, 897 | "D": 36 898 | }, 899 | { 900 | "A": 12, 901 | "B": 19, 902 | "C": 41, 903 | "D": 86 904 | }, 905 | { 906 | "A": 88, 907 | "B": 73, 908 | "C": 42, 909 | "D": 24 910 | }, 911 | { 912 | "A": 13, 913 | "B": 87, 914 | "C": 62, 915 | "D": 75 916 | }, 917 | { 918 | "A": 0, 919 | "B": 95, 920 | "C": 44, 921 | "D": 69 922 | }, 923 | { 924 | "A": 93, 925 | "B": 94, 926 | "C": 91, 927 | "D": 12 928 | }, 929 | { 930 | "A": 93, 931 | "B": 99, 932 | "C": 76, 933 | "D": 75 934 | }, 935 | { 936 | "A": 41, 937 | "B": 51, 938 | "C": 73, 939 | "D": 39 940 | }, 941 | { 942 | "A": 46, 943 | "B": 66, 944 | "C": 32, 945 | "D": 4 946 | }, 947 | { 948 | "A": 50, 949 | "B": 15, 950 | "C": 59, 951 | "D": 58 952 | }, 953 | { 954 | "A": 34, 955 | "B": 53, 956 | "C": 20, 957 | "D": 94 958 | }, 959 | { 960 | "A": 69, 961 | "B": 66, 962 | "C": 91, 963 | "D": 82 964 | }, 965 | { 966 | "A": 14, 967 | "B": 11, 968 | "C": 3, 969 | "D": 50 970 | }, 971 | { 972 | "A": 85, 973 | "B": 68, 974 | "C": 22, 975 | "D": 81 976 | }, 977 | { 978 | "A": 6, 979 | "B": 97, 980 | "C": 78, 981 | "D": 72 982 | }, 983 | { 984 | "A": 63, 985 | "B": 95, 986 | "C": 64, 987 | "D": 71 988 | }, 989 | { 990 | "A": 43, 991 | "B": 93, 992 | "C": 85, 993 | "D": 82 994 | }, 995 | { 996 | "A": 76, 997 | "B": 95, 998 | "C": 3, 999 | "D": 30 1000 | }, 1001 | { 1002 | "A": 53, 1003 | "B": 40, 1004 | "C": 54, 1005 | "D": 48 1006 | }, 1007 | { 1008 | "A": 79, 1009 | "B": 59, 1010 | "C": 74, 1011 | "D": 49 1012 | }, 1013 | { 1014 | "A": 94, 1015 | "B": 24, 1016 | "C": 41, 1017 | "D": 32 1018 | }, 1019 | { 1020 | "A": 28, 1021 | "B": 28, 1022 | "C": 51, 1023 | "D": 14 1024 | }, 1025 | { 1026 | "A": 3, 1027 | "B": 78, 1028 | "C": 8, 1029 | "D": 59 1030 | }, 1031 | { 1032 | "A": 92, 1033 | "B": 10, 1034 | "C": 61, 1035 | "D": 55 1036 | }, 1037 | { 1038 | "A": 26, 1039 | "B": 9, 1040 | "C": 22, 1041 | "D": 62 1042 | }, 1043 | { 1044 | "A": 12, 1045 | "B": 83, 1046 | "C": 15, 1047 | "D": 41 1048 | }, 1049 | { 1050 | "A": 17, 1051 | "B": 38, 1052 | "C": 62, 1053 | "D": 22 1054 | }, 1055 | { 1056 | "A": 35, 1057 | "B": 37, 1058 | "C": 29, 1059 | "D": 8 1060 | }, 1061 | { 1062 | "A": 90, 1063 | "B": 69, 1064 | "C": 80, 1065 | "D": 81 1066 | }, 1067 | { 1068 | "A": 76, 1069 | "B": 29, 1070 | "C": 46, 1071 | "D": 35 1072 | }, 1073 | { 1074 | "A": 43, 1075 | "B": 51, 1076 | "C": 69, 1077 | "D": 17 1078 | }, 1079 | { 1080 | "A": 25, 1081 | "B": 68, 1082 | "C": 76, 1083 | "D": 61 1084 | }, 1085 | { 1086 | "A": 58, 1087 | "B": 74, 1088 | "C": 48, 1089 | "D": 38 1090 | }, 1091 | { 1092 | "A": 13, 1093 | "B": 30, 1094 | "C": 14, 1095 | "D": 37 1096 | }, 1097 | { 1098 | "A": 76, 1099 | "B": 10, 1100 | "C": 86, 1101 | "D": 39 1102 | }, 1103 | { 1104 | "A": 39, 1105 | "B": 14, 1106 | "C": 86, 1107 | "D": 94 1108 | }, 1109 | { 1110 | "A": 26, 1111 | "B": 90, 1112 | "C": 48, 1113 | "D": 80 1114 | }, 1115 | { 1116 | "A": 89, 1117 | "B": 13, 1118 | "C": 62, 1119 | "D": 48 1120 | }, 1121 | { 1122 | "A": 32, 1123 | "B": 66, 1124 | "C": 22, 1125 | "D": 30 1126 | }, 1127 | { 1128 | "A": 76, 1129 | "B": 0, 1130 | "C": 19, 1131 | "D": 51 1132 | }, 1133 | { 1134 | "A": 32, 1135 | "B": 60, 1136 | "C": 41, 1137 | "D": 37 1138 | }, 1139 | { 1140 | "A": 59, 1141 | "B": 52, 1142 | "C": 14, 1143 | "D": 45 1144 | }, 1145 | { 1146 | "A": 22, 1147 | "B": 59, 1148 | "C": 87, 1149 | "D": 81 1150 | }, 1151 | { 1152 | "A": 84, 1153 | "B": 68, 1154 | "C": 48, 1155 | "D": 76 1156 | }, 1157 | { 1158 | "A": 93, 1159 | "B": 87, 1160 | "C": 24, 1161 | "D": 44 1162 | }, 1163 | { 1164 | "A": 85, 1165 | "B": 37, 1166 | "C": 61, 1167 | "D": 35 1168 | }, 1169 | { 1170 | "A": 40, 1171 | "B": 44, 1172 | "C": 5, 1173 | "D": 8 1174 | }, 1175 | { 1176 | "A": 6, 1177 | "B": 77, 1178 | "C": 59, 1179 | "D": 78 1180 | }, 1181 | { 1182 | "A": 75, 1183 | "B": 67, 1184 | "C": 15, 1185 | "D": 51 1186 | }, 1187 | { 1188 | "A": 35, 1189 | "B": 0, 1190 | "C": 45, 1191 | "D": 9 1192 | }, 1193 | { 1194 | "A": 18, 1195 | "B": 49, 1196 | "C": 24, 1197 | "D": 23 1198 | }, 1199 | { 1200 | "A": 53, 1201 | "B": 9, 1202 | "C": 96, 1203 | "D": 22 1204 | }, 1205 | { 1206 | "A": 59, 1207 | "B": 98, 1208 | "C": 48, 1209 | "D": 33 1210 | }, 1211 | { 1212 | "A": 19, 1213 | "B": 15, 1214 | "C": 98, 1215 | "D": 48 1216 | }, 1217 | { 1218 | "A": 76, 1219 | "B": 43, 1220 | "C": 44, 1221 | "D": 41 1222 | }, 1223 | { 1224 | "A": 80, 1225 | "B": 64, 1226 | "C": 97, 1227 | "D": 57 1228 | }, 1229 | { 1230 | "A": 97, 1231 | "B": 87, 1232 | "C": 35, 1233 | "D": 70 1234 | }, 1235 | { 1236 | "A": 98, 1237 | "B": 19, 1238 | "C": 66, 1239 | "D": 36 1240 | }, 1241 | { 1242 | "A": 94, 1243 | "B": 84, 1244 | "C": 45, 1245 | "D": 85 1246 | }, 1247 | { 1248 | "A": 86, 1249 | "B": 93, 1250 | "C": 45, 1251 | "D": 56 1252 | }, 1253 | { 1254 | "A": 71, 1255 | "B": 60, 1256 | "C": 55, 1257 | "D": 98 1258 | }, 1259 | { 1260 | "A": 32, 1261 | "B": 30, 1262 | "C": 71, 1263 | "D": 5 1264 | }, 1265 | { 1266 | "A": 71, 1267 | "B": 44, 1268 | "C": 87, 1269 | "D": 16 1270 | }, 1271 | { 1272 | "A": 42, 1273 | "B": 39, 1274 | "C": 91, 1275 | "D": 86 1276 | }, 1277 | { 1278 | "A": 41, 1279 | "B": 89, 1280 | "C": 86, 1281 | "D": 21 1282 | }, 1283 | { 1284 | "A": 51, 1285 | "B": 80, 1286 | "C": 13, 1287 | "D": 91 1288 | }, 1289 | { 1290 | "A": 5, 1291 | "B": 54, 1292 | "C": 19, 1293 | "D": 58 1294 | }, 1295 | { 1296 | "A": 4, 1297 | "B": 20, 1298 | "C": 20, 1299 | "D": 35 1300 | }, 1301 | { 1302 | "A": 37, 1303 | "B": 44, 1304 | "C": 58, 1305 | "D": 51 1306 | }, 1307 | { 1308 | "A": 44, 1309 | "B": 88, 1310 | "C": 21, 1311 | "D": 99 1312 | }, 1313 | { 1314 | "A": 82, 1315 | "B": 21, 1316 | "C": 1, 1317 | "D": 98 1318 | }, 1319 | { 1320 | "A": 92, 1321 | "B": 34, 1322 | "C": 24, 1323 | "D": 32 1324 | }, 1325 | { 1326 | "A": 28, 1327 | "B": 49, 1328 | "C": 67, 1329 | "D": 92 1330 | }, 1331 | { 1332 | "A": 11, 1333 | "B": 83, 1334 | "C": 32, 1335 | "D": 25 1336 | }, 1337 | { 1338 | "A": 63, 1339 | "B": 93, 1340 | "C": 36, 1341 | "D": 27 1342 | }, 1343 | { 1344 | "A": 29, 1345 | "B": 46, 1346 | "C": 77, 1347 | "D": 75 1348 | }, 1349 | { 1350 | "A": 93, 1351 | "B": 45, 1352 | "C": 79, 1353 | "D": 38 1354 | }, 1355 | { 1356 | "A": 69, 1357 | "B": 84, 1358 | "C": 14, 1359 | "D": 7 1360 | }, 1361 | { 1362 | "A": 1, 1363 | "B": 89, 1364 | "C": 96, 1365 | "D": 39 1366 | }, 1367 | { 1368 | "A": 31, 1369 | "B": 58, 1370 | "C": 22, 1371 | "D": 89 1372 | }, 1373 | { 1374 | "A": 26, 1375 | "B": 64, 1376 | "C": 79, 1377 | "D": 98 1378 | }, 1379 | { 1380 | "A": 3, 1381 | "B": 94, 1382 | "C": 66, 1383 | "D": 60 1384 | }, 1385 | { 1386 | "A": 43, 1387 | "B": 80, 1388 | "C": 28, 1389 | "D": 57 1390 | }, 1391 | { 1392 | "A": 7, 1393 | "B": 73, 1394 | "C": 49, 1395 | "D": 23 1396 | }, 1397 | { 1398 | "A": 16, 1399 | "B": 70, 1400 | "C": 70, 1401 | "D": 24 1402 | }, 1403 | { 1404 | "A": 33, 1405 | "B": 25, 1406 | "C": 50, 1407 | "D": 75 1408 | }, 1409 | { 1410 | "A": 58, 1411 | "B": 9, 1412 | "C": 13, 1413 | "D": 61 1414 | }, 1415 | { 1416 | "A": 39, 1417 | "B": 71, 1418 | "C": 75, 1419 | "D": 50 1420 | }, 1421 | { 1422 | "A": 64, 1423 | "B": 45, 1424 | "C": 5, 1425 | "D": 39 1426 | }, 1427 | { 1428 | "A": 72, 1429 | "B": 23, 1430 | "C": 14, 1431 | "D": 18 1432 | }, 1433 | { 1434 | "A": 16, 1435 | "B": 29, 1436 | "C": 58, 1437 | "D": 62 1438 | }, 1439 | { 1440 | "A": 99, 1441 | "B": 22, 1442 | "C": 23, 1443 | "D": 52 1444 | }, 1445 | { 1446 | "A": 39, 1447 | "B": 86, 1448 | "C": 44, 1449 | "D": 16 1450 | } 1451 | ], 1452 | "description": "", 1453 | "layout": "IPY_MODEL_7484c55fdf7d4c729c13ea0164b43f28", 1454 | "options": { 1455 | "overviewMode": true, 1456 | "rowHeight": 20 1457 | }, 1458 | "rankings": [ 1459 | { 1460 | "sort_by": [ 1461 | "A:desc" 1462 | ] 1463 | }, 1464 | { 1465 | "sort_by": [ 1466 | "B:desc" 1467 | ] 1468 | } 1469 | ] 1470 | } 1471 | }, 1472 | "7484c55fdf7d4c729c13ea0164b43f28": { 1473 | "model_module": "@jupyter-widgets/base", 1474 | "model_module_version": "1.2.0", 1475 | "model_name": "LayoutModel", 1476 | "state": { 1477 | "align_self": "stretch", 1478 | "height": "600px" 1479 | } 1480 | }, 1481 | "9ec384e60d8847ad926f4920185390fa": { 1482 | "model_module": "@jupyter-widgets/base", 1483 | "model_module_version": "1.2.0", 1484 | "model_name": "LayoutModel", 1485 | "state": { 1486 | "align_self": "stretch", 1487 | "height": "600px" 1488 | } 1489 | }, 1490 | "d84a8a5f4f7a4c9da5a5adc414246850": { 1491 | "model_module": "@jupyter-widgets/base", 1492 | "model_module_version": "1.2.0", 1493 | "model_name": "LayoutModel", 1494 | "state": {} 1495 | }, 1496 | "e36cccb2b16249bb94c667832ab875c8": { 1497 | "model_module": "@jupyter-widgets/output", 1498 | "model_module_version": "1.0.0", 1499 | "model_name": "OutputModel", 1500 | "state": { 1501 | "layout": "IPY_MODEL_d84a8a5f4f7a4c9da5a5adc414246850", 1502 | "outputs": [ 1503 | { 1504 | "data": { 1505 | "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n
ABCD
\n
", 1506 | "text/plain": "Empty DataFrame\nColumns: [A, B, C, D]\nIndex: []" 1507 | }, 1508 | "metadata": {}, 1509 | "output_type": "display_data" 1510 | } 1511 | ] 1512 | } 1513 | }, 1514 | "ebe199b4f4bf4bd8946d020f392cbda3": { 1515 | "model_module": "lineup_widget", 1516 | "model_module_version": "4.6.0", 1517 | "model_name": "LineUpModel", 1518 | "state": { 1519 | "_columns": [ 1520 | { 1521 | "colorMapping": "#1f77b4", 1522 | "column": "A", 1523 | "domain": [ 1524 | 0, 1525 | 99 1526 | ], 1527 | "groupRenderer": "boxplot", 1528 | "label": "A", 1529 | "summaryRenderer": "histogram", 1530 | "type": "number" 1531 | }, 1532 | { 1533 | "colorMapping": "#ff7f0e", 1534 | "column": "B", 1535 | "domain": [ 1536 | 0, 1537 | 99 1538 | ], 1539 | "groupRenderer": "boxplot", 1540 | "label": "B", 1541 | "summaryRenderer": "histogram", 1542 | "type": "number" 1543 | }, 1544 | { 1545 | "colorMapping": "#2ca02c", 1546 | "column": "C", 1547 | "domain": [ 1548 | 1, 1549 | 99 1550 | ], 1551 | "groupRenderer": "boxplot", 1552 | "label": "C", 1553 | "summaryRenderer": "histogram", 1554 | "type": "number" 1555 | }, 1556 | { 1557 | "colorMapping": "#d62728", 1558 | "column": "D", 1559 | "domain": [ 1560 | 4, 1561 | 99 1562 | ], 1563 | "groupRenderer": "boxplot", 1564 | "label": "D", 1565 | "summaryRenderer": "histogram", 1566 | "type": "number" 1567 | } 1568 | ], 1569 | "_data": [ 1570 | { 1571 | "A": 15, 1572 | "B": 40, 1573 | "C": 14, 1574 | "D": 51 1575 | }, 1576 | { 1577 | "A": 80, 1578 | "B": 19, 1579 | "C": 58, 1580 | "D": 12 1581 | }, 1582 | { 1583 | "A": 35, 1584 | "B": 89, 1585 | "C": 94, 1586 | "D": 46 1587 | }, 1588 | { 1589 | "A": 1, 1590 | "B": 36, 1591 | "C": 99, 1592 | "D": 25 1593 | }, 1594 | { 1595 | "A": 78, 1596 | "B": 19, 1597 | "C": 38, 1598 | "D": 16 1599 | }, 1600 | { 1601 | "A": 96, 1602 | "B": 41, 1603 | "C": 48, 1604 | "D": 78 1605 | }, 1606 | { 1607 | "A": 10, 1608 | "B": 84, 1609 | "C": 11, 1610 | "D": 33 1611 | }, 1612 | { 1613 | "A": 87, 1614 | "B": 67, 1615 | "C": 6, 1616 | "D": 36 1617 | }, 1618 | { 1619 | "A": 12, 1620 | "B": 19, 1621 | "C": 41, 1622 | "D": 86 1623 | }, 1624 | { 1625 | "A": 88, 1626 | "B": 73, 1627 | "C": 42, 1628 | "D": 24 1629 | }, 1630 | { 1631 | "A": 13, 1632 | "B": 87, 1633 | "C": 62, 1634 | "D": 75 1635 | }, 1636 | { 1637 | "A": 0, 1638 | "B": 95, 1639 | "C": 44, 1640 | "D": 69 1641 | }, 1642 | { 1643 | "A": 93, 1644 | "B": 94, 1645 | "C": 91, 1646 | "D": 12 1647 | }, 1648 | { 1649 | "A": 93, 1650 | "B": 99, 1651 | "C": 76, 1652 | "D": 75 1653 | }, 1654 | { 1655 | "A": 41, 1656 | "B": 51, 1657 | "C": 73, 1658 | "D": 39 1659 | }, 1660 | { 1661 | "A": 46, 1662 | "B": 66, 1663 | "C": 32, 1664 | "D": 4 1665 | }, 1666 | { 1667 | "A": 50, 1668 | "B": 15, 1669 | "C": 59, 1670 | "D": 58 1671 | }, 1672 | { 1673 | "A": 34, 1674 | "B": 53, 1675 | "C": 20, 1676 | "D": 94 1677 | }, 1678 | { 1679 | "A": 69, 1680 | "B": 66, 1681 | "C": 91, 1682 | "D": 82 1683 | }, 1684 | { 1685 | "A": 14, 1686 | "B": 11, 1687 | "C": 3, 1688 | "D": 50 1689 | }, 1690 | { 1691 | "A": 85, 1692 | "B": 68, 1693 | "C": 22, 1694 | "D": 81 1695 | }, 1696 | { 1697 | "A": 6, 1698 | "B": 97, 1699 | "C": 78, 1700 | "D": 72 1701 | }, 1702 | { 1703 | "A": 63, 1704 | "B": 95, 1705 | "C": 64, 1706 | "D": 71 1707 | }, 1708 | { 1709 | "A": 43, 1710 | "B": 93, 1711 | "C": 85, 1712 | "D": 82 1713 | }, 1714 | { 1715 | "A": 76, 1716 | "B": 95, 1717 | "C": 3, 1718 | "D": 30 1719 | }, 1720 | { 1721 | "A": 53, 1722 | "B": 40, 1723 | "C": 54, 1724 | "D": 48 1725 | }, 1726 | { 1727 | "A": 79, 1728 | "B": 59, 1729 | "C": 74, 1730 | "D": 49 1731 | }, 1732 | { 1733 | "A": 94, 1734 | "B": 24, 1735 | "C": 41, 1736 | "D": 32 1737 | }, 1738 | { 1739 | "A": 28, 1740 | "B": 28, 1741 | "C": 51, 1742 | "D": 14 1743 | }, 1744 | { 1745 | "A": 3, 1746 | "B": 78, 1747 | "C": 8, 1748 | "D": 59 1749 | }, 1750 | { 1751 | "A": 92, 1752 | "B": 10, 1753 | "C": 61, 1754 | "D": 55 1755 | }, 1756 | { 1757 | "A": 26, 1758 | "B": 9, 1759 | "C": 22, 1760 | "D": 62 1761 | }, 1762 | { 1763 | "A": 12, 1764 | "B": 83, 1765 | "C": 15, 1766 | "D": 41 1767 | }, 1768 | { 1769 | "A": 17, 1770 | "B": 38, 1771 | "C": 62, 1772 | "D": 22 1773 | }, 1774 | { 1775 | "A": 35, 1776 | "B": 37, 1777 | "C": 29, 1778 | "D": 8 1779 | }, 1780 | { 1781 | "A": 90, 1782 | "B": 69, 1783 | "C": 80, 1784 | "D": 81 1785 | }, 1786 | { 1787 | "A": 76, 1788 | "B": 29, 1789 | "C": 46, 1790 | "D": 35 1791 | }, 1792 | { 1793 | "A": 43, 1794 | "B": 51, 1795 | "C": 69, 1796 | "D": 17 1797 | }, 1798 | { 1799 | "A": 25, 1800 | "B": 68, 1801 | "C": 76, 1802 | "D": 61 1803 | }, 1804 | { 1805 | "A": 58, 1806 | "B": 74, 1807 | "C": 48, 1808 | "D": 38 1809 | }, 1810 | { 1811 | "A": 13, 1812 | "B": 30, 1813 | "C": 14, 1814 | "D": 37 1815 | }, 1816 | { 1817 | "A": 76, 1818 | "B": 10, 1819 | "C": 86, 1820 | "D": 39 1821 | }, 1822 | { 1823 | "A": 39, 1824 | "B": 14, 1825 | "C": 86, 1826 | "D": 94 1827 | }, 1828 | { 1829 | "A": 26, 1830 | "B": 90, 1831 | "C": 48, 1832 | "D": 80 1833 | }, 1834 | { 1835 | "A": 89, 1836 | "B": 13, 1837 | "C": 62, 1838 | "D": 48 1839 | }, 1840 | { 1841 | "A": 32, 1842 | "B": 66, 1843 | "C": 22, 1844 | "D": 30 1845 | }, 1846 | { 1847 | "A": 76, 1848 | "B": 0, 1849 | "C": 19, 1850 | "D": 51 1851 | }, 1852 | { 1853 | "A": 32, 1854 | "B": 60, 1855 | "C": 41, 1856 | "D": 37 1857 | }, 1858 | { 1859 | "A": 59, 1860 | "B": 52, 1861 | "C": 14, 1862 | "D": 45 1863 | }, 1864 | { 1865 | "A": 22, 1866 | "B": 59, 1867 | "C": 87, 1868 | "D": 81 1869 | }, 1870 | { 1871 | "A": 84, 1872 | "B": 68, 1873 | "C": 48, 1874 | "D": 76 1875 | }, 1876 | { 1877 | "A": 93, 1878 | "B": 87, 1879 | "C": 24, 1880 | "D": 44 1881 | }, 1882 | { 1883 | "A": 85, 1884 | "B": 37, 1885 | "C": 61, 1886 | "D": 35 1887 | }, 1888 | { 1889 | "A": 40, 1890 | "B": 44, 1891 | "C": 5, 1892 | "D": 8 1893 | }, 1894 | { 1895 | "A": 6, 1896 | "B": 77, 1897 | "C": 59, 1898 | "D": 78 1899 | }, 1900 | { 1901 | "A": 75, 1902 | "B": 67, 1903 | "C": 15, 1904 | "D": 51 1905 | }, 1906 | { 1907 | "A": 35, 1908 | "B": 0, 1909 | "C": 45, 1910 | "D": 9 1911 | }, 1912 | { 1913 | "A": 18, 1914 | "B": 49, 1915 | "C": 24, 1916 | "D": 23 1917 | }, 1918 | { 1919 | "A": 53, 1920 | "B": 9, 1921 | "C": 96, 1922 | "D": 22 1923 | }, 1924 | { 1925 | "A": 59, 1926 | "B": 98, 1927 | "C": 48, 1928 | "D": 33 1929 | }, 1930 | { 1931 | "A": 19, 1932 | "B": 15, 1933 | "C": 98, 1934 | "D": 48 1935 | }, 1936 | { 1937 | "A": 76, 1938 | "B": 43, 1939 | "C": 44, 1940 | "D": 41 1941 | }, 1942 | { 1943 | "A": 80, 1944 | "B": 64, 1945 | "C": 97, 1946 | "D": 57 1947 | }, 1948 | { 1949 | "A": 97, 1950 | "B": 87, 1951 | "C": 35, 1952 | "D": 70 1953 | }, 1954 | { 1955 | "A": 98, 1956 | "B": 19, 1957 | "C": 66, 1958 | "D": 36 1959 | }, 1960 | { 1961 | "A": 94, 1962 | "B": 84, 1963 | "C": 45, 1964 | "D": 85 1965 | }, 1966 | { 1967 | "A": 86, 1968 | "B": 93, 1969 | "C": 45, 1970 | "D": 56 1971 | }, 1972 | { 1973 | "A": 71, 1974 | "B": 60, 1975 | "C": 55, 1976 | "D": 98 1977 | }, 1978 | { 1979 | "A": 32, 1980 | "B": 30, 1981 | "C": 71, 1982 | "D": 5 1983 | }, 1984 | { 1985 | "A": 71, 1986 | "B": 44, 1987 | "C": 87, 1988 | "D": 16 1989 | }, 1990 | { 1991 | "A": 42, 1992 | "B": 39, 1993 | "C": 91, 1994 | "D": 86 1995 | }, 1996 | { 1997 | "A": 41, 1998 | "B": 89, 1999 | "C": 86, 2000 | "D": 21 2001 | }, 2002 | { 2003 | "A": 51, 2004 | "B": 80, 2005 | "C": 13, 2006 | "D": 91 2007 | }, 2008 | { 2009 | "A": 5, 2010 | "B": 54, 2011 | "C": 19, 2012 | "D": 58 2013 | }, 2014 | { 2015 | "A": 4, 2016 | "B": 20, 2017 | "C": 20, 2018 | "D": 35 2019 | }, 2020 | { 2021 | "A": 37, 2022 | "B": 44, 2023 | "C": 58, 2024 | "D": 51 2025 | }, 2026 | { 2027 | "A": 44, 2028 | "B": 88, 2029 | "C": 21, 2030 | "D": 99 2031 | }, 2032 | { 2033 | "A": 82, 2034 | "B": 21, 2035 | "C": 1, 2036 | "D": 98 2037 | }, 2038 | { 2039 | "A": 92, 2040 | "B": 34, 2041 | "C": 24, 2042 | "D": 32 2043 | }, 2044 | { 2045 | "A": 28, 2046 | "B": 49, 2047 | "C": 67, 2048 | "D": 92 2049 | }, 2050 | { 2051 | "A": 11, 2052 | "B": 83, 2053 | "C": 32, 2054 | "D": 25 2055 | }, 2056 | { 2057 | "A": 63, 2058 | "B": 93, 2059 | "C": 36, 2060 | "D": 27 2061 | }, 2062 | { 2063 | "A": 29, 2064 | "B": 46, 2065 | "C": 77, 2066 | "D": 75 2067 | }, 2068 | { 2069 | "A": 93, 2070 | "B": 45, 2071 | "C": 79, 2072 | "D": 38 2073 | }, 2074 | { 2075 | "A": 69, 2076 | "B": 84, 2077 | "C": 14, 2078 | "D": 7 2079 | }, 2080 | { 2081 | "A": 1, 2082 | "B": 89, 2083 | "C": 96, 2084 | "D": 39 2085 | }, 2086 | { 2087 | "A": 31, 2088 | "B": 58, 2089 | "C": 22, 2090 | "D": 89 2091 | }, 2092 | { 2093 | "A": 26, 2094 | "B": 64, 2095 | "C": 79, 2096 | "D": 98 2097 | }, 2098 | { 2099 | "A": 3, 2100 | "B": 94, 2101 | "C": 66, 2102 | "D": 60 2103 | }, 2104 | { 2105 | "A": 43, 2106 | "B": 80, 2107 | "C": 28, 2108 | "D": 57 2109 | }, 2110 | { 2111 | "A": 7, 2112 | "B": 73, 2113 | "C": 49, 2114 | "D": 23 2115 | }, 2116 | { 2117 | "A": 16, 2118 | "B": 70, 2119 | "C": 70, 2120 | "D": 24 2121 | }, 2122 | { 2123 | "A": 33, 2124 | "B": 25, 2125 | "C": 50, 2126 | "D": 75 2127 | }, 2128 | { 2129 | "A": 58, 2130 | "B": 9, 2131 | "C": 13, 2132 | "D": 61 2133 | }, 2134 | { 2135 | "A": 39, 2136 | "B": 71, 2137 | "C": 75, 2138 | "D": 50 2139 | }, 2140 | { 2141 | "A": 64, 2142 | "B": 45, 2143 | "C": 5, 2144 | "D": 39 2145 | }, 2146 | { 2147 | "A": 72, 2148 | "B": 23, 2149 | "C": 14, 2150 | "D": 18 2151 | }, 2152 | { 2153 | "A": 16, 2154 | "B": 29, 2155 | "C": 58, 2156 | "D": 62 2157 | }, 2158 | { 2159 | "A": 99, 2160 | "B": 22, 2161 | "C": 23, 2162 | "D": 52 2163 | }, 2164 | { 2165 | "A": 39, 2166 | "B": 86, 2167 | "C": 44, 2168 | "D": 16 2169 | } 2170 | ], 2171 | "description": "", 2172 | "layout": "IPY_MODEL_9ec384e60d8847ad926f4920185390fa", 2173 | "options": { 2174 | "rowHeight": 20 2175 | }, 2176 | "value": [ 2177 | 82, 2178 | 83, 2179 | 84, 2180 | 85, 2181 | 86, 2182 | 87, 2183 | 88, 2184 | 89, 2185 | 90, 2186 | 91, 2187 | 92 2188 | ] 2189 | } 2190 | } 2191 | }, 2192 | "version_major": 2, 2193 | "version_minor": 0 2194 | } 2195 | } 2196 | }, 2197 | "nbformat": 4, 2198 | "nbformat_minor": 4 2199 | } 2200 | --------------------------------------------------------------------------------