Skip to content

Commit

Permalink
added piecewise demo chart, other minor fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
russbiggs committed Aug 8, 2024
1 parent 7d59c20 commit e9d9703
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 19 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ This site built based upon the [Observable Framework](https://observablehq.com/f

The source code for the repository is licensed under an MIT license, found at [LICENSE](./LICENSE).

All content (markdown files) in the docs directory is licenses CC BY SA, found at [LICENSE-content](./LICENSE-content).
All content (markdown files) in the docs directory is licensed CC BY SA, found at [LICENSE-content](./LICENSE-content).
2 changes: 0 additions & 2 deletions docs/data/breakpoints.csv.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ header_written=false

find . -type f -name 'breakpoints.csv' | while read csv_file; do
if [ "$header_written" = false ]; then
# Output the header to stdout
head -n 1 "$csv_file"
header_written=true
fi
# Append the rest of the CSV file, skipping the header
tail -n +2 "$csv_file"
echo
done
14 changes: 11 additions & 3 deletions docs/data/pages.json.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
#!/bin/bash

# Check if the directory is correct
if [ ! -d "../indices" ]; then
echo "Directory ../indices does not exist or is not accessible."
exit 1
fi

echo "["

first=true

for file in ../../indices/*; do
# List all files found in the ../indices directory
for file in $(find ../docs/indices/* -type f); do
echo "Found file: $file" # Debug output
if [ -f "$file" ]; then
filename=$(basename "$file")
name_without_extension="${filename%.*}"
Expand All @@ -13,8 +21,8 @@ for file in ../../indices/*; do
else
echo ","
fi
echo "\"$name_without_extension\""
echo " \"$name_without_extension\""
fi
done

echo "]"
echo "]"
13 changes: 0 additions & 13 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,6 @@ Plot.plot({

### Read more

```js
const pages = await FileAttachment('./data/pages.json').json();

```

```js
JSON.stringify(pages)
```

```js
const f = pages.map(o => `* ${o}\n`);
f
```

## References

Expand Down
82 changes: 82 additions & 0 deletions docs/methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import {piecewiseChart} from './components/piecewise-chart.js';
piecewiseChart()
```

_Note:_ Adapted from https://observablehq.com/@mbostock/pm25-to-aqi

This can be expressed as:

```tex
Expand All @@ -46,4 +48,84 @@ import {piecewiseLatexDoc} from './components/piecewise.js';

piecewiseLatexDoc('AQI')

```

The variable slope of a piecewise linear function can be demonstrated with different country AQI sub-index values for PM<sub>2.5</sub> and their varying breakpoints. For the same PM<sub>2.5</sub> concentration value different AQIs change and interpolate across their respective range at different rates, due to the different slopes in the piecewise linear function:

```js
const concentration = view(Inputs.range([0, 500], {value: 42, step: 1, label: html`PM<sub>2.5</sub> 24 hr. mean concentration`}));

```

```js
import {piecewise} from './components/calculators.js';
import {getContrast} from './utils/colors.js';
const breakpoints = await FileAttachment('./data/breakpoints.csv').csv({typed: true});
const countriesMap = await FileAttachment('./data/countries.json').json();
let pm2524hr = breakpoints.filter(o => o.pollutant == 'PM2.5' && o.averaging_period == '24');
pm2524hr = pm2524hr.map(o => { o.concentration_upper ? o.concentration_upper : o.concentration_upper = 500; return o})

```

```js
function filterData(data, value) {
function findBestMatch(objects, value) {
let matches = objects.filter(obj => {
return value >= obj.concentration_lower && (obj.concentration_upper === null || value <= obj.concentration_upper);
});

if (matches.length > 0) {
return matches[0];
}

return objects.reduce((max, obj) => {
if (obj.concentration_upper === null) {
return obj;
}
return (!max.concentration_upper || obj.concentration_upper > max.concentration_upper) ? obj : max;
});
}

let groupedData = data.reduce((acc, obj) => {
if (!acc[obj.ISO]) {
acc[obj.ISO] = [];
}
acc[obj.ISO].push(obj);
return acc;
}, {});

let results = [];

for (let iso in groupedData) {
if (groupedData.hasOwnProperty(iso)) {
let bestMatch = findBestMatch(groupedData[iso], value);
if (bestMatch) {
results.push(bestMatch);
}
}
}

return results;
}

const d = filterData(pm2524hr, concentration)
const b = d.map(o => { o.value = o['concentration_upper'] && o['category_upper'] ? piecewise(concentration, o['category_upper'], o['category_lower'], o['concentration_upper'], o['concentration_lower']) : o['category_lower']; o.name = countriesMap[o.ISO]; return o})
```

```js

Plot.plot({
height: 350,
width: width,
marginLeft: 150,
y: {label: 'Country/territory', tickPadding: 12, tickSize: 0},
x: {label: 'Index value', domain: [0,500]},
marks: [
Plot.ruleY(b, {y: "name", x: "value", stroke: "black",strokeWidth: 6}),
Plot.dot(b, {y: "name", x: "value", fill: "black", r: 12}),
Plot.ruleY(b, {y: "name", x: "value", stroke:'hex',strokeWidth: 4}),
Plot.dot(b, {y: "name", x: "value", fill: "hex", r: 11}),
Plot.text(b, {y: "name", x: "value", fill:d => getContrast(d.hex), text: "value"})
]
})
```

0 comments on commit e9d9703

Please sign in to comment.