Public
Edited
Dec 2, 2022
2 stars
Insert cell
Insert cell
Insert cell
// Colors
// #66c2a5, #fc8d62, #8da0cb, #e78ac3, #a6d854, #ffd92f, #e5c494, #b3b3b3
colorScale = ({
range: ["#fc8d62", "#8da0cb", "#66c2a5", "#e78ac3", "#a6d854", "#ffd92f", "#e5c494", "#b3b3b3"] ,
// domain: [ "Importado", "Relacionado", "En Estudio"]
domain: ["N/A","AsintomáTico","Leve", "Moderado","Grave", "Fallecido", undefined]
})
// colorScale = ({schema:"category20"})
Insert cell
new Set(data.map(d=> d.estado))
Insert cell
makeHist = (yField, brush, colorScale) => vl.layer(
vl.markBar({"tooltip": true})
.encode(
yField,
vl.x().count().title("# de Casos Reportados"),
vl.color().value("#ccc")
)
.select(brush),
vl.markBar({"tooltip": true})
.transform(vl.filter(brush))
.encode(
yField,
vl.x().count().title("# de Casos Reportados"),
colorScale || vl.color().value("#e5c494")
),
vl.markText({dx:10})
.transform(vl.filter(brush))
.encode(
yField,
vl.x().count().title("# de Casos Reportados"),
vl.text().count()
)
).width(width/3)
Insert cell
facetWidth = 100
Insert cell
dailyCasesByDepto = {
const iwidth = Math.min(650, width);

// select a point for which to provide details-on-demand
const hover = vl
.selectSingle()
.encodings('x') // limit selection to x-axis value
.on('mouseover') // select on mouseover events
.nearest(true) // select data point nearest the cursor
.empty('none'); // empty selection includes no data points

const base = await vl
.markBar({ tooltip: true })
.transform(
vl.filter("datum.fecha_inicio_sintomas")
// vl.groupby(["departamento_nom", 'fecha']).aggregate(vl.count().as('Count')),
// vl.window(vl.sum('Count')
// .as('Total Acumulado')
// )
// .groupby(["departamento_nom"])
// .sort(vl.field('fecha_inicio_sintomas'))
)
.encode(
vl
.x()
.fieldT("fecha_inicio_sintomas")
.timeUnit("yearmonthdate")
.title(null)
.axis(null),
// vl.y().fieldQ("Total Acumulado")
vl
.y()
.count()
// vl.y().fieldQ("Count")
.axis({
grid: false
})
.title(null)
// .title("# de Casos Reportados"),
);
// .config({
// // axisX: {titleLimit: 150, grid: false},
// // axis: {grid: false}
// });

const lineChart = vl
// .select(hover)
.layer(
base.markLine({ tooltip: true }),
base.select(hover),
// vl
// .markRule()
// .transform(vl.filter(hover))
// .encode(
// vl
// .opacity()
// .if(hover, vl.value(1))
// .value(0)
// )
// base.markText({dx: 0, dy: -8})
// .encode(
// vl.text().fieldQ("Total Acumulado")
// )
)

.width(facetWidth)
.height(facetWidth)
.facet(
vl
.facet()
.field("departamento_nom")
.header({
labelLimit: facetWidth,
labelFontSize: 13,
labelPadding: 0,
labelBaseline: "top",
labelColor: "#777"
})
.title(null)
.sort(vl.count().order("descending"))
)
.data(data)
.spacing(25)
.columns(Math.floor((iwidth - 30) / (facetWidth + 5)));

// return lineChart.toJSON();
// console.log("Linechart", JSON.stringify(lineChart.toJSON(),null, 2));
const chart = await lineChart
.config({
view: { stroke: null }
})
.render();

return html`
<h2>Casos Confirmados diarios por Departamento</h2>
${chart}
<div>${footer2}
<br>
<a href="https://johnguerra.co">John Alexis Guerra Gómez</a> &nbsp;&nbsp; <a href="https://twitter.com/duto_guerra"> @duto_guerra</a> &nbsp;<a href="https://twitter.com/guerravis"> @guerravis</a>
<br>
<a href="https://johnguerra.co/coronavirus">https://johnguerra.co/coronavirus</a>
<br>
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by/4.0/88x31.png" /></a>
</div>
`;
}
Insert cell
chartByDpto = {
const lineChart = await vl
.markLine()
// .data(data)
.transform(
vl.groupby(["departamento_nom", 'fecha']).aggregate(vl.count().as('Count')),
vl
.window(vl.sum('Count').as('Total Acumulado'))
.groupby(["departamento_nom"])
.sort(vl.field('fecha'))
)
.encode(
// vl.row().fieldN("departamento_nom"),
vl
.x()
.fieldT("fecha")
.timeUnit("yearmonthdate")
.title("Fecha de diagnóstico"),
vl
.y()
.fieldQ("Total Acumulado")
.title("# de Casos Reportados")
)
// .layer(
// vl.markLine({"tooltip": true}),
// vl.markCircle({"tooltip": true}),
// vl.markText({dx: 0, dy: -8})
// .encode(
// vl.text().fieldQ("Total Acumulado")
// )
// )
// .markLine({tooltip: true})
.config({
axisX: { titleLimit: 150 }
})
.width(100)
.height(100)
.facet(
vl
.facet()
.field("departamento_nom")
.header({ labelLimit: 100, labelFontSize: 14 })

.sort(vl.average("Total Acumulado").order("descending"))
)
.data(data)
.columns(Math.floor((width * .8) / 100));

// return lineChart.toJSON();
// console.log("Linechart", JSON.stringify(lineChart.toJSON(),null, 2));
const chart = await lineChart.render();

return html`
<style>* { font-family: "sans-serif"; }
select {
font-size: 18pt;
display: inline-block;
}</style>
<h2>Casos Confirmados de Coronavirus por Departamentos</h2>
${chart}
<div>${footer2}
<br>
<a href="https://johnguerra.co">John Alexis Guerra Gómez</a> &nbsp;&nbsp; <a href="https://twitter.com/duto_guerra"> @duto_guerra</a> &nbsp;<a href="https://twitter.com/guerravis"> @guerravis</a>
<br>
<a href="https://johnguerra.co/coronavirus">https://johnguerra.co/coronavirus</a>
<br>
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by/4.0/88x31.png" /></a>
</div>
`;
}
Insert cell
agesByDpto = {
const brush = vl.selectMulti().encodings('y');
const ageChart = vl
.data(data)
.transform(
vl.filter(brush),
vl.calculate("floor(datum.edad/10)*10").as("grupo_edad"),
vl.groupby(["departamento_nom", 'grupo_edad']).aggregate(vl.count().as('Count')),
vl.window(vl.sum('Count')
.as('total_count')
).frame([null, null])
.groupby(["departamento_nom"])
.sort(vl.field('grupo_edad')),
// vl.filter("datum.total_count > 10"),
vl.calculate("datum.Count/datum.total_count ").as("pct"),

// vl.density("pct").bandwidth(0.3).groupby(["departamento_nom"]),
)
.encode(
vl.y()
.fieldN("departamento_nom").axis({labelFontSize:12}),
vl.size().fieldQ("pct")
.scale({rangcoe: [0, 700]})
.legend({ format: "0.1%" }).title("% casos reportados"),
vl.color().fieldQ("pct").legend({ format: "0.1%" }).title("% casos reportados"),
vl.x().fieldO("grupo_edad").title("Edad"),
vl.detail().fieldQ("Count")
)
.layer([
vl.markCircle({tooltip: true}),
vl.markText({tooltip: true})
.encode(
vl.text().fieldQ("pct").format("0.1%").title("% casos reportados"),
vl.color().value("#302E5F"),
vl.size().value(10)
)
])
.width(width*.6)
.height(500)
.config({
// "view": {"step": 50 },
padding: { bottom : 20, top: 20 }
});
// console.log("Linechart", JSON.stringify(lineChart.toJSON(),null, 2));
const chart = await vl
.data(data)
.vconcat(
vl.hconcat(
makeHist(vl.y().fieldN("tipo").title("Tipo de Infección"),
brush,
),
makeHist(vl.y().fieldN("sexo").title("Género"),
brush),
),
vl.hconcat(makeHist(vl.y().fieldN("atenci_n").title("Tipo de Atención"),
brush)
),
ageChart
)
.render();

return html`
<style>* { font-family: "sans-serif"; }</style>
<h2>Distribución de Edades Casos Confirmados de Coronavirus por Departamentos</h2>
<div>Seleccione alguna de las barras para filtrar</div>
${chart}
<div>${footer3}
<br>
<a href="https://johnguerra.co">John Alexis Guerra Gómez</a> &nbsp;&nbsp; <a href="https://twitter.com/duto_guerra"> @duto_guerra</a> &nbsp;<a href="https://twitter.com/guerravis"> @guerravis</a>
<br>
<a href="https://johnguerra.co/coronavirus">https://johnguerra.co/coronavirus</a>
<br>
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by/4.0/88x31.png" /></a>
</div>
`;
}
Insert cell
// vl.data(data).layer(
// // vl.markBar({"tooltip": true})
// // .encode(
// // vl.x().fieldT("fecha").timeUnit("yearmonthdate"),
// // vl.y().count().title("Casos nuevos diarios"),
// // vl.color().value("#ccc"),
// // )
// // .select(brush),
// vl.markBar({"tooltip": true})
// .encode(
// vl.x().fieldT("fecha").timeUnit("yearmonthdate").title("Fecha de diagnóstico"),
// vl.y().count().title("Casos nuevos diarios"),
// vl.color().fieldN("tipo")
// .scale(({
// range: ["#eee", "#ddd", "#66c2a5", "#e78ac3", "#a6d854"] ,
// domain: [ "Importado", "Relacionado", "En Estudio"]
// })),
// vl.order().fieldN("tipo")
// ),
// vl.markText({dx: 10, dy: -8})
// .encode(
// vl.x().fieldT("fecha").timeUnit("yearmonthdate").title("Fecha de diagnóstico"),
// vl.y().count().title("Casos nuevos diarios"),
// vl.text().count()
// )
// ).width(600).render()
Insert cell
brush = vl.selectInterval()
Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
d3.nest().key(d => d.departamento_nom).key(d=> d.ciudad_de_ubicaci_n).entries(data)
Insert cell
Insert cell
footer2 = html`Según datos del <a href="https://www.datos.gov.co/Salud-y-Protecci-n-Social/Casos-positivos-de-COVID-19-en-Colombia/gt2j-8ykr">Instituto Nacional de Salud</a>. Última actualización ${fechaActualizacion}.`
Insert cell
footer3 = html`Según datos del <a href="https://www.datos.gov.co/Salud-y-Protecci-n-Social/Casos-positivos-de-COVID-19-en-Colombia/gt2j-8ykr">Instituto Nacional de Salud</a>. Última actualización ${fechaActualizacion}.`
Insert cell
fechaActualizacion = d3.timeFormat("%d/%m/%Y")(d3.max(data, d=> d.fecha))
Insert cell
esLocale = fetch("https://unpkg.com/d3-time-format@2/locale/es-MX.json").then(res => res.json())
Insert cell
url = "https://www.datos.gov.co/resource/gt2j-8ykr.csv?$ORDER=fecha_reporte_web DESC"
Insert cell
parseLong2 = d3.timeParse("%Y-%m-%d 00:00:00")
Insert cell
data = {
const generator = await loadSocrata(url, {
progressive: false,
max_pages: 2
}).next();
const data = generator.value;

const fechas = Object.keys(data[data.length - 1])
.filter(a => a.includes("fecha"));
// .concat(["fecha_inicio_sintomas"]);
data.forEach(d => {
fechas.forEach(f => {
d[f] = parseLong2(d[f]);
});

d.fecha = d.fecha_diagnostico;
});

return data;
}
Insert cell
// data = {
// // based on https://stackoverflow.com/questions/2970525/converting-any-string-into-camel-case
// const camelize = str =>
// str
// .toLowerCase()
// .replace(/(?:^\w|[A-Z]|\b\w)/g, word => word.toUpperCase());

// let data = [],
// res,
// i = 0;
// const MAX_PAGES = 100,
// ROWS_PER_PAGE = 50000;

// while (i++ < MAX_PAGES) {
// res = await fetch(
// `${url}?$limit=${ROWS_PER_PAGE}&$offset=${data.length}`
// ).then(res => res.json());
// if (!res.length) break;
// data = data.concat(res);
// // console.log("data",res, data.length, i)
// // yield data;
// }

// const parse = d3.timeParse("%d/%m/%y");
// const parseLong = d3.timeParse("%d/%m/%Y");
// const parseLong2 = d3.timeParse("%Y-%m-%dT00:00:00.000");

// const attribs = [
// "sexo",
// "atenci_n",
// "tipo",
// "estado"
// // "ciudad_de_ubicaci_n",
// // "departamento_nom",
// ];

// data.forEach((d, i) => {
// if (d.fecha_diagnostico) {
// const fechaSplited = d.fecha_diagnostico.split("/");
// if (d.fecha_diagnostico.length > 10) {
// d.fecha = parseLong2(d.fecha_diagnostico);
// }
// // Sometimes dates come with 2 year digits, sometimes 4 digits 🤦‍♂️
// else if (fechaSplited.length >= 3 && fechaSplited[2].length > 2) {
// d.fecha = parseLong(d.fecha_diagnostico);
// } else {
// d.fecha = parse(d.fecha_diagnostico);
// }
// } else {
// d.fecha = null;
// }

// d.fecha_reporte_web = parseLong2(d.fecha_reporte_web);
// d.fecha_de_notificaci_n = parseLong2(d.fecha_de_notificaci_n);
// if (d.fecha_recuperado) d.fecha_recuperado = parseLong2(d.fecha_recuperado);
// if (d.fecha_inicio_sintomas) d.fecha_inicio_sintomas = parseLong2(d.fecha_inicio_sintomas);

// d.fecha_de_muerte =
// d.fecha_de_muerte && d.fecha_de_muerte.length > 7
// ? parseLong2(d.fecha_de_muerte)
// : null;

// // // Year 1900??
// // if (d.fecha.getYear() === -1) {
// // d.fecha = undefined;
// // }

// for (let attr of attribs) {
// if (d[attr]) {
// d[attr] = camelize(d[attr].trim());
// }
// }

// d.edad = +d.edad;
// d.id_de_caso = +d.id_de_caso;
// });

// yield data;
// }
Insert cell
maxDate = data.sort((a,b) => b.fecha-a.fecha)[0].fecha
Insert cell
Insert cell
viewof departamento_nom = select({
options: ["Colombia"].concat(Array.from(new Set(data.map(d=> d.departamento_nom))).sort()),
value: "Colombia"
})
Insert cell
data[1]
Insert cell
d3 = require("d3@5")
Insert cell
import { table } from "@tmcw/tables/2"
Insert cell
import {select} from "@jashkenas/inputs"
Insert cell
import { loadSocrata } from "@john-guerra/socrata-load-multiples-pages"
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