Public
Edited
Oct 14, 2024
1 fork
Importers
3 stars
Insert cell
Insert cell
Insert cell
Insert cell
range_interval_a
Insert cell
range_interval_b
Insert cell
intervals_intersection(range_interval_a, range_interval_b).concat("A ∩ B")
Insert cell
intervals_intersection([2, 7], [3, 8])
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
sample_precede = [[0, 3], [4, 7]]
Insert cell
Insert cell
sample_meet = [[0, 4], [4, 7]]
Insert cell
intervals_union(sample_meet[0], sample_meet[1])
Insert cell
sample_meet
Insert cell
Insert cell
sample_equal = [[0, 3], [0, 3]]
Insert cell
Insert cell
sample_overlap = [[1, 4], [3, 5]]
Insert cell
Insert cell
sample_start = [[1, 3], [1, 5]]
Insert cell
Insert cell
sample_end = [[3, 5], [1, 5]]
Insert cell
Insert cell
sample_during = [[3, 4], [1, 5]]
Insert cell
Insert cell
samples = ({
"intervals": intervals,
"sample_precede": sample_precede,
"sample_meet": sample_meet,
"sample_equal": sample_equal,
"sample_overlap": sample_overlap,
"sample_start": sample_start,
"sample_end": sample_end,
"sample_during": sample_during
})
Insert cell
intervals = [[1, 3], [4, 6]]
Insert cell
Insert cell
duration = (interval) => {
return interval.length == 0 ? 0: interval[1] - interval[0];
}
Insert cell
equals = (a, b) => {
return a.length === b.length &&
a.every((v, i) => v === b[i])
}
Insert cell
extent = (intervals) => {
var start = d3.min(intervals.map(d => d[0]));
var end = d3.max(intervals.map(d => d[1]));
return [start, end]
}
Insert cell
extent([[1,2], [3,4]])
Insert cell
longest([[1,2], [3,5]])
Insert cell
longest = (intervals) => {
let longest = [0, 1]
intervals.forEach(interval => {
if (interval[1] - interval[0] > longest[1] - longest[0]) {
longest = interval;
}
});
return longest;
}
Insert cell
shortest([[1,2], [3,5]])
Insert cell
shortest = (intervals) => {
let shortest = [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]
intervals.forEach(interval => {
if (interval[1] - interval[0] < shortest[1] - shortest[0]) {
shortest = interval;
}
});
return shortest;
}
Insert cell
intervals_union = (interval1, interval2) => {
var start = Math.min(interval1[0], interval2[0]);
var end = Math.max(interval1[1], interval2[1]);
return [start, end];
}
Insert cell
intervals_complement_between = (interval1, interval2) => {
return [interval1[1], interval2[0]];
}
Insert cell
calculate_complement = (interval, boundaries = [0, 10]) => {
return [[boundaries[0], interval[0]], [interval[1], boundaries[1]]];
}
Insert cell
intervals_intersection([1, 2], [3, 4])
Insert cell
intervals_intersection = (interval1, interval2) => {
var start = Math.max(interval1[0], interval2[0]);
var end = Math.min(interval1[1], interval2[1]);
if (start <= end) {
return [start, end];
} else {
return [];
}
}
Insert cell
intervals_permutations = (intervals) => {
let permutations = [];
function permute(arr, m = []) {
if (arr.length === 0) {
permutations.push(m)
} else {
for (let i = 0; i < arr.length; i++) {
let curr = arr.slice();
let next = curr.splice(i, 1);
permute(curr.slice(), m.concat(next))
}
}
}
permute(intervals)
return permutations;
}
Insert cell
function detectSimilarIntervals(intervals) {
let similarIntervals = [];
for (let i = 0; i < intervals.length; i++) {
for (let j = i + 1; j < intervals.length; j++) {
if (intervalsEqual(intervals[i], intervals[j]) == true) {
similarIntervals = similarIntervals.concat([intervals[i]]);
break;
}
}
}
return similarIntervals;
}
Insert cell
function detectOverlappingIntervals(intervals) {
let overlappingIntervals = [];
for (let i = 0; i < intervals.length; i++) {
for (let j = i + 1; j < intervals.length; j++) {
if (intervals[i][1] > intervals[j][0] && intervals[i][0] < intervals[j][1]) {
overlappingIntervals.push([intervals[i], intervals[j]]);
break;
}
}
}
return overlappingIntervals;
}
Insert cell
function sortIntervalsByStart(intervals) {
intervals.sort((a, b) => {
return a[0] - b[0];
});
return intervals;
}
Insert cell
function intervalsEqual(interval1, interval2) {
return interval1[0] === interval2[0] && interval1[1] === interval2[1];
}
Insert cell
function sortIntervalsByEnd(intervals) {
intervals.sort((a, b) => {
return a[1] - b[1];
});
return intervals;
}
Insert cell
function areIntervalListsEqual(list1, list2) {
if (list1.length !== list2.length) {
return false;
}
for (let i = 0; i < list1.length; i++) {
if (list1[i][0] !== list2[i][0] || list1[i][1] !== list2[i][1]) {
return false;
}
}
return true;
}
Insert cell
countOverlaps(sample_during)
Insert cell
function countOverlaps(intervals) {
var start = Math.min(...intervals.map(d => d[0]));
var end = Math.max(...intervals.map(d => d[1]));
let counts = d3.range(0, end-start).map(_ => 1)
for (let i = 0; i < intervals.length; i++) {
for (let j = 0; j < intervals.length; j++) {
if (i === j) continue;
let overlapStart = Math.max(intervals[i][0], intervals[j][0]);
let overlapEnd = Math.min(intervals[i][1], intervals[j][1]);
if (overlapStart < overlapEnd) {
for (let k = overlapStart; k < overlapEnd; k++) {
counts[k]++;
}
}
}
}
return counts;
}
Insert cell
mergeOverlaps(sample_overlap)
Insert cell
//mergeOverlaps(sample_meet)
Insert cell
function mergeOverlaps(intervals) {
if(intervals.length <= 1) return intervals;
let result = [intervals[0]];
let current = result[0];

for(let i = 1; i< intervals.length; i++) {
const next = intervals[i]
if(current[1] >= next[0]) {
current[1] = Math.max(current[1], next[1]);
} else {
current = next;
result.push(current);
}
}
return result;
}
Insert cell
function countAllInterval(intervals) {
var start = Math.min(...intervals.map(d => d[0]));
var end = Math.max(...intervals.map(d => d[1]));
let counts = d3.range(start, end-start).map(_ => 1)
for (let i = start; i <= end-start; i++) {
counts[i] = countInterval(i, intervals)
}
return counts;
}
Insert cell
function convertIntervals(intervals) {
let counts = [];
let maxEnd = Math.max(...intervals.map(interval => interval[1]));
for (let i = 0; i <= maxEnd; i++) {
let count = 0;
for (let j = 0; j < intervals.length; j++) {
if (intervals[j][0] <= i && intervals[j][1] > i) {
count++;
}
}
counts.push(count);
}
return counts;
}
Insert cell
function countInterval(value, intervals) {
let count = 0;
for (let i = 0; i < intervals.length; i++) {
if (value >= intervals[i][0] && value <= intervals[i][1]) {
count++;
}
}
return count;
}
Insert cell
Insert cell
Insert cell
viewof sample = Inputs.select(Object.keys(samples), {value: "intervals"})
Insert cell
viewof order = Inputs.radio(["default", "aligned"], {value: "default"})
Insert cell
renderIntervalsPlot([...sample_during, ...normalizeIntervals(sample_during), ...sidebysideIntervals(sample_during)])
Insert cell
normalizeIntervals(intervals)
Insert cell
// align intervals on their middle
function normalizeIntervals(intervals) {
let normalizedIntervals = [];
for (let interval of intervals) {
let min = interval[0];
let max = interval[1];
let midpoint = (min + max) / 2;
normalizedIntervals.push([min - midpoint, max - midpoint]);
}
return normalizedIntervals;
}
Insert cell
Plot.barX(sidebysideIntervals([[1, 2], [4, 5], [5, 7]]), {x1: d => d[0], x2: d => d[1], fill: (d, i) => i}).plot()
Insert cell
sidebysideIntervals([[1, 2], [4, 5], [5, 7]])
Insert cell
function sidebysideIntervals(intervals) {
let result = [];
let currentStart = 0;

intervals.sort((a, b) => a[0] - b[0]);

for (let i = 0; i < intervals.length; i++) {
let interval = intervals[i];
let start = interval[0];
let stop = interval[1];

result.push([currentStart, currentStart + stop - start]);
currentStart = currentStart + stop - start;
}

return result;
}
Insert cell
cumul(convertToIntervals(d3.range(10)))
Insert cell
function cumul(intervals) {
return intervals.map(duration).reduce((d, v, i) => [...d, v + (d[i-1] || 0)], [])
}
Insert cell
renderIntervals(convertToIntervals(d3.range(10)))
Insert cell
renderIntervals(convertToIntervals(d3.range(10)))
Insert cell
sparkline(convertToIntervals(d3.range(10)), 100, 100)
Insert cell
Insert cell
Insert cell
import {terms} from "@berzeg/presidents-and-presidential-terms-us"
Insert cell
Insert cell
Insert cell
Plot.barX(terms_intervals, {x1: d => d[0], x2: d => d[1], fill: (d, i) => i, y: (d,i) => i % 10}).plot()
Insert cell
detectOverlappingIntervals(terms_intervals).map(d => d[0])
Insert cell
areIntervalListsEqual(
detectSimilarIntervals(terms_intervals),
detectOverlappingIntervals(terms_intervals).map(d => d[0])
)
Insert cell
Insert cell
Insert cell
function renderIntervals(intervals, startCode = 65) {
let output = "\n";
let current = 0;
let i = 0;
for (const interval of intervals) {
while (current < interval[0]) {
output += " ";
current++;
}
while (current <= interval[1]) {
output += String.fromCharCode(startCode + i);
current++;
}
output += "\n"
current = 0;
i++
}
return output
}
Insert cell
## Using Plot
Insert cell
renderIntervalsPlot(intervals)
Insert cell
function renderIntervalsPlot(intervals, options = {x_domain: null}) {
return Plot.plot({
marginLeft: 50,
color: {
type: "categorical",
legend: false
},
x: {
axis: "top",
grid: true,
domain: options.x_domain == null ? [Math.min(...intervals.map(d => d[0])),
Math.max(...intervals.map(d => d[1]))
] : options.x_domain
},
marks: [
Plot.barX(intervals, {x1: d => d[0], x2: d => d[1], fill: (d, i) => i, y: (d, i) => i}),
Plot.text(intervals, {x: d => d[1], y: (d,i) => i, text: d => d[2] ? d[2]: "-", dy: 0, dx: 10, fontSize:15, textAnchor:"start"})
]
})
}
Insert cell
renderListPlot([1, 1, 1, 2, 1, 1])
Insert cell
countAllInterval(intervals)
Insert cell
function renderListPlot(list) {
return Plot.plot({
height: 100,
marginTop: -10,
marginBottom: 20,
color: {
scheme: "BuRd"
},
marks: [
Plot.barY(list, {x: (d, i) => i, y: (d, i) => 10, fill: d=>d})
]
})
}
Insert cell
Plot.plot({
height: 200,
marginTop: -10,
marginBottom: 20,
color: {
scheme: "BuRd"
},
marks: [
Plot.barY(countOverlaps(sample_during), {x: (d, i) => i, y: (d, i) => 10}),
Plot.barX(sample_during, {x1: d => d[0], x2: d => d[1], fill: (d, i) => i, y: (d,i) => i + .5})
]
})
Insert cell
sample_during
Insert cell
countOverlaps(sample_during)
Insert cell
renderIntervalsPlot([[10, 30, "A"], [30, 50, "B"], [25, 35, "C"]])
Insert cell
countAllInterval(intervals)
Insert cell
Plot.plot({
marks: [
Plot.barY(countOverlaps(intervals), {x: (d, i) => i, y: (d, i) => d})
]
})
Insert cell
countOverlaps(intervals)
Insert cell
## Using D3.JS
Insert cell
sparkline()
Insert cell
sparkline = (data = [[1,2], [3,4]], w=30, h=15, g, xt) => {
const svg = d3
.create("svg")
.attr("viewBox", [0, 0, w, h])
.attr("width", w)
.attr("height", h);

let ext = xt || extent(data);

let margin = {top: 2, right: 2, bottom: 2, left: 2};

let width = w - margin.left - margin.right,
height = h - margin.top - margin.bottom;

let y = d3.scaleBand()
.domain(d3.range(data.length))
.range([0, height])
let x = d3.scaleLinear()
.domain(ext)
.range([0, width])
svg.selectAll("rect").data(data).enter()
.append("rect")
.attr("x", d => x(d[0]))
.attr("y", (d, i) => y(i))
.attr("width", d => x(d[1]) - x(d[0]))
.attr("opacity", 1)
.attr("stroke", "white")
.attr("fill", "black")
.attr("height", y.bandwidth());
return svg.node();
}
Insert cell
Insert cell
import {intervals as intervalstimeline} from '@severo/intervals-timeline'
Insert cell
t2(intervals)
Insert cell
t2 = (intervals) => {
const min = extent(intervals)[0];
const max = extent(intervals)[1];
const colorScale = d3
.scaleLinear()
.domain([min, max])
.range(["#a52a2a88", "#4682b488"])
.interpolate(d3.interpolateHcl);
return intervalstimeline({
value: 5.72,
min,
max,
step: 0.1,
width: 600,
zoomRange: [min, max],
zoomTranslateExtent: [min - 1, max + 1],
zoomScaleExtent: [1, 10000],
zoomDisabled: false,
data: intervals,
color: d => colorScale(d[0]),
startof: d => d[0],
endof: d => d[1],
height: 50,
rectHeight: 20
});
}
Insert cell
Insert cell
Insert cell
Insert cell
civilizations_intervals = civilizations.map(d => [d.start, d.end])
Insert cell
Insert cell
renderIntervalsPlot(sortIntervalsByStart(civilizations_intervals))
Insert cell
Plot.barX(civilizations_intervals,
{x1: d => d[0], x2: d => d[1], fill: (d, i) => i, y: (d,i) => i % 10}
).plot()
Insert cell
renderIntervalsPlot([countAllInterval(civilizations_intervals)])
Insert cell
sortIntervalsByStart(civilizations_intervals)
Insert cell
countAllInterval(sample_during)
Insert cell
countAllInterval(sample_equal)
Insert cell
sample_precede
Insert cell
countOverlaps(sample_precede)
Insert cell
countAllInterval(sample_precede)
Insert cell
countOverlaps(sample_meet)
Insert cell
countOverlaps(civilizations_intervals)
Insert cell
countOverlaps(sample_precede)
Insert cell
sample_precede
Insert cell
countAllInterval([[1, 2]])
Insert cell
equals(countOverlaps([[1, 2]]), [1, 1])
Insert cell
Insert cell
## From temporal values
Insert cell
sample_hours = [
{ start: '01:00', end: '04:00' },
{ start: '05:00', end: '08:00' },
]
Insert cell
parser_hours = d3.timeParse("%H:%M")
Insert cell
parser_hours(sample_hours[0].start)
Insert cell
parse = (intervals, parser, accessor_start, accessor_end) => {
return intervals.map(d => [parser(d.start), parser(d.end)])
}
Insert cell
parse(sample_hours, parser_hours)
Insert cell
renderIntervalsPlot(parse(sample_hours, parser_hours))
Insert cell
## From a series of events
Insert cell
Insert cell
import {rangeSlider} from "@mootari/range-slider"
Insert cell
import { vl } from "@vega/vega-lite-api"
Insert cell
# Tests

*to be updated*
Insert cell
equals(countAllInterval([[0, 2], [2, 4]]), [1, 1, 2, 1, 1])
&&
equals(countAllInterval([[0, 3], [2, 4]]), [1, 1, 2, 2, 1])
&&
equals(countAllInterval([[0, 2], [3, 4]]), [1, 1, 1, 1, 1])
&&
equals(countAllInterval([]), [])
&&
equals(countAllInterval([[]]), [])
&&
equals(countAllInterval([[0, 0]]), [1])
&&
equals(countAllInterval([[1, 3], [2, 4]]), [1, 2, 2, 1])
Insert cell
md`---
## WIP

- more tests in functions to check correctly formatted
- add option for open/closed intervals
- non-bounded intervals
- visualize empty intervals
- improve overlapping interval visualizations
- generate random intervals

`
Insert cell
Insert cell
function al() {
let data = []
let intervals = []
let select = ""
function table(data, options = {}) {

let {
mode = "startstop"
} = options;

const values_extent = extent(data);
data = data
}

table.data = function(_) {
return arguments.length
? (data = _, table)
: data;
};
table.intervals = function(_) {
intervals = data

// intervals = intervals.sort((a,b) => a[0] - b[0])
return arguments.length
? table//(intervals = _, table)
: intervals;
};

table.shortest = () => {
let shortest = [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]

intervals.forEach(interval => {
if (interval[1] - interval[0] < shortest[1] - shortest[0]) {
shortest = interval;
}
});
intervals = [shortest]
return table;
}
table.duration = () => {
return intervals == null ? 0: intervals[0][1] - intervals[0][0];
}

table.cumul = () => {
return intervals.map(table.duration).reduce((d, v, i) => [...d, v + (d[i-1] || 0)], [])
}

table.overlaps = () => {
let overlappingIntervals = [];
for (let i = 0; i < intervals.length; i++) {
for (let j = i + 1; j < intervals.length; j++) {
if (intervals[i][1] > intervals[j][0] && intervals[i][0] < intervals[j][1]) {
overlappingIntervals.push([intervals[i], intervals[j]]);
break;
}
}
}
intervals = overlappingIntervals
return intervals;
}

// TODO: check for intervals
table.equals = (interval) => {
return intervals[0].length === interval.length && interval[0] == intervals[0][0] && interval[1] == intervals[0][1]
}
table.startstop = function() {
};
return table
}

Insert cell
al()
.data([[1, 3], [4, 7]]).intervals(al().startstop).overlaps()
Insert cell
al()
.data([[1, 3], [4, 7]]).data()
Insert cell
detectOverlappingIntervals([[1, 3], [4, 7]])
Insert cell
al()
.data([[1, 3], [4, 7]])
.intervals(al().startstop)
.shortest()
.equals([1, 3])
Insert cell
al()
.data([[1, 3], [4, 7]])
.intervals(al().startstop)
.equals([[1, 3], [4, 7]])
Insert cell
al()
.data([[1, 3], [4, 7]])
.intervals(al().startstop)
.cumul()
Insert cell
cumul([[1, 3], [4, 7]])
Insert cell
shortest(intervals)
Insert cell
function get_days() {
var days = [];
var date = new Date(2022, 11, 1); // December 1, 2022
var lastDay = new Date(2022, 11, 31); // December 31, 2022
var daysOfWeek = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
while (date <= lastDay) {
days.push([new Date(date), new Date(date.getTime() + 24 * 60 * 60 * 1000), date.getUTCDay(), daysOfWeek[date.getUTCDay()]]);
date.setDate(date.getDate() + 1);
}
return days;
}
Insert cell
get_days()
Insert cell
al()
.data(get_days())
.intervals(al().startstop)
.overlaps()
Insert cell
al()
.data([[1, 3], [4, 7]])
Insert cell
Plot.barX(get_days(), {
x1: d => d[0], x2: d => d[1], fill: (d, i) => i % 7, y: (d,i) => i % 7
}).plot()
Insert cell
intervals
Insert cell
Plot.plot({
marks: [
Plot.barY(cumul(civilizations_intervals), {x: (d, i) => i, y: d => d})
]
})
Insert cell
Plot.plot({
marks: [
Plot.barY([[1, 3], [3, 4], [5, 8]], {x: (d, i) => i, y: d => d[1]-d[0]})
]
})
Insert cell
// https://observablehq.com/@observablehq/plot-link?collection=@observablehq/plot
Plot.link(intervals, {
x1: (d, i) => d[0],
y1: (d, i) => i,
x2: (d, i) => d[1],
y2: (d, i) => i,
stroke: (d, i) => i,
markerStart: "dot",
markerEnd: "dot"
}).plot()
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