├── website ├── src │ ├── index.css │ ├── main.js │ └── reducers.js ├── header.html ├── babel.config.js ├── README.md └── package.json ├── examples └── manifold │ ├── src │ ├── index.css │ ├── utils.js │ ├── constants.js │ ├── reducers.js │ ├── store.js │ └── main.js │ └── package.json ├── .eslintignore ├── bindings ├── jupyter │ ├── mlvis │ │ ├── widget_ext │ │ │ └── __init__.py │ │ ├── __init__.py │ │ ├── jrequirements.json │ │ ├── widget.py │ │ └── widget_builder.py │ ├── docs │ │ ├── make-docs.sh │ │ ├── graph-builder.md │ │ ├── introduction.md │ │ ├── development.md │ │ └── stacked-calendar.md │ ├── MANIFEST.in │ ├── js │ │ ├── src │ │ │ ├── components.js │ │ │ ├── extension.js │ │ │ ├── index.js │ │ │ └── widgets │ │ │ │ ├── react-widget-builder.js │ │ │ │ └── index.js │ │ ├── package.json │ │ └── webpack.config.js │ ├── Makefile │ ├── README.md │ └── notebooks │ │ └── graph-builder.ipynb ├── jupyter-modules │ ├── jupyter-manifold │ │ ├── src │ │ │ ├── controls │ │ │ │ ├── index.js │ │ │ │ ├── control-bar.js │ │ │ │ └── segment-export-button.js │ │ │ ├── manifold.js │ │ │ └── index.js │ │ └── package.json │ ├── jupyter-ma-causal │ │ ├── src │ │ │ ├── utils │ │ │ │ └── index.js │ │ │ ├── actions │ │ │ │ └── index.js │ │ │ ├── selectors │ │ │ │ ├── indicator-line-selectors.js │ │ │ │ ├── index.js │ │ │ │ ├── multi-line-chart-selectors.js │ │ │ │ ├── slidebar-selectors.js │ │ │ │ ├── staticbar-selectors.js │ │ │ │ ├── base-selectors.js │ │ │ │ └── factories.js │ │ │ ├── index.js │ │ │ ├── reducer.js │ │ │ ├── containers │ │ │ │ ├── line-charts-container │ │ │ │ │ └── index.js │ │ │ │ ├── multi-line-chart-container │ │ │ │ │ └── index.js │ │ │ │ ├── line-chart-container │ │ │ │ │ └── index.js │ │ │ │ └── staticbar-svg-container │ │ │ │ │ └── index.js │ │ │ └── components │ │ │ │ └── slidebar-svg │ │ │ │ └── index.js │ │ └── package.json │ ├── jupyter-graph-builder │ │ ├── src │ │ │ └── index.js │ │ └── package.json │ └── jupyter-multi-way-plot │ │ ├── src │ │ └── index.js │ │ └── package.json └── python-compute │ ├── __init__.py │ ├── constants.py │ └── utils.py ├── modules ├── manifold │ ├── src │ │ ├── components │ │ │ ├── index.js │ │ │ ├── ui │ │ │ │ ├── headline │ │ │ │ │ ├── index.js │ │ │ │ │ └── tabs.js │ │ │ │ ├── help-dialog │ │ │ │ │ ├── index.js │ │ │ │ │ └── step-progress.js │ │ │ │ ├── segment-filters-control │ │ │ │ │ ├── index.js │ │ │ │ │ └── segment-panel-list.js │ │ │ │ ├── legend-group │ │ │ │ │ ├── index.js │ │ │ │ │ ├── legend-item.js │ │ │ │ │ └── legend-group.js │ │ │ │ ├── segment-groups-control │ │ │ │ │ ├── index.js │ │ │ │ │ └── segment-group-panel.js │ │ │ │ ├── index.js │ │ │ │ ├── styled-control-wrapper.js │ │ │ │ └── side-bar.js │ │ │ └── container.js │ │ ├── actions │ │ │ ├── index.js │ │ │ └── ui-actions.js │ │ ├── selectors │ │ │ ├── index.js │ │ │ └── data.js │ │ ├── utils │ │ │ ├── index.js │ │ │ └── data-slicing.js │ │ ├── constants │ │ │ ├── index.js │ │ │ ├── theme.js │ │ │ ├── kepler-constants.js │ │ │ ├── types.js │ │ │ └── constants.js │ │ ├── reducers │ │ │ ├── utils.js │ │ │ ├── data-generation.js │ │ │ ├── data-slicing.js │ │ │ └── __tests__ │ │ │ │ └── data-generation.spec.js │ │ ├── index.js │ │ ├── middleware │ │ │ └── index.js │ │ ├── custom-connect.js │ │ ├── __tests__ │ │ │ ├── selector-kepler.spec.js │ │ │ └── selector-adaptors.spec.js │ │ └── io.js │ └── package.json ├── mlvis-common │ ├── src │ │ ├── ui │ │ │ └── index.js │ │ ├── index.js │ │ ├── icons │ │ │ ├── more.js │ │ │ ├── cancel.js │ │ │ ├── file-type.js │ │ │ ├── question.js │ │ │ ├── arrow-left.js │ │ │ ├── minus.js │ │ │ ├── play.js │ │ │ ├── pin.js │ │ │ ├── pause.js │ │ │ ├── messages.js │ │ │ ├── add.js │ │ │ ├── arrow-down.js │ │ │ ├── upload.js │ │ │ ├── line-chart.js │ │ │ ├── files.js │ │ │ ├── clock.js │ │ │ ├── arrow-right.js │ │ │ ├── left-arrow.js │ │ │ ├── cube-3d.js │ │ │ ├── file.js │ │ │ ├── email.js │ │ │ ├── table.js │ │ │ ├── drag-n-drop.js │ │ │ ├── search.js │ │ │ ├── reset.js │ │ │ ├── filter-funnel.js │ │ │ ├── picture.js │ │ │ ├── legend.js │ │ │ ├── histogram.js │ │ │ ├── zoom.js │ │ │ ├── share.js │ │ │ ├── cursor-click.js │ │ │ ├── save.js │ │ │ ├── square-select.js │ │ │ ├── settings.js │ │ │ ├── docs.js │ │ │ ├── vert-dots.js │ │ │ ├── trash.js │ │ │ └── layers.js │ │ ├── utils │ │ │ ├── decorators.js │ │ │ ├── index.js │ │ │ ├── gen-data.js │ │ │ └── color.js │ │ ├── __tests__ │ │ │ └── selectors.spec.js │ │ └── constants │ │ │ └── data.js │ └── package.json ├── feature-list-view │ ├── src │ │ ├── index.js │ │ ├── feature-list-view.js │ │ ├── utils.js │ │ └── constants.js │ └── package.json ├── multi-way-plot │ ├── src │ │ ├── index.js │ │ ├── utils.js │ │ └── selectors.js │ └── package.json ├── stacked-calendar │ ├── src │ │ ├── index.js │ │ └── __tests__ │ │ │ └── utils.spec.js │ ├── package.json │ └── README.md └── graph-builder │ ├── package.json │ └── README.md ├── utils └── setupTests.js ├── lerna.json ├── .travis.yml ├── ocular-dev-tools.config.js ├── .gitpod.yml ├── CHANGELOG.md ├── babel.config.js ├── .eslintrc.js ├── .gitignore ├── .github ├── pull_request_template.md └── issue_template.md ├── alias.config.js └── jest.config.js /website/src/index.css: -------------------------------------------------------------------------------- 1 | @import '~antd/dist/antd.css'; 2 | -------------------------------------------------------------------------------- /examples/manifold/src/index.css: -------------------------------------------------------------------------------- 1 | @import '~antd/dist/antd.css'; 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/node_modules/** 2 | **/dist/** 3 | **/__fixtures__/** 4 | -------------------------------------------------------------------------------- /bindings/jupyter/mlvis/widget_ext/__init__.py: -------------------------------------------------------------------------------- 1 | from .Manifold import Manifold 2 | -------------------------------------------------------------------------------- /modules/manifold/src/components/index.js: -------------------------------------------------------------------------------- 1 | export {default as Manifold, default} from './container'; 2 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-manifold/src/controls/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './control-bar'; 2 | -------------------------------------------------------------------------------- /modules/manifold/src/actions/index.js: -------------------------------------------------------------------------------- 1 | export * from './io-actions'; 2 | export * from './ui-actions'; 3 | -------------------------------------------------------------------------------- /bindings/python-compute/__init__.py: -------------------------------------------------------------------------------- 1 | from .main import ServiceSession 2 | 3 | service_session = ServiceSession() -------------------------------------------------------------------------------- /modules/mlvis-common/src/ui/index.js: -------------------------------------------------------------------------------- 1 | export {default as FileUploadWrapper} from '../file-upload-wrapper'; 2 | -------------------------------------------------------------------------------- /modules/manifold/src/components/ui/headline/index.js: -------------------------------------------------------------------------------- 1 | import Headline from './headline'; 2 | 3 | export default Headline; 4 | -------------------------------------------------------------------------------- /modules/manifold/src/components/ui/help-dialog/index.js: -------------------------------------------------------------------------------- 1 | import HelpDialog from './help-dialog'; 2 | 3 | export default HelpDialog; 4 | -------------------------------------------------------------------------------- /modules/manifold/src/components/ui/segment-filters-control/index.js: -------------------------------------------------------------------------------- 1 | export {default as SegmentFiltersControl} from './segment-filters-control'; 2 | -------------------------------------------------------------------------------- /modules/feature-list-view/src/index.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | import FeatureListView from './feature-list-view'; 3 | 4 | export default FeatureListView; 5 | -------------------------------------------------------------------------------- /modules/manifold/src/components/ui/legend-group/index.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | import LegendGroup from './legend-group'; 3 | 4 | export default LegendGroup; 5 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-ma-causal/src/utils/index.js: -------------------------------------------------------------------------------- 1 | export const clamp = (d, domain = [0, 1]) => 2 | Math.min(Math.max(d, domain[0]), domain[1]); 3 | -------------------------------------------------------------------------------- /modules/manifold/src/selectors/index.js: -------------------------------------------------------------------------------- 1 | import {getDataIdsInSegmentGroups} from './compute'; 2 | 3 | export default {compute: {getDataIdsInSegmentGroups}}; 4 | -------------------------------------------------------------------------------- /modules/manifold/src/utils/index.js: -------------------------------------------------------------------------------- 1 | export * from './utils'; 2 | export * from './data-processor'; 3 | export * from './default-states'; 4 | export * from './data-slicing'; 5 | -------------------------------------------------------------------------------- /modules/manifold/src/constants/index.js: -------------------------------------------------------------------------------- 1 | export * from './constants'; 2 | export {HELP_PAGES} from './help-pages'; 3 | export {default as THEME} from './theme'; 4 | export * from './types'; 5 | -------------------------------------------------------------------------------- /modules/multi-way-plot/src/index.js: -------------------------------------------------------------------------------- 1 | import MultiwayPlot from './multi-way-plot'; 2 | export {default as SegmentGrouping} from './segment-grouping'; 3 | 4 | export default MultiwayPlot; 5 | -------------------------------------------------------------------------------- /utils/setupTests.js: -------------------------------------------------------------------------------- 1 | // necessary for using enzyme for testing 2 | 3 | import {configure} from 'enzyme'; 4 | import Adapter from 'enzyme-adapter-react-16'; 5 | 6 | configure({adapter: new Adapter()}); 7 | -------------------------------------------------------------------------------- /bindings/jupyter/docs/make-docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | jupyter nbconvert --to html ../notebooks/*.ipynb 3 | jupyter nbconvert --to markdown ../notebooks/*.ipynb 4 | mv ../notebooks/*.html . 5 | mv ../notebooks/*.md . 6 | -------------------------------------------------------------------------------- /modules/manifold/src/components/ui/segment-groups-control/index.js: -------------------------------------------------------------------------------- 1 | export {default as SegmentButtonGroup} from './segment-button-group'; 2 | export {default as SegmentGroupsControl} from './segment-group-panel'; 3 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "lerna": "3.14.1", 3 | "version": "1.1.4", 4 | "packages": [ 5 | "modules/*", 6 | "bindings/jupyter-modules/*" 7 | ], 8 | "npmClient": "yarn", 9 | "useWorkspaces": true 10 | } 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | install: 3 | - npm install -g codecov 4 | - yarn --ignore-engines 5 | node_js: 6 | - 10 7 | script: 8 | - npm run lint 9 | - npm run test 10 | - codecov 11 | cache: yarn 12 | -------------------------------------------------------------------------------- /modules/stacked-calendar/src/index.js: -------------------------------------------------------------------------------- 1 | import StackedCalendar from './stacked-calendar'; 2 | import {withDerivedLayout, withDerivedData} from './utils'; 3 | 4 | export default withDerivedLayout(withDerivedData(StackedCalendar)); 5 | -------------------------------------------------------------------------------- /ocular-dev-tools.config.js: -------------------------------------------------------------------------------- 1 | const {resolve} = require('path'); 2 | 3 | const config = { 4 | lint: { 5 | paths: ['modules', 'examples'], 6 | extensions: ['js'], 7 | }, 8 | }; 9 | 10 | module.exports = config; 11 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/index.js: -------------------------------------------------------------------------------- 1 | import * as constants from './constants/index'; 2 | import * as icons from './icons/index'; 3 | import * as ui from './ui/index'; 4 | import * as utils from './utils/index'; 5 | 6 | export {constants, icons, ui, utils}; 7 | -------------------------------------------------------------------------------- /bindings/python-compute/constants.py: -------------------------------------------------------------------------------- 1 | FILTER_TYPE = { 2 | 'RANGE': 'range', 3 | 'INCLUDE': 'include', 4 | 'EXCLUDE': 'exclude', 5 | 'FUNC': 'func' 6 | } 7 | METRICS = { 8 | 'PERFORMANCE': 'performance', 9 | 'ABSOLUTE': 'absolute' 10 | } 11 | 12 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-graph-builder/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import GraphBuilder from '@mlvis/graph-builder'; 3 | 4 | export default props => ( 5 |
6 | 7 |
8 | ); 9 | -------------------------------------------------------------------------------- /bindings/jupyter/mlvis/__init__.py: -------------------------------------------------------------------------------- 1 | from .widget_builder import * 2 | 3 | 4 | def _jupyter_nbextension_paths(): 5 | return [{ 6 | 'section': 'notebook', 7 | 'src': 'static', 8 | 'dest': 'mlvis', 9 | 'require': 'mlvis/extension' 10 | }] 11 | -------------------------------------------------------------------------------- /modules/manifold/src/components/ui/index.js: -------------------------------------------------------------------------------- 1 | export {default as Control} from './styled-control-wrapper'; 2 | export {default as Headline} from './headline'; 3 | export {default as SideBar} from './side-bar'; 4 | export {default as LegendGroup} from './legend-group'; 5 | export {default as HelpDialog} from './help-dialog'; 6 | -------------------------------------------------------------------------------- /examples/manifold/src/utils.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | /** 3 | * @param {Object} userData - {x: , yPred: [], yTrue: file} 4 | */ 5 | export function isDatasetIncomplete(userData) { 6 | const {x, yPred, yTrue} = userData; 7 | if (!x || !yPred || !yTrue || !yPred.length) { 8 | return true; 9 | } 10 | return false; 11 | } 12 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-ma-causal/src/actions/index.js: -------------------------------------------------------------------------------- 1 | import {createAction} from 'redux-actions'; 2 | 3 | export const UPDATE_DATA = 'UPDATE_DATA'; 4 | export const UPDATE_SLIDER_VALUES = 'UPDATE_SLIDER_VALUES'; 5 | 6 | export const updateData = createAction(UPDATE_DATA); 7 | export const updateSliderValues = createAction(UPDATE_SLIDER_VALUES); 8 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | - init: > 3 | yarn install && 4 | yarn build && 5 | yarn test && 6 | yarn && 7 | cd examples/manifold && 8 | yarn 9 | command: cd examples/manifold && npm run start 10 | name: split-term 11 | - command: echo split 12 | openMode: split-right 13 | ports: 14 | - port: 8080 15 | onOpen: open-preview 16 | -------------------------------------------------------------------------------- /modules/manifold/src/reducers/utils.js: -------------------------------------------------------------------------------- 1 | import {validateAndSetDefaultStatesConfigurator} from '../utils'; 2 | import {setDefaultFuncs, isValidFuncs} from '../utils/default-states'; 3 | 4 | export const validateAndSetDefaultStates = validateAndSetDefaultStatesConfigurator( 5 | ['metric', 'baseCols', 'nClusters', 'segmentFilters', 'segmentGroups'], 6 | isValidFuncs, 7 | setDefaultFuncs 8 | ); 9 | -------------------------------------------------------------------------------- /website/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v0.0.1 2 | 3 | - Support model types: 4 | - Regression 5 | - Binary classification 6 | - Support more feature types 7 | - Numeric feature 8 | - Categorical feature 9 | - Data slicing logic 10 | - Automatic segmentation by all model performance scores 11 | - Manual segmentation by feature value 12 | - User experience 13 | - Added n-UI documentation widget 14 | -------------------------------------------------------------------------------- /bindings/jupyter/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include README.md 3 | include .coverage.rc 4 | include mlvis/jrequirements.json 5 | graft docs 6 | 7 | # Javascript files 8 | graft mlvis/static 9 | prune **/node_modules 10 | prune coverage 11 | prune lib 12 | 13 | # Patterns to exclude from any directory 14 | global-exclude *~ 15 | global-exclude *.pyc 16 | global-exclude *.pyo 17 | global-exclude .git 18 | global-exclude .ipynb_checkpoints 19 | -------------------------------------------------------------------------------- /bindings/jupyter/js/src/components.js: -------------------------------------------------------------------------------- 1 | export {default as StackedCalendar} from '@mlvis/stacked-calendar'; 2 | export {default as GraphBuilder} from '@mlvis/jupyter-graph-builder'; 3 | export {default as FeatureListView} from '@mlvis/feature-list-view'; 4 | export {default as MultiWayPlot} from '@mlvis/jupyter-multi-way-plot'; 5 | export {default as Manifold} from '@mlvis/jupyter-manifold'; 6 | export {default as MACausal} from '@mlvis/jupyter-ma-causal'; 7 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | /* global module */ 2 | module.exports = { 3 | plugins: ['@babel/plugin-proposal-class-properties'], 4 | presets: [ 5 | '@babel/preset-react', 6 | [ 7 | '@babel/preset-env', 8 | { 9 | useBuiltIns: 'entry', 10 | corejs: '3', 11 | }, 12 | ], 13 | '@babel/preset-flow', 14 | ], 15 | env: { 16 | test: { 17 | presets: [['@babel/preset-env'], '@babel/preset-react'], 18 | }, 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /website/babel.config.js: -------------------------------------------------------------------------------- 1 | /* global module */ 2 | module.exports = { 3 | plugins: ['@babel/plugin-proposal-class-properties'], 4 | presets: [ 5 | '@babel/preset-react', 6 | [ 7 | '@babel/preset-env', 8 | { 9 | useBuiltIns: 'entry', 10 | corejs: '3', 11 | }, 12 | ], 13 | '@babel/preset-flow', 14 | ], 15 | env: { 16 | test: { 17 | presets: [['@babel/preset-env'], '@babel/preset-react'], 18 | }, 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-manifold/src/controls/control-bar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import SegmentExportButton from './segment-export-button'; 3 | const style = { 4 | display: 'flex', 5 | alignItems: 'center', 6 | justifyContent: 'flex-end', 7 | height: 50, 8 | width: '100%', 9 | padding: '5px 5px', 10 | backgroundColor: '#f5f5f5', 11 | }; 12 | 13 | export default props => ( 14 |
15 | 16 |
17 | ); 18 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-ma-causal/src/selectors/indicator-line-selectors.js: -------------------------------------------------------------------------------- 1 | import {createSelector} from 'reselect'; 2 | import { 3 | getColumnWidth, 4 | getChartPaddingLeft, 5 | getChartPaddingRight, 6 | } from './base-selectors'; 7 | 8 | export const getChartWidth = createSelector(getColumnWidth, width => width); 9 | 10 | export const getChartPadding = createSelector( 11 | [getChartPaddingLeft, getChartPaddingRight], 12 | (left, right) => ({bottom: 15, top: 10, left, right}) 13 | ); 14 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-ma-causal/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {createStore} from 'redux'; 3 | import {Provider} from 'react-redux'; 4 | import Container from './containers'; 5 | import reducer from './reducer'; 6 | import {window} from 'global'; 7 | export default props => { 8 | const store = createStore( 9 | reducer, 10 | window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() 11 | ); 12 | return ( 13 | 14 | 15 | 16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /modules/manifold/src/index.js: -------------------------------------------------------------------------------- 1 | // Reducers 2 | export {default as manifoldReducer} from './reducers'; 3 | 4 | // Actions 5 | export * from './actions'; 6 | 7 | // Constants 8 | export {THEME} from './constants'; 9 | 10 | // Containers 11 | export * from './components'; 12 | 13 | // Utilities 14 | export {validateInputData} from './utils'; 15 | 16 | // Selectors 17 | export {default as selectors} from './selectors'; 18 | 19 | // middleware 20 | export {enhanceReduxMiddleware} from './middleware'; 21 | 22 | // Default export 23 | export {default} from './components'; 24 | -------------------------------------------------------------------------------- /examples/manifold/src/constants.js: -------------------------------------------------------------------------------- 1 | export const convertToCdnUrl = fileName => 2 | `https://d1a3f4spazzrp4.cloudfront.net/manifold/data/${fileName}`; 3 | 4 | export const DATASET = { 5 | REG: 'REGRESSION', 6 | BIN: 'BINARY_CLASSIFICATION', 7 | }; 8 | 9 | // urls for sample data 10 | export const SAMPLE_DATA_S3 = { 11 | [DATASET.REG]: { 12 | x: 'reg_x_anon.csv', 13 | yPred: ['reg_y_pred_0.csv'], 14 | yTrue: 'reg_y_true.csv', 15 | }, 16 | [DATASET.BIN]: { 17 | x: 'feature.csv', 18 | yPred: ['pred_bin_0.csv', 'pred_bin_1.csv'], 19 | yTrue: 'pred_reg.csv', 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-ma-causal/src/selectors/index.js: -------------------------------------------------------------------------------- 1 | import * as factories from './factories'; 2 | import * as baseSelectors from './base-selectors'; 3 | import * as multiLineChartSelectors from './multi-line-chart-selectors'; 4 | import * as slidebarSelectors from './slidebar-selectors'; 5 | import * as indicatorLineSelectors from './indicator-line-selectors'; 6 | import * as staticbarSelectors from './staticbar-selectors'; 7 | export { 8 | factories, 9 | baseSelectors, 10 | multiLineChartSelectors, 11 | slidebarSelectors, 12 | indicatorLineSelectors, 13 | staticbarSelectors, 14 | }; 15 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-ma-causal/src/selectors/multi-line-chart-selectors.js: -------------------------------------------------------------------------------- 1 | import {createSelector} from 'reselect'; 2 | import { 3 | rootSelector, 4 | getColumnWidth, 5 | getChartPaddingLeft, 6 | getChartPaddingRight, 7 | } from './base-selectors'; 8 | 9 | export const getChartWidth = createSelector(getColumnWidth, width => width); 10 | 11 | export const getChartHeight = createSelector(rootSelector, height => 260); 12 | 13 | export const getChartPadding = createSelector( 14 | [getChartPaddingLeft, getChartPaddingRight], 15 | (left, right) => ({top: 10, bottom: 20, left, right}) 16 | ); 17 | -------------------------------------------------------------------------------- /bindings/jupyter/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: develop install clean build publish publish-test 2 | 3 | develop: 4 | python setup.py develop 5 | 6 | install: 7 | python setup.py install 8 | 9 | clean: 10 | rm -rf dist 11 | 12 | build-js: 13 | cd js && yarn build 14 | 15 | build-py: clean 16 | python setup.py sdist bdist_wheel 17 | 18 | build: build-js build-py 19 | 20 | publish: 21 | twine upload dist/* 22 | 23 | publish-test: 24 | # install the test publish via: 25 | # pip install --index-url https://test.pypi.org/simple/ mlvis==x.x.x 26 | twine upload --repository-url https://test.pypi.org/legacy/ dist/* 27 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | module.exports = { 3 | extends: [ 4 | 'plugin:react/recommended', 5 | 'plugin:jest/recommended', 6 | 'eslint-config-uber-universal-stage-3', 7 | ], 8 | env: { 9 | jest: true, 10 | node: true, 11 | }, 12 | settings: { 13 | react: { 14 | version: '16.9.0', 15 | }, 16 | }, 17 | plugins: ['eslint-plugin-react'], 18 | rules: { 19 | 'react/prop-types': 'off', 20 | 'import/no-unresolved': 'off', 21 | 'import/no-extraneous-dependencies': 'off', 22 | 'import/no-dynamic-require': 'off', 23 | 'react/display-name': 'off', 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /bindings/jupyter/js/src/extension.js: -------------------------------------------------------------------------------- 1 | import {window} from 'global'; 2 | 3 | // This file contains the javascript that is run when the notebook is loaded. 4 | // It contains some requirejs configuration and the `load_ipython_extension` 5 | // which is required for any notebook extension. 6 | 7 | // Configure requirejs 8 | if (window.require) { 9 | window.require.config({ 10 | map: { 11 | '*': { 12 | mlviswidget: 'nbextensions/mlvis/index', 13 | }, 14 | }, 15 | }); 16 | } 17 | 18 | // Export the required load_ipython_extention 19 | export default { 20 | load_ipython_extension: function() {}, 21 | }; 22 | -------------------------------------------------------------------------------- /examples/manifold/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "webpack": "^4.27.1", 4 | "webpack-cli": "^3.1.2", 5 | "webpack-dev-server": "^3.1.10" 6 | }, 7 | "scripts": { 8 | "start": "webpack-dev-server --progress --hot --open", 9 | "start-local": "webpack-dev-server --env.local --progress --hot --open" 10 | }, 11 | "dependencies": { 12 | "antd": "^3.23.2", 13 | "react-container-dimensions": "^1.4.1", 14 | "react-dom": "^16.8.6", 15 | "react-redux": "^7.1.1", 16 | "react-router": "^5.1.2", 17 | "react-router-dom": "^5.1.2", 18 | "react-router-redux": "^4.0.8" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/more.js: -------------------------------------------------------------------------------- 1 | //@noflow 2 | import React, {PureComponent} from 'react'; 3 | import Base from './base'; 4 | 5 | export default class More extends PureComponent { 6 | render() { 7 | return ( 8 | 9 | 14 | 15 | ); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-multi-way-plot/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {scaleLinear, scaleBand, scaleOrdinal} from 'd3-scale'; 3 | import {schemeSet2 as colorScheme} from 'd3-scale-chromatic'; 4 | import MultiWayPlot from '@mlvis/multi-way-plot'; 5 | 6 | // TODO: Pass real functions from Python to Javascript 7 | export default props => ( 8 | [0, 12]} 12 | getYScale={() => scaleBand().padding(0.1)} 13 | getYDomain={() => [0, 1, 2]} 14 | colorScale={scaleOrdinal(colorScheme).domain([0, 1])} 15 | /> 16 | ); 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | examples/**/*-lock.json 4 | examples/**/data 5 | scripts/sync.sh 6 | scripts/traverse.sh 7 | scripts/traverse.py 8 | test/ 9 | *.log 10 | .cache/ 11 | coverage*/ 12 | jsconfig.json 13 | dist 14 | data/ 15 | .DS_Store 16 | .prettierrc.js 17 | 18 | # Python 19 | 20 | __pycache__ 21 | *.pyc 22 | .idea 23 | *.egg-info 24 | .python-version 25 | 26 | # Jupyter Bindings 27 | .ipynb_checkpoints 28 | bindings/jupyter/**/static/ 29 | bindings/jupyter/**/*env/ 30 | bindings/jupyter/build 31 | bindings/jupyter/*.ipynb 32 | bindings/jupyter/**/metadata 33 | examples/jupyter/**/venv/ 34 | examples/jupyter/**/lib/ 35 | -------------------------------------------------------------------------------- /modules/manifold/src/middleware/index.js: -------------------------------------------------------------------------------- 1 | // Extra helpers for redux 2 | // We are exposing this because react-palm has no UMD module and 3 | // users need taskMiddleware to initiate their redux middle ware 4 | import {enhanceReduxMiddleware as keplerEnhanceMiddleware} from 'kepler.gl/middleware'; 5 | 6 | /** 7 | * This method is used to pass kepler `enhanceReduxMiddleware` to support react-palm 8 | * @param middlewares current redux middlewares 9 | * @returns {*[]} the original list of middlewares plus the react-palm middleware 10 | */ 11 | export function enhanceReduxMiddleware(middlewares = []) { 12 | return keplerEnhanceMiddleware(middlewares); 13 | } 14 | -------------------------------------------------------------------------------- /modules/graph-builder/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mlvis/graph-builder", 3 | "description": "A thin wrapper over graph.gl for exploring a graph interactively.", 4 | "license": "Apache-2.0", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/uber/manifold.git" 8 | }, 9 | "version": "1.1.4", 10 | "author": "Javid Hsueh", 11 | "main": "index.js", 12 | "publishConfig": { 13 | "note": "lerna requires this to publish scoped packages", 14 | "access": "public" 15 | }, 16 | "dependencies": { 17 | "graph.gl": "^1.0.0" 18 | }, 19 | "peerDependencies": { 20 | "prop-types": "^15.7.1", 21 | "react": "^16.9.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-ma-causal/src/selectors/slidebar-selectors.js: -------------------------------------------------------------------------------- 1 | import {createSelector} from 'reselect'; 2 | import { 3 | rootSelector, 4 | getColumnWidth, 5 | getChartPaddingLeft, 6 | getChartPaddingRight, 7 | } from './base-selectors'; 8 | 9 | export const getChartWidth = createSelector(getColumnWidth, width => width); 10 | 11 | // TODO: move this to a constant or implement adjustable width logic depending on the needs 12 | export const getChartHeight = createSelector(rootSelector, height => 50); 13 | 14 | export const getChartPadding = createSelector( 15 | [getChartPaddingLeft, getChartPaddingRight], 16 | (left, right) => ({bottom: 0, top: 0, left, right}) 17 | ); 18 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-ma-causal/src/selectors/staticbar-selectors.js: -------------------------------------------------------------------------------- 1 | import {createSelector} from 'reselect'; 2 | import { 3 | rootSelector, 4 | getColumnWidth, 5 | getChartPaddingLeft, 6 | getChartPaddingRight, 7 | } from './base-selectors'; 8 | 9 | export const getChartWidth = createSelector(getColumnWidth, width => width); 10 | 11 | // TODO: move this to a constant or implement adjustable width logic depending on the needs 12 | export const getChartHeight = createSelector(rootSelector, height => 50); 13 | 14 | export const getChartPadding = createSelector( 15 | [getChartPaddingLeft, getChartPaddingRight], 16 | (left, right) => ({bottom: 0, top: 0, left, right}) 17 | ); 18 | -------------------------------------------------------------------------------- /bindings/jupyter/mlvis/jrequirements.json: -------------------------------------------------------------------------------- 1 | { 2 | "FeatureListView": { 3 | "module": { 4 | "@mlvis/feature-list-view": "external" 5 | } 6 | }, 7 | "GraphBuilder": { 8 | "module": { 9 | "@mlvis/jupyter-graph-builder": "external" 10 | } 11 | }, 12 | "Manifold": { 13 | "module": { 14 | "@mlvis/juypter-manifold": "external" 15 | } 16 | }, 17 | "MultiWayPlot": { 18 | "module": { 19 | "@mlvis/jupyter-multi-way-plot": "external" 20 | } 21 | }, 22 | "StackedCalendar": { 23 | "module": { 24 | "@mlvis/stacked-calendar": "external" 25 | } 26 | }, 27 | "MACausal": { 28 | "module": { 29 | "@mlvis/jupyter-ma-causal": "external" 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /bindings/jupyter/js/src/index.js: -------------------------------------------------------------------------------- 1 | import {window} from 'global'; 2 | import Jupyter from 'base/js/namespace'; 3 | 4 | // Frontend error handling 5 | window.addEventListener('error', event => { 6 | const msgId = Jupyter.notebook.kernel.last_msg_id; 7 | const cell = Jupyter.notebook.get_msg_cell(msgId); 8 | if (cell) { 9 | const {error} = event; 10 | cell.output_area.append_output({ 11 | output_type: 'error', 12 | ename: error.name, 13 | evalue: error.message, 14 | traceback: ['Javascript Error', error.stack], 15 | }); 16 | cell.output_area.element 17 | .find('.output_subarea.output_error') 18 | .css({'background-color': '#ff9999'}); 19 | } 20 | }); 21 | 22 | export * from './widgets'; 23 | -------------------------------------------------------------------------------- /examples/manifold/src/reducers.js: -------------------------------------------------------------------------------- 1 | import {combineReducers} from 'redux'; 2 | import {handleActions} from 'redux-actions'; 3 | import {UPDATE_VIEWPORT} from './actions'; 4 | import manifoldReducer from '@mlvis/manifold/reducers'; 5 | 6 | export const DEFAULT_STATE = { 7 | width: 0, 8 | height: 0, 9 | }; 10 | 11 | const handleUpdateViewport = (state, {payload}) => ({ 12 | ...state, 13 | width: payload.width, 14 | height: payload.height, 15 | }); 16 | 17 | export const appReducer = handleActions( 18 | { 19 | [UPDATE_VIEWPORT]: handleUpdateViewport, 20 | }, 21 | DEFAULT_STATE 22 | ); 23 | 24 | export default combineReducers({ 25 | // mount Manifold reducer 26 | manifold: manifoldReducer, 27 | app: appReducer, 28 | }); 29 | -------------------------------------------------------------------------------- /modules/multi-way-plot/src/utils.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {getPercentiles, getDensity, getNumDataPoints} from './selectors'; 3 | 4 | /** 5 | * HOC function to compute derived data for multi-way-unit, multi-way-group, and multi-way-plot 6 | */ 7 | export const withDerivedDataUnit = WrappedComponent => props => { 8 | const derivedData = { 9 | percentiles: getPercentiles(props), 10 | density: getDensity(props), 11 | }; 12 | return ; 13 | }; 14 | 15 | export const withDerivedDataGroup = WrappedComponent => props => { 16 | const derivedData = { 17 | numDataPoints: getNumDataPoints(props), 18 | }; 19 | return ; 20 | }; 21 | -------------------------------------------------------------------------------- /modules/graph-builder/README.md: -------------------------------------------------------------------------------- 1 | # GraphBuilder 2 | 3 | A generic node-link diagram 4 | 5 | ## Usage 6 | 7 | Example: 8 | 9 | ```js 10 | import GraphBuilder from '@mlvis/graph-builder'; 11 | 12 | const data = { 13 | nodes: [{id: '1'}, {id: '2'}, {id: '3'}], 14 | edge: [ 15 | {id: 'e1', sourceId: '1', targetId: '2'}, 16 | {id: 'e2', sourceId: '1', targetId: '3'}, 17 | {id: 'e3', sourceId: '2', targetId: '3'}, 18 | ], 19 | }; 20 | 21 | const App = () => ( 22 | console.log(n)} 29 | onEdgeClick={e => console.log(e)} 30 | /> 31 | ); 32 | ``` 33 | 34 | 35 | -------------------------------------------------------------------------------- /modules/manifold/src/constants/theme.js: -------------------------------------------------------------------------------- 1 | import {LightTheme} from 'baseui'; 2 | 3 | export const THEME_COLOR = '#276ef1'; 4 | const CONTROL_BG_COLOR = 'rgba(230, 230, 230, 0.7)'; 5 | 6 | // refer to default theme at https://baseweb.design/guides/theming/#the-shape-of-the-theme-file 7 | // and style implementation at https://github.com/uber-web/baseui/blob/master/src/some-component/styled-components.js to override 8 | export const OVERRIDE_COLORS = { 9 | borderFocus: THEME_COLOR, 10 | 11 | // tagPrimarySolidBackground: THEME_COLOR, 12 | // tagPrimarySolidFontHover: THEME_COLOR, 13 | 14 | inputFill: CONTROL_BG_COLOR, 15 | }; 16 | 17 | export default { 18 | ...LightTheme, 19 | colors: { 20 | ...LightTheme.colors, 21 | ...OVERRIDE_COLORS, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /modules/manifold/src/components/ui/styled-control-wrapper.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | const Container = styled.div` 5 | display: flex; 6 | flex-direction: column; 7 | 8 | & > label { 9 | color: #999; 10 | font-size: 12px; 11 | font-weight: 500; 12 | white-space: nowrap; 13 | padding: 10px 0; 14 | } 15 | & > div { 16 | color: #000; 17 | font-size: 13px; 18 | } 19 | `; 20 | 21 | export default class Control extends PureComponent { 22 | render() { 23 | const {name, children, className} = this.props; 24 | return ( 25 | 26 | 27 | {children} 28 | 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-graph-builder/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mlvis/jupyter-graph-builder", 3 | "version": "1.1.4", 4 | "license": "Apache-2.0", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/uber/manifold.git" 8 | }, 9 | "main": "index.js", 10 | "publishConfig": { 11 | "access": "public" 12 | }, 13 | "scripts": { 14 | "watch": "webpack --watch --progress", 15 | "clean": "rm -rf dist", 16 | "copy": "cp -r package.json dist", 17 | "build": "yarn clean && yarn transpile", 18 | "transpile": "NODE_ENV=production babel src -d dist -s --config-file ../../../babel.config.js && yarn copy" 19 | }, 20 | "dependencies": { 21 | "@mlvis/graph-builder": "^1.1.4", 22 | "react": "^16.9.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/cancel.js: -------------------------------------------------------------------------------- 1 | //@noflow 2 | import React, {PureComponent} from 'react'; 3 | import Base from './base'; 4 | 5 | export default class Cancel extends PureComponent { 6 | render() { 7 | return ( 8 | 9 | 15 | 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /website/src/main.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {render} from 'react-dom'; 3 | import document from 'global/document'; 4 | import {Provider} from 'react-redux'; 5 | import {Route} from 'react-router'; 6 | import {BrowserRouter} from 'react-router-dom'; 7 | import store from './reducers'; 8 | import Demo from '../../examples/manifold/src/app'; 9 | // TODO: removing antd in index.css will mess up the styles. Adjust styles before getting rid of antd dependency. 10 | import './index.css'; 11 | import 'mapbox-gl/dist/mapbox-gl.css'; 12 | 13 | const Root = () => ( 14 | 15 | 16 | 17 | 18 | 19 | ); 20 | 21 | render(, document.body.appendChild(document.createElement('div'))); 22 | -------------------------------------------------------------------------------- /modules/stacked-calendar/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mlvis/stacked-calendar", 3 | "description": "A day-hour calendar view with stacked charts for each hour", 4 | "version": "1.1.4", 5 | "license": "Apache-2.0", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/uber/manifold.git" 9 | }, 10 | "author": "Yang Wang", 11 | "main": "index.js", 12 | "dependencies": { 13 | "@mlvis/mlvis-common": "^1.1.4", 14 | "d3-array": "^2.0.3", 15 | "d3-scale": "^3.0.0", 16 | "d3-scale-chromatic": "^1.3.3" 17 | }, 18 | "peerDependencies": { 19 | "prop-types": "^15.7.1", 20 | "react": "^16.9.0", 21 | "reselect": "^4.0.0" 22 | }, 23 | "publishConfig": { 24 | "note": "lerna requires this to publish scoped packages", 25 | "access": "public" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/manifold/src/store.js: -------------------------------------------------------------------------------- 1 | import {combineReducers, createStore, applyMiddleware, compose} from 'redux'; 2 | import {routerReducer} from 'react-router-redux'; 3 | import thunk from 'redux-thunk'; 4 | import window from 'global/window'; 5 | import demoReducer from './reducers'; 6 | import {enhanceReduxMiddleware} from '@mlvis/manifold/middleware'; 7 | 8 | const reducers = combineReducers({ 9 | demo: demoReducer, 10 | routing: routerReducer, 11 | }); 12 | 13 | export const middlewares = enhanceReduxMiddleware([thunk]); 14 | export const enhancers = [applyMiddleware(...middlewares)]; 15 | 16 | const initialState = {}; 17 | 18 | // add redux devtools 19 | const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 20 | 21 | export default createStore( 22 | reducers, 23 | initialState, 24 | composeEnhancers(...enhancers) 25 | ); 26 | -------------------------------------------------------------------------------- /modules/manifold/src/custom-connect.js: -------------------------------------------------------------------------------- 1 | import {connect as reduxConnect} from 'react-redux'; 2 | 3 | const defaultMapStateToProps = state => state; 4 | const defaultMapDispatchToProps = dispatch => ({dispatch}); 5 | 6 | export const connect = ( 7 | mapStateToProps = defaultMapStateToProps, 8 | mapDispatchToProps = defaultMapDispatchToProps, 9 | reduxMergeProps, 10 | options 11 | ) => BaseComponent => { 12 | const mapStateToPropsFunc = 13 | typeof mapStateToProps === 'function' 14 | ? mapStateToProps 15 | : (state, props) => mapStateToProps; 16 | 17 | const reduxMapState = (state, props) => { 18 | return mapStateToPropsFunc(props.selector(state), props, state); 19 | }; 20 | 21 | return reduxConnect( 22 | reduxMapState, 23 | mapDispatchToProps, 24 | reduxMergeProps, 25 | options 26 | )(BaseComponent); 27 | }; 28 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | #### Summary | Related Issue(s) 4 | 5 | 6 | 7 | 8 | #### Changes 9 | 10 | 11 | 12 | 1. TBA 13 | 2. TBA 14 | 3. TBA 15 | 16 | #### Test Plan 17 | 18 | 19 | 20 | #### Checklist 21 | 22 | - [ ] I have made this PR atomic. 23 | - [ ] I have provided enough context for others to review. 24 | - [ ] I have added tests to cover my changes (for features and bug fixes). 25 | - [ ] I have updated the documentation and changelogs accordingly. 26 | - [ ] All new and existing tests passed. 27 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-multi-way-plot/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mlvis/jupyter-multi-way-plot", 3 | "version": "1.1.4", 4 | "license": "Apache-2.0", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/uber/manifold.git" 8 | }, 9 | "main": "dist/index.js", 10 | "publishConfig": { 11 | "access": "public" 12 | }, 13 | "scripts": { 14 | "watch": "webpack --watch --progress", 15 | "clean": "rm -rf dist", 16 | "copy": "cp -r package.json dist", 17 | "build": "yarn clean && yarn transpile", 18 | "transpile": "NODE_ENV=production babel src -d dist -s --config-file ../../../babel.config.js && yarn copy" 19 | }, 20 | "dependencies": { 21 | "@mlvis/multi-way-plot": "^1.1.4", 22 | "d3-scale": "^3.0.0", 23 | "d3-scale-chromatic": "^1.3.3", 24 | "global": "^4.4.0", 25 | "react": "^16.9.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /modules/manifold/src/reducers/data-generation.js: -------------------------------------------------------------------------------- 1 | import {sliceDataset, concatDataset, columnsAndFieldsFromScore} from '../utils'; 2 | 3 | export const handleUpdateMetric = (state, {payload}) => { 4 | const {data, columnTypeRanges, modelsMeta} = state; 5 | 6 | const {columns: columnsYPred} = sliceDataset(data, columnTypeRanges.yPred); 7 | const {columns: columnsYTrue} = sliceDataset(data, columnTypeRanges.yTrue); 8 | const {func: scoreFunc} = payload; 9 | 10 | // todo: memoize to reduce repeated computing 11 | const scoreDataset = columnsAndFieldsFromScore( 12 | columnsYPred, 13 | columnsYTrue, 14 | modelsMeta, 15 | scoreFunc 16 | ); 17 | const _data = concatDataset([ 18 | sliceDataset(data, [0, columnTypeRanges.yTrue[1]]), 19 | scoreDataset, 20 | ]); 21 | 22 | return { 23 | ...state, 24 | data: _data, 25 | metric: payload, 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-ma-causal/src/selectors/base-selectors.js: -------------------------------------------------------------------------------- 1 | import {createSelector} from 'reselect'; 2 | 3 | export const rootSelector = state => state; 4 | 5 | // TODO: move this to a constant or implement adjustable width logic depending on the needs 6 | export const getColumnWidth = createSelector(rootSelector, () => 300); 7 | 8 | // TODO: move this to a constant or implement adjustable width logic depending on the needs 9 | export const getChartPaddingLeft = createSelector(rootSelector, () => 30); 10 | 11 | // TODO: move this to a constant or implement adjustable width logic depending on the needs 12 | export const getChartPaddingRight = createSelector(rootSelector, () => 20); 13 | 14 | export const getData = createSelector(rootSelector, state => state.data); 15 | 16 | export const getSliderValues = createSelector( 17 | rootSelector, 18 | state => state.sliderValues 19 | ); 20 | -------------------------------------------------------------------------------- /bindings/jupyter/README.md: -------------------------------------------------------------------------------- 1 | # Jupyter Binding 2 | 3 | Jupyter 4 | 5 | The jupyter binding of this project is a [Python library](https://pypi.org/project/mlvis/) that consists of several prebuilt visualization components: 6 | 7 | - [Stacked Calendar](docs/stacked-calendar.md) 8 | - [Graph Builder](docs/graph-builder.md) 9 | - [Manifold](docs/manifold.md) 10 | - [Feature List View](docs/feature-list-view.md) 11 | 12 | ## Installation 13 | 14 | ``` 15 | pip install mlvis 16 | ``` 17 | 18 | Then install the nbextension into the Jupyter notebook 19 | 20 | ``` 21 | jupyter nbextension install --py --symlink --sys-prefix mlvis 22 | jupyter nbextension enable --py --sys-prefix mlvis 23 | ``` 24 | 25 | ## Development 26 | 27 | Instructions for building this project locally can be found in the [development documentation](docs/development.md). 28 | -------------------------------------------------------------------------------- /bindings/jupyter/docs/graph-builder.md: -------------------------------------------------------------------------------- 1 | # Graph Builder 2 | 3 | ## Usage 4 | 5 | ```python 6 | from mlvis import GraphBuilder 7 | from IPython.display import display 8 | 9 | data = { 10 | "nodes": [ 11 | {"id": '1'}, 12 | {"id": '2'}, 13 | {"id": '3'} 14 | ], 15 | "edges": [ 16 | {"id": 'e1', "sourceId": '1', "targetId": '2'}, 17 | {"id": 'e2', "sourceId": '1', "targetId": '3'}, 18 | {"id": 'e3', "sourceId": '2', "targetId": '3'} 19 | ] 20 | } 21 | 22 | graph = GraphBuilder(props={'data': data, 23 | "nodeSize": 10, 24 | "nodeColor": "#7743CE", 25 | "edgeWidth": 1, 26 | "edgeColor": "#777777", 27 | "height": 500}) 28 | 29 | display(graph) 30 | ``` 31 | 32 | Jupyter 33 | -------------------------------------------------------------------------------- /modules/feature-list-view/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mlvis/feature-list-view", 3 | "version": "1.1.4", 4 | "license": "Apache-2.0", 5 | "description": "Feature List View component of the Manifold", 6 | "publishConfig": { 7 | "access": "public" 8 | }, 9 | "contributors": [ 10 | "Yang Wang ", 11 | "Lezhi Li " 12 | ], 13 | "main": "index.js", 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/uber/manifold.git" 17 | }, 18 | "keywords": [ 19 | "ml", 20 | "babel", 21 | "react", 22 | "redux", 23 | "es6" 24 | ], 25 | "dependencies": { 26 | "@mlvis/mlvis-common": "^1.1.4", 27 | "d3-scale": "^3.0.0", 28 | "numeral": "^2.0.6", 29 | "prop-types": "^15.7.2", 30 | "react-d3-axis": "^0.1.1", 31 | "reselect": "^4.0.0" 32 | }, 33 | "peerDependencies": { 34 | "react": "^16.9.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /bindings/jupyter/mlvis/widget.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | import json 4 | import ipywidgets as widgets 5 | from traitlets import Unicode 6 | 7 | MODULE_NAME = 'mlviswidget' 8 | VERSION = '0.0.1' 9 | 10 | class Widget(widgets.DOMWidget): 11 | _model_module = Unicode(MODULE_NAME).tag(sync=True) 12 | _model_name = Unicode('WidgetModel').tag(sync=True) 13 | _model_module_version = Unicode(VERSION).tag(sync=True) 14 | _view_module = Unicode(MODULE_NAME).tag(sync=True) 15 | _view_name = Unicode('WidgetView').tag(sync=True) 16 | _view_module_version = Unicode(VERSION).tag(sync=True) 17 | props = Unicode('{}').tag(sync=True) 18 | 19 | 20 | class CommonComponent(Widget): 21 | def __init__(self, props={}, **kwargs): 22 | super(CommonComponent, self).__init__() 23 | # TODO: make explicit exception message for the json input is invalid 24 | self.props = json.dumps(props) 25 | -------------------------------------------------------------------------------- /examples/manifold/src/main.js: -------------------------------------------------------------------------------- 1 | // generator function from kepler layers need it 2 | // https://github.com/GitbookIO/expect-firestore/issues/1#issuecomment-370590583 3 | import '@babel/polyfill'; 4 | import React from 'react'; 5 | import document from 'global/document'; 6 | import {Provider} from 'react-redux'; 7 | import {Route} from 'react-router'; 8 | import {BrowserRouter} from 'react-router-dom'; 9 | import {render} from 'react-dom'; 10 | import store from './store'; 11 | import App from './app'; 12 | // TODO: removing antd in index.css will mess up the styles. Adjust styles before getting rid of antd dependency. 13 | import './index.css'; 14 | import 'mapbox-gl/dist/mapbox-gl.css'; 15 | 16 | const Root = () => ( 17 | 18 | 19 | 20 | 21 | 22 | ); 23 | 24 | render(, document.body.appendChild(document.createElement('div'))); 25 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-ma-causal/src/reducer.js: -------------------------------------------------------------------------------- 1 | import {handleActions} from 'redux-actions'; 2 | 3 | import {UPDATE_DATA, UPDATE_SLIDER_VALUES} from './actions'; 4 | 5 | const DEFAULT_STATE = { 6 | containerWidth: 800, 7 | containerHeight: 800, 8 | data: [], 9 | sliderValues: [], // associative array 10 | }; 11 | 12 | const handleUpdateData = (state, {payload}) => { 13 | return { 14 | ...state, 15 | data: payload, 16 | }; 17 | }; 18 | 19 | /** 20 | * @params payload - {[idx]: value, ...} 21 | */ 22 | const handleUpdateSliderValues = (state, {payload}) => ({ 23 | ...state, 24 | sliderValues: Object.entries(payload).reduce((values, [index, value]) => { 25 | values[index] = value; 26 | return values; 27 | }, state.sliderValues.slice(0)), 28 | }); 29 | 30 | export default handleActions( 31 | { 32 | [UPDATE_DATA]: handleUpdateData, 33 | [UPDATE_SLIDER_VALUES]: handleUpdateSliderValues, 34 | }, 35 | DEFAULT_STATE 36 | ); 37 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/file-type.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | import React from 'react'; 3 | import styled from 'styled-components'; 4 | 5 | import File from './file'; 6 | 7 | const FileNameTag = styled.div` 8 | background-color: currentColor; 9 | border-radius: 1px; 10 | display: inline-block; 11 | padding: 0 4px; 12 | position: absolute; 13 | top: 45%; 14 | left: 10%; 15 | 16 | .text { 17 | color: white; 18 | font-size: ${props => props.fontSize}; 19 | }, 20 | `; 21 | 22 | const FileTypeIconWrapper = styled.div` 23 | display: inline-block; 24 | position: relative; 25 | color: currentColor; 26 | height: ${props => props.height}; 27 | `; 28 | 29 | const FileTypeIcon = ({ext, height, fontSize}) => ( 30 | 31 | 32 | 33 |
{ext}
34 |
35 |
36 | ); 37 | 38 | export default FileTypeIcon; 39 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-ma-causal/src/containers/line-charts-container/index.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {connect} from 'react-redux'; 3 | 4 | import LineChartContainer from '../line-chart-container'; 5 | import {getColumnDataFactory} from '../../selectors/factories'; 6 | 7 | const mapStateToProps = (state, props) => { 8 | const {index} = props; 9 | const getColumnData = getColumnDataFactory(index); 10 | return { 11 | data: getColumnData(state), 12 | }; 13 | }; 14 | 15 | const mapDispatchToProps = {}; 16 | 17 | class Chart extends Component { 18 | render() { 19 | const {index, data} = this.props; 20 | return ( 21 | 22 | {data.lines.map(({name}) => ( 23 |
24 | 25 |
26 | ))} 27 |
28 | ); 29 | } 30 | } 31 | 32 | export default connect( 33 | mapStateToProps, 34 | mapDispatchToProps 35 | )(Chart); 36 | -------------------------------------------------------------------------------- /modules/multi-way-plot/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mlvis/multi-way-plot", 3 | "version": "1.1.4", 4 | "publishConfig": { 5 | "access": "public" 6 | }, 7 | "description": "Multi Way Plot component of the Manifold", 8 | "license": "Apache-2.0", 9 | "author": "Firenze11 ", 10 | "main": "index.js", 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/uber/manifold.git" 14 | }, 15 | "keywords": [ 16 | "ml", 17 | "visualization", 18 | "model agnostic", 19 | "model debugging", 20 | "babel", 21 | "react", 22 | "redux", 23 | "es6" 24 | ], 25 | "dependencies": { 26 | "@mlvis/mlvis-common": "^1.1.4", 27 | "d3-scale": "^3.0.0", 28 | "d3-scale-chromatic": "^1.3.3", 29 | "d3-shape": "^1.3.5", 30 | "prop-types": "^15.7.2", 31 | "react-d3-axis": "^0.1.1", 32 | "reselect": "^4.0.0" 33 | }, 34 | "peerDependencies": { 35 | "react": "^16.9.0", 36 | "styled-components": "^4.2.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-ma-causal/src/containers/multi-line-chart-container/index.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {connect} from 'react-redux'; 3 | 4 | import MultiLineChart from '../../components/multi-line-chart'; 5 | import { 6 | getChartWidth, 7 | getChartHeight, 8 | getChartPadding, 9 | } from '../../selectors/multi-line-chart-selectors'; 10 | import {getColumnDataFactory} from '../../selectors/factories'; 11 | 12 | const mapStateToProps = (state, props) => { 13 | const {index} = props; 14 | const getColumnData = getColumnDataFactory(index); 15 | return { 16 | width: getChartWidth(state), 17 | height: getChartHeight(state), 18 | data: getColumnData(state), 19 | padding: getChartPadding(state), 20 | }; 21 | }; 22 | 23 | const mapDispatchToProps = {}; 24 | 25 | class Chart extends Component { 26 | render() { 27 | return ; 28 | } 29 | } 30 | 31 | export default connect( 32 | mapStateToProps, 33 | mapDispatchToProps 34 | )(Chart); 35 | -------------------------------------------------------------------------------- /website/src/reducers.js: -------------------------------------------------------------------------------- 1 | import {combineReducers, createStore, applyMiddleware, compose} from 'redux'; 2 | import {routerReducer} from 'react-router-redux'; 3 | import thunk from 'redux-thunk'; 4 | import demoReducer from '../../examples/manifold/src/reducers'; 5 | import {taskMiddleware} from 'react-palm/tasks'; 6 | // TODO: add analytics 7 | // import analyticsMiddleware from './analytics'; 8 | 9 | const initialState = {}; 10 | const reducers = { 11 | demo: demoReducer, 12 | routing: routerReducer, 13 | }; 14 | 15 | const combinedReducers = combineReducers(reducers); 16 | 17 | export const middlewares = [ 18 | taskMiddleware, 19 | thunk, 20 | // analyticsMiddleware, 21 | ]; 22 | 23 | export const enhancers = [applyMiddleware(...middlewares)]; 24 | 25 | const composeEnhancers = compose; 26 | // add redux devtools 27 | // const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 28 | 29 | export default createStore( 30 | combinedReducers, 31 | initialState, 32 | composeEnhancers(...enhancers) 33 | ); 34 | -------------------------------------------------------------------------------- /modules/manifold/src/utils/data-slicing.js: -------------------------------------------------------------------------------- 1 | import {dotRange, computeClusters} from '@mlvis/mlvis-common/utils'; 2 | import {gatherDataset, filterDataset} from './utils'; 3 | 4 | export const computeManualSegmentationResult = (data, segmentFilters) => { 5 | return segmentFilters.map(filter => filterDataset(data, filter)); 6 | }; 7 | 8 | export const computeAutoSegmentationResult = ( 9 | data, 10 | columnTypeRanges, 11 | nClusters 12 | ) => { 13 | const colIds = dotRange(...columnTypeRanges.score); 14 | const clusteringInputDataset = gatherDataset(data, colIds); 15 | 16 | const {columns} = clusteringInputDataset; 17 | const clusterIds = computeClusters(columns, nClusters, true); 18 | 19 | // todo: simplify the following logic. `clusterIds` representation is sufficient 20 | const result = []; 21 | for (let i = 0; i < nClusters; i++) { 22 | result.push([]); 23 | } 24 | for (let i = 0; i < clusterIds.length; i++) { 25 | result[clusterIds[i]].push(i); 26 | } 27 | return result.filter(r => r.length > 0); 28 | }; 29 | -------------------------------------------------------------------------------- /modules/multi-way-plot/src/selectors.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | import {createSelector} from 'reselect'; 3 | import {computePercentiles, computeDensity} from '@mlvis/mlvis-common/utils'; 4 | 5 | const getData = props => props.data; 6 | 7 | const PERCENTILE_LIST = [0.01, 0.1, 0.25, 0.5, 0.75, 0.9, 0.99]; 8 | // "percentiles" includes [.01, .1, .25, .5, .75, .9, .99] percentiles of model performance 9 | export const getPercentiles = createSelector( 10 | [getData, props => props.percentiles], 11 | (data, percentiles) => 12 | percentiles || computePercentiles(data, PERCENTILE_LIST) 13 | ); 14 | 15 | const MODEL_PERF_HISTOGRAM_RESOLUTION = 50; 16 | export const getDensity = createSelector( 17 | [getData, props => props.density], 18 | (data, density) => 19 | density || computeDensity(data, MODEL_PERF_HISTOGRAM_RESOLUTION) 20 | ); 21 | 22 | export const getNumDataPoints = createSelector( 23 | [getData, props => props.numDataPoints], 24 | (data, numDataPoints) => 25 | numDataPoints && !isNaN(numDataPoints) ? numDataPoints : data[0].length 26 | ); 27 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/question.js: -------------------------------------------------------------------------------- 1 | //@noflow 2 | import React, {PureComponent} from 'react'; 3 | import Base from './base'; 4 | 5 | export default class Question extends PureComponent { 6 | render() { 7 | return ( 8 | 9 | 10 | 15 | 20 | 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /bindings/jupyter/js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mlvis/jupyter-binding", 3 | "version": "0.0.1", 4 | "license": "Apache-2.0", 5 | "main": "index.js", 6 | "scripts": { 7 | "watch": "node --max_old_space_size=8192 node_modules/.bin/webpack --mode development --progress --watch", 8 | "clean": "rm -rf ../mlvis/static", 9 | "build": "yarn clean && webpack --mode production --progress" 10 | }, 11 | "dependencies": { 12 | "global": "^4.4.0", 13 | "react": "^16.9.0", 14 | "reselect": "^4.0.0" 15 | }, 16 | "devDependencies": { 17 | "@babel/cli": "^7.1.2", 18 | "@babel/core": "^7.4.4", 19 | "babel-eslint": "^10.0.1", 20 | "babel-loader": "^8.0.5", 21 | "@babel/polyfill": "^7.1.0", 22 | "@babel/plugin-proposal-class-properties": "^7.1.0", 23 | "@babel/preset-env": "^7.1.0", 24 | "@babel/preset-flow": "^7.0.0", 25 | "@babel/preset-react": "^7.0.0", 26 | "css-loader": "^0.28.11", 27 | "file-loader": "^1.1.6", 28 | "style-loader": "^0.19.1", 29 | "webpack": "^4.27.1", 30 | "webpack-cli": "^3.1.2" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-manifold/src/manifold.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react'; 2 | import Manifold, { 3 | THEME, 4 | loadProcessedData, 5 | validateInputData, 6 | } from '@mlvis/manifold'; 7 | import {connect} from 'react-redux'; 8 | 9 | const manifoldGetState = state => state; 10 | 11 | class ManifoldApp extends PureComponent { 12 | static defaultProps = { 13 | width: 1000, 14 | height: 700, 15 | }; 16 | 17 | componentDidMount() { 18 | const {data} = this.props; 19 | validateInputData(data); 20 | this.props.dispatch(loadProcessedData(data)); 21 | } 22 | 23 | render() { 24 | const {data, width, height, mapboxAccessToken} = this.props; 25 | const [valid] = validateInputData(data, true); 26 | if (!data || !valid) { 27 | return
; 28 | } 29 | return ( 30 | 37 | ); 38 | } 39 | } 40 | 41 | export default connect()(ManifoldApp); 42 | -------------------------------------------------------------------------------- /modules/mlvis-common/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mlvis/mlvis-common", 3 | "version": "1.1.4", 4 | "license": "Apache-2.0", 5 | "publishConfig": { 6 | "access": "public" 7 | }, 8 | "description": "A suite of common utilities for mlvis", 9 | "contributors": [ 10 | "Lezhi Li ", 11 | "Yang Wang " 12 | ], 13 | "main": "index.js", 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/uber/manifold.git" 17 | }, 18 | "keywords": [ 19 | "ml", 20 | "babel", 21 | "react", 22 | "redux", 23 | "es6" 24 | ], 25 | "dependencies": { 26 | "@loaders.gl/arrow": "^1.2.0-alpha.1", 27 | "@loaders.gl/core": "^1.1.0", 28 | "@tensorflow/tfjs-core": "^0.15.3", 29 | "d3-scale-chromatic": "^1.3.3", 30 | "file-saver": "^2.0.2", 31 | "global": "^4.3.2", 32 | "lodash.memoize": "^4.1.2", 33 | "mathjs": "^5.2.3", 34 | "papaparse": "^4.6.3", 35 | "prop-types": "^15.7.2", 36 | "random": "^2.1.1", 37 | "reselect": "^4.0.0" 38 | }, 39 | "peerDependencies": { 40 | "styled-components": "^4.2.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-manifold/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mlvis/jupyter-manifold", 3 | "version": "1.1.4", 4 | "license": "Apache-2.0", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/uber/manifold.git" 8 | }, 9 | "main": "index.js", 10 | "publishConfig": { 11 | "access": "public" 12 | }, 13 | "scripts": { 14 | "watch": "webpack --watch --progress", 15 | "clean": "rm -rf dist", 16 | "copy": "cp -r package.json dist", 17 | "build": "yarn clean && yarn transpile", 18 | "transpile": "NODE_ENV=production babel src -d dist -s --config-file ../../../babel.config.js && yarn copy" 19 | }, 20 | "dependencies": { 21 | "@mlvis/manifold": "^1.1.4", 22 | "global": "^4.4.0", 23 | "react": "^16.9.0", 24 | "react-dom": "^16.9.0", 25 | "react-redux": "^7.1.1", 26 | "reduce-reducers": "^1.0.4", 27 | "redux": "^4.0.0", 28 | "redux-actions": "^2.6.5", 29 | "redux-thunk": "^2.3.0" 30 | }, 31 | "peerDependencies": { 32 | "styled-components": "^4.2.0", 33 | "styletron-engine-atomic": "^1.4.1", 34 | "styletron-react": "^5.2.1" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /bindings/jupyter/js/src/widgets/react-widget-builder.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDom from 'react-dom'; 3 | import {DOMWidgetModel, DOMWidgetView} from '@jupyter-widgets/base'; 4 | 5 | export default (Component, name, renderCallback = function() {}) => { 6 | const classes = { 7 | [name + 'WidgetModel']: class extends DOMWidgetModel {}, 8 | [name + 'WidgetView']: class extends DOMWidgetView { 9 | render = () => { 10 | super.render(this); 11 | this._update(); 12 | this.listenTo(this.model, 'change:props', this._update, this); 13 | renderCallback.apply(this); 14 | }; 15 | 16 | _update = () => { 17 | const props = JSON.parse(this.model.get('props') || '{}'); 18 | props.widgetModel = this.model; 19 | props.widgetView = this; 20 | const component = React.createElement(Component, props); 21 | ReactDom.render(component, this.el); 22 | }; 23 | }, 24 | }; 25 | Object.entries(classes).forEach(([name, cls]) => { 26 | Object.defineProperty(cls, 'name', { 27 | value: name, 28 | writable: false, 29 | }); 30 | }); 31 | return classes; 32 | }; 33 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-ma-causal/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mlvis/jupyter-ma-causal", 3 | "version": "1.1.4", 4 | "license": "Apache-2.0", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/uber/manifold.git" 8 | }, 9 | "main": "index.js", 10 | "-------- Avoid publishing the prototype for now --------": "", 11 | "private": true, 12 | "publishConfig": { 13 | "access": "public" 14 | }, 15 | "scripts": { 16 | "watch": "webpack --watch --progress", 17 | "clean": "rm -rf dist", 18 | "copy": "cp -r package.json dist", 19 | "build": "yarn clean && yarn transpile", 20 | "transpile": "NODE_ENV=production babel src -d dist -s --config-file ../../../babel.config.js && yarn copy" 21 | }, 22 | "dependencies": { 23 | "d3-array": "^2.4.0", 24 | "d3-color": "^1.4.0", 25 | "d3-format": "^1.4.2", 26 | "d3-scale": "^3.2.0", 27 | "d3-scale-chromatic": "^1.5.0", 28 | "d3-shape": "^1.3.7", 29 | "prop-types": "^15.7.2", 30 | "react": "^16.9.0", 31 | "react-redux": "^7.1.3", 32 | "redux": "^4.0.4", 33 | "redux-actions": "^2.6.5", 34 | "reselect": "^4.0.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-ma-causal/src/containers/line-chart-container/index.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {connect} from 'react-redux'; 3 | 4 | import LineChart from '../../components/line-chart'; 5 | import { 6 | getChartWidth, 7 | getChartHeight, 8 | getChartPadding, 9 | } from '../../selectors/multi-line-chart-selectors'; 10 | import { 11 | getLineDataFactory, 12 | getLineDataYDomainFactory, 13 | } from '../../selectors/factories'; 14 | 15 | const mapStateToProps = (state, props) => { 16 | const {index, name} = props; 17 | const getLineData = getLineDataFactory(index, name); 18 | const getLineDataYDomain = getLineDataYDomainFactory(getLineData); 19 | return { 20 | width: getChartWidth(state), 21 | height: getChartHeight(state), 22 | data: getLineData(state), 23 | domain: getLineDataYDomain(state), 24 | padding: getChartPadding(state), 25 | }; 26 | }; 27 | 28 | const mapDispatchToProps = {}; 29 | 30 | class Chart extends Component { 31 | render() { 32 | return ; 33 | } 34 | } 35 | 36 | export default connect( 37 | mapStateToProps, 38 | mapDispatchToProps 39 | )(Chart); 40 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-ma-causal/src/selectors/factories.js: -------------------------------------------------------------------------------- 1 | import {createSelector} from 'reselect'; 2 | import {getData, getSliderValues} from './base-selectors'; 3 | import {extent as d3Extent} from 'd3-array'; 4 | 5 | export const getColumnDataFactory = index => 6 | createSelector(getData, data => data[index]); 7 | 8 | export const getSliderValueFactory = index => 9 | createSelector(getSliderValues, sliderValues => sliderValues[index]); 10 | 11 | // Due to the nature of being a prototype, the component gets the entire column 12 | // data and process it by itself to avoid nested structures which are hard to 13 | // modify later. Once the data format is finalized, these code can be easily changed 14 | // to make it more efficent and elegant. 15 | // TODO: refactor these data related code once the input format is finalized 16 | export const getLineDataFactory = (index, lineName) => 17 | createSelector(getData, data => 18 | data[index].lines 19 | .find(d => d.name === lineName) 20 | .line.map(d => ({...d})) 21 | .sort((a, b) => a.x - b.x) 22 | ); 23 | 24 | export const getLineDataYDomainFactory = getLineData => 25 | createSelector(getLineData, data => d3Extent(data, d => d.y)); 26 | -------------------------------------------------------------------------------- /modules/manifold/src/reducers/data-slicing.js: -------------------------------------------------------------------------------- 1 | import {validateAndSetDefaultStates} from './utils'; 2 | import {SEGMENTATION_METHOD} from '../constants'; 3 | 4 | export const handleUpdateSegmentationMethod = (state, {payload}) => { 5 | const isManualSegmentation = payload === SEGMENTATION_METHOD.MANUAL; 6 | return validateAndSetDefaultStates({ 7 | ...state, 8 | isManualSegmentation, 9 | }); 10 | }; 11 | 12 | export const handleUpdateBaseCols = (state, {payload}) => { 13 | return validateAndSetDefaultStates({ 14 | ...state, 15 | baseCols: payload, 16 | }); 17 | }; 18 | 19 | export const handleUpdateNClusters = (state, {payload}) => { 20 | const delta = payload === 'INC' ? 1 : payload === 'DEC' ? -1 : 0; 21 | return validateAndSetDefaultStates({ 22 | ...state, 23 | nClusters: state.nClusters + delta, 24 | }); 25 | }; 26 | 27 | export const handleUpdateSegmentFilters = (state, {payload}) => { 28 | return validateAndSetDefaultStates({ 29 | ...state, 30 | segmentFilters: payload, 31 | }); 32 | }; 33 | 34 | export const handleUpdateSegmentGroups = (state, {payload}) => { 35 | return validateAndSetDefaultStates({ 36 | ...state, 37 | segmentGroups: payload, 38 | }); 39 | }; 40 | -------------------------------------------------------------------------------- /bindings/python-compute/utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from .constants import FILTER_TYPE 3 | 4 | 5 | # transform a dict of value range> mappings into a mask 6 | def compute_filter(filters, full_df): 7 | masks = [] 8 | 9 | for f in filters: 10 | filter_func = lambda x: True 11 | if f['key'] in full_df.columns: 12 | if f['type'] == FILTER_TYPE['INCLUDE'] : 13 | filter_func = lambda x: x[f['key']] in f['value'] 14 | 15 | elif f['type'] == FILTER_TYPE['EXCLUDE']: 16 | filter_func = lambda x: x[f['key']] not in f['value'] 17 | 18 | elif f['type'] == FILTER_TYPE['RANGE']: 19 | filter_func = lambda x: (f['value'][0] is None or x[f['key']] >= f['value'][0]) and \ 20 | (f['value'][1] is None or x[f['key']] <= f['value'][1]) 21 | 22 | elif f['type'] == FILTER_TYPE['FUNC']: 23 | filter_func = eval(f['value']) 24 | 25 | masks.append(full_df.apply(filter_func, axis=1).values) 26 | 27 | if len(masks) == 0: 28 | return np.ones(len(full_df), dtype=bool) 29 | if len(masks) == 1: 30 | return masks[0] 31 | return np.all(np.column_stack(masks), axis=1) -------------------------------------------------------------------------------- /modules/manifold/src/constants/kepler-constants.js: -------------------------------------------------------------------------------- 1 | import {interpolateRgb} from 'd3-interpolate'; 2 | import {color} from 'd3-color'; 3 | import {dotRange} from '@mlvis/mlvis-common/utils'; 4 | 5 | export const FILTER_TYPES = { 6 | range: 'range', 7 | select: 'select', 8 | timeRange: 'timeRange', 9 | multiSelect: 'multiSelect', 10 | }; 11 | 12 | export const LAYER_TYPES = { 13 | point: 'point', 14 | arc: 'arc', 15 | line: 'line', 16 | grid: 'grid', 17 | hexagon: 'hexagon', 18 | geojson: 'geojson', 19 | cluster: 'cluster', 20 | icon: 'icon', 21 | heatmap: 'heatmap', 22 | hexagonId: 'hexagonId', 23 | '3D': '3D', 24 | }; 25 | 26 | export const KEPLER_GL_VERSION = 'v1'; 27 | 28 | export const KEPLER_DATASET_NAME = 'kepler_data'; 29 | 30 | const colorInterpolatorPink = interpolateRgb('#fff', '#ff0099'); 31 | const colorInterpolatorGrey = interpolateRgb('#fff', '#818c81'); 32 | 33 | export const HEATMAP_COLORS = [ 34 | dotRange(1, 10).map(d => color(colorInterpolatorPink(0.1 * d)).formatHex()), 35 | dotRange(1, 10).map(d => color(colorInterpolatorGrey(0.1 * d)).formatHex()), 36 | ]; 37 | 38 | export const DIVERGING_COLORS = HEATMAP_COLORS[1] 39 | .slice(1) 40 | .reverse() 41 | .concat(HEATMAP_COLORS[0].slice(1)); 42 | -------------------------------------------------------------------------------- /bindings/jupyter/docs/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Jupyter 4 | 5 | The mlvis library contains a suite a visualization components. This library is still under active development. The current version consists of several prebuilt visualization components: 6 | 7 | - [Stacked Calendar](https://github.com/uber/manifold/tree/master/bindings/jupyter/docs/stacked-calendar.md) 8 | - [Graph Builder](https://github.com/uber/manifold/tree/master/bindings/jupyter/docs/graph-builder.md) 9 | - [Manifold](https://github.com/uber/manifold/tree/master/bindings/jupyter/docs/manifold.md) 10 | - [Feature List View](https://github.com/uber/manifold/tree/master/bindings/jupyter/docs/feature-list-view.md) 11 | 12 | ## Installation 13 | 14 | ``` 15 | pip install mlvis 16 | ``` 17 | 18 | Then install the nbextension into the Jupyter notebook 19 | 20 | ``` 21 | jupyter nbextension install --py --symlink --sys-prefix mlvis 22 | jupyter nbextension enable --py --sys-prefix mlvis 23 | ``` 24 | 25 | ## Development 26 | 27 | Instructions for building this project locally can be found in the [development documentation](https://github.com/uber/manifold/tree/master/bindings/jupyter/docs/development.md). 28 | -------------------------------------------------------------------------------- /modules/manifold/src/components/ui/legend-group/legend-item.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | import React, {PureComponent} from 'react'; 3 | import PropTypes from 'prop-types'; 4 | import styled from 'styled-components'; 5 | 6 | const Container = styled.div` 7 | display: flex; 8 | align-items: center; 9 | cursor: pointer; 10 | margin-right: 16px; 11 | `; 12 | 13 | const Icon = styled.div` 14 | width: 16px; 15 | height: 16px; 16 | border-radius: 8px; 17 | background-color: ${props => props.color}; 18 | opacity: ${props => (props.selected ? 1 : 0.6)}; 19 | margin-right: 4px; 20 | `; 21 | 22 | export default class LegendItem extends PureComponent { 23 | static propTypes = { 24 | text: PropTypes.string, 25 | color: PropTypes.string, 26 | // todo: fix the type in manifold 27 | // selected: PropTypes.bool, 28 | onModelClick: PropTypes.func, 29 | }; 30 | 31 | static defaultProps = { 32 | text: '', 33 | color: '#000', 34 | selected: false, 35 | onModelClick: () => {}, 36 | }; 37 | 38 | render() { 39 | const {text, color, selected, onModelClick} = this.props; 40 | return ( 41 | 42 | 43 | {text} 44 | 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-manifold/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {createStore, compose, applyMiddleware} from 'redux'; 3 | import {Provider} from 'react-redux'; 4 | import thunk from 'redux-thunk'; 5 | import {manifoldReducer, enhanceReduxMiddleware} from '@mlvis/manifold'; 6 | import {Client as Styletron} from 'styletron-engine-atomic'; 7 | import {Provider as StyletronProvider} from 'styletron-react'; 8 | import Manifold from './manifold'; 9 | import Controls from './controls'; 10 | import {window} from 'global'; 11 | 12 | // https://github.com/jhen0409/react-native-debugger/issues/280 13 | const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 14 | 15 | export default props => { 16 | const store = createStore( 17 | manifoldReducer, 18 | composeEnhancer(applyMiddleware(...enhanceReduxMiddleware([thunk]))) 19 | ); 20 | const engine = new Styletron(); 21 | return ( 22 | 23 | 24 | 25 | 29 | 30 | 31 | 32 | 33 | ); 34 | }; 35 | -------------------------------------------------------------------------------- /website/README.md: -------------------------------------------------------------------------------- 1 | # Developing website 2 | 3 | `/website/src/` contains the content of Manifold [website](https://uber.github.io/manifold/), which can be bundled and copied to gh-pages branch for deployment. 4 | 5 | ## Run website locally 6 | 7 | In root directory: 8 | ```sh 9 | yarn install:all # or `yarn install:web` if you have already installed in root directory 10 | ``` 11 | 12 | In `/website` directory: 13 | ```sh 14 | yarn start 15 | ``` 16 | 17 | ## Deploy website 18 | 19 | After finishing developing the website, you can deploy it to gh-pages. 20 | 21 | In root directory: 22 | ```sh 23 | yarn deploy 24 | ``` 25 | This will bundle the website into `/website/dist` directory, copy the content in `/website/dist` to the root of `gh-pages` branch, and push ro remote `gh-pages` branch (so that it is accessible through Manifold [website](https://uber.github.io/manifold/)) 26 | 27 | ## Include a demo example in the website 28 | 29 | Import the example from the `/examples` folder and add to the website. E.g.: 30 | 31 | In `/website/src/main.js`: 32 | 33 | ```js 34 | import Demo from '../../examples/manifold/src/app'; 35 | 36 | const Root = () => ( 37 | 38 | 39 | 40 | 41 | 42 | ); 43 | ``` -------------------------------------------------------------------------------- /alias.config.js: -------------------------------------------------------------------------------- 1 | const resolve = require('path').resolve; 2 | const fs = require('fs'); 3 | 4 | const ROOT = resolve(__dirname, '.'); 5 | const MODULES_ROOT = resolve(ROOT, 'modules'); 6 | 7 | const AliasConfig = modulesDir => { 8 | const mdir = modulesDir || MODULES_ROOT; 9 | // this function looks into all packages under ./src and creates alias for local dev 10 | // {@mlvis/some-package: './modules/some-package/src'} 11 | return () => 12 | fs.readdirSync(mdir).reduce((aliasMap, pkg) => { 13 | // src/some-package 14 | const packagePath = resolve(mdir, pkg); 15 | if (!fs.lstatSync(packagePath).isDirectory()) { 16 | return aliasMap; 17 | } 18 | // packages/some-package/package.json 19 | const packageJsonPath = resolve(packagePath, 'package.json'); 20 | // bypass alias mapping if no package.json exists 21 | if (!fs.existsSync(packageJsonPath)) { 22 | return aliasMap; 23 | } 24 | const packageInfo = require(packageJsonPath); 25 | // @mlvis/some-package => some-package 26 | const packageName = packageInfo.name.replace(/^(@mlvis\/)/, ''); 27 | aliasMap[packageInfo.name] = resolve(mdir, packageName, 'src'); 28 | return aliasMap; 29 | }, {}); 30 | }; 31 | module.exports = AliasConfig(MODULES_ROOT); 32 | module.exports.AliasConfig = AliasConfig; 33 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | #### Summary 4 | 5 | 6 | 7 | 8 | #### Expected Behavior 9 | 10 | 11 | 12 | 13 | #### Current Behavior 14 | 15 | 16 | 17 | 18 | #### Context and Environment 19 | 20 | 21 | 22 | 23 | - Module & version: [e.g. @mlvis/stacked-calendar, v1.0.0] 24 | 25 | #### Steps to Reproduce 26 | 27 | 28 | 29 | 30 | 1. TBA 31 | 2. TBA 32 | 3. TBA 33 | 34 | #### Possible Solution 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /* global module */ 2 | const packageAliases = require('./alias.config')(); 3 | 4 | // ensure sub-directories of these packages can also be resolved 5 | // e.g. `@uber/mivis-common/utils` will map to `/packages/mlvis-common/src/utils` 6 | // instead of `/packages/mlvis-common/src` 7 | const aliasMapper = Object.keys(packageAliases).reduce((acc, key) => { 8 | acc[`^${key}(.*)$`] = `${packageAliases[key]}$1`; 9 | return acc; 10 | }, {}); 11 | 12 | module.exports = { 13 | verbose: true, 14 | collectCoverage: true, 15 | collectCoverageFrom: ['packages/**/*.js'], 16 | coverageDirectory: './coverage', 17 | coveragePathIgnorePatterns: ['__fixtures__', 'stories', '__tests__'], 18 | coverageReporters: ['cobertura', 'text', 'html'], 19 | moduleNameMapper: aliasMapper, 20 | modulePathIgnorePatterns: ['/dist/'], 21 | testMatch: ['**/*.(spec|test).js', '**/__tests__/*.js'], 22 | testPathIgnorePatterns: [ 23 | '/node_modules/', 24 | '/dist/', 25 | '__fixtures__', 26 | 'stories', 27 | '.cache', 28 | ], 29 | // jest defaults to browser environment which raises errors in tfjs during testing 30 | // https://github.com/tensorflow/tfjs/issues/540#issuecomment-408716995 31 | testEnvironment: 'node', 32 | transformIgnorePatterns: ['node_modules/(?!(gatsby|apache-arrow)/)'], 33 | setupTestFrameworkScriptFile: './utils/setupTests.js', 34 | }; 35 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/utils/decorators.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react'; 2 | import {xScaleSelectorFactory, yScaleSelectorFactory} from './selectors'; 3 | import {generateRandomId} from './gen-data'; 4 | 5 | /* 6 | * HOC for adding `xScale`, `yScale` props that are derived from `data` prop. 7 | * currently used by MultiWayPlot and SegmentGrouping 8 | * @{params} WrappedComponent - a react component that has the following props: 9 | * `data`; one or more of: `getXScale`, `getYScale`, `getXDomain`, `getYDomain`, `getXRange`, `getYRange`. 10 | * @{returns} a react component that has props: `xScale`, `yScale`. 11 | */ 12 | export const withXYScales = WrappedComponent => { 13 | // todo: make sure hocId is consitent throughout the returned wrapper class's lifetime. 14 | const hocId = generateRandomId(); 15 | 16 | return class extends PureComponent { 17 | // to get props rendered on storybook 18 | static propTypes = WrappedComponent.propTypes; 19 | static defaultProps = WrappedComponent.defaultProps; 20 | xScaleSelector = xScaleSelectorFactory(hocId); 21 | yScaleSelector = yScaleSelectorFactory(hocId); 22 | render() { 23 | const derivedData = { 24 | xScale: this.xScaleSelector(this.props), 25 | yScale: this.yScaleSelector(this.props), 26 | }; 27 | return ; 28 | } 29 | }; 30 | }; 31 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/utils/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * check if the given array of values is discrete or not 3 | * 4 | * @param {[]} values: array of values, can be numbers or strings 5 | * @return {Boolean}: whether the input array is discrete 6 | */ 7 | export const discrete = values => { 8 | const isNumeric = n => 9 | !Number.isNaN(n) && (Number.isFinite(n) || Math.abs(n) === Infinity); 10 | 11 | return !values.every(isNumeric); 12 | }; 13 | 14 | // 12345 => 12,345 15 | // 123456789 => 1.235e+8 16 | // 0.123456789 => 0.123 17 | // 0.0012345 => 0.001 18 | // 0.0000005 => 0 19 | export const formatNumber = (n, asPercent) => { 20 | if (n === null || n === undefined) { 21 | return ''; 22 | } 23 | if (Number.isNaN(n) || n === 'NaN') { 24 | return NaN.toLocaleString(); 25 | } 26 | if (asPercent) { 27 | return `${formatNumber(n * 100, false)}%`; 28 | } 29 | const rounded = n - Math.floor(n) <= 1e-6 ? parseInt(Math.floor(n)) : n; 30 | if (rounded === 0) { 31 | return '0'; 32 | } 33 | 34 | if (Math.abs(rounded) > 1e8 || Math.abs(rounded) < 1e-3) { 35 | return rounded.toExponential(3).toLocaleString(); 36 | } 37 | return rounded.toLocaleString(); 38 | }; 39 | 40 | export * from './color'; 41 | export * from './computation'; 42 | export * from './gen-data'; 43 | export * from './gen-feature'; 44 | export * from './io'; 45 | export * from './kmeans'; 46 | export * from './decorators'; 47 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/__tests__/selectors.spec.js: -------------------------------------------------------------------------------- 1 | import {createMemoizedSelectorFactory} from '../utils/selectors'; 2 | 3 | test('utils: createMemoizedSelectorFactory', () => { 4 | const resultFunc = jest.fn((a, b) => a + b + 1); 5 | const selectorFacrory1 = createMemoizedSelectorFactory( 6 | [props => props, props => props + 2], 7 | resultFunc 8 | ); 9 | const selector = selectorFacrory1('id1'); 10 | const memoizedSelector = selectorFacrory1('id1'); 11 | const selectorVersion2 = selectorFacrory1('id2'); 12 | 13 | // testing the selectors are being properly momoized 14 | expect(selector).toBe(memoizedSelector); 15 | expect(selector).not.toBe(selectorVersion2); 16 | 17 | // testing the selector result 18 | expect(selector(9)).toBe(21); 19 | expect(resultFunc.mock.calls.length).toBe(1); 20 | 21 | // testing single selector memoization 22 | selector(9); 23 | expect(resultFunc.mock.calls.length).toBe(1); 24 | 25 | // `memoizedSelector` should remember the input to `selector` and should not recompute 26 | memoizedSelector(9); 27 | expect(resultFunc.mock.calls.length).toBe(1); 28 | 29 | // `memoizedSelector` should only recompute if the input changes 30 | memoizedSelector(1); 31 | expect(resultFunc.mock.calls.length).toBe(2); 32 | 33 | // `selectorVersion2` shouldn't remember the input to `selector` and should recompute 34 | selectorVersion2(9); 35 | expect(resultFunc.mock.calls.length).toBe(3); 36 | }); 37 | -------------------------------------------------------------------------------- /modules/manifold/src/reducers/__tests__/data-generation.spec.js: -------------------------------------------------------------------------------- 1 | import {METRIC} from '../../constants'; 2 | import {handleUpdateMetric} from '../data-generation'; 3 | 4 | test('reducers: data-generation/handleUpdateMetric', () => { 5 | const data2 = { 6 | fields: [ 7 | {name: 'uuid'}, 8 | {name: 'feature1'}, 9 | {name: '@pred:model_0_class_0'}, 10 | {name: '@pred:model_0_class_1'}, 11 | {name: '@groundTruth'}, 12 | {name: '@score:model_0'}, 13 | ], 14 | columns: [ 15 | ['uuid_0', 'uuid_1'], 16 | [1, 2], 17 | [0.1, 0.6], 18 | [0.9, 0.4], 19 | ['cat2', 'cat1'], 20 | [undefined, undefined], 21 | ], 22 | }; 23 | const columnTypeRanges2 = { 24 | yPred: [2, 4], 25 | yTrue: [4, 5], 26 | score: [5, 6], 27 | }; 28 | const meta2 = { 29 | nModels: 1, 30 | nClasses: 2, 31 | classLabels: ['cat1', 'cat2'], 32 | }; 33 | const state2 = { 34 | data: data2, 35 | columnTypeRanges: columnTypeRanges2, 36 | modelsMeta: meta2, 37 | }; 38 | const stateOut2 = handleUpdateMetric(state2, { 39 | payload: METRIC.LOG_LOSS, 40 | }); 41 | 42 | expect(Array.from(stateOut2.data.columns[5])).toMatchObject([ 43 | expect.any(Number), 44 | expect.any(Number), 45 | ]); 46 | // check scores are updated 47 | expect(stateOut2.data.columns[5]).not.toEqual(state2.data.columns[5]); 48 | // check creating new object 49 | expect(stateOut2).not.toBe(state2); 50 | }); 51 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/constants/data.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | export const STATES = { 3 | AL: 'Alabama', 4 | AK: 'Alaska', 5 | AS: 'American Samoa', 6 | AZ: 'Arizona', 7 | AR: 'Arkansas', 8 | CA: 'California', 9 | CO: 'Colorado', 10 | CT: 'Connecticut', 11 | DE: 'Delaware', 12 | DC: 'District Of Columbia', 13 | FM: 'Federated States Of Micronesia', 14 | FL: 'Florida', 15 | GA: 'Georgia', 16 | GU: 'Guam', 17 | HI: 'Hawaii', 18 | ID: 'Idaho', 19 | IL: 'Illinois', 20 | IN: 'Indiana', 21 | IA: 'Iowa', 22 | KS: 'Kansas', 23 | KY: 'Kentucky', 24 | LA: 'Louisiana', 25 | ME: 'Maine', 26 | MH: 'Marshall Islands', 27 | MD: 'Maryland', 28 | MA: 'Massachusetts', 29 | MI: 'Michigan', 30 | MN: 'Minnesota', 31 | MS: 'Mississippi', 32 | MO: 'Missouri', 33 | MT: 'Montana', 34 | NE: 'Nebraska', 35 | NV: 'Nevada', 36 | NH: 'New Hampshire', 37 | NJ: 'New Jersey', 38 | NM: 'New Mexico', 39 | NY: 'New York', 40 | NC: 'North Carolina', 41 | ND: 'North Dakota', 42 | MP: 'Northern Mariana Islands', 43 | OH: 'Ohio', 44 | OK: 'Oklahoma', 45 | OR: 'Oregon', 46 | PW: 'Palau', 47 | PA: 'Pennsylvania', 48 | PR: 'Puerto Rico', 49 | RI: 'Rhode Island', 50 | SC: 'South Carolina', 51 | SD: 'South Dakota', 52 | TN: 'Tennessee', 53 | TX: 'Texas', 54 | UT: 'Utah', 55 | VT: 'Vermont', 56 | VI: 'Virgin Islands', 57 | VA: 'Virginia', 58 | WA: 'Washington', 59 | WV: 'West Virginia', 60 | WI: 'Wisconsin', 61 | WY: 'Wyoming', 62 | }; // const states 63 | -------------------------------------------------------------------------------- /modules/manifold/src/components/container.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import {console} from 'global'; 4 | import Manifold from './manifold'; 5 | 6 | class Container extends PureComponent { 7 | static propTypes = { 8 | statePath: PropTypes.string, 9 | }; 10 | 11 | static defaultProps = { 12 | statePath: 'manifold', 13 | }; 14 | 15 | recomputations = 0; 16 | 17 | getSelector = getManifoldState => { 18 | this.recomputations++; 19 | if (this.recomputations > 1) { 20 | console.warn( 21 | 'Manifold is caused to rerender. For better performance, avoid instantiate a new function as props' 22 | ); 23 | } 24 | return allState => { 25 | const manifoldState = getManifoldState(allState); 26 | if (!manifoldState) { 27 | /* eslint-disable no-console */ 28 | console.error( 29 | 'Manifold root component is not mounted to the correct address in app reducer.' 30 | ); 31 | /* eslint-enable no-console */ 32 | return null; 33 | } 34 | return manifoldState; 35 | }; 36 | }; 37 | 38 | render() { 39 | const {getState, ...otherProps} = this.props; 40 | const selector = this.getSelector(getState); 41 | 42 | if (!selector) { 43 | // instance state hasn't been mounted yet 44 | return
; 45 | } 46 | 47 | return ; 48 | } 49 | } 50 | 51 | export default Container; 52 | -------------------------------------------------------------------------------- /modules/manifold/src/__tests__/selector-kepler.spec.js: -------------------------------------------------------------------------------- 1 | import {getAvailableVisualChannelFeatures} from '../selectors/kepler-selectors'; 2 | 3 | test('selector-kepler: getAvailableVisualChannelFeatures', () => { 4 | const geoFeatures = [ 5 | {name: 'aggByFeature_0'}, 6 | { 7 | name: 'otherGeoFeature_1', 8 | pair: [{name: 'otherGeoFeature_1_lat'}, {name: 'otherGeoFeature_1_lng'}], 9 | }, 10 | ]; 11 | const scoreData = { 12 | fields: [{name: '@score:model_0'}, {name: '@score:model_1'}], 13 | }; 14 | const aggData = { 15 | aggByFeature_0: { 16 | fields: [ 17 | {name: 'aggByFeature_0'}, 18 | {name: '@score:model_0_mean'}, 19 | {name: '@score:model_1_mean'}, 20 | ], 21 | }, 22 | }; 23 | const visualChannelFeatures0 = getAvailableVisualChannelFeatures.resultFunc( 24 | true, 25 | geoFeatures, 26 | [0], 27 | scoreData, 28 | aggData 29 | ); 30 | const visualChannelFeatures1 = getAvailableVisualChannelFeatures.resultFunc( 31 | true, 32 | geoFeatures, 33 | [1], 34 | scoreData, 35 | aggData 36 | ); 37 | const visualChannelFeatures2 = getAvailableVisualChannelFeatures.resultFunc( 38 | false, 39 | [], 40 | [0], 41 | scoreData, 42 | aggData 43 | ); 44 | expect(visualChannelFeatures0).toEqual([ 45 | {name: '@score:model_0_mean'}, 46 | {name: '@score:model_1_mean'}, 47 | ]); 48 | expect(visualChannelFeatures1).toEqual([{name: 'groupId'}]); 49 | expect(visualChannelFeatures2).toEqual(null); 50 | }); 51 | -------------------------------------------------------------------------------- /bindings/jupyter/mlvis/widget_builder.py: -------------------------------------------------------------------------------- 1 | # Dynamically build component wrappers utilizing the ipywidget 2 | import os, sys, json, re, importlib 3 | from pkg_resources import resource_string 4 | from ast import literal_eval 5 | from traitlets import Unicode 6 | from .widget import CommonComponent 7 | from .widget_ext import Manifold 8 | 9 | 10 | extensions = {'Manifold': Manifold} 11 | 12 | 13 | current_module = sys.modules[__name__] 14 | 15 | 16 | def load_jrequirements(): 17 | return literal_eval(resource_string('mlvis', 'jrequirements.json').decode('utf8')) 18 | 19 | 20 | # Extract the Python component wrapper names from the jrequirements 21 | def extract_components(jrequirements): 22 | return [component for component in jrequirements] 23 | 24 | 25 | jrequirements = load_jrequirements() 26 | components = extract_components(jrequirements) 27 | 28 | 29 | def init(self, props={}): 30 | for parent in self.__class__.__bases__: 31 | parent.__init__(self, props=props) 32 | 33 | 34 | # Dynamically create module classes 35 | for component in components: 36 | deps = (extensions[component], ) if component in extensions else (CommonComponent, ) 37 | setattr(current_module, 38 | component, 39 | type(component, 40 | deps, 41 | { 42 | '_model_name': Unicode(component + 'WidgetModel').tag(sync=True), 43 | '_view_name': Unicode(component + 'WidgetView').tag(sync=True), 44 | '__init__': init 45 | })) 46 | -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mlvis/manifold-website", 3 | "description": "Website for manifold", 4 | "license": "Apache-2.0", 5 | "author": "Firenze11 ", 6 | "main": "dist/index.js", 7 | "version": "0.0.1", 8 | "engines": { 9 | "node": ">=10.15.0", 10 | "npm": ">=6.4.0", 11 | "yarn": ">=1.17.0" 12 | }, 13 | "scripts": { 14 | "start": "webpack-dev-server --mode development --env.prod --progress --open", 15 | "start-prod": "webpack-dev-server -p --env.prod --progress --open", 16 | "build-clean": "rm -rf ./dist && mkdir dist", 17 | "build-script": "webpack -p --env.prod", 18 | "build": "npm run build-clean && npm run build-script", 19 | "lint": "eslint src" 20 | }, 21 | "dependencies": { 22 | "antd": "^3.10.1", 23 | "global": "^4.3.2", 24 | "html-webpack-partials-plugin": "^0.5.3", 25 | "html-webpack-plugin": "^4.0.0-beta.8", 26 | "prop-types": "^15.6.2", 27 | "react": "^16.9.0", 28 | "react-dom": "^16.5.2", 29 | "react-palm": "^3.1.2", 30 | "react-redux": "^7.1.1", 31 | "react-router": "^5.1.2", 32 | "react-router-dom": "^5.1.2", 33 | "react-router-redux": "^4.0.8", 34 | "redux": "^4.0.0", 35 | "redux-actions": "^2.6.1", 36 | "redux-thunk": "^2.3.0", 37 | "styled-components": "^4.0.0" 38 | }, 39 | "devDependencies": { 40 | "webpack": "^4.27.1", 41 | "webpack-cli": "^3.1.2", 42 | "webpack-dev-server": "^3.1.10", 43 | "terser-webpack-plugin": "^2.2.1" 44 | }, 45 | "peerDependencies": {} 46 | } 47 | -------------------------------------------------------------------------------- /bindings/jupyter/js/src/widgets/index.js: -------------------------------------------------------------------------------- 1 | const Jupyter = require('base/js/namespace'); 2 | const widgetBuilder = require('./react-widget-builder').default; 3 | const req = require('../../../mlvis/jrequirements.json'); 4 | 5 | // Specify the widget customization of the corresponding components, 6 | // Currently it only specifies the renderCallback function. 7 | // TODO: Move these to jrequirements.json 8 | const reqExt = { 9 | Manifold: function() { 10 | this.listenTo( 11 | this.model, 12 | 'change:segments', 13 | () => { 14 | const segments = this.model.get('segments'); 15 | if (segments) { 16 | const msgId = Jupyter.notebook.kernel.last_msg_id; 17 | const cell = Jupyter.notebook.get_msg_cell(msgId); 18 | const cellIndex = Jupyter.notebook.find_cell_index(cell); 19 | const newCell = Jupyter.notebook.insert_cell_below('code', cellIndex); 20 | newCell.set_text(`segments = ${segments}`); 21 | newCell.execute(); 22 | } 23 | }, 24 | this 25 | ); 26 | this.options.output.wrapper.css({'z-index': 2}); 27 | }, 28 | }; 29 | 30 | // TODO: Implement dynamic import of components 31 | for (const name of Object.keys(req)) { 32 | const Component = require('../components')[name]; 33 | const callback = reqExt[name] || function() {}; 34 | const widgets = widgetBuilder(Component, name, callback); 35 | 36 | module.exports[name + 'WidgetView'] = widgets[name + 'WidgetView']; 37 | module.exports[name + 'WidgetModel'] = widgets[name + 'WidgetModel']; 38 | } 39 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-ma-causal/src/components/slidebar-svg/index.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Slidebar from '../slidebar'; 4 | 5 | export default class Chart extends Component { 6 | static propTypes = { 7 | width: PropTypes.number.isRequired, 8 | height: PropTypes.number.isRequired, 9 | extent: PropTypes.array.isRequired, 10 | renderLeftLabel: PropTypes.bool, 11 | renderRightLabel: PropTypes.bool, 12 | leftLabel: PropTypes.string, 13 | rightLabel: PropTypes.string, 14 | x: PropTypes.number, 15 | onDragStart: PropTypes.func.isRequired, 16 | onDrag: PropTypes.func.isRequired, 17 | onDragEnd: PropTypes.func.isRequired, 18 | style: PropTypes.object, 19 | disableDrag: PropTypes.bool, 20 | }; 21 | 22 | static defaultProps = { 23 | extent: [[0, 0], [1, 1]], 24 | onDragStart: () => {}, 25 | onDrag: () => {}, 26 | onDragEnd: () => {}, 27 | }; 28 | 29 | render() { 30 | const {width, height, style, ...rest} = this.props; 31 | return ( 32 | (this.svg = input)} 34 | width={width} 35 | height={height} 36 | style={style} 37 | > 38 | { 41 | const {clientX, clientY} = event; 42 | const {left, top} = this.svg.getBoundingClientRect(); 43 | return [clientX - left, clientY - top]; 44 | }} 45 | /> 46 | 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /modules/manifold/src/components/ui/legend-group/legend-group.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | import React, {PureComponent} from 'react'; 3 | import PropTypes from 'prop-types'; 4 | import LegendItem from './legend-item'; 5 | import styled from 'styled-components'; 6 | 7 | const Container = styled.div` 8 | display: flex; 9 | align-items: center; 10 | `; 11 | 12 | export default class LegendGroup extends PureComponent { 13 | static propTypes = { 14 | /** Array of legend objects {id, name} */ 15 | data: PropTypes.arrayOf( 16 | PropTypes.shape({ 17 | id: PropTypes.number, 18 | name: PropTypes.string, 19 | }) 20 | ), 21 | /** Color scale function */ 22 | colorScale: PropTypes.func.isRequired, 23 | /** Callback on model selected */ 24 | onModelSelect: PropTypes.func, 25 | }; 26 | 27 | static defaultProps = { 28 | data: [], 29 | onModelSelect: () => {}, 30 | }; 31 | 32 | constructor(props) { 33 | super(props); 34 | this.state = { 35 | selectedModels: props.data.map(d => ({ 36 | d: false, 37 | })), 38 | }; 39 | } 40 | 41 | render() { 42 | const {colorScale, data, onModelSelect, className} = this.props; 43 | 44 | return ( 45 | 46 | {data.map(({id, name}, i) => ( 47 | onModelSelect(id)} 53 | /> 54 | ))} 55 | 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /modules/manifold/src/constants/types.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import {FILTER_TYPE, FEATURE_TYPE} from '@mlvis/mlvis-common/constants'; 3 | 4 | export const FIELD = PropTypes.shape({ 5 | name: PropTypes.string, 6 | tableFieldIndex: PropTypes.number, 7 | type: PropTypes.oneOf(Object.values(FEATURE_TYPE)), 8 | domain: PropTypes.arrayOf( 9 | PropTypes.oneOfType([PropTypes.string, PropTypes.number]) 10 | ), 11 | }); 12 | 13 | export const FILTER = PropTypes.shape({ 14 | name: PropTypes.string.isRequired, 15 | key: PropTypes.number.isRequired, 16 | value: PropTypes.oneOfType([ 17 | PropTypes.number, 18 | PropTypes.string, 19 | PropTypes.func, 20 | PropTypes.arrayOf(PropTypes.number), 21 | PropTypes.arrayOf(PropTypes.string), 22 | PropTypes.arrayOf(PropTypes.bool), 23 | ]).isRequired, 24 | type: PropTypes.oneOf(Object.values(FILTER_TYPE)), 25 | }); 26 | 27 | export const STATE_DATA_TYPES = { 28 | modelsMeta: PropTypes.shape({ 29 | nClasses: PropTypes.number, 30 | classLabels: PropTypes.arrayOf( 31 | PropTypes.oneOfType([PropTypes.string, PropTypes.number]) 32 | ), 33 | }), 34 | metric: PropTypes.shape({ 35 | name: PropTypes.string.isRequired, 36 | description: PropTypes.string, 37 | func: PropTypes.func.isRequired, 38 | }), 39 | isManualSegmentation: PropTypes.bool, 40 | baseCols: PropTypes.arrayOf(PropTypes.number), 41 | nClusters: PropTypes.number, 42 | /** input segment filters, each of shape key, value, and type */ 43 | segmentFilters: PropTypes.arrayOf(PropTypes.arrayOf(FILTER)), 44 | segmentGroups: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.number)), 45 | }; 46 | -------------------------------------------------------------------------------- /modules/manifold/src/actions/ui-actions.js: -------------------------------------------------------------------------------- 1 | import {createAction} from 'redux-actions'; 2 | 3 | export const UPDATE_SELECTED_INSTANCES = 'UPDATE_SELECTED_INSTANCES'; 4 | export const UPDATE_DIVERGENCE_THRESHOLD = 'UPDATE_DIVERGENCE_THRESHOLD'; 5 | export const UPDATE_SELECTED_MODELS = 'UPDATE_SELECTED_MODELS'; 6 | export const UPDATE_N_CLUSTERS = 'UPDATE_N_CLUSTERS'; 7 | export const UPDATE_METRIC = 'UPDATE_METRIC'; 8 | export const UPDATE_SEGMENTATION_METHOD = 'UPDATE_SEGMENTATION_METHOD'; 9 | export const UPDATE_SEGMENT_FILTERS = 'UPDATE_SEGMENT_FILTERS'; 10 | export const UPDATE_BASE_COLS = 'UPDATE_BASE_COLS'; 11 | export const UPDATE_SEGMENT_GROUPS = 'UPDATE_SEGMENT_GROUPS'; 12 | export const UPDATE_DISPLAY_GEO_FEATURES = 'UPDATE_DISPLAY_GEO_FEATURES'; 13 | export const UPDATE_COLOR_BY_FEATURE = 'UPDATE_COLOR_BY_FEATURE'; 14 | 15 | export const updateDivergenceThreshold = createAction( 16 | UPDATE_DIVERGENCE_THRESHOLD 17 | ); 18 | export const updateSelectedInstances = createAction(UPDATE_SELECTED_INSTANCES); 19 | export const updateSelectedModels = createAction(UPDATE_SELECTED_MODELS); 20 | export const updateNClusters = createAction(UPDATE_N_CLUSTERS); 21 | export const updateMetric = createAction(UPDATE_METRIC); 22 | export const updateSegmentationMethod = createAction( 23 | UPDATE_SEGMENTATION_METHOD 24 | ); 25 | export const updateSegmentFilters = createAction(UPDATE_SEGMENT_FILTERS); 26 | export const updateBaseCols = createAction(UPDATE_BASE_COLS); 27 | export const updateSegmentGroups = createAction(UPDATE_SEGMENT_GROUPS); 28 | export const updateDisplayGeoFeatures = createAction( 29 | UPDATE_DISPLAY_GEO_FEATURES 30 | ); 31 | export const updateColorByFeature = createAction(UPDATE_COLOR_BY_FEATURE); 32 | -------------------------------------------------------------------------------- /bindings/jupyter/docs/development.md: -------------------------------------------------------------------------------- 1 | # Development 2 | 3 | #### Javascript 4 | 5 | Build the JS code 6 | 7 | ``` 8 | cd js 9 | yarn 10 | yarn build 11 | ``` 12 | 13 | #### Python 14 | 15 | It is recommended to first start a virtual environment in the project folder: 16 | 17 | ``` 18 | python -m venv venv 19 | source venv/bin/activate 20 | ``` 21 | 22 | First install ipywidgets 23 | 24 | 25 | 26 | ``` 27 | pip install ipywidgets 28 | jupyter nbextension enable --py widgetsnbextension 29 | ``` 30 | 31 | To run the code directly under this directory, the Jupyter nbextension needs to be installed in this local environment: 32 | 33 | ``` 34 | python setup.py install 35 | ``` 36 | 37 | If running `python setup.py install` for the first time, the following two commands need to be executed (these should only need to be executed once). 38 | 39 | ``` 40 | jupyter nbextension install --py --symlink --sys-prefix mlvis 41 | jupyter nbextension enable --py --sys-prefix mlvis 42 | ``` 43 | 44 | #### Run 45 | 46 | Start the Jupyter Notebook directly under this directory: 47 | 48 | ``` 49 | jupyter notebook 50 | ``` 51 | 52 | #### Build 53 | 54 | To build the package into the example directly, please use the following command: 55 | 56 | ``` 57 | python setup.py sdist bdist_wheel 58 | mv dist ../../examples/jupyter/ 59 | ``` 60 | 61 | To test the built package in the example folder, go to _mlvis-toolkit/examples/jupyter_, and perform the following stepts: 62 | 63 | 1. start a virtual environment 64 | 2. run `pip install -r requirements.txt` 65 | 3. run `pip install ./dist/mlvis-[version].tar.gz` 66 | 4. execute `jupyter notebook` there. 67 | -------------------------------------------------------------------------------- /modules/manifold/src/selectors/data.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | import {rootSelector, getHasBackend} from './base'; 3 | import { 4 | getMetaDataFromRaw, 5 | getSegmentedCatNumFeatures, 6 | getModelPerfHistograms, 7 | } from './compute'; 8 | import {createSelector} from 'reselect'; 9 | 10 | // ----------------------------------------------------------------------------------------------------------- // 11 | // -- THE DATA SELECTORS SELECTS DATA FROM EITHER API RESPONSES OR FRONT END TRANSFORMATION RESULTS IN COMPUTE -- // 12 | // ----------------------------------------------------------------------------------------------------------- // 13 | 14 | // -- computed data from API -- // 15 | const getApiMetaData = createSelector(rootSelector, state => state.metaData); 16 | const getApiModels = createSelector(rootSelector, state => state.models); 17 | const getApiFeatures = createSelector(rootSelector, state => state.features); 18 | 19 | // -- computed data. Either came directly from BE API, or computed in FE -- // 20 | export const getMetaData = createSelector( 21 | getHasBackend, 22 | getApiMetaData, 23 | getMetaDataFromRaw, 24 | (hasBackend, apiMeta = {}, feMeta = []) => { 25 | return hasBackend ? apiMeta : feMeta; 26 | } 27 | ); 28 | 29 | export const getModelsPerformance = createSelector( 30 | getHasBackend, 31 | getApiModels, 32 | getModelPerfHistograms, 33 | (hasBackend, apiModels, feModels) => { 34 | return hasBackend ? apiModels : feModels; 35 | } 36 | ); 37 | 38 | export const getFeaturesDistribution = createSelector( 39 | getHasBackend, 40 | getApiFeatures, 41 | getSegmentedCatNumFeatures, 42 | (hasBackend, apiFeatures, feFeatures) => { 43 | return hasBackend ? apiFeatures : feFeatures; 44 | } 45 | ); 46 | -------------------------------------------------------------------------------- /modules/feature-list-view/src/feature-list-view.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import FeatureView from './feature-view'; 4 | import {COLOR} from '@mlvis/mlvis-common/constants'; 5 | 6 | import {ITEM_HEIGHT} from './constants'; 7 | 8 | export default class FeatureListView extends PureComponent { 9 | static defaultProps = { 10 | data: [], 11 | selectedInstances: [], 12 | colors: [COLOR.GREEN, COLOR.PURPLE], 13 | }; 14 | 15 | static propTypes = { 16 | data: PropTypes.arrayOf(PropTypes.object).isRequired, 17 | colors: PropTypes.arrayOf(PropTypes.string), 18 | selectedInstances: PropTypes.arrayOf(PropTypes.object), 19 | }; 20 | 21 | _renderFeatureList = width => { 22 | const {data, selectedInstances, colors} = this.props; 23 | if (!data || data.length === 0) { 24 | return null; 25 | } 26 | 27 | const featureList = data.map((feature, i) => { 28 | const layout = { 29 | x: 0, 30 | y: ITEM_HEIGHT * i, 31 | width, 32 | height: ITEM_HEIGHT, 33 | }; 34 | return ( 35 | 43 | ); 44 | }); 45 | 46 | return ( 47 | 52 | {featureList} 53 | 54 | ); 55 | }; 56 | 57 | render() { 58 | const {width} = this.props; 59 | return {this._renderFeatureList(width)}; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /modules/feature-list-view/src/utils.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {scaleLinear} from 'd3-scale'; 3 | import {RIGHT_MARGIN_WIDTH} from './constants'; 4 | import { 5 | getFeatureName, 6 | getFeatureType, 7 | getFeatureDomain, 8 | getFeatureDistributions, 9 | getDistributionsMaxValues, 10 | getFeatureDistributionsNormalized, 11 | getFeatureDivergence, 12 | getCategoriesSortedOrder, 13 | } from './selectors'; 14 | 15 | export const pointsToPolyline = (points, dx, dy) => { 16 | const polyline = points.map( 17 | (val, i) => `${(dx * i).toFixed(1)},${(val * dy).toFixed(1)}` 18 | ); 19 | return `0,0 ${polyline.join(' ')} ${points.length * dx},0`; 20 | }; 21 | 22 | /** 23 | * HOC function to check if feature meta is precomputed, if not, compute and inject result 24 | */ 25 | export const withDerivedData = FeatureView => props => { 26 | const {data, width} = props; 27 | const featureWithDerivedData = { 28 | ...data, 29 | name: getFeatureName(props), 30 | type: getFeatureType(props), 31 | domain: getFeatureDomain(props), 32 | distributions: getFeatureDistributions(props), 33 | distributionsNormalized: getFeatureDistributionsNormalized(props), 34 | distributionsMaxValues: getDistributionsMaxValues(props), 35 | divergence: getFeatureDivergence(props), 36 | // for categorical features only 37 | categoriesSortedOrder: getCategoriesSortedOrder(props), 38 | }; 39 | 40 | const xScale = scaleLinear() 41 | .domain([ 42 | featureWithDerivedData.domain[0], 43 | featureWithDerivedData.domain[featureWithDerivedData.domain.length - 1], 44 | ]) 45 | .range([0, width - RIGHT_MARGIN_WIDTH]); 46 | 47 | return ( 48 | 49 | ); 50 | }; 51 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-manifold/src/controls/segment-export-button.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react'; 2 | import Jupyter from 'base/js/namespace'; 3 | import {selectors} from '@mlvis/manifold'; 4 | import {connect} from 'react-redux'; 5 | 6 | const mapStateToProps = state => ({ 7 | segments: selectors.compute.getDataIdsInSegmentGroups(state), 8 | }); 9 | 10 | const buttonStyle = { 11 | display: 'flex', 12 | alignItems: 'center', 13 | height: 30, 14 | backgroundColor: '#80ced6', 15 | padding: '5px 5px', 16 | cursor: 'pointer', 17 | }; 18 | 19 | class Button extends PureComponent { 20 | render() { 21 | const {widgetModel, segments} = this.props; 22 | return ( 23 |
{ 26 | if (widgetModel) { 27 | const segStr = JSON.stringify(segments); 28 | if (segStr !== widgetModel.get('segments')) { 29 | widgetModel.set('segments', JSON.stringify(segments)); 30 | widgetModel.save_changes(); 31 | } else { 32 | // TODO: clean this up, there are duplicate code in the widget part 33 | const msgId = Jupyter.notebook.kernel.last_msg_id; 34 | const cell = Jupyter.notebook.get_msg_cell(msgId); 35 | const cellIndex = Jupyter.notebook.find_cell_index(cell); 36 | const newCell = Jupyter.notebook.insert_cell_below( 37 | 'code', 38 | cellIndex 39 | ); 40 | newCell.set_text(`segments = ${segStr}`); 41 | newCell.execute(); 42 | } 43 | } 44 | }} 45 | > 46 | Export Segmentation 47 |
48 | ); 49 | } 50 | } 51 | 52 | export default connect(mapStateToProps)(Button); 53 | -------------------------------------------------------------------------------- /modules/stacked-calendar/src/__tests__/utils.spec.js: -------------------------------------------------------------------------------- 1 | import { 2 | extractHourAndValue, 3 | extractValueRange, 4 | createHourlyValueMap, 5 | // TODO add unit tests for the three functions below 6 | // createHourlyValueDistribution, 7 | // withDerivedLayout, 8 | // withDerivedData, 9 | } from '../utils'; 10 | 11 | test('utils: extractHourAndValue', () => { 12 | const data = [ 13 | {order_time: 0, metric: 0.0, count: 100}, 14 | {order_time: 1, metric: 0.5, count: 200}, 15 | {order_time: 2, metric: 1.0, count: 300}, 16 | ]; 17 | const output = [ 18 | {hour: 0, value: 0.0}, 19 | {hour: 1, value: 0.5}, 20 | {hour: 2, value: 1.0}, 21 | ]; 22 | expect(extractHourAndValue(data, d => d.order_time, d => d.metric)).toEqual( 23 | output 24 | ); 25 | }); 26 | 27 | test('utils: extractValueRange', () => { 28 | const data = [ 29 | {hour: 0, value: 0.1}, 30 | {hour: 1, value: 0.5}, 31 | {hour: 2, value: 0.9}, 32 | ]; 33 | expect(extractValueRange([])).toEqual([0, 1]); 34 | expect(extractValueRange(data)).toEqual([0.1, 0.9]); 35 | }); 36 | 37 | test('utils: createHourlyValueMap', () => { 38 | const data = [ 39 | {hour: 0, value: 0.1}, 40 | {hour: 0, value: 0.2}, 41 | {hour: 1, value: 0.5}, 42 | {hour: 2, value: 0.6}, 43 | {hour: 3, value: 1.0}, 44 | {hour: 3, value: 0.8}, 45 | ]; 46 | const output = { 47 | 0: [0.1, 0.2], 48 | 1: [0.5], 49 | 2: [0.6], 50 | 3: [1.0, 0.8], 51 | }; 52 | expect(createHourlyValueMap([])).toEqual({}); 53 | expect(createHourlyValueMap(data)).toEqual(output); 54 | }); 55 | 56 | test('utils: createHourlyValueDistribution', () => { 57 | // TODO 58 | }); 59 | 60 | test('utils: withDerivedLayout', () => { 61 | // TODO 62 | }); 63 | 64 | test('utils: withDerivedData', () => { 65 | // TODO 66 | }); 67 | -------------------------------------------------------------------------------- /modules/manifold/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mlvis/manifold", 3 | "version": "1.1.4", 4 | "publishConfig": { 5 | "access": "public" 6 | }, 7 | "description": "Model-Agnostic Visual Debugging Tool for Machine Learning", 8 | "license": "Apache-2.0", 9 | "author": "Firenze11 ", 10 | "main": "index.js", 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/uber/manifold.git" 14 | }, 15 | "homepage": "https://github.com/uber/manifold#readme", 16 | "keywords": [ 17 | "ml", 18 | "visualization", 19 | "react", 20 | "redux", 21 | "es6" 22 | ], 23 | "dependencies": { 24 | "@babel/polyfill": "^7.0.0", 25 | "@loaders.gl/arrow": "^1.2.0-alpha.1", 26 | "@loaders.gl/core": "^1.1.0", 27 | "@mlvis/feature-list-view": "^1.1.4", 28 | "@mlvis/mlvis-common": "^1.1.4", 29 | "@mlvis/multi-way-plot": "^1.1.4", 30 | "@tensorflow/tfjs": "^0.15.3", 31 | "baseui": "^9.2.0", 32 | "d3-color": "^1.3.0", 33 | "d3-scale": "^3.0.0", 34 | "d3-scale-chromatic": "^1.3.3", 35 | "d3-shape": "^1.3.5", 36 | "file-saver": "^1.3.3", 37 | "global": "^4.3.2", 38 | "kepler.gl": "^1.1.7", 39 | "lodash.clone": "^4.5.0", 40 | "lodash.get": "^4.4.2", 41 | "lodash.setwith": "^4.3.2", 42 | "mathjs": "^5.2.3", 43 | "numeral": "^2.0.6", 44 | "papaparse": "^4.6.3", 45 | "prop-types": "^15.7.2", 46 | "random": "^2.1.1", 47 | "react-d3-axis": "^0.1.1", 48 | "reduce-reducers": "^1.0.4", 49 | "redux": "^4.0.0", 50 | "redux-actions": "^2.6.5", 51 | "redux-thunk": "^2.3.0", 52 | "reselect": "^4.0.0" 53 | }, 54 | "peerDependencies": { 55 | "react": "^16.9.0", 56 | "styled-components": "^4.2.0", 57 | "styletron-engine-atomic": "^1.4.1", 58 | "styletron-react": "^5.2.1" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/arrow-left.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class ArrowLeft extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-arrowleft', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /modules/manifold/src/components/ui/headline/tabs.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import styled from 'styled-components'; 4 | 5 | const Container = styled.div` 6 | display: flex; 7 | flex-direction: row; 8 | align-items: stretch; 9 | `; 10 | 11 | export const Tab = styled.div` 12 | font-weight: 500; 13 | height: 100% 14 | width: max-content; 15 | padding-top: 12px; 16 | margin-right: 20px; 17 | cursor: ${props => (props.onClick ? 'pointer' : 'default')} 18 | border-bottom: ${props => '2px solid ' + props.themeColor}; 19 | opacity: ${props => (props.isInactive ? 0.2 : 1)} 20 | `; 21 | 22 | export class TabGroup extends PureComponent { 23 | static defaultProps = { 24 | /** An array of tab titles */ 25 | tabs: PropTypes.arrayOf(PropTypes.string), 26 | /** Callback on tab change */ 27 | onTabChange: PropTypes.func, 28 | /** Theme colorof the tab */ 29 | themeColor: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | tabs: [], 34 | onTabChange: () => {}, 35 | themeColor: '#000', 36 | }; 37 | 38 | state = { 39 | activeTab: 0, 40 | }; 41 | 42 | _onTabChange = (tab, i) => { 43 | const {onTabChange} = this.props; 44 | this.setState( 45 | { 46 | activeTab: i, 47 | }, 48 | onTabChange(tab, i) 49 | ); 50 | }; 51 | 52 | render() { 53 | const {tabs, themeColor} = this.props; 54 | const {activeTab} = this.state; 55 | return ( 56 | 57 | {tabs.map((tab, i) => ( 58 | this._onTabChange(tab, i)} 60 | themeColor={themeColor} 61 | isInactive={i !== activeTab} 62 | key={i} 63 | > 64 | {tab} 65 | 66 | ))} 67 | 68 | ); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/minus.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class Minus extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-minus', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | 43 | 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /modules/manifold/src/components/ui/segment-groups-control/segment-group-panel.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import SegmentButtonGroup from './segment-button-group'; 5 | import {SegmentPanel} from '../styled-components'; 6 | import {updateSegmentGroups} from './utils'; 7 | 8 | export default class SegmentGroupPanel extends PureComponent { 9 | static propTypes = { 10 | /** candidate segments shared across all segment groups: e.g., [0, 1, 2, 3] */ 11 | candidates: PropTypes.arrayOf(PropTypes.number), 12 | /** selected segments for each segment group: e.g., [[1], [2, 3]] */ 13 | selected: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.number)), 14 | /** callback function to surface the updated segment grouping */ 15 | onUpdateSegmentGroups: PropTypes.func, 16 | /** colors indicatin each group */ 17 | colors: PropTypes.arrayOf(PropTypes.string), 18 | }; 19 | 20 | static defaultProps = { 21 | candidates: [], 22 | selected: [[], []], 23 | onUpdateSegmentGroups: () => {}, 24 | colors: ['#f00', '#000'], 25 | }; 26 | 27 | _updateSelectedSegmentGroups = ({groupId, segmentId}) => { 28 | const newSelected = updateSegmentGroups( 29 | this.props.selected, 30 | groupId, 31 | segmentId 32 | ); 33 | this.props.onUpdateSegmentGroups(newSelected); 34 | }; 35 | 36 | render() { 37 | const {colors, candidates, selected} = this.props; 38 | 39 | return ( 40 | 41 | {[0, 1].map(groupId => ( 42 | 50 | ))} 51 | 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /modules/manifold/src/io.js: -------------------------------------------------------------------------------- 1 | import {fetch, Request, Headers} from 'global'; 2 | 3 | const toParam = p => encodeURIComponent(JSON.stringify(p)); 4 | 5 | // [WIP] actions for sending requests if connected to a backend. 6 | export function loadData({featureDataset, predDatasets, dataFilter}) { 7 | const h = new Headers({ 8 | Accept: 'application/json', 9 | }); 10 | const opts = { 11 | method: 'GET', 12 | headers: h, 13 | }; 14 | const req = new Request( 15 | 'manifold_py/api/load_data?' + 16 | `feature_dataset=${featureDataset}&pred_datasets=${toParam( 17 | predDatasets 18 | )}&data_filter=${toParam(dataFilter)}`, 19 | opts 20 | ); 21 | return fetch(req); 22 | } 23 | 24 | // [WIP] actions for sending requests if connected to a backend. 25 | export function modelsPerformance({ 26 | nClusters = 5, 27 | metric = 'performance', 28 | baseModels = [], 29 | segmentFilters = [], 30 | }) { 31 | const h = new Headers({ 32 | Accept: 'application/json', 33 | }); 34 | const opts = { 35 | method: 'GET', 36 | headers: h, 37 | }; 38 | const req = new Request( 39 | 'manifold_py/api/models_performance?' + 40 | `n_clusters=${nClusters}&metric=${metric}&` + 41 | `base_models=${toParam(baseModels)}&segment_filters=${toParam( 42 | segmentFilters 43 | )}`, 44 | opts 45 | ); 46 | return fetch(req); 47 | } 48 | 49 | // [WIP] actions for sending requests if connected to a backend. 50 | export function featuresDistribution({ 51 | nClusters = 5, 52 | segmentGroups = [[1], [0, 2, 3]], 53 | }) { 54 | const h = new Headers({ 55 | Accept: 'application/json', 56 | }); 57 | const opts = { 58 | method: 'GET', 59 | headers: h, 60 | }; 61 | const req = new Request( 62 | 'manifold_py/api/features_distribution?' + 63 | `segment_groups=${toParam(segmentGroups)}`, 64 | opts 65 | ); 66 | return fetch(req); 67 | } 68 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/play.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class Play extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-play', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/utils/gen-data.js: -------------------------------------------------------------------------------- 1 | import random from 'random'; 2 | 3 | /** 4 | * generate one random ID, unique for single client 5 | * 6 | * @param {Number} seed: random seed, use Date.now() by default 7 | * @return a unique ID string 8 | */ 9 | export const generateRandomId = (seed = Date.now()) => 10 | (seed * Math.random()).toString(36).substr(2, 6); 11 | 12 | /** 13 | * generate n random points with position x and y in [0, 1] 14 | * 15 | * @param {Number} n: number of points 16 | * @return a list of randomly positioned points 17 | */ 18 | export const generateRandomPoints = n => 19 | Array(n) 20 | .fill(0) 21 | .map(_ => ({x: Math.random(), y: Math.random()})); 22 | 23 | /** 24 | * generate k random steps within the domain of [0, 1] 25 | * 26 | * @param {Number} k: number of steps 27 | * @return a list of krandom steps of length k + 1 [0, 0.25, 0.34, 0.78, ..., 1] 28 | */ 29 | export const generateRandomSteps = k => 30 | Array(k) 31 | .fill(0) 32 | .map(_ => Math.random() / 2) 33 | .reduce((acc, val, idx) => [...acc, acc[idx] + (1 - acc[idx]) * val], [0]) 34 | .slice(0, -1) 35 | .concat([1]); 36 | 37 | /** 38 | * generate n random values following normal distribution 39 | * 40 | * @param {Number} n: number of steps 41 | * @param {Number} mu: mean 42 | * @param {Number} sigma: standard deviation 43 | * @return {Array} 44 | */ 45 | export const generateRandomNormal = (n, mu, sigma) => { 46 | return Array(n) 47 | .fill(0) 48 | .map(random.normal(mu, sigma)); 49 | }; 50 | 51 | /** 52 | * generate n random values following log-normal distribution 53 | * 54 | * @param {Number} n: number of steps 55 | * @param {Number} mu: mean 56 | * @param {Number} sigma: standard deviation 57 | * @return {Array} 58 | */ 59 | export const generateRandomLogNormal = (n, mu, sigma) => { 60 | return Array(n) 61 | .fill(0) 62 | .map(random.logNormal(mu, sigma)); 63 | }; 64 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/pin.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class Pin extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | size: 'tiny', 34 | predefinedClassName: 'data-ex-icons-pin', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/pause.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class Pause extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-pause', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /modules/manifold/src/components/ui/side-bar.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import styled from 'styled-components'; 4 | import {Cancel} from '@mlvis/mlvis-common/icons'; 5 | 6 | const Container = styled.div` 7 | transition: 0.5s; 8 | transition-timing-function: ease-in-out; 9 | width: ${props => (props.isOpen ? props.width + 'px' : 0)}; 10 | padding: ${props => (props.isOpen ? '12px 24px' : '12px 0')}; 11 | overflow-x: hidden; 12 | display: flex; 13 | flex-direction: column; 14 | align-items: stretch; 15 | position: relative; 16 | div { 17 | opacity: ${props => (props.isOpen ? 1 : 0)}; 18 | transition: 0.5s; 19 | transition-delay: 0.5s; 20 | } 21 | `; 22 | 23 | const StyledIcon = styled.div``; 24 | 25 | const IconButton = styled.div` 26 | cursor: pointer; 27 | margin-right: 12px; 28 | padding: 12px; 29 | position: absolute; 30 | transition: 0.3s; 31 | transition-delay: 0.2s; 32 | right: 0; 33 | top: 0px; 34 | `; 35 | 36 | export default class SideBar extends PureComponent { 37 | static propTypes = { 38 | /** True if the side bar is open. */ 39 | isOpen: PropTypes.bool, 40 | /** Width of the side bar. */ 41 | width: PropTypes.number, 42 | /** Callback on toggle open the side bar. */ 43 | onToggleOpen: PropTypes.func, 44 | }; 45 | 46 | static defaultProps = { 47 | isOpen: false, 48 | width: 300, 49 | onToggleOpen: () => {}, 50 | }; 51 | 52 | _onClose = () => { 53 | this.props.onToggleOpen(false); 54 | }; 55 | 56 | render() { 57 | const {children, isOpen, width, className} = this.props; 58 | return ( 59 | 60 | 61 | 62 | 63 | {children} 64 | 65 | ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/messages.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class Messages extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-messages', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/add.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class Add extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-add', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/arrow-down.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class ArrowDown extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-arrowdown', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/upload.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class Upload extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | predefinedClassName: PropTypes.string, 31 | }; 32 | 33 | static defaultProps = { 34 | height: '16px', 35 | predefinedClassName: 'data-ex-icons-upload', 36 | }; 37 | 38 | render() { 39 | return ( 40 | 41 | 42 | 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/line-chart.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class LineChart extends Component { 27 | static propTypes = { 28 | height: PropTypes.string, 29 | }; 30 | 31 | static defaultProps = { 32 | height: '16px', 33 | predefinedClassName: 'data-ex-icons-linechart', 34 | }; 35 | 36 | render() { 37 | return ( 38 | 39 | 43 | 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /modules/feature-list-view/src/constants.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import {FEATURE_TYPE, COLOR} from '@mlvis/mlvis-common/constants'; 3 | 4 | // height of each feature view chart 5 | export const ITEM_HEIGHT = 72; 6 | // for tooltips in feature views 7 | // width in number of pixels for one character 8 | export const CHAR_WIDTH = 7; 9 | // number of characters separating each text 10 | export const CHAR_SEPARATION = 2; 11 | 12 | export const HEADER_HEIGHT = 16; 13 | export const FOOTER_HEIGHT = 18; 14 | 15 | export const RIGHT_MARGIN_WIDTH = 36; 16 | 17 | export const CHART_PROP_TYPES = { 18 | id: PropTypes.string.isRequired, 19 | x: PropTypes.number, 20 | y: PropTypes.number, 21 | width: PropTypes.number, 22 | height: PropTypes.number, 23 | data: PropTypes.shape({ 24 | distributions: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.number)), 25 | distributionsMaxValues: PropTypes.arrayOf(PropTypes.number), 26 | distributionsNormalized: PropTypes.arrayOf( 27 | PropTypes.arrayOf(PropTypes.number) 28 | ), 29 | divergence: PropTypes.number, 30 | domain: PropTypes.arrayOf( 31 | PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]) 32 | ), 33 | categoriesSortedOrder: PropTypes.arrayOf(PropTypes.number), 34 | name: PropTypes.string, 35 | type: PropTypes.oneOf(Object.values(FEATURE_TYPE)), 36 | values: PropTypes.arrayOf( 37 | PropTypes.arrayOf( 38 | PropTypes.oneOfType([ 39 | PropTypes.string, 40 | PropTypes.number, 41 | PropTypes.bool, 42 | PropTypes.instanceOf(Date), 43 | ]) 44 | ) 45 | ), 46 | }), 47 | colors: PropTypes.arrayOf(PropTypes.string), 48 | xScale: PropTypes.func, 49 | }; 50 | 51 | export const CHART_DEFAULT_PROPS = { 52 | id: 'default-numerical-feature-id', 53 | x: 0, 54 | y: 0, 55 | width: 0, 56 | height: 0, 57 | data: null, 58 | colors: [COLOR.GREEN, COLOR.PURPLE], 59 | xScale: () => 0, 60 | }; 61 | -------------------------------------------------------------------------------- /bindings/jupyter/docs/stacked-calendar.md: -------------------------------------------------------------------------------- 1 | # Stacked Calendar 2 | 3 | The Stacked Calendar component aggregates hourly-stamped values and visualizes the results in the form of a calendar. 4 | 5 | The columns and rows are corresponding to the day_of_week and hour_of_day, respectively. 6 | The data values of each cell (hour) are aggregated, percentiled, and mapped to color gradients to reveal the value distributions. 7 | 8 | ## Usage 9 | 10 | 11 | ```python 12 | from mlvis import StackedCalendar 13 | from IPython.display import display 14 | import pandas as pd 15 | 16 | PREFIX = 'https://d1a3f4spazzrp4.cloudfront.net/mlvis/' 17 | data_frame = pd.read_csv(PREFIX + 'jupyter/stacked-calendar-sample.csv') 18 | data = data_frame.to_dict('records') 19 | 20 | calendar = StackedCalendar(props={"data": data, "valueRange": [0, 1.5]}) 21 | display(calendar) 22 | ``` 23 | 24 | Jupyter 25 | 26 | ## Data Format 27 | 28 | 29 | ```python 30 | print(pd.DataFrame(data)[10000:10010]) 31 | ``` 32 | 33 | hour_of_week value 34 | 10000 66 0.272818888 35 | 10001 66 0.645657585 36 | 10002 66 0.243684833 37 | 10003 66 0.237157524 38 | 10004 66 0.238575395 39 | 10005 66 0.175796772 40 | 10006 66 0.226707148 41 | 10007 66 0.221181026 42 | 10008 66 0.263068202 43 | 10009 66 0.140494919 44 | 45 | 46 | ## Properties 47 | 48 | #### `data` (List, required) 49 | 50 | - Default: [] 51 | 52 | The input data list, each datum should at least two attributes: _hour_of_week_ (range: [0, 167]) and _value_. 53 | 54 | #### `valueRange` (List, optional) 55 | 56 | - Default: [0, 1] 57 | 58 | The value range used for the color mapping. If data is not specified, valueRange is default to [0, 1]. Otherwise, it will be derived as the [min, max] of the values. 59 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/files.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import PropTypes from 'prop-types'; 23 | import React from 'react'; 24 | import Base from './base'; 25 | 26 | export default class Files extends React.Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | static defaultProps = { 32 | height: '16px', 33 | predefinedClassName: 'data-ex-icons-minus', 34 | }; 35 | 36 | render() { 37 | return ( 38 | 39 | 40 | 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /modules/manifold/src/components/ui/segment-filters-control/segment-panel-list.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import styled, {css} from 'styled-components'; 4 | 5 | import {FILTER, FIELD} from '../../../constants'; 6 | import SegmentPanel from './segment-panel'; 7 | 8 | const SegmentPanelListContainer = styled.div` 9 | color: #666; 10 | font-family: ff-clan-web-pro, 'Helvetica Neue', Helvetica, sans-serif; 11 | font-size: 12px; 12 | 13 | ${props => 14 | props.withPadding && 15 | css` 16 | background: #fff; 17 | padding: 12px; 18 | `}; 19 | `; 20 | 21 | export default class SegmentPanelList extends PureComponent { 22 | static propTypes = { 23 | columnDefs: PropTypes.arrayOf(FIELD), 24 | segmentFilters: PropTypes.arrayOf( 25 | PropTypes.shape({ 26 | key: PropTypes.string.isRequired, 27 | filters: PropTypes.arrayOf(FILTER), 28 | }) 29 | ), 30 | onUpdateFilterValue: PropTypes.func, 31 | onRemoveSegment: PropTypes.func, 32 | }; 33 | 34 | static defaultProps = { 35 | baseCols: [], 36 | columnDefs: [], 37 | segmentFilters: [{key: '0', filters: []}, {key: '1', filters: []}], 38 | onUpdateFilterValue: () => {}, 39 | }; 40 | 41 | render() { 42 | const { 43 | segmentFilters, 44 | columnDefs, 45 | onRemoveSegment, 46 | onUpdateFilterValue, 47 | } = this.props; 48 | 49 | return ( 50 | 51 | {segmentFilters.map((singleSegmentFilters, i) => ( 52 | 61 | ))} 62 | 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/clock.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class Clock extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-clock', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/arrow-right.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class ArrowRight extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-arrowright', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 44 | 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/left-arrow.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class LeftArrow extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-left-arrow', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /bindings/jupyter-modules/jupyter-ma-causal/src/containers/staticbar-svg-container/index.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {connect} from 'react-redux'; 3 | import {format as d3Format} from 'd3-format'; 4 | 5 | import {getLineDataFactory} from '../../selectors/factories'; 6 | 7 | import { 8 | getChartWidth, 9 | getChartHeight, 10 | getChartPadding, 11 | } from '../../selectors/staticbar-selectors'; 12 | 13 | const mapStateToProps = (state, props) => { 14 | const {index, lineName} = props; 15 | // lineName: the name of the line representing the uplifting effect 16 | const getLineData = getLineDataFactory(index, lineName); 17 | return { 18 | data: getLineData(state), 19 | width: getChartWidth(state), 20 | height: getChartHeight(state), 21 | padding: getChartPadding(state), 22 | }; 23 | }; 24 | 25 | const mapDispatchToProps = {}; 26 | 27 | class Chart extends Component { 28 | render() { 29 | const { 30 | data, 31 | width, 32 | height, 33 | padding: {left, right, top, bottom}, 34 | groupName, // treatment or control 35 | } = this.props; 36 | 37 | const format = d3Format('.2f'); 38 | 39 | return ( 40 | 41 | 48 | 54 | {groupName === 'treatment' 55 | ? data.length 56 | ? format(data[data.length - 1].y) 57 | : null 58 | : data.length 59 | ? format(data[0].y) 60 | : null} 61 | 62 | 63 | ); 64 | } 65 | } 66 | 67 | export default connect( 68 | mapStateToProps, 69 | mapDispatchToProps 70 | )(Chart); 71 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/cube-3d.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class Cube3D extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-cube3d', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | 43 | 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /modules/manifold/src/__tests__/selector-adaptors.spec.js: -------------------------------------------------------------------------------- 1 | import { 2 | getSegmentIds, 3 | getSegmentOrdering, 4 | getRawDataRange, 5 | getDensityRange, 6 | getModelIds, 7 | getFeatures, 8 | } from '../selectors/adaptors'; 9 | 10 | // Use `.resultFunc` to test individual selector logic without having to mock dependency selectors 11 | // https://github.com/reduxjs/reselect#q-how-do-i-test-a-selector 12 | 13 | test('selector: base/getSegmentIds', () => { 14 | const ids1 = [0, 1, 2, 3, 4]; 15 | expect(getSegmentIds.resultFunc(false, 5, [7, 8, 9])).toEqual(ids1); 16 | expect(getSegmentIds.resultFunc(true, 5, [7, 8, 9])).toEqual([0, 1, 2]); 17 | }); 18 | 19 | test('selector: base/getSegmentOrdering', () => { 20 | expect(getSegmentOrdering.resultFunc([0, 1, 2, 3, 4], [[3, 4], [0]])).toEqual( 21 | [1, 2, 0, 3, 4] 22 | ); 23 | }); 24 | 25 | test('selector: adapter/getRawDataRange', () => { 26 | const perfBySegment = [ 27 | {data: [{percentiles: [-10, 0, 5]}, {percentiles: [-1, 0, 1]}]}, 28 | {data: [{percentiles: [-1, 0, 10]}, {percentiles: [-5, 0, 1]}]}, 29 | ]; 30 | expect(getRawDataRange.resultFunc(perfBySegment)).toEqual([-10, 10]); 31 | }); 32 | 33 | test('selector: adapter/getDensityRange', () => { 34 | const perfBySegment = [ 35 | { 36 | data: [{density: [[1, 6, 5], []]}, {density: [[2, 3, 4], []]}], 37 | segmentId: 1, 38 | }, 39 | { 40 | data: [{density: [[4, 2, 5], []]}, {density: [[1, 3, 7], []]}], 41 | segmentId: 0, 42 | }, 43 | ]; 44 | expect(getDensityRange.resultFunc(perfBySegment)).toEqual([[0, 7], [0, 6]]); 45 | }); 46 | 47 | test('selector: adapter/getModelIds', () => { 48 | const perfBySegment = [{data: [{}, {}, {}]}, {data: [{}, {}, {}]}]; 49 | expect(getModelIds.resultFunc(perfBySegment)).toEqual([0, 1, 2]); 50 | }); 51 | 52 | test('selector: adapter/getFeatures', () => { 53 | const rawFeatures = [{divergence: 0}, {divergence: 1}, {divergence: 2}]; 54 | const features = getFeatures.resultFunc(rawFeatures, 0.5); 55 | expect(features.length).toBe(2); 56 | }); 57 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/file.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class File extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-file', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/email.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class Email extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-email', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | 43 | 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/table.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class Table extends Component { 27 | static propTypes = { 28 | height: PropTypes.string, 29 | }; 30 | 31 | static defaultProps = { 32 | height: '16px', 33 | predefinedClassName: 'data-ex-icons-table', 34 | }; 35 | 36 | render() { 37 | return ( 38 | 39 | 44 | 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/utils/color.js: -------------------------------------------------------------------------------- 1 | import {interpolatePuBu} from 'd3-scale-chromatic'; 2 | 3 | const DEFAULT_COLOR = [255, 128, 128]; 4 | 5 | const clampValue = (min, max) => value => 6 | value < min ? min : value > max ? max : value; 7 | 8 | /** 9 | * map an input value to a given color scale, and return a color hex string 10 | * 11 | * @param {Number} value: input value 12 | * @param {Boolean} clamp: whether to clamp the input value to [0, 1], default yes 13 | * @param {[Number]} defaultColor: fallback color 14 | * @return interpolated color in hex string format #1234FF 15 | */ 16 | export const interpolateColorToHex = ({ 17 | value, 18 | clamp = true, 19 | defaultColor = DEFAULT_COLOR, 20 | interpolator = interpolatePuBu, 21 | }) => { 22 | const newValue = clamp ? clampValue(0, 1)(value) : value; 23 | 24 | if (!Number.isFinite(newValue)) { 25 | // [r, g, b] => `rgb(r, g, b)` 26 | return `rgb(${defaultColor.join(', ')})`; 27 | } 28 | 29 | return interpolator(newValue); 30 | }; 31 | 32 | /** 33 | * map an input value to a given color scale, and return a rgb array 34 | * 35 | * @param {Number} value: intpu value 36 | * @param {Boolean} clamp: whether to clamp the input value to [0, 1], default yes 37 | * @param {[Number]} defaultColor: fallback color 38 | * @return interpolated color in [255, 255, 255] format 39 | */ 40 | export const interpolateColor = ({ 41 | value, 42 | clamp = true, 43 | defaultColor = DEFAULT_COLOR, 44 | interpolator = interpolatePuBu, 45 | }) => { 46 | return interpolateColorToHex({value, clamp, defaultColor, interpolator}) 47 | .match(/[0-9]+/g) 48 | .map(n => Number(n)); 49 | }; 50 | 51 | export function hexToRGB(hex, alpha) { 52 | var r = parseInt(hex.slice(1, 3), 16), 53 | g = parseInt(hex.slice(3, 5), 16), 54 | b = parseInt(hex.slice(5, 7), 16); 55 | 56 | if (alpha || alpha === 0) { 57 | const _alpha = Math.max(Math.min(alpha, 1), 0); 58 | return 'rgba(' + r + ', ' + g + ', ' + b + ', ' + _alpha + ')'; 59 | } else { 60 | return 'rgb(' + r + ', ' + g + ', ' + b + ')'; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /bindings/jupyter/js/webpack.config.js: -------------------------------------------------------------------------------- 1 | const {resolve} = require('path'); 2 | 3 | const ROOT = resolve(__dirname, '../../..'); 4 | const JUPYTER_MODULES = resolve(__dirname, '../../jupyter-modules'); 5 | 6 | const AliasConfig = require(resolve(ROOT, 'alias.config.js')).AliasConfig; 7 | const getLocalModuleAliases = AliasConfig(); 8 | const getJupyterModuleAliases = AliasConfig(JUPYTER_MODULES); 9 | 10 | const externals = [ 11 | '@jupyter-widgets/base', 12 | 'base/js/namespace', 13 | 'base/js/events', 14 | 'base/js/utils', 15 | ]; 16 | 17 | module.exports = [ 18 | { 19 | entry: { 20 | app: [resolve('./src/index.js')], 21 | }, 22 | devtool: 'source-maps', 23 | output: { 24 | path: resolve('../mlvis/static'), 25 | filename: 'index.js', 26 | libraryTarget: 'amd', 27 | }, 28 | resolve: { 29 | extensions: ['.js'], 30 | alias: { 31 | // resolving deck.gl version conflict issue: 32 | // deck.gl does not allow different versions of deck.gl instances 33 | // running at the same time. So these settings corece all packages 34 | // to use the same deck.gl instance 35 | '@deck.gl': resolve(ROOT, 'node_modules', '@deck.gl'), 36 | '@luma.gl': resolve(ROOT, 'node_modules', '@luma.gl'), 37 | ...getJupyterModuleAliases(), 38 | ...getLocalModuleAliases(), 39 | }, 40 | }, 41 | module: { 42 | rules: [ 43 | { 44 | test: /\.js$/, 45 | loader: 'babel-loader', 46 | options: { 47 | rootMode: 'upward', 48 | }, 49 | exclude: [/node_modules/], 50 | }, 51 | { 52 | test: /\.css$/, 53 | use: ['style-loader', 'css-loader'], 54 | }, 55 | ], 56 | }, 57 | externals, 58 | }, 59 | { 60 | entry: { 61 | app: [resolve('./src/extension.js')], 62 | }, 63 | output: { 64 | path: resolve('../mlvis/static'), 65 | filename: 'extension.js', 66 | libraryTarget: 'amd', 67 | }, 68 | resolve: { 69 | extensions: ['.js'], 70 | }, 71 | }, 72 | ]; 73 | -------------------------------------------------------------------------------- /modules/manifold/src/constants/constants.js: -------------------------------------------------------------------------------- 1 | import {absoluteError, logLoss} from '@mlvis/mlvis-common/utils'; 2 | 3 | export const COLORS = { 4 | PINK: '#ff0099', 5 | BLUE: '#818c81', 6 | }; 7 | 8 | export const MODEL_TYPE = { 9 | REGRESSION: 'REGRESSION', 10 | BIN_CLASS: 'BIN_CLASS', 11 | MULT_CLASS: 'MULT_CLASS', 12 | }; 13 | 14 | export const MODEL_TYPE_FROM_N_CLASSES = nClasses => { 15 | return nClasses === 1 16 | ? MODEL_TYPE.REGRESSION 17 | : nClasses === 2 18 | ? MODEL_TYPE.BIN_CLASS 19 | : MODEL_TYPE.MULT_CLASS; 20 | }; 21 | 22 | export const METRIC = { 23 | ABSOLUTE_ERROR: { 24 | name: 'absolute error', 25 | description: 26 | 'the absolute value of difference between predicted value and ground truth', 27 | func: absoluteError, 28 | }, 29 | LOG_LOSS: { 30 | name: 'log loss', 31 | description: 'the logarithmic loss for predicted probability values', 32 | func: logLoss, 33 | }, 34 | }; 35 | 36 | export const METRIC_OPTIONS = { 37 | REGRESSION: [METRIC.ABSOLUTE_ERROR], 38 | BIN_CLASS: [METRIC.LOG_LOSS], 39 | MULT_CLASS: [METRIC.LOG_LOSS], 40 | }; 41 | 42 | export const SEGMENTATION_METHOD = { 43 | MANUAL: 'manual', 44 | AUTO: 'auto', 45 | }; 46 | 47 | export const SCORE_PREFIX = '@score:'; 48 | export const scoreColName = modelId => `${SCORE_PREFIX}model_${modelId}`; 49 | export const PRED_PREFIX = `@pred:`; 50 | export const predColName = (modelId, classId) => 51 | `${PRED_PREFIX}model_${modelId}_class_${classId}`; 52 | export const GROUND_TRUTH_NAME = `@groundTruth`; 53 | export const makeUuid = dataId => `uuid${dataId}`; 54 | 55 | // view constants 56 | export const THEME_COLOR = '#276ef1'; 57 | export const VIEW_MODE = { 58 | SINGLE: 'SINGLE', 59 | COORDINATED: 'COORDINATED', 60 | }; 61 | export const VIEW_TAB = { 62 | PERF: 'PERF', 63 | FEATURE: 'FEATURE', 64 | }; 65 | 66 | export const VIEW_NAME = { 67 | PERF: 'Performance comparison', 68 | FEATURE: 'Feature attribution', 69 | }; 70 | 71 | export const HELP_TYPE = { 72 | PERF: 'PERF', 73 | FEATURE: 'FEATURE', 74 | }; 75 | 76 | export const CONTROL_MARGIN = 20; 77 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/drag-n-drop.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class DragNDrop extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-dragndrop', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/search.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class Search extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-search', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/reset.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class Reset extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-reset', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/filter-funnel.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class FilterFunnel extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-filterfunnel', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/picture.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import PropTypes from 'prop-types'; 23 | import React from 'react'; 24 | import Base from './base'; 25 | 26 | export default class Picture extends React.Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-minus', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/legend.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class Legend extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-legend', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/histogram.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class Histogram extends Component { 27 | static propTypes = { 28 | height: PropTypes.string, 29 | }; 30 | 31 | static defaultProps = { 32 | height: '16px', 33 | predefinedClassName: 'data-ex-icons-histogram', 34 | }; 35 | 36 | render() { 37 | return ( 38 | 39 | 40 | 44 | 45 | 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/zoom.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class Zoom extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | viewBox: '0, 0, 30, 30', 35 | predefinedClassName: 'data-ex-icons-zoom', 36 | }; 37 | 38 | render() { 39 | return ( 40 | 41 | 46 | 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/share.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class Share extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-share', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /modules/manifold/src/components/ui/help-dialog/step-progress.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled, {css, keyframes} from 'styled-components'; 3 | import {dotRange} from '@mlvis/mlvis-common/utils'; 4 | 5 | const ProgressTrack = styled.div` 6 | width: 100%; 7 | height: 2px; 8 | top: 2px; 9 | background-color: #eee; 10 | position: absolute; 11 | `; 12 | 13 | const ProgressContainer = styled.div` 14 | display: flex; 15 | width: ${props => (props.numSteps - 1) * 50 + 'px'}; 16 | justify-content: space-between; 17 | position: relative; 18 | `; 19 | 20 | const pulse = keyframes` 21 | 0% { 22 | box-shadow: 0 0 0 0 rgba(33, 131, 221, 0.8); 23 | } 24 | 70% { 25 | box-shadow: 0 0 0 6px rgba(33, 131, 221, 0); 26 | } 27 | 100% { 28 | box-shadow: 0 0 0 0 rgba(33, 131, 221, 0); 29 | } 30 | `; 31 | 32 | const animation = css` 33 | ${pulse} 3s infinite; 34 | `; 35 | 36 | const Step = styled.div` 37 | position: relative; 38 | width: 100%; 39 | :last-child { 40 | width: 0; 41 | } 42 | :before { 43 | content: ''; 44 | position: absolute; 45 | width: 6px; 46 | height: 6px; 47 | border-radius: 50%; 48 | background-color: ${props => 49 | props.isFinished || props.isCurrent ? props.color : '#ccc'}; 50 | animation: ${props => (props.isCurrent ? animation : 'none')}; 51 | z-index: 2; 52 | } 53 | :after { 54 | content: ''; 55 | position: absolute; 56 | width: ${props => (props.isFinished ? '100%' : '0%')}; 57 | height: 2px; 58 | top: 2px; 59 | left: 3px; 60 | background-color: ${props => props.color}; 61 | transition: width 0.5s ease-in; 62 | } 63 | :last-child:after { 64 | display: none; 65 | } 66 | `; 67 | 68 | export const StepProgress = ({numSteps, currentStep, color, className}) => { 69 | return ( 70 | 71 | 72 | {dotRange(numSteps).map(step => ( 73 | step} 77 | isCurrent={currentStep === step} 78 | /> 79 | ))} 80 | 81 | ); 82 | }; 83 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/cursor-click.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class CursorClick extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-cursorclick', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | 46 | 47 | 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/save.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class Save extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-save', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/square-select.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class SquareSelect extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-select', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/settings.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class Settings extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-settings', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | 43 | 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/docs.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class Docs extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-docs', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | 50 | 51 | 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /modules/stacked-calendar/README.md: -------------------------------------------------------------------------------- 1 | # Stacked Calendar 2 | 3 | The _Stacked Calendar_ component aggregates hourly-stamped values and visualizes the results in the form of a calendar. 4 | 5 | The columns and rows are corresponding to the day_of_week and hour_of_day, respectively.\ 6 | The data values of each cell (hour) are aggregated, percentiled, and mapped to color gradients to reveal the value distributions. 7 | 8 | ## Usage 9 | 10 | ```js 11 | import StackedCalendar from '@mlvis/stacked-calendar'; 12 | 13 | const App = ({data, valueRange}) => { 14 | /** 15 | * data: [ 16 | * {hour_of_week: 0, value: 0.5}, 17 | * {hour_of_week: 0, value: 1.0}, 18 | * {hour_of_week: 1, value: 2.0}, 19 | * ... 20 | * {hour_of_week: 167, value: 0.8}, 21 | * {hour_of_week: 167, value: 0.4}, 22 | * ], 23 | * valueRange: [0, 1] 24 | */ 25 | return ; 26 | }; 27 | ``` 28 | 29 | ## Data Format 30 | 31 | | hour_of_week (range: [0, 167]) | value | 32 | | :----------------------------: | :---: | 33 | | 0 | 0.5 | 34 | | 0 | 1.0 | 35 | | 1 | 2.0 | 36 | | ... | ... | 37 | | 167 | 0.8 | 38 | | 167 | 0.4 | 39 | 40 | ## Installation 41 | 42 | To install the dependencies from NPM: 43 | 44 | ```bash 45 | npm install @mlvis/stacked-calendar 46 | # or 47 | yarn install @mlvis/stacked-calendar 48 | ``` 49 | 50 | ## Properties 51 | 52 | #### `data` (Array, required) 53 | 54 | - Default: [] 55 | 56 | The input data array, each datum should at least two attributes: _hour_of_week_ (range: [0, 167]) and _value_. 57 | 58 | #### `valueRange` (Array, optional) 59 | 60 | - Default: [0, 1] 61 | 62 | The value range used for the color mapping. If data is not specified, valueRange is default to [0, 1]. Otherwise, it will be derived as the [min, max] of the values. 63 | 64 | ## Source 65 | 66 | [packages/stacked-calendar](https://code.uberinternal.com/diffusion/VIMLVMN/browse/master/packages/stacked-calendar/) 67 | 68 | 69 | 70 | Content here won't be shown in stories. 71 | You can add some unfinished documentation here. 72 | 73 | 74 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/vert-dots.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class VertDots extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-vertdot', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/trash.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class Trash extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-trash', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 44 | 49 | 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /modules/mlvis-common/src/icons/layers.js: -------------------------------------------------------------------------------- 1 | // @noflow 2 | // Copyright (c) 2018 Uber Technologies, Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import React, {Component} from 'react'; 23 | import PropTypes from 'prop-types'; 24 | import Base from './base'; 25 | 26 | export default class Layers extends Component { 27 | static propTypes = { 28 | /** Set the height of the icon, ex. '16px' */ 29 | height: PropTypes.string, 30 | }; 31 | 32 | static defaultProps = { 33 | height: '16px', 34 | predefinedClassName: 'data-ex-icons-layers', 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | 41 | 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /bindings/jupyter/notebooks/graph-builder.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Graph Builder\n", 8 | "\n", 9 | "## Usage" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "outputs": [ 17 | { 18 | "data": { 19 | "application/vnd.jupyter.widget-view+json": { 20 | "model_id": "30dd102baceb4b6a8b2c09d8a6f9e05f", 21 | "version_major": 2, 22 | "version_minor": 0 23 | }, 24 | "text/plain": [ 25 | "GraphBuilder(props='{\"data\": {\"nodes\": [{\"id\": \"1\"}, {\"id\": \"2\"}, {\"id\": \"3\"}], \"edges\": [{\"id\": \"e1\", \"source…" 26 | ] 27 | }, 28 | "metadata": {}, 29 | "output_type": "display_data" 30 | } 31 | ], 32 | "source": [ 33 | "from mlvis import GraphBuilder\n", 34 | "from IPython.display import display\n", 35 | "\n", 36 | "data = {\n", 37 | " \"nodes\": [\n", 38 | " {\"id\": '1'},\n", 39 | " {\"id\": '2'},\n", 40 | " {\"id\": '3'}\n", 41 | " ],\n", 42 | " \"edges\": [\n", 43 | " {\"id\": 'e1', \"sourceId\": '1', \"targetId\": '2'},\n", 44 | " {\"id\": 'e2', \"sourceId\": '1', \"targetId\": '3'},\n", 45 | " {\"id\": 'e3', \"sourceId\": '2', \"targetId\": '3'}\n", 46 | " ]\n", 47 | "}\n", 48 | "\n", 49 | "graph = GraphBuilder(props={'data': data,\n", 50 | " \"nodeSize\": 10, \n", 51 | " \"nodeColor\": \"#7743CE\", \n", 52 | " \"edgeWidth\": 1, \n", 53 | " \"edgeColor\": \"#777777\",\n", 54 | " \"height\": 500})\n", 55 | "\n", 56 | "display(graph)" 57 | ] 58 | } 59 | ], 60 | "metadata": { 61 | "kernelspec": { 62 | "display_name": "Python 3", 63 | "language": "python", 64 | "name": "python3" 65 | }, 66 | "language_info": { 67 | "codemirror_mode": { 68 | "name": "ipython", 69 | "version": 3 70 | }, 71 | "file_extension": ".py", 72 | "mimetype": "text/x-python", 73 | "name": "python", 74 | "nbconvert_exporter": "python", 75 | "pygments_lexer": "ipython3", 76 | "version": "3.7.0" 77 | } 78 | }, 79 | "nbformat": 4, 80 | "nbformat_minor": 2 81 | } 82 | --------------------------------------------------------------------------------