Merge pull request #458 from quantified-uncertainty/real-log-scales
Real log scales
This commit is contained in:
		
						commit
						202cb80ebf
					
				|  | @ -1,16 +1,18 @@ | ||||||
| import * as React from "react"; | import * as React from "react"; | ||||||
| import _ from "lodash"; | import _ from "lodash"; | ||||||
| import type { Spec } from "vega"; |  | ||||||
| import type { Distribution } from "@quri/squiggle-lang"; | import type { Distribution } from "@quri/squiggle-lang"; | ||||||
| import { distributionErrorToString } from "@quri/squiggle-lang"; | import { distributionErrorToString } from "@quri/squiggle-lang"; | ||||||
| import { createClassFromSpec } from "react-vega"; | import { Vega, VisualizationSpec } from "react-vega"; | ||||||
| import * as chartSpecification from "../vega-specs/spec-distributions.json"; | import * as chartSpecification from "../vega-specs/spec-distributions.json"; | ||||||
| import { ErrorBox } from "./ErrorBox"; | import { ErrorBox } from "./ErrorBox"; | ||||||
| import { useSize } from "react-use"; | import { useSize } from "react-use"; | ||||||
| 
 | import { | ||||||
| let SquiggleVegaChart = createClassFromSpec({ |   linearXScale, | ||||||
|   spec: chartSpecification as Spec, |   logXScale, | ||||||
| }); |   linearYScale, | ||||||
|  |   expYScale, | ||||||
|  | } from "./DistributionVegaScales"; | ||||||
|  | import styled from "styled-components"; | ||||||
| 
 | 
 | ||||||
| type DistributionChartProps = { | type DistributionChartProps = { | ||||||
|   distribution: Distribution; |   distribution: Distribution; | ||||||
|  | @ -23,13 +25,24 @@ export const DistributionChart: React.FC<DistributionChartProps> = ({ | ||||||
|   height, |   height, | ||||||
|   width, |   width, | ||||||
| }: DistributionChartProps) => { | }: DistributionChartProps) => { | ||||||
|  |   let [isLogX, setLogX] = React.useState(false); | ||||||
|  |   let [isExpY, setExpY] = React.useState(false); | ||||||
|  |   let shape = distribution.pointSet(); | ||||||
|   const [sized, _] = useSize((size) => { |   const [sized, _] = useSize((size) => { | ||||||
|     let shape = distribution.pointSet(); |     var disableLog = false; | ||||||
|     let widthProp = width !== undefined ? width - 20 : size.width - 10; |  | ||||||
|     if (shape.tag === "Ok") { |     if (shape.tag === "Ok") { | ||||||
|       return ( |       let massBelow0 = | ||||||
|  |         shape.value.continuous.some((x) => x.x <= 0) || | ||||||
|  |         shape.value.discrete.some((x) => x.x <= 0); | ||||||
|  |       if (massBelow0) { | ||||||
|  |         disableLog = true; | ||||||
|  |       } | ||||||
|  |       let spec = buildVegaSpec(isLogX, isExpY); | ||||||
|  |       let widthProp = width ? width - 20 : size.width - 10; | ||||||
|  |       var result = ( | ||||||
|         <div> |         <div> | ||||||
|           <SquiggleVegaChart |           <Vega | ||||||
|  |             spec={spec} | ||||||
|             data={{ con: shape.value.continuous, dis: shape.value.discrete }} |             data={{ con: shape.value.continuous, dis: shape.value.discrete }} | ||||||
|             width={widthProp} |             width={widthProp} | ||||||
|             height={height} |             height={height} | ||||||
|  | @ -38,14 +51,75 @@ export const DistributionChart: React.FC<DistributionChartProps> = ({ | ||||||
|         </div> |         </div> | ||||||
|       ); |       ); | ||||||
|     } else { |     } else { | ||||||
|       return ( |       var result = ( | ||||||
|         <div> |         <ErrorBox heading="Distribution Error"> | ||||||
|           <ErrorBox heading="Distribution Error"> |           {distributionErrorToString(shape.value)} | ||||||
|             {distributionErrorToString(shape.value)} |         </ErrorBox> | ||||||
|           </ErrorBox> |  | ||||||
|         </div> |  | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|  |     return ( | ||||||
|  |       <> | ||||||
|  |         {result} | ||||||
|  |         <div> | ||||||
|  |           {disableLog ? ( | ||||||
|  |             <CheckBox | ||||||
|  |               label="Log X scale" | ||||||
|  |               value={isLogX} | ||||||
|  |               onChange={setLogX} | ||||||
|  |               disabled={true} | ||||||
|  |               tooltip={ | ||||||
|  |                 "Your distribution has mass lower than or equal to 0. Log only works on strictly positive values." | ||||||
|  |               } | ||||||
|  |             /> | ||||||
|  |           ) : ( | ||||||
|  |             <CheckBox label="Log X scale" value={isLogX} onChange={setLogX} /> | ||||||
|  |           )} | ||||||
|  |           <CheckBox label="Exp Y scale" value={isExpY} onChange={setExpY} /> | ||||||
|  |         </div> | ||||||
|  |       </> | ||||||
|  |     ); | ||||||
|   }); |   }); | ||||||
|   return sized; |   return sized; | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
|  | function buildVegaSpec(isLogX: boolean, isExpY: boolean): VisualizationSpec { | ||||||
|  |   return { | ||||||
|  |     ...chartSpecification, | ||||||
|  |     scales: [ | ||||||
|  |       isLogX ? logXScale : linearXScale, | ||||||
|  |       isExpY ? expYScale : linearYScale, | ||||||
|  |     ], | ||||||
|  |   } as VisualizationSpec; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | interface CheckBoxProps { | ||||||
|  |   label: string; | ||||||
|  |   onChange: (x: boolean) => void; | ||||||
|  |   value: boolean; | ||||||
|  |   disabled?: boolean; | ||||||
|  |   tooltip?: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const Label = styled.label<{ disabled: boolean }>` | ||||||
|  |   ${(props) => props.disabled && "color: #999;"} | ||||||
|  | `;
 | ||||||
|  | 
 | ||||||
|  | export const CheckBox = ({ | ||||||
|  |   label, | ||||||
|  |   onChange, | ||||||
|  |   value, | ||||||
|  |   disabled = false, | ||||||
|  |   tooltip, | ||||||
|  | }: CheckBoxProps) => { | ||||||
|  |   return ( | ||||||
|  |     <span title={tooltip}> | ||||||
|  |       <input | ||||||
|  |         type="checkbox" | ||||||
|  |         value={value + ""} | ||||||
|  |         onChange={() => onChange(!value)} | ||||||
|  |         disabled={disabled} | ||||||
|  |       /> | ||||||
|  |       <Label disabled={disabled}>{label}</Label> | ||||||
|  |     </span> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  |  | ||||||
							
								
								
									
										80
									
								
								packages/components/src/components/DistributionVegaScales.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								packages/components/src/components/DistributionVegaScales.ts
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,80 @@ | ||||||
|  | import type { LogScale, LinearScale, PowScale } from "vega"; | ||||||
|  | export let linearXScale: LinearScale = { | ||||||
|  |   name: "xscale", | ||||||
|  |   type: "linear", | ||||||
|  |   range: "width", | ||||||
|  |   zero: false, | ||||||
|  |   nice: false, | ||||||
|  |   domain: { | ||||||
|  |     fields: [ | ||||||
|  |       { | ||||||
|  |         data: "con", | ||||||
|  |         field: "x", | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         data: "dis", | ||||||
|  |         field: "x", | ||||||
|  |       }, | ||||||
|  |     ], | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | export let linearYScale: LinearScale = { | ||||||
|  |   name: "yscale", | ||||||
|  |   type: "linear", | ||||||
|  |   range: "height", | ||||||
|  |   zero: true, | ||||||
|  |   domain: { | ||||||
|  |     fields: [ | ||||||
|  |       { | ||||||
|  |         data: "con", | ||||||
|  |         field: "y", | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         data: "dis", | ||||||
|  |         field: "y", | ||||||
|  |       }, | ||||||
|  |     ], | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export let logXScale: LogScale = { | ||||||
|  |   name: "xscale", | ||||||
|  |   type: "log", | ||||||
|  |   range: "width", | ||||||
|  |   zero: false, | ||||||
|  |   base: 10, | ||||||
|  |   nice: false, | ||||||
|  |   domain: { | ||||||
|  |     fields: [ | ||||||
|  |       { | ||||||
|  |         data: "con", | ||||||
|  |         field: "x", | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         data: "dis", | ||||||
|  |         field: "x", | ||||||
|  |       }, | ||||||
|  |     ], | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export let expYScale: PowScale = { | ||||||
|  |   name: "yscale", | ||||||
|  |   type: "pow", | ||||||
|  |   exponent: 0.1, | ||||||
|  |   range: "height", | ||||||
|  |   zero: true, | ||||||
|  |   nice: false, | ||||||
|  |   domain: { | ||||||
|  |     fields: [ | ||||||
|  |       { | ||||||
|  |         data: "con", | ||||||
|  |         field: "y", | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         data: "dis", | ||||||
|  |         field: "y", | ||||||
|  |       }, | ||||||
|  |     ], | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | @ -3,7 +3,6 @@ | ||||||
|   "description": "A basic area chart example", |   "description": "A basic area chart example", | ||||||
|   "width": 500, |   "width": 500, | ||||||
|   "height": 100, |   "height": 100, | ||||||
|   "autosize": "fit", |  | ||||||
|   "padding": 5, |   "padding": 5, | ||||||
|   "data": [ |   "data": [ | ||||||
|     { |     { | ||||||
|  | @ -13,72 +12,8 @@ | ||||||
|       "name": "dis" |       "name": "dis" | ||||||
|     } |     } | ||||||
|   ], |   ], | ||||||
|   "signals": [ |   "signals": [], | ||||||
|     { |   "scales": [], | ||||||
|       "name": "xscale", |  | ||||||
|       "description": "The transform of the x scale", |  | ||||||
|       "value": false, |  | ||||||
|       "bind": { |  | ||||||
|         "input": "checkbox", |  | ||||||
|         "name": "log x scale" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "name": "yscale", |  | ||||||
|       "description": "The transform of the y scale", |  | ||||||
|       "value": false, |  | ||||||
|       "bind": { |  | ||||||
|         "input": "checkbox", |  | ||||||
|         "name": "log y scale" |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   ], |  | ||||||
|   "scales": [ |  | ||||||
|     { |  | ||||||
|       "name": "xscale", |  | ||||||
|       "type": "pow", |  | ||||||
|       "exponent": { |  | ||||||
|         "signal": "xscale ? 0.1 : 1" |  | ||||||
|       }, |  | ||||||
|       "range": "width", |  | ||||||
|       "zero": false, |  | ||||||
|       "nice": false, |  | ||||||
|       "domain": { |  | ||||||
|         "fields": [ |  | ||||||
|           { |  | ||||||
|             "data": "con", |  | ||||||
|             "field": "x" |  | ||||||
|           }, |  | ||||||
|           { |  | ||||||
|             "data": "dis", |  | ||||||
|             "field": "x" |  | ||||||
|           } |  | ||||||
|         ] |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "name": "yscale", |  | ||||||
|       "type": "pow", |  | ||||||
|       "exponent": { |  | ||||||
|         "signal": "yscale ? 0.1 : 1" |  | ||||||
|       }, |  | ||||||
|       "range": "height", |  | ||||||
|       "nice": true, |  | ||||||
|       "zero": true, |  | ||||||
|       "domain": { |  | ||||||
|         "fields": [ |  | ||||||
|           { |  | ||||||
|             "data": "con", |  | ||||||
|             "field": "y" |  | ||||||
|           }, |  | ||||||
|           { |  | ||||||
|             "data": "dis", |  | ||||||
|             "field": "y" |  | ||||||
|           } |  | ||||||
|         ] |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   ], |  | ||||||
|   "axes": [ |   "axes": [ | ||||||
|     { |     { | ||||||
|       "orient": "bottom", |       "orient": "bottom", | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ export { | ||||||
|   makeSampleSetDist, |   makeSampleSetDist, | ||||||
|   errorValueToString, |   errorValueToString, | ||||||
|   distributionErrorToString, |   distributionErrorToString, | ||||||
|  |   distributionError, | ||||||
| } from "../rescript/TypescriptInterface.gen"; | } from "../rescript/TypescriptInterface.gen"; | ||||||
| export type { | export type { | ||||||
|   samplingParams, |   samplingParams, | ||||||
|  | @ -26,9 +27,9 @@ import { | ||||||
|   convertRawToTypescript, |   convertRawToTypescript, | ||||||
| } from "./rescript_interop"; | } from "./rescript_interop"; | ||||||
| import { result, resultMap, tag, tagged } from "./types"; | import { result, resultMap, tag, tagged } from "./types"; | ||||||
| import { Distribution } from "./distribution"; | import { Distribution, shape } from "./distribution"; | ||||||
| 
 | 
 | ||||||
| export { Distribution, squiggleExpression, result, resultMap }; | export { Distribution, squiggleExpression, result, resultMap, shape }; | ||||||
| 
 | 
 | ||||||
| export let defaultSamplingInputs: samplingParams = { | export let defaultSamplingInputs: samplingParams = { | ||||||
|   sampleCount: 10000, |   sampleCount: 10000, | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user