Public
Edited
Jun 11
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
sortedDifferenceData = differenceData
.slice()
.sort((a, b) => b.difference - a.difference);

Insert cell
Plot.plot({
x: {
label: "ID_T",
tickRotate: -65,
domain: sortedDifferenceData.map(d => d.ID_T) // Ordre forcé
},
y: {
label: "Différence de Trajets"
},
marks: [
Plot.dot(sortedDifferenceData, {
x: "ID_T",
y: "difference"
})
],
width: 1600,
height: 500
})

Insert cell
Insert cell
Insert cell
Insert cell
mappedDTPlus = filteredDTPlus.map(d => ({
...d,
Teletravail: d.Teletravail === 1 ? "Télétravail" : "Non télétravail",
Mode:
d.Mode === "VE" ? "Voiture électrique" :
d.Mode === "VT" || d.Mode === "VTR" ? "Voiture thermique" :
d.Mode
}))
Insert cell
Plot.plot({
marginBottom: 100,
fx: {padding: 0, label: null, tickRotate: 90, tickSize: 6},
x: {axis: null, paddingOuter: 0.2},
y: {grid: true},
color: {type: "categorical", scheme: "Category10", legend: true},
marks: [
// Barres groupées
Plot.barY(
mappedDTPlus,
Plot.groupX({y2: "count"}, {x: "Teletravail", fx: "Mode", fill: "Teletravail"})
),

// Texte sur barres Télétravail (orange) → en noir
Plot.text(
mappedDTPlus.filter(d => d.Teletravail === "Télétravail"),
Plot.groupX(
{text: d => d.length},
{
x: "Teletravail",
fx: "Mode",
fill: "Teletravail",
y: d => d.length / 2,
textAnchor: "middle",
fill: "black",
fontSize: 12
}
)
),

// Texte sur barres Non télétravail (bleu) → en blanc
Plot.text(
mappedDTPlus.filter(d => d.Teletravail === "Non télétravail"),
Plot.groupX(
{text: d => d.length},
{
x: "Teletravail",
fx: "Mode",
fill: "Teletravail",
y: d => d.length / 2,
textAnchor: "middle",
fill: "white",
fontSize: 12
}
)
),

Plot.ruleY([0])
]
})

Insert cell
Insert cell
mappedDTMoins = filteredDTMoins.map(d => ({
...d,
Teletravail: d.Teletravail === 1 ? "Télétravail" : "Non télétravail",
Mode:
d.Mode === "VE" ? "Voiture électrique" :
d.Mode === "VT" || d.Mode === "VTR" ? "Voiture thermique" :
d.Mode === "VH" ? "Voiture hybride" :
d.Mode
}))

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
ordrePersonnes = Array.from(
d3.rollup(
DistancesEmpilees,
v => d3.sum(v, d => d.distance),
d => d.personne
),
([personne, total]) => ({ personne, total })
)
.sort((a, b) => b.total - a.total) // décroissant
.map(d => d.personne); // liste ordonnée de personnes
Insert cell
Plot.plot({
title: "Distances journalières empilées (Travail + Télétravail)",
y: { label: "Distance (km)" },
x: {
label: "Personne",
type: "band",
domain: ordrePersonnes // Ordre personnalisé
},
color: {
type: "categorical",
legend: true,
label: "Type de jour",
scheme: "Category10"
},
width: 800,
height: 500,
marks: [
Plot.barY(DistancesEmpilees, {
x: "personne",
y: "distance",
fill: "type",
tip: true,
title: d => `${d.type} : ${d.distance.toFixed(1)} km`
}),

// Texte blanc sur barres bleues
Plot.text(
DistancesEmpilees.filter(d => d.type === "Travail"),
{
x: "personne",
y: d => d.distance / 2,
text: d => d.distance.toFixed(1),
fill: "white",
textAnchor: "middle",
fontSize: 11
}
),

// Texte noir sur barres orange (empilées)
Plot.text(
DistancesEmpilees.filter(d => d.type === "Télétravail"),
{
x: "personne",
y: d => {
const base = DistancesEmpilees.find(
e => e.personne === d.personne && e.type === "Travail"
)?.distance || 0;
return base + d.distance / 2;
},
text: d => d.distance.toFixed(1),
fill: "black",
textAnchor: "middle",
fontSize: 11
}
),

Plot.ruleY([0])
]
})
Insert cell
Insert cell
freqs = Array.from(
d3.rollup(DT_labelled, v => v.length, d => d.TeletravailLabel, d => d.Echelle),
([TeletravailLabel, group]) =>
Array.from(group, ([Echelle, count]) => ({
TeletravailLabel,
Echelle,
count
}))
).flat();
Insert cell
Insert cell
Insert cell
Emissions = {
const personnes = [...new Set(DT.map(e => e.ID_T))];

return personnes.map(p => {
const trajetsTravail = DT.filter(e => e.ID_T === p && e.Teletravail === 0);
const trajetsTeletravail = DT.filter(e => e.ID_T === p && e.Teletravail === 1);

const travailEmission = d3.sum(trajetsTravail, e => e.EmissionTotale ?? 0);
const teletravailEmission = d3.sum(trajetsTeletravail, e => e.EmissionTotale ?? 0);

return {
personne: p,
travail: travailEmission,
teletravail: teletravailEmission,
hasBoth: trajetsTravail.length > 0 && trajetsTeletravail.length > 0
};
}).filter(d => d.hasBoth);
}

Insert cell
EmissionsEmpilees = Emissions.flatMap(d => [
{
personne: d.personne,
type: "Travail",
emission: d.travail / 1000
},
{
personne: d.personne,
type: "Télétravail",
emission: d.teletravail / 1000
}
])

Insert cell
Insert cell
EmissionsTriees = EmissionsEmpilees.slice().sort(
(a, b) => personnesTriees.indexOf(a.personne) - personnesTriees.indexOf(b.personne)
);
Insert cell
Plot.plot({
title: "Émissions quotidiennes de CO₂ par personne et par mode de travail (barres empilées, en kilogrammes)",
y: { label: "kgCO₂ / jour" },
x: {
label: "Personne",
type: "band",
domain: personnesTriees
},
color: {
type: "categorical",
domain: ["Travail", "Télétravail"],
range: ["#1f77b4", "#ff7f0e"],
legend: true
},
width: 1000,
height: 500,
marks: [
// Barres empilées
Plot.barY(EmissionsTriees, {
x: "personne",
y: "emission",
fill: "type",
tip: true,
title: d => `${d.type} : ${d.emission.toFixed(1)} gCO₂/jour`
}),

// Texte centré
Plot.text(EmissionsTriees, {
x: "personne",
y: d => d.type === "Travail"
? d.emission / 2
: (Emissions.find(p => p.personne === d.personne)?.travail ?? 0) / 1000 + d.emission / 2,
text: d => d.emission.toFixed(1),
fill: d => d.type === "Travail" ? "white" : "black",
fontSize: 10,
textAnchor: "middle"
}),

Plot.ruleY([0])
]
});
Insert cell
Insert cell
TotauxHebdo = d3.rollups(
EmissionsHebdoEmpilees,
v => d3.sum(v, d => d.emission),
d => d.personne
);
Insert cell
personnesTrieesHebdo = TotauxHebdo
.sort((a, b) => b[1] - a[1])
.map(d => d[0]);
Insert cell
Plot.plot({
title: "Émissions hebdomadaires de CO₂ par personne (barres empilées)",
y: { label: "kgCO₂ / semaine" },
x: {
label: "Personne",
type: "band",
domain: personnesTrieesHebdo
},
color: {
type: "categorical",
domain: ["Travail", "Télétravail"],
range: ["#1f77b4", "#ff7f0e"],
legend: true
},
width: 1000,
height: 500,
marks: [
// Barres empilées
Plot.barY(EmissionsHebdoEmpilees, {
x: "personne",
y: "emission",
fill: "type",
tip: true,
title: d => `${d.type} : ${d.emission.toFixed(2)} kgCO₂/semaine`
}),

// Texte sur les barres
Plot.text(EmissionsHebdoEmpilees, {
x: "personne",
y: d => d.type === "Travail"
? d.emission / 2
: (Emissions.find(p => p.personne === d.personne)?.travail ?? 0) * 3 / 1000 + d.emission / 2,
text: d => d.emission.toFixed(1),
fill: d => d.type === "Travail" ? "white" : "black",
fontSize: 10,
textAnchor: "middle"
}),

Plot.ruleY([0])
]
});
Insert cell
Distances = {
const personnes = [...new Set(DT.map(e => e.ID_T))];

return personnes.map(p => {
const trajetsTravail = DT.filter(e => e.ID_T === p && e.Teletravail === 0);
const trajetsTeletravail = DT.filter(e => e.ID_T === p && e.Teletravail === 1);

const travailDistance = d3.sum(trajetsTravail, e => e.KM ?? 0);
const teletravailDistance = d3.sum(trajetsTeletravail, e => e.KM ?? 0);

return {
personne: p,
travail: travailDistance,
teletravail: teletravailDistance,
hasBoth: trajetsTravail.length > 0 && trajetsTeletravail.length > 0
};
}).filter(d => d.hasBoth);
}

Insert cell
DistancesEmpilees = Distances.flatMap(d => [
{
personne: d.personne,
type: "Travail",
distance: d.travail
},
{
personne: d.personne,
type: "Télétravail",
distance: d.teletravail
}
])

Insert cell
Insert cell
Insert cell
Plot.plot({
title: "Distances quotidiennes par personne (barres empilées)",
y: { label: "km / jour" },
x: {
label: "Personne",
type: "band",
domain: personnesTrieesDist // ← ordre trié personnalisé
},
color: {
type: "categorical",
domain: ["Travail", "Télétravail"],
range: ["#1f77b4", "#ff7f0e"],
legend: true
},
width: 1000,
height: 500,
marks: [
// Barres empilées
Plot.barY(DistancesTriees, {
x: "personne",
y: "distance",
fill: "type",
tip: true,
title: d => `${d.type} : ${d.distance.toFixed(1)} km/jour`
}),

// Texte centré
Plot.text(DistancesTriees, {
x: "personne",
y: d => d.type === "Travail"
? d.distance / 2
: (Distances.find(p => p.personne === d.personne)?.travail ?? 0) + d.distance / 2,
text: d => d.distance.toFixed(1),
fill: d => d.type === "Travail" ? "white" : "black",
fontSize: 10,
textAnchor: "middle"
}),

Plot.ruleY([0])
]
});
Insert cell
Insert cell
{
const data = [
{ type: "Travail standard", valeur: 123456 },
{ type: "Télétravail", valeur: 4321 }
];

const width = 500;
const height = 500;
const radius = Math.min(width, height) / 2;

const color = d3.scaleOrdinal()
.domain(data.map(d => d.type))
.range(["#1f77b4", "#ff7f0e"]);

const svg = d3.select("#pie")
.attr("viewBox", [0, 0, width, height]);

const pie = d3.pie().value(d => d.valeur);
const arc = d3.arc().innerRadius(0).outerRadius(radius - 10);
const label = d3.arc().innerRadius(radius / 1.5).outerRadius(radius / 1.5);

const g = svg.append("g")
.attr("transform", `translate(${width / 2},${height / 2})`);

const arcs = pie(data);

g.selectAll("path")
.data(arcs)
.join("path")
.attr("fill", d => color(d.data.type))
.attr("d", arc)
.append("title")
.text(d => `${d.data.type} : ${(d.data.valeur / d3.sum(data, d => d.valeur) * 100).toFixed(1)} %`);

g.selectAll("text")
.data(arcs)
.join("text")
.attr("transform", d => `translate(${label.centroid(d)})`)
.attr("text-anchor", "middle")
.attr("font-size", 16)
.attr("fill", d => d.data.type === "Travail standard" ? "white" : "black")
.text(d => `${(d.data.valeur / d3.sum(data, d => d.valeur) * 100).toFixed(1)}%`);
}

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
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
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
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
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
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
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