Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle Null Values #106

Open
wants to merge 11 commits into
base: dev
Choose a base branch
from
25 changes: 12 additions & 13 deletions data/fruit.json
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
[
{
"label" : "apples",
"value" : 20
},
"label": "apples",
"value": 20
},
{
"label" : "bananas",
"value" : 40
"label": "bananas",
"value": 40
},
{
"label" : "pears",
"value" : 30
"label": "pears",
"value": 30
},
{
"label" : "papaya",
"value" : 50
"label": "papaya",
"value": 50
},
{
"label" : "oranges",
"value" : 70
"label": "oranges",
"value": 70
}

]
]
4 changes: 2 additions & 2 deletions data/penguins.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"culmen_length_mm": 39.1,
"culmen_depth_mm": 18.7,
"flipper_length_mm": 181,
"body_mass_g": 3750,
"body_mass_g": null,
"sex": "MALE"
},
{
Expand Down Expand Up @@ -3059,4 +3059,4 @@
"body_mass_g": 5400,
"sex": "MALE"
}
]
]
2 changes: 1 addition & 1 deletion data/portfolio.json
Original file line number Diff line number Diff line change
Expand Up @@ -895,4 +895,4 @@
"marketvalue": 93951.489511,
"value": -2.6029436385849567
}
]
]
6 changes: 3 additions & 3 deletions data/skinny_fruit.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
{
"date": "Thu Mar 01 2018 00:00:00 GMT-0500 (Eastern Standard Time)",
"fruit": "Bananas",
"value":15
"value": 15
},
{
"date": "Thu Mar 08 2018 00:00:00 GMT-0500 (Eastern Standard Time)",
Expand All @@ -67,7 +67,7 @@
{
"date": "Thu Mar 01 2018 00:00:00 GMT-0500 (Eastern Standard Time)",
"fruit": "Apricots",
"value":3
"value": 3
},
{
"date": "Thu Mar 08 2018 00:00:00 GMT-0500 (Eastern Standard Time)",
Expand All @@ -79,4 +79,4 @@
"fruit": "Apricots",
"value": 40
}
]
]
2 changes: 1 addition & 1 deletion data/unemployment.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{
"division": "Bethesda-Rockville-Frederick, MD Met Div",
"date": "2000-01-01T00:00:00.000Z",
"unemployment": 2.6
"unemployment": null
},
{
"division": "Bethesda-Rockville-Frederick, MD Met Div",
Expand Down
42 changes: 26 additions & 16 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useState, useEffect } from 'react';
import React, { useState, useEffect } from 'react';

import BarChart from './charts/BarChart/BarChart';
import AreaChart from './charts/AreaChart/AreaChart';
Expand All @@ -10,24 +10,35 @@ import { Container } from './styles/componentStyles';
import portfolio from '../data/portfolio.json';
import penguins from '../data/penguins.json';
import fruit from '../data/fruit.json';
import unemployment from '../data/unemployment.json'
import unemployment from '../data/unemployment.json';
import skinny_fruit from '../data/skinny_fruit.json';


function App() {
const [pie, setPie] = useState(fruit.sort((a, b) => a.value - b.value).slice(2))
const [bar, setBar] = useState(skinny_fruit.reverse().slice(2))
const [area, setArea] = useState(portfolio.slice(30, 60))
const [line, setLine] = useState(unemployment.slice(0, 60))
const [scatter, setScatter] = useState(penguins.slice(30, 60))
const [pie, setPie] = useState(
fruit.sort((a, b) => a.value - b.value).slice(2)
);
const [bar, setBar] = useState(skinny_fruit.reverse().slice(2));
const [area, setArea] = useState(portfolio.slice(30, 60));
const [line, setLine] = useState(unemployment.slice(0, 60));
const [scatter, setScatter] = useState(penguins.slice(30, 60));

useEffect(() => {
setTimeout(() => {setPie(fruit.sort((a, b) => a.value - b.value))}, 1000);
setTimeout(() => {setBar(skinny_fruit.reverse())}, 2000);
setTimeout(() => {setArea(portfolio.slice(0, 60))}, 4000);
setTimeout(() => {setLine(unemployment)}, 6000);
setTimeout(() => {setScatter(penguins)}, 8000);
}, [])
setTimeout(() => {
setPie(fruit.sort((a, b) => a.value - b.value));
}, 1000);
setTimeout(() => {
setBar(skinny_fruit.reverse());
}, 2000);
setTimeout(() => {
setArea(portfolio.slice(0, 60));
}, 4000);
setTimeout(() => {
setLine(unemployment);
}, 6000);
setTimeout(() => {
setScatter(penguins);
}, 8000);
}, []);
return (
<Container className="app">
<PieChart
Expand Down Expand Up @@ -74,7 +85,7 @@ function App() {
data={line}
xKey="date"
xDataType="date"
groupBy='division'
groupBy="division"
yKey="unemployment"
xAxis="bottom"
yAxis="left"
Expand All @@ -100,7 +111,6 @@ function App() {
yAxis="right"
yAxisLabel="Body Mass"
legend={'right'}

/>
</Container>
);
Expand Down
17 changes: 11 additions & 6 deletions src/charts/AreaChart/AreaChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,23 +76,28 @@ export default function AreaChart({
return (d) => d[yKey];
}, [yKey]);

const cleanData = useMemo(
() => data.filter((el) => el[xKey] !== null && el[yKey] !== null),
[data]
);

// if no xKey datatype is passed in, determine if it's Date
if (!xDataType) {
xDataType = inferXDataType(data[0], xKey);
xDataType = inferXDataType(cleanData[0], xKey);
}

// generate arr of keys. these are used to render discrete areas to be displayed
const keys = useMemo(() => {
const groupAccessor = (d: Data) => d[groupBy ?? ''];
const groups = d3.group(data, groupAccessor);
const groups = d3.group(cleanData, groupAccessor);
return groupBy ? Array.from(groups).map((group) => group[0]) : [yKey];
}, [groupBy, yKey, data]);
}, [groupBy, yKey, cleanData]);

const transData = useMemo(() => {
return groupBy
? transformSkinnyToWide(data, keys, groupBy, xKey, yKey)
: data;
}, [data, keys, groupBy, xKey, yKey]);
? transformSkinnyToWide(cleanData, keys, groupBy, xKey, yKey)
: cleanData;
}, [cleanData, keys, groupBy, xKey, yKey]);

// generate stack: an array of Series representing the x and associated y0 & y1 values for each area
const stack = d3.stack().keys(keys);
Expand Down
29 changes: 20 additions & 9 deletions src/charts/BarChart/BarChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ import {
} from '../../utils';
import { yScaleDef } from '../../functionality/yScale';
import { Label } from '../../components/Label';
import{ ThemeProvider } from 'styled-components';

import { ThemeProvider } from 'styled-components';

export default function BarChart({
theme = 'light',
Expand Down Expand Up @@ -61,6 +60,18 @@ export default function BarChart({
// Look at the data structure and declare how to access the values we'll need.
// ********************

const cleanData = useMemo(() => {
return data
.filter((d) => d[xKey] !== null)
.map((d) => {
if (d[yKey] === null) {
d[yKey] = 0;
}

return d;
});
}, [data]);

const xAccessor: (d: Data) => string = useMemo(() => {
return (d) => d[xKey];
}, []);
Expand All @@ -72,15 +83,15 @@ export default function BarChart({
// When the yKey key has been assigned to the groupBy variable we know the user didn't specify grouping
const keys: string[] = useMemo(() => {
const groupAccessor = (d: Data) => d[groupBy ?? ''];
const groups = d3.group(data, groupAccessor);
const groups = d3.group(cleanData, groupAccessor);
return groupBy ? Array.from(groups).map((group) => group[0]) : [yKey];
}, [groupBy, yKey, data]);
}, [groupBy, yKey, cleanData]);

const transData = useMemo(() => {
return groupBy
? transformSkinnyToWide(data, keys, groupBy, xKey, yKey)
: data;
}, [data, keys, groupBy, xKey, yKey]);
? transformSkinnyToWide(cleanData, keys, groupBy, xKey, yKey)
: cleanData;
}, [cleanData, keys, groupBy, xKey, yKey]);

const stack = d3.stack().keys(keys).order(d3.stackOrderAscending);

Expand Down Expand Up @@ -156,7 +167,7 @@ export default function BarChart({
.scaleBand()
.paddingInner(0.1)
.paddingOuter(0.1)
.domain(data.map(xAccessor))
.domain(transData.map(xAccessor))
.range([0, rangeMax > 40 ? rangeMax : 40]);
}, [transData, xAccessor, cWidth, margin]);

Expand Down Expand Up @@ -311,7 +322,7 @@ export default function BarChart({
</g>
)
)
: data.map((d: Data, i: number) => {
: cleanData.map((d: Data, i: number) => {
return (
// SINGLE CHART
<Rectangle
Expand Down
39 changes: 24 additions & 15 deletions src/charts/LineChart/LineChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import Tooltip from '../../components/Tooltip';

import { ThemeProvider } from 'styled-components';


export default function LineChart({
theme = 'light',
data,
Expand Down Expand Up @@ -62,8 +61,15 @@ export default function LineChart({
// Look at the data structure and declare how to access the values we'll need.
// ********************

// Null values must be removed from the dataset so as to not break our the
// Line generator function.
const cleanData = useMemo(
() => data.filter((el) => el[xKey] !== null && el[yKey] !== null),
[data]
);

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of copying this in each chart, we might want to add it to a util function and import it. @robcrock

// if no xKey datatype is passed in, determine if it's Date
let xType: 'number' | 'date' = inferXDataType(data[0], xKey);
let xType: 'number' | 'date' = inferXDataType(cleanData[0], xKey);
if (xDataType !== undefined) xType = xDataType;

const xAccessor: xAccessorFunc = useMemo(() => {
Expand All @@ -74,13 +80,7 @@ export default function LineChart({
return (d) => d[yKey];
}, []);

// Null values must be removed from the dataset so as to not break our the
// Line generator function.
const cleanData = useMemo(() => {
return data.filter((el) => el[yKey] !== null);
}, [data]);

const lineGroups: any = d3.group(data, (d) => d[groupBy ?? '']);
const lineGroups: any = d3.group(cleanData, (d) => d[groupBy ?? '']);

let keys: string[] = [];
if (groupBy !== undefined) {
Expand Down Expand Up @@ -137,12 +137,12 @@ export default function LineChart({
// ********************

const yScale = useMemo(() => {
return yScaleDef(data, yAccessor, margin, cHeight, 'line-chart');
}, [data, yAccessor, margin, cHeight]);
return yScaleDef(cleanData, yAccessor, margin, cHeight, 'line-chart');
}, [cleanData, yAccessor, margin, cHeight]);

const { xScale, xMin, xMax } = useMemo(() => {
return xScaleDef(data, xType, xAccessor, margin, cWidth, chartType);
}, [data, cWidth, margin]);
return xScaleDef(cleanData, xType, xAccessor, margin, cWidth, chartType);
}, [cleanData, cWidth, margin]);

const line: any = d3
.line()
Expand Down Expand Up @@ -192,7 +192,7 @@ export default function LineChart({

const voronoi = useMemo(() => {
return d3Voronoi(
data,
cleanData,
xScale,
yScale,
xAccessor,
Expand All @@ -201,7 +201,16 @@ export default function LineChart({
cWidth,
margin
);
}, [data, xScale, yScale, xAccessor, yAccessor, cHeight, cWidth, margin]);
}, [
cleanData,
xScale,
yScale,
xAccessor,
yAccessor,
cHeight,
cWidth,
margin,
]);
return (
<ThemeProvider theme={themes[theme]}>
<div ref={anchor} style={{ width: width, height: height }}>
Expand Down
4 changes: 4 additions & 0 deletions src/charts/PieChart/PieChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ export default function PieChart({
// STEP 1. Process data
// Look at the data structure and declare how to access the values we'll need.
// ********************
const cleanData = useMemo(
() => data.filter((el: any) => el.value !== null),
[data]
);

const keys = useMemo(() => {
const groupAccessor = (d: Data) => d[label ?? ''];
Expand Down
Loading