Skip to content

Commit

Permalink
Merge pull request #197 from conglei/conglei-add-stats
Browse files Browse the repository at this point in the history
Conglei add stats
  • Loading branch information
hshoff authored Nov 13, 2017
2 parents a590ef3 + 21ca91d commit a4def35
Show file tree
Hide file tree
Showing 24 changed files with 785 additions and 126 deletions.
4 changes: 2 additions & 2 deletions packages/vx-demo/components/gallery.js
Original file line number Diff line number Diff line change
Expand Up @@ -527,9 +527,9 @@ export default class Gallery extends React.Component {
className="details"
style={{ color: '#FFFFFF', zIndex: 1 }}
>
<div className="title">BoxPlot</div>
<div className="title">Stats Plots</div>
<div className="description">
<pre>{`<BoxPlot /> `}</pre>
<pre>{`<BoxPlot /> + <ViolinPlot /> `}</pre>
</div>
</div>
</div>
Expand Down
198 changes: 110 additions & 88 deletions packages/vx-demo/components/tiles/boxplot.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
import React from 'react';
import { Group } from '@vx/group';
import { BoxPlot } from '@vx/boxplot';
import { ViolinPlot, BoxPlot } from '@vx/stats';
import { LinearGradient } from '@vx/gradient';
import { scaleBand, scaleLinear } from '@vx/scale';
import { genBoxPlot } from '@vx/mock-data';
import { genStats } from '@vx/mock-data';
import { withTooltip, Tooltip } from '@vx/tooltip';
import { extent } from 'd3-array';
import { format } from 'd3-format';
import { PatternLines } from '@vx/pattern';

const data = genBoxPlot(5);
const data = genStats(5);
const twoDecimalFormat = format('.2f');

// accessors
const x = d => d.x;
const min = d => d.min;
const max = d => d.max;
const median = d => d.median;
const firstQuartile = d => d.firstQuartile;
const thirdQuartile = d => d.thirdQuartile;
const x = d => d.boxPlot.x;
const min = d => d.boxPlot.min;
const max = d => d.boxPlot.max;
const median = d => d.boxPlot.median;
const firstQuartile = d => d.boxPlot.firstQuartile;
const thirdQuartile = d => d.boxPlot.thirdQuartile;
const outliers = d => d.boxPlot.outliers;

export default withTooltip(
({
Expand Down Expand Up @@ -45,7 +47,7 @@ export default withTooltip(
});

const values = data.reduce(
(r, e) => r.push(e.min, e.max) && r,
(r, { boxPlot:e }) => r.push(e.min, e.max) && r,
[]
);
const minYValue = Math.min(...values);
Expand Down Expand Up @@ -75,86 +77,106 @@ export default withTooltip(
fill={`url(#boxplot)`}
rx={14}
/>
<PatternLines
id='hViolinLines'
height={3}
width={3}
stroke='#ced4da'
strokeWidth={1}
fill='rgba(0,0,0,0.3)'
orientation={['horizontal']}
/>
<Group top={40}>
{data.map((d, i) =>
<BoxPlot
key={i}
data={d}
min={yScale(min(d))}
max={yScale(max(d))}
left={xScale(x(d))}
firstQuartile={yScale(firstQuartile(d))}
thirdQuartile={yScale(thirdQuartile(d))}
median={yScale(median(d))}
boxWidth={actualyWidth}
fill="#FFFFFF"
fillOpacity={0.3}
stroke="#FFFFFF"
strokeWidth={2}
minProps={{
onMouseOver: data => event => {
showTooltip({
tooltipTop: yScale(data.data.min) + 40,
tooltipLeft: data.x2 + 5,
tooltipData: {
min: data.data.min,
name: x(d)
}
});
},
onMouseLeave: event => event => {
hideTooltip();
}
}}
maxProps={{
onMouseOver: data => event => {
showTooltip({
tooltipTop: yScale(data.data.max) + 40,
tooltipLeft: data.x2 + 5,
tooltipData: {
max: data.data.max,
name: x(d)
}
});
},
onMouseLeave: event => event => {
hideTooltip();
}
}}
boxProps={{
onMouseOver: data => event => {
showTooltip({
tooltipTop: yScale(data.data.median) + 40,
tooltipLeft: data.x2 + 5,
tooltipData: {
...data.data,
name: x(d)
}
});
},
onMouseLeave: event => event => {
hideTooltip();
}
}}
medianProps={{
style: {
stroke: 'white'
},
onMouseOver: data => event => {
showTooltip({
tooltipTop: data.median + 40,
tooltipLeft: data.x2 + 5,
tooltipData: {
median: data.data.median,
name: x(d)
}
});
},
onMouseLeave: data => event => {
hideTooltip();
}
}}
/>
<g key={i}>
<ViolinPlot
stroke='#dee2e6'
binData={d.binData}
left={xScale(x(d))}
width={actualyWidth}
valueScale={yScale}
fill="url(#hViolinLines)"
/>
<BoxPlot
data={d}
min={min(d)}
max={max(d)}
left={xScale(x(d))+0.3*actualyWidth}
firstQuartile={firstQuartile(d)}
thirdQuartile={thirdQuartile(d)}
median={median(d)}
boxWidth={actualyWidth*0.4}
fill="#FFFFFF"
fillOpacity={0.3}
stroke="#FFFFFF"
strokeWidth={2}
valueScale={yScale}
outliers={outliers(d)}
minProps={{
onMouseOver: data => event => {
showTooltip({
tooltipTop: yScale(data.data.boxPlot.min) + 40,
tooltipLeft: data.x2 + 5,
tooltipData: {
min: data.data.boxPlot.min,
name: x(d)
}
});
},
onMouseLeave: event => event => {
hideTooltip();
}
}}
maxProps={{
onMouseOver: data => event => {
showTooltip({
tooltipTop: yScale(data.data.boxPlot.max) + 40,
tooltipLeft: data.x2 + 5,
tooltipData: {
max: data.data.boxPlot.max,
name: x(d)
}
});
},
onMouseLeave: event => event => {
hideTooltip();
}
}}
boxProps={{
onMouseOver: data => event => {
showTooltip({
tooltipTop: yScale(data.data.boxPlot.median) + 40,
tooltipLeft: data.x2 + 5,
tooltipData: {
...data.data.boxPlot,
name: x(d)
}
});
},
onMouseLeave: event => event => {
hideTooltip();
}
}}
medianProps={{
style: {
stroke: 'white'
},
onMouseOver: data => event => {
showTooltip({
tooltipTop: data.median + 40,
tooltipLeft: data.x2 + 5,
tooltipData: {
median: data.data.boxPlot.median,
name: x(d)
}
});
},
onMouseLeave: data => event => {
hideTooltip();
}
}}
/>
</g>
)}
</Group>
</svg>
Expand Down
1 change: 1 addition & 0 deletions packages/vx-demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"@vx/text": "0.0.143",
"@vx/tooltip": "0.0.143",
"@vx/voronoi": "0.0.143",
"@vx/stats": "0.0.143",
"classnames": "^2.2.5",
"d3-array": "^1.1.1",
"d3-collection": "^1.0.4",
Expand Down
2 changes: 1 addition & 1 deletion packages/vx-demo/pages/boxplot.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default () => {
events={true}
margin={{ top: 80 }}
component={BoxPlot}
title="Box Plot"
title="BoxPlot With ViolinPlot"
>
{`import React from 'react';
import { Group } from '@vx/group';
Expand Down
21 changes: 0 additions & 21 deletions packages/vx-mock-data/src/generators/genBoxPlot.js

This file was deleted.

67 changes: 67 additions & 0 deletions packages/vx-mock-data/src/generators/genStats.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { randomNormal } from 'd3-random';

const random = randomNormal(4, 3);
const randomOffset = () => Math.random() * 10;
const sampleSize = 1000;

export default function genStats(number) {
const data = [];
let i;
for (i = 0; i < number; i += 1) {
const points = [];
let j;
const offset = randomOffset();
for (j = 0; j < sampleSize; j += 1) {
points.push(offset + random());
}

points.sort((a, b) => a-b);

const firstQuartile = points[Math.round(sampleSize/4)];
const thirdQuartile = points[Math.round(3 * sampleSize/4)];
const IQR = thirdQuartile - firstQuartile;

const min = firstQuartile - 1.5 * IQR;
const max = thirdQuartile + 1.5 * IQR;

const outliers = points.filter(p => p < min || p > max);
const binWidth = 2 * IQR * ((sampleSize - outliers.length) ** (-1/3));
const binNum = Math.round((max - min) / binWidth);
const actualBinWidth = (max - min) / binNum;

const bins = Array(binNum + 2).fill(0);
const values = Array(binNum + 2).fill(min);

for (let i = 1; i <= binNum; i += 1){
values[i] += actualBinWidth * (i - 0.5);
}

values[values.length-1] = max;

points.filter(p => p >= min && p <= max).forEach(p => {
bins[Math.floor((p-min)/actualBinWidth) + 1] += 1;
});

const binData = values.map((v, i) => ({
value: v,
count: bins[i],
}));

const boxPlot = {
x: `Statistics ${i}`,
min,
firstQuartile,
median: points[Math.round(sampleSize/2)],
thirdQuartile,
max,
outliers,
};


data.push({
boxPlot,
binData,
});
}
return data;
}
2 changes: 1 addition & 1 deletion packages/vx-mock-data/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export {
} from './generators/genRandomNormalPoints';
export { default as genBin } from './generators/genBin';
export { default as genBins } from './generators/genBins';
export { default as genBoxPlot } from './generators/genBoxPlot';
export { default as genStats } from './generators/genStats';
export { default as appleStock } from './mocks/appleStock';
export { default as letterFrequency } from './mocks/letterFrequency';
export { default as browserUsage } from './mocks/browserUsage';
Expand Down
13 changes: 0 additions & 13 deletions packages/vx-mock-data/test/genBoxPlot.test.js

This file was deleted.

15 changes: 15 additions & 0 deletions packages/vx-mock-data/test/genStats.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { genStats } from '../src';

describe('generators/genStats', () => {
test('it should be defined', () => {
expect(genStats).toBeDefined();
});

test('it should have boxPlot and binData', () => {
const data = genStats(2);
expect(data.length).toBeDefined();
expect(data.length).toEqual(2);
expect(data[0].boxPlot).toBeDefined();
expect(data[0].binData).toBeDefined();
});
});
Loading

0 comments on commit a4def35

Please sign in to comment.