Published
Edited
Jan 10, 2021
Importers
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
dataOfDate(new Date('2020-08-06'))
Insert cell
Insert cell
municData
Insert cell
Insert cell
Insert cell
Insert cell
accChart(currentDate)
Insert cell
Insert cell
data_municOfDateRange('新宿区', new Date('2020-08-01'), new Date('2020-08-10'))
Insert cell
data_municOfDateRange = (municipality, startDate, endDate) =>
data_munic(municipality).filter(
d =>
d.date.getTime() >= startDate.getTime() &&
d.date.getTime() <= endDate.getTime()
)
Insert cell
sparkline = (selection, date) => {
const width = 74; // 2 + 14 * 3 + 2
const height = 28;
const r = 3;

const countKey = 'hospitalCount';

const duration = 14;
const startDate = new Date(Number(date));
startDate.setDate(date.getDate() - duration + 1);

const svg = selection
.append('svg')
.datum(d => data_municOfDateRange(d.municipality, startDate, date))
.attr('viewBox', [0, 0, width, height])
.attr('width', width)
.attr('height', height)
.style('margin-bottom', `-8px`)
.each(function(d, i) {
const svg = d3.select(this);

const x = d3
.scaleTime()
.domain(d3.extent(d, d => d.date))
// .domain(d3.extent(lastDays(duration, date)))
.range([r, width - r]);

const y = d3
.scaleLinear()
.domain([0, d3.max(d, d => d[countKey])])
// .domain([0, 100])
.range([height - r, r]);
if (y.domain()[0] === 0 && y.domain()[1] === 0) {
y.domain([0, 1]);
}

const line = d3
.line()
.defined(d => !isNaN(d[countKey]))
.x(d => x(d.date))
.y(d => y(d[countKey]));

const baseline = d3
.line()
.defined(d => !isNaN(d[countKey]))
.x(d => x(d.date))
.y(d => height - r);

const area = d3
.area()
.curve(d3.curveLinear)
.x(d => x(d.date))
.y0(y(0))
.y1(d => y(d[countKey]));

const diff = d[d.length - 1][countKey] - d[0][countKey];
const lineColor =
diff > 0
? `hsl(0, 20%, 50%)`
: diff === 0
? `hsl(0, 0%, 50%)`
: `hsl(220, 20%, 50%)`;

svg
.append('path')
.attr("fill", "gray")
.attr("opacity", 0.25)
.attr("d", area);

svg
.append('path')
.attr("fill", "none")
.attr("stroke", "hsl(0, 0%, 50%)")
.attr("stroke-width", 1)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("d", baseline);

svg
.append('path')
.attr("fill", "none")
.attr("stroke", lineColor)
.attr("stroke-width", 2)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
// .attr('stroke-opacity', .75)
.attr("d", line);

svg
.selectAll('circle.outer')
.data(d => [d[0], d[d.length - 1]])
.join('circle')
.classed('outer', true)
.attr('cx', d => x(d.date))
.attr('cy', d => y(d[countKey]))
.attr('r', r)
.attr("fill", lineColor);
svg
.selectAll('circle.inner')
.data(d => [d[0], d[d.length - 1]])
.join('circle')
.classed('inner', true)
.attr('cx', d => x(d.date))
.attr('cy', d => y(d[countKey]))
.attr('r', r - 1)
.attr("fill", "#fff");

const text = svg
.append('g')
.selectAll('text')
.data(d => [d[0], d[d.length - 1]])
.join('text')
.attr('x', d => x(d.date))
.attr('y', d =>
y(d[countKey]) + 12 <= height
? y(d[countKey]) + 12
: y(d[countKey]) - 5
)
.attr('font-family', 'Roboto Condensed')
.attr('font-size', '9px')
.text(d => d[countKey])
.attr('text-anchor', (_, i) => (i === 0 ? 'start' : 'end'))
.attr("fill", "hsl(0, 0%, 0%)");
text
.clone(true)
.lower()
.attr("stroke-linejoin", "round")
.attr("stroke-opacity", 1)
.attr("stroke-width", 3)
.attr("stroke", "white");

console.log(this, d);
});

// const municipality = selection.datum().municipality;
// const data = data_municOfDateRange(municipality, startDate, date);

// console.log(selection.data());
// console.log(selection.datum());
// console.log(municipality);
// console.log(startDate, date);
// console.log(data);

// svg
// .append('circle')
// .datum(d => data_munic(d.municipality).slice(-1))
// .attr('cx', d => x(d.date))
// // .attr('cy', d => y(d.rate))
// .attr('cy', d => {
// if (d.rate <= 0) {
// return -99;
// }
// const maxRate = d3.max(data_munic(d.municipality), d => d.rate);
// y.domain(yDomain(maxRate)).nice();
// return y(d.rate);
// })
// .attr('r', r)
// // .attr('r', d => (d.rate >= 1 ? r : 0))
// .attr("fill", "hsl(0, 20%, 50%)");
}
Insert cell
Insert cell
sparkMap = (selection, date) => {
const width = 42; // 14 * 3
const height = 28;
const barWidth = width / 14;
const r = 0;
const svg = selection
.append('svg')
.attr('viewBox', [0, 0, width, height])
.attr('width', width)
.attr('height', height)
.style('margin-bottom', `-8px`);

const x = d3
.scaleTime()
// .domain(d3.extent(data_jag, d => d.date))
// .domain([dates[1], dates[dates.length - 1]])
// .domain(d3.extent(dates.slice(-14)))
// .domain(d3.extent(dates.filter(d => d.getTime() <= currentDate).slice(-14)))
.domain(d3.extent(lastDays(14, date)))
.rangeRound([0, width - r - barWidth]);

const color = d3.scaleSequential(d3.interpolateOranges).domain([0, 10]);

svg
.selectAll('rect')
.data(d => data_munic(d.municipality))
.join('rect')
.attr('x', d => x(d.date))
.attr('y', 4)
.attr('width', barWidth)
.attr('height', height - 8)
.attr('fill', d => color(diff(data_munic(d.municipality), d.date)))
.attr('stroke', 'transparent')
.attr('stroke-width', 0);
}
Insert cell
Insert cell
Insert cell
Insert cell
data_munic('港区')
Insert cell
style = html`${styleString}`
Insert cell
Insert cell
Insert cell
Insert cell
diff(data_munic('港区'), new Date('2020-03-31'))
Insert cell
diff = (data, date) => {
const prevDate = new Date(
new Date(date).setDate(new Date(date).getDate() - 1)
);
const currentCase = data.find(d => d.date.getTime() === date.getTime()).case;
const prevCase = data.find(d => d.date.getTime() === prevDate.getTime())
? data.find(d => d.date.getTime() === prevDate.getTime()).case
: -1;
if (prevCase === -1) return 0;
// return currentCase - prevCase;

const currentRate = data.find(d => d.date.getTime() === date.getTime()).rate;
const prevRate = data.find(d => d.date.getTime() === prevDate.getTime())
? data.find(d => d.date.getTime() === prevDate.getTime()).rate
: -1;
if (prevRate === -1) return 0;
return currentRate - prevRate;
}
Insert cell
Insert cell
data_munic('港区')
Insert cell
// data_munic = munic => allData.filter(d => d.municipality === munic)
data_munic = munic => municData.find(d => d.key === munic).values
Insert cell
allData.filter(d => d.date.getTime() === new Date('2020-03-31').getTime())
Insert cell
md`東京都新型コロナウイルス感染症対策本部報
https://www.bousai.metro.tokyo.lg.jp/taisaku/saigai/1007261/index.html`
Insert cell
getData(await FileAttachment("all@114.csv").text()).slice(-100)
Insert cell
municData[0].values.map(d => ({
case: d.diffCase,
accCase: d.accCase
}))
Insert cell
municData[0].values.map(d => d.accCase)
Insert cell
municData[0].values.slice(-10)
Insert cell
municData = {
const data = d3
.nest()
.key(d => d.municipality)
.entries(allData)
.map(d => ({
key: d.key,
values: d.values
.map((e, i, a) => ({
hospitalCount: e.case - e.discharged,
diffCase: a[i - 1] ? e.case - a[i - 1].case : e.case,
diffRate: a[i - 1]
? ((e.case - a[i - 1].case) / e.population) * 100000
: (e.case / e.population) * 100000,
accumulationZero: 0,
...e
}))
.map((e, i, a) => {
e.accumulationZero = a[i - 1]
? e.diffCase > 0
? 0
: a[i - 1].accumulationZero + 1
: 0;
return e;
})
.map((e, i, a) => {
e.accCase = Math.max(
0,
d3.sum(a.slice(Math.max(0, i - 6), i + 1), f => f.diffCase)
);
return e;
})
.map((e, i, a) => {
e.accRate = (e.accCase / e.population) * 100000;
return e;
})
}));

// add islands
const islandsData = data.filter(d => d.values[0].island === 1);
data.push(totalData({ name: '島嶼部', data: islandsData }));

const tokyoTotalData = data.filter(d => /[区市町村]$/.test(d.key));
data.push(totalData({ name: '都内計', data: tokyoTotalData }));

const allTotalData = data.filter(
// d => /[区市町村]$/.test(d.key) || d.key === '都外' || d.key === '調査中'
d => d.key === '都内計' || d.key === '都外' || d.key === '調査中'
);
data.push(totalData({ name: '総計', data: allTotalData }));
data
.find(d => d.key === '総計')
.values.map(d => {
d.diffRate = Infinity;
d.accRate = Infinity;
d.population = null;
});

return data;
}
Insert cell
totalData = ({ name, data }) => {
const dataValues = data.map(d => d.values).flat();
const dataValuesByDate = d3
.nest()
.key(d => d.date)
.entries(dataValues);
const totalData = {
key: name,
values: dataValuesByDate.map(d =>
d.values.reduce(
(acc, cur) => {
acc.diffCase += cur.diffCase;
acc.accCase += cur.accCase;
cur.population !== null
? (acc.population += cur.population)
: (acc.population = null);
acc.date = cur.date;
return acc;
},
{
island: 0,
municipality: name,
diffCase: 0,
accCase: 0,
population: 0
}
)
)
};
totalData.values.forEach(d => {
d.hospitalCount = d.case - d.discharged;
d.diffRate = (d.diffCase / d.population) * 100000;
d.accRate = (d.accCase / d.population) * 100000;
});
return totalData;
}
Insert cell
Insert cell
d3
.nest()
.key(d => d.municipality)
.entries(allData)
Insert cell
municipalities = d3
.nest()
.key(d => d.municipality)
.entries(allData)
.map(d => ({
name: d.key,
island: isIsland(d.values[0].id) ? 1 : 0
}))
Insert cell
isIsland = id => id >= 13361 && id <= 13421
Insert cell
allData = getData(
await d3.text(
'https://raw.githubusercontent.com/sugi2000/covid-19-data/main/tokyo-munic-daily.csv'
)
)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
tokyoPatients = patients
.filter(d => d['患者_居住地'] === '都内')
.sort((a, b) => (a.退院済フラグ > b.退院済フラグ ? 1 : -1))
Insert cell
Insert cell
Insert cell
sexData = d3.csvParse(`男,女\r6805301,7029624`)
Insert cell
md`平成27年国勢調査 東京都区市町村町丁別報告
https://www.toukei.metro.tokyo.lg.jp/kokusei/2015/kd-15index.htm`
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more