Public
Edited
Nov 21, 2024
1 fork
Insert cell
Insert cell
ua = FileAttachment("ukraine2@1.json").json()
Insert cell
regions = topojson.feature(ua, ua.objects.regions)
Insert cell
// vis11 = md`<span style="color:#000;"><span style="background:#2b8cbe;padding:1px;color:#000;border-radius: 4px">Синім</span> кольором показана сумарна кількість вакансій в містах області на початку 2023 року. <span style="border: dashed 2px #333;">Пунктирними</span> лініями показана кількість вакансій на листопад 2024 року.</span>
// `
Insert cell
// {
// const n = 3; // number of facet columns
// const keys = Array.from(d3.union(data.map((d) => d.region)));
// const index = new Map(keys.map((key, i) => [key, i]));
// const fx = (key) => index.get(key) % n;
// const fy = (key) => Math.floor(index.get(key) / n);

// return Plot.plot({
// aspectRatio: 1,
// style: { fontSize: 13 },
// width: width > 650 ? 650 : 350,
// height: 450,
// marginRight: 60,
// marginBottom: 60,
// fx: { padding: 0.25, axis: null },
// fy: { padding: 0.15, axis: null },
// y: { domain: [0, 170] },
// x: { domain: [0, 170] },
// axis: null,
// marks: [
// Plot.rect(data, {
// x1: 0,
// y1: 0,
// x2: (d) => d3.scaleSqrt()(d.vacancy22),
// y2: (d) => d3.scaleSqrt()(d.vacancy22),
// fill: null,
// // opacity: 0.2,
// // strokeDasharray: 4,
// fill: "#2b8cbe",
// // title: (d) => d.vacancy22,
// fx: (d) => (width > 600 ? d.X : fx(d.region)),
// fy: (d) => (width > 600 ? d.Y : fy(d.region))
// }),

// Plot.rect(data, {
// x1: 0,
// y1: 0,
// x2: (d) => d3.scaleSqrt()(d.vacancy24),
// y2: (d) => d3.scaleSqrt()(d.vacancy24),
// fill: null,
// stroke: "#333",
// strokeDasharray: 4,
// // title: (d) => d.vacancy24,
// // fill: "#fc8d59",
// fx: (d) => (width > 600 ? d.X : fx(d.region)),
// fy: (d) => (width > 600 ? d.Y : fy(d.region)),
// tip: true,
// title: (d) =>
// `Кількість вакансій, 2023: ${d.vacancy22}\nКількість вакансій, 2024: ${d.vacancy24}`
// }),

// Plot.text(data, {
// fx: (d) => (width > 600 ? d.X : fx(d.region)),
// fy: (d) => (width > 600 ? d.Y : fy(d.region)),
// frameAnchor: "bottom-left",
// dx: 2,
// dy: 12,
// fontWeight: 300,
// text: (d) => d.region,
// fontSize: width > 600 ? 12 : 10
// }),

// Plot.text(
// [
// "Після Києва за останні півтора роки саме східні міста мають найбільший приріст вакансій"
// ],
// {
// fx: [5],
// fy: [3],
// frameAnchor: "bottom-left",
// // textAnchor: "end",
// dx: 70,
// dy: -50,
// fontWeight: 400,
// lineWidth: 12
// // opacity: width > 600 ? 1 : 0.1
// }
// ),
// Plot.arrow([0], {
// x1: 90,
// x2: 20,
// y1: 60,
// y2: 20,
// fx: [5],
// fy: [3],
// bend: 40,
// strokeWidth: 1,
// dx: 30,
// dy: -20
// })
// ]
// });
// }
Insert cell
// data = [
// {
// region: "Вінницька",
// X: 2,
// Y: 3,
// vacancy22: 2422,
// vacancy24: 2246.0,
// vac23: 114.864625,
// vac24: 83.53166
// },
// {
// region: "Волинська",
// X: 0,
// Y: 2,
// vacancy22: 1575,
// vacancy24: 1715.0,
// vac23: 111.210923,
// vac24: 116.711803
// },
// {
// region: "Дніпр.",
// X: 5,
// Y: 3,
// vacancy22: 7339,
// vacancy24: 7985.0,
// vac23: 82.361186,
// vac24: 95.344059
// },
// {
// region: "Донецька",
// X: 6,
// Y: 3,
// vacancy22: 213,
// vacancy24: 215.0,
// vac23: 31.219423,
// vac24: 18.095495
// },
// {
// region: "Житомирська",
// X: 2,
// Y: 1,
// vacancy22: 1514,
// vacancy24: 1402.0,
// vac23: 111.044444,
// vac24: 105.704933
// },
// {
// region: "Закарпатська",
// X: 0,
// Y: 4,
// vacancy22: 1642,
// vacancy24: 1535.0,
// vac23: 292.670223,
// vac24: 259.384786
// },
// {
// region: "Запорізька",
// X: 5,
// Y: 4,
// vacancy22: 1280,
// vacancy24: 1682.0,
// vac23: 82.883801,
// vac24: 77.299549
// },
// {
// region: "Івано-Франк.",
// X: 1,
// Y: 3,
// vacancy22: 2275,
// vacancy24: 2339.0,
// vac23: 195.288189,
// vac24: 217.660877
// },
// {
// region: "Київ",
// X: 3,
// Y: 3,
// vacancy22: 20413,
// vacancy24: 25315.0,
// vac23: 709.249973,
// vac24: 853.115227
// },
// {
// region: "Київська",
// X: 3,
// Y: 2,
// vacancy22: 2983,
// vacancy24: 3822.0,
// vac23: 238.006494,
// vac24: 298.334238
// },
// {
// region: "Кіровоградська",
// X: 4,
// Y: 3,
// vacancy22: 960,
// vacancy24: 924.0,
// vac23: 92.307755,
// vac24: 81.294694
// },
// {
// region: "Львівська",
// X: 0,
// Y: 3,
// vacancy22: 7461,
// vacancy24: 7372.0,
// vac23: 163.638847,
// vac24: 158.391744
// },
// {
// region: "Миколаївська",
// X: 4,
// Y: 4,
// vacancy22: 974,
// vacancy24: 1000.0,
// vac23: 63.183965,
// vac24: 57.96323
// },
// {
// region: "Одеська",
// X: 3,
// Y: 5,
// vacancy22: 5511,
// vacancy24: 4896.0,
// vac23: 85.328958,
// vac24: 71.218618
// },
// {
// region: "Полтавська",
// X: 4,
// Y: 2,
// vacancy22: 2003,
// vacancy24: 2104.0,
// vac23: 88.004929,
// vac24: 85.350757
// },
// {
// region: "Рівненська",
// X: 1,
// Y: 1,
// vacancy22: 1575,
// vacancy24: 1687.0,
// vac23: 116.177412,
// vac24: 142.496716
// },
// {
// region: "Сумська",
// X: 4,
// Y: 1,
// vacancy22: 684,
// vacancy24: 803.0,
// vac23: 45.361705,
// vac24: 75.500692
// },
// {
// region: "Тернопільська",
// X: 1,
// Y: 2,
// vacancy22: 1741,
// vacancy24: 1490.0,
// vac23: 122.883745,
// vac24: 99.180711
// },
// {
// region: "Харківська",
// X: 5,
// Y: 2,
// vacancy22: 2556,
// vacancy24: 2711.0,
// vac23: 41.600269,
// vac24: 40.895813
// },
// {
// region: "Херсонська",
// X: 4,
// Y: 5,
// vacancy22: 112,
// vacancy24: 161.0,
// vac23: 45.791207,
// vac24: 44.434138
// },
// {
// region: "Хмельницька",
// X: 2,
// Y: 2,
// vacancy22: 1924,
// vacancy24: 1820.0,
// vac23: 100.500632,
// vac24: 106.267288
// },
// {
// region: "Черкаська",
// X: 3,
// Y: 4,
// vacancy22: 1953,
// vacancy24: 1895.0,
// vac23: 116.149557,
// vac24: 108.256109
// },
// {
// region: "Чернівецька",
// X: 1,
// Y: 4,
// vacancy22: 1556,
// vacancy24: 1353.0,
// vac23: 128.56212,
// vac24: 109.007772
// },
// {
// region: "Чернігівська",
// X: 3,
// Y: 1,
// vacancy22: 814,
// vacancy24: 998.0,
// vac23: 63.853081,
// vac24: 99.882826
// }
// ]
Insert cell
Insert cell
vis22 = Plot.plot({
style: { fontSize: 13 },
width: width > 600 ? 800 : 400,
height: width > 600 ? 500 : 280,
projection: d3
.geoMercator()
.scale(width > 600 ? 2200 : 1150)
.center([width > 600 ? 34 : 46, width > 600 ? 48.5 : 45]),
// y: { domain: [0, 170] },
// x: { domain: [0, 170] },
length: { type: "sqrt", domain: [0, width > 650 ? 0.15 : 0.1] },
marks: [
// Plot.frame(),
Plot.geo(regions, {
strokeWidth: 0.5,
stroke: "#333"
}),
Plot.vector(
work.filter((d) => d.city != "Кeиїв"),
{
x: "lon",
y: "lat",
anchor: "start",
length: "diff2",
stroke: "red",
opacity: 0.7,
stroke: (d) => (d.diff2 > 0 ? "blue" : "red"),
rotate: (d) => (d.diff2 > 0 ? -60 : 60)
// tip: true
}
),
Plot.text(
work.filter(
(d) =>
(d.diff2 > 80) &
!["Кам'янське", "Новомосковськ", "Бровари"].includes(d.city)
),
{
x: "lon",
y: "lat",
// dx: width > 650 ? 15 : 200,
dy: 10,
fill: "#000",
stroke: "#fff",
fontWeight: 400,
fontSize: 10,
text: "city"
}
),
Plot.arrow([0], {
x1: 29.525732,
x2: 30.225732,
y1: 51.747323,
y2: 50.447323,
dy: -15,
// stroke: "black",
// opacity: width > 650 ? 1 : 0.1,
bend: 30,
inset: 1,
strokeWidth: 1
// strokeOpacity: 0.7
}),
Plot.text(
["Найбільше вакансій за останній рік в Києві і його містах-супутниках"],
{
x: 29.12573,
y: 53.447323,
frameAnchor: "right",
dx: 10,
dy: width > 650 ? 75 : 30,
fill: "#000",
stroke: "#fff",
fontWeight: 400,
lineWidth: 14
// opacity: width > 600 ? 1 : 0.1
}
),
Plot.arrow([0], {
x1: 37.4,
x2: 35.589531,
y1: 46.565355,
y2: 48.265355,
// dx: -10,
// stroke: "black",
// opacity: width > 650 ? 1 : 0.1,
bend: -50,
inset: 1,
strokeWidth: 1
// strokeOpacity: 0.7
}),
Plot.text(
[
"Після Києва, найбільшу кількість вакансій за останній рік створили в Запоріжжі та містах Дніпропетровської області"
],
{
x: 38.4,
y: 46.765355,
frameAnchor: "middle",
dx: width > 650 ? 10 : -20,
dy: width > 650 ? 50 : 35,
fill: "#000",
stroke: "#fff",
fontWeight: 400,
lineWidth: 16
// opacity: width > 600 ? 1 : 0.1
}
)
],
caption: html`<small style="color:grey">На мапі підписані п'ять міст з найбільшим приростом нових вакансій з початку 2023 року</small>`
})
Insert cell
Insert cell
vis222 = Plot.plot({
style: { fontSize: 13 },
width: width > 600 ? 800 : 400,
height: width > 600 ? 500 : 280,
projection: d3
.geoMercator()
.scale(width > 600 ? 2200 : 1150)
.center([width > 600 ? 33.8 : 44.3, width > 600 ? 48.7 : 45]),
// y: { domain: [0, 170] },
// x: { domain: [0, 170] },
// length: { type: "sqrt", domain: [0, 10] },
marks: [
// Plot.frame(),
Plot.geo(regions, {
strokeWidth: 0.5,
stroke: "#333"
}),
Plot.vector(work, {
x: "lon",
y: "lat",
anchor: "start",
length: "vac24",
stroke: "red",
opacity: 0.7,
stroke: (d) => (d.vac24 >= 0 ? "blue" : "red"),
rotate: (d) => (d.vac24 >= 0 ? -60 : 60)
// tip: true
}),
Plot.text(
work.filter(
(d) =>
(d.vac24 > 650) &
![
"Петропавлівська Борщагівка",
"Вишневе (Київська обл.)",
"Гатне"
].includes(d.city)
),
{
x: "lon",
y: "lat",
// frameAnchor: "right",
// dx: width > 650 ? 15 : 200,
// dy: width > 650 ? 70 : 25,
fill: "#000",
stroke: "#fff",
fontWeight: 400,
fontSize: 10,
text: "city"
}
),
Plot.arrow([0], {
x1: 29.725732,
x2: 30.225732,
y1: 51.747323,
y2: 50.647323,
dy: -15,
dx: -5,
// stroke: "black",
// opacity: width > 650 ? 1 : 0.1,
bend: 30,
inset: 1,
strokeWidth: 1
// strokeOpacity: 0.7
}),
Plot.text(
[
"Найлегше знайти роботу в Києві (приблизно 850 вакансій на 100 тисяч довоєнного населення) і його містах-супутниках"
],
{
x: 29.12573,
y: 53.447323,
frameAnchor: "right",
dx: width > 650 ? 15 : 200,
dy: width > 650 ? 70 : 25,
fill: "#000",
stroke: "#fff",
fontWeight: 400,
lineWidth: width > 650 ? 20 : 32
// opacity: width > 600 ? 1 : 0.1
}
)
// Plot.arrow([0], {
// x1: 38.4,
// x2: 37.4089531,
// y1: 46.765355,
// y2: 48.365355,
// // dx: -10,
// // stroke: "black",
// // opacity: width > 650 ? 1 : 0.1,
// bend: -20,
// inset: 1,
// strokeWidth: 1,
// strokeOpacity: 0.7
// }),
// Plot.text(
// [
// "В цілому ряді міст Донбасу зникли вакансії, як, майже, і самі міста - Вугдедар, Курахове, тощо. Проте рекордний приріст в Краматорську і Слов 'янську, які остаточно перебрали на себе залишки економічної активності. Те саме стосується і Дніпра, який, як і інші великі міста не далеко від фронту, приймають найбільше ВПО"
// ],
// {
// x: 38.4,
// y: 46.765355,
// frameAnchor: "middle",
// dx: 10,
// dy: 60,
// // fontSize: 11,
// fontWeight: 400,
// lineWidth: 18
// // opacity: width > 600 ? 1 : 0.1
// }
// )
],
caption: html`<small style="color:grey">На мапі підписані міста, де в листопаді було більше 600 відкритих вакансій на 100 тис оцінки довоєнного населення</small>`
})
Insert cell
work = (await FileAttachment("final_vac_24_work@2.csv").csv()).map((d) => {
return {
...d,
lon: +d.lon,
lat: +d.lat,
diff: +d.diff,
diff2: +d.diff2,
vacancy22: +d.vacancy22,
vacancy24: +d.vacancy24,
diff_: d.vac24 - d.vac23
};
})
Insert cell
Plot.plot({
style: { fontSize: 13, background: "#000", color: "#aaa", fill: "#aaa" },
width: width > 600 ? 800 : 400,
height: width > 600 ? 500 : 280,
projection: d3
.geoMercator()
.scale(width > 600 ? 2200 : 1150)
.center([width > 600 ? 34 : 46, width > 600 ? 48.5 : 45]),
// y: { domain: [0, 170] },
// x: { domain: [0, 170] },
length: { type: "sqrt", domain: [0, width > 650 ? 0.15 : 0.1] },
marks: [
// Plot.frame(),
Plot.geo(regions, {
strokeWidth: 0.5,
stroke: "#aaa"
}),
Plot.vector(
work.filter((d) => d.city != "Кeиїв"),
{
x: "lon",
y: "lat",
anchor: "start",
length: "diff2",
stroke: "red",
opacity: 0.7,
stroke: (d) => (d.diff2 > 0 ? "blue" : "red"),
rotate: (d) => (d.diff2 > 0 ? -60 : 60)
// tip: true
}
),
Plot.text(
work.filter(
(d) =>
(d.diff2 > 80) &
!["Кам'янське", "Новомосковськ", "Бровари"].includes(d.city)
),
{
x: "lon",
y: "lat",
// dx: width > 650 ? 15 : 200,
dy: 10,
fill: "#aaa",
// stroke: "#fff",
fontWeight: 400,
fontSize: 10,
text: "city"
}
),
Plot.arrow([0], {
x1: 29.525732,
x2: 30.225732,
y1: 51.747323,
y2: 50.447323,
dy: -15,
// stroke: "black",
// opacity: width > 650 ? 1 : 0.1,
bend: 30,
inset: 1,
strokeWidth: 1
// strokeOpacity: 0.7
}),
Plot.text(
["Найбільше вакансій за останній рік в Києві і його містах-супутниках"],
{
x: 29.12573,
y: 53.447323,
frameAnchor: "right",
dx: 10,
dy: width > 650 ? 75 : 30,
fill: "#aaa",
// stroke: "#fff",
fontWeight: 400,
lineWidth: 14
// opacity: width > 600 ? 1 : 0.1
}
),
Plot.arrow([0], {
x1: 37.4,
x2: 35.589531,
y1: 46.565355,
y2: 48.265355,
// dx: -10,
// stroke: "black",
// opacity: width > 650 ? 1 : 0.1,
bend: -50,
inset: 1,
strokeWidth: 1
// strokeOpacity: 0.7
}),
Plot.text(
[
"Після Києва, найбільшу кількість вакансій за останній рік створили в Запоріжжі та містах Дніпропетровської області"
],
{
x: 38.4,
y: 46.765355,
frameAnchor: "middle",
dx: width > 650 ? 10 : -20,
dy: width > 650 ? 50 : 35,
fill: "#aaa",
// stroke: "#fff",
fontWeight: 400,
lineWidth: 16
// opacity: width > 600 ? 1 : 0.1
}
)
],
caption: html`<small style="color:grey">На мапі підписані п'ять міст з найбільшим приростом нових вакансій з початку 2023 року</small>`
})
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