Published
Edited
Apr 12, 2021
1 fork
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
width = 660
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// Performing data transformation by changing the key names and parsing dates and numbers (parseDate funtion at the end of the document)
data = {
const data = await d3.csv("https://data.civio.es/quiencobralaobra/contratacion-menores/data-complete-services.csv", ({feed_id, budgeted_tax_exclusive_amount, awarded_tax_exclusive_amount, tender_award_date}) => ({id: feed_id, budgetedTax: +budgeted_tax_exclusive_amount, date: parseDate(tender_award_date)}))
data.x = "Periodo de tiempo registrado";
data.y = "Importes de los contratos menores $$";
return data;
}
Insert cell
Insert cell
dataFiltered = {
const cutoffDate = new Date(2019,7,1); // Cuttoff date (01/08/2019)
const dataFiltered = data
.filter(d => (d.budgetedTax <= 19000) && (d.budgetedTax >= taxDomainSelector))
.filter(d => d.date < cutoffDate)
.slice().sort((a,b) => d3.ascending(a.date, b.date));
return dataFiltered;
}
Insert cell
Insert cell
allDateDataDomain = d3.extent(dataFiltered, d => d.date)
Insert cell
allDateDataDomainNice = {
// return [new Date(2018,0,1), new Date(2019,7,1)]
return [new Date(2018,0,1), new Date(2019,6,31)]

}
Insert cell
allTaxesDomain = d3.extent(dataFiltered, d => d[taxSelector])
Insert cell
Insert cell
xTime = d3.scaleTime() // Continuous time scale
// .domain([dateDataDomain[0], d3.timeMonth.ceil(dateDataDomain[1])+1])
// .domain(dateDataDomain).nice() // To get nice start and end values "rounding" the domain
.domain(allDateDataDomainNice)
.range([margin.left, width - margin.right])
Insert cell
yTaxes = d3.scaleLinear()
.domain(allTaxesDomain)
.range([height - margin.bottom, margin.top])
Insert cell
Insert cell
// Custom function to pack the data the way we want
histogramTime = d3.histogram()
.value(d => d.date) // The vector of value (TIME!)
.domain(xTime.domain())
// .thresholds(xTime.ticks(detailBinsTime)) // Number of bins (user choice via slider)
// .thresholds(xTime.ticks(19))
.thresholds(xTime.ticks(d3[detailBinsTimeIntervals]))
//.theresholds(xTime.ticks(detailBinsTimeIntervals))
Insert cell
// Apply the histogram function to our data to get the bins
binsTime = histogramTime(dataFiltered)
Insert cell
binsTime[0].x0
Insert cell
// Custom function to pack the data the way we want
histogramTaxes = d3.histogram()
.value(d => d[taxSelector]) // The vector of value (TAXES: user choice via selector)
.domain(yTaxes.domain())
.thresholds(yTaxes.ticks(detailBinsTaxes)); // Number of bins (user choice via slider)
Insert cell
// Apply the histogram function to each item of our packed data to get the bins on a second level
binsTaxes = binsTime
.map(d => histogramTaxes(d))
Insert cell
binsTaxes
.forEach((d,i) => {
d.x0 = binsTime[i].x0;
d.x1 = binsTime[i].x1;
})
Insert cell
//binsTaxes
//.forEach((d,i) => d.x1 = binsTime[i].x1)
Insert cell
binsTaxes
Insert cell
Insert cell
xAxisTimeMonths = g => g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(xTime)
// .tickFormat(d3.timeFormat('%b %Y'))
.tickFormat(d3.timeFormat('%b'))
// .tickFormat(d => d <= d3.timeYear(d) ? d.getFullYear() : d.getMonth())
//.ticks(d3.timeMonth.every(1))
.ticks(d3.timeMonth)
)
.call(g => g.select(".domain").remove())
.call(g => g.append("text")
.attr("x", width - margin.right)
.attr("y", 35)
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", "end")
.text(data.x))
Insert cell
// Mark the years!
xAxisTimeYears = g => g
.attr("transform", `translate(0,${height - margin.bottom + 25})`)
.call(d3.axisBottom(xTime)
.ticks(d3.timeYear)
.tickSize(0))
.call(g => g.select(".domain")
.remove())
.call(g => g.selectAll("g text")
.attr("font-weight", "bold")
.attr("font-size", "12px"))
Insert cell
yAxisTaxes = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(yTaxes)
.tickValues([3000, 6000, 9000, 12000, 15000, 18000]) // Custom tick spacing
)
.call(g => g.select(".domain").remove())
// .call(g => g.select(".tick:last-of-type text").clone()
.call(g => g.append("text")
// .attr("x", 5)
// .attr("y", -60)
.attr("x", -margin.left)
.attr("y", -margin.top)
.attr("fill", "#000")
.attr("text-anchor", "end")
.attr("transform", "rotate(-90)")
.attr("font-weight", "bold")
.text(data.y))
Insert cell
Insert cell
colorScale = d3.scaleSequential()
.domain([0, colorLevel]) // Custom cutoff
// .domain([0, d3.max(binsTimeMultiple.flat(), d => d.length)]).nice() // Aquí los histogramas según se empaquete
.interpolator(d3["interpolate" + colorScheme])
Insert cell
d3.max(binsTaxes.flat(), d => d.length)
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
Insert cell
Insert cell
Insert cell
infoItems = itemId => dataFiltered.find(element => element.id === itemId);
Insert cell
infoItems("3374552")
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
fisheye
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
symbols = svg`<?xml version="1.0" encoding="UTF-8"?>
<svg id="Positivos" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><style>.cls-1{fill:#56ffa9;}.cls-2,.cls-3,.cls-4,.cls-5{fill:none;stroke-linecap:round;stroke-linejoin:round;}.cls-2,.cls-3,.cls-4{stroke:#002a6d;}.cls-2,.cls-3,.cls-5{stroke-width:3px;}.cls-3{stroke-dasharray:0 6.25;}.cls-4{stroke-width:3.16px;}.cls-5{stroke:#ffed6a;}</style></defs><title>Contratación</title><circle class="cls-1" cx="31.25" cy="71.87" r="9.38"/><line class="cls-2" x1="28.13" y1="25" x2="65.63" y2="25"/><line class="cls-2" x1="28.13" y1="37.5" x2="65.63" y2="37.5"/><line class="cls-2" x1="28.13" y1="50" x2="28.13" y2="50"/><line class="cls-3" x1="34.38" y1="50" x2="62.5" y2="50"/><line class="cls-2" x1="65.63" y1="50" x2="65.63" y2="50"/><polyline class="cls-4" points="34.38 87.5 78.13 87.5 78.13 12.5 71.18 12.5"/><polyline class="cls-4" points="71.18 12.5 15.63 12.5 15.63 87.5 34.38 87.5"/><polyline class="cls-2" points="46.88 75 53.13 68.75 53.13 75 65.63 75"/><line class="cls-5" x1="84.38" y1="25" x2="84.38" y2="62.5"/></svg>
`

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