Public
Edited
May 2
Fork of apteka
Insert cell
Insert cell
vis1 = {
d3.timeFormatDefaultLocale({
dateTime: "%A, %e %B %Y р. %X",
date: "%d.%m.%Y",
time: "%H:%M:%S",
periods: ["дп", "пп"],
days: [
"неділя",
"понеділок",
"вівторок",
"середа",
"четвер",
"п'ятниця",
"субота"
],
shortDays: ["нд", "пн", "вт", "ср", "чт", "пт", "сб"],
months: [
"січня",
"лютого",
"березня",
"квітня",
"травня",
"червня",
"липня",
"серпня",
"вересня",
"жовтня",
"листопада",
"грудня"
],
shortMonths: [
"січ",
"лют",
"бер",
"квіт",
"трав",
"черв",
"лип",
"серп",
"вер",
"жов",
"лист",
"груд"
]
});

return Plot.plot({
subtitle: "Як змінилися ціни на 100 популярних ліків",
style: { fontSize: 14, fontWeight: 400 },
marginTop: 40,
marginBottom: 40,
marginRight: 100,
marginLeft: 45,
y: {
percent: true,
tickFormat: (d) => d - 100 + "%",
label: null,
labelArrow: false,
ticks: 8,
grid: true,
nice: true
},
x: { nice: true, ticks: width > 650 ? 8 : 4 },
width: width > 650 ? 750 : 350,
// marginLeft: 80,
marks: [
Plot.text(
[
`В січні середня ціна на сотню популярних ліків підскочила одразу на 15%.`
],
{
x: new Date("2024-10-09"),
y: 1.3,
textAnchor: "end",
bend: true,
dx: width > 650 ? 0 : 0,
lineWidth: 10,
fill: "#333"
}
),
Plot.text(
[
`Президент зібрав "лікарське" РНБО. Після цього ринок пообіцяв знизити ціни на 30%.`
],
{
x: new Date("2025-03-29"),
y: 1.14,
textAnchor: "end",
bend: true,
dx: width > 650 ? 70 : 100,
lineWidth: 10,
fill: "#333"
}
),
Plot.lineY(
data2,
Plot.normalizeY("first", {
x: "date",
y: "value",
z: "name",
stroke: "#ddd",
tip: true,
title: (d) => `${d.drug}`,
opacity: 0.7
})
),
Plot.dot(
data2,
Plot.normalizeY(
"first",
Plot.pointerX({
x: "date",
y: "value",
z: "name",
stroke: "#333",
opacity: 0.6
})
)
),

Plot.ruleY([1], { strokeDasharray: "2 2" }),

Plot.lineY(
data2,
Plot.normalizeY(
"first",
Plot.binX(
{ y: "mean" },
{
x: "date",
y: "value",
// z: "name",
interval: "months",
stroke: "red",
strokeWidth: 2.5,
dx: width > 650 ? 25 : 10
}
)
)
),
Plot.dot(
data2,
Plot.normalizeY(
"first",
Plot.binX(
{ y: "mean" },
{
x: "date",
y: "value",
// z: "name",
interval: "months",
stroke: "red",
strokeWidth: 2.5,
dx: width > 650 ? 25 : 10,
r: 2
}
)
)
),
Plot.text([`Середня`], {
x: new Date("2024-12-01"),
y: width > 650 ? 1.07 : 1.05,
dx: width > 650 ? 0 : -10,
textAnchor: "start",
bend: true,
lineWidth: 6,
fill: "red",
lineWidth: 9,
rotate: width > 650 ? -55 : -73
}),
Plot.text(
[
`В березні ціни впали на 15% порівняно з лютим, і на 25% – порівняно з січнем`
],
{
x: new Date("2025-03-10"),
y: 0.87,
dx: width > 650 ? 0 : 5,
textAnchor: "start",
bend: true,
lineWidth: 6,
fill: "#333",
lineWidth: 9
}
),
Plot.arrow([0], {
x1: new Date("2024-10-15"),
x2: new Date("2025-01-01"),
y1: 1.3,
y2: 1.17,
bend: 30,
dx: width > 650 ? 0 : 0,
strokeWidth: 1
}),
Plot.arrow([0], {
x1: new Date("2025-02-21"),
x2: new Date("2025-02-05"),
y1: 1.1,
y2: 1.07,
bend: width > 650 ? -20 : 20,
dx: width > 650 ? 0 : 5,
strokeWidth: 1
})
],
caption: html`<small style="color:grey">Дані: Сайт https://tabletki.ua</small>`
});
}
Insert cell
drug = FileAttachment("drugs.csv").csv()
Insert cell
data = (await FileAttachment("final_data_drug_prices_march@2.csv").csv())
.map((d) => {
return {
...d,
date: d3.timeParse("%m.%y")(d.date),
value: +d.value,
name: d.name.replace(".png", ""),
exp: +d.value > 200 ? "дорожче 200 грн" : "дешевше 200 грн"
};
})
.filter(
(d) =>
d.value > 0 &&
d.name != "Натрия-хлорид_1100125_" &&
d.name != "Витаксон_22913_"
)
Insert cell
data2 = {
for (const value of data) {
// console.log(value.name.replace(/.+_\d/, "").replace(/_/, ""));
let res = drug.find((d) =>
d.href.includes(value.name.replace(/.+_\d/, "").replace(/_/, ""))
);
console.log("!!!", res);
value.drug = res ? res?.drug : "na";
value.company = res?.company;
value.href = res?.href;
}
return data;
}
Insert cell
data_feb = (await FileAttachment("final_data_drug_prices_march_2.csv").csv())
.map((d) => {
return {
...d,
date: d3.timeParse("%m.%y")(d.date),
value: +d.value,
name: d.name.replace(".png", ""),
exp: +d.value > 200 ? "дорожче 200 грн" : "дешевше 200 грн"
};
})
.filter(
(d) =>
d.value > 0 &&
d.name != "Натрия-хлорид_1100125_" &&
d.name != "Витаксон_22913_"
)
Insert cell
function renderFilter(initialTest = true) {
const updates = new Array(); // 🌶🌶 support sharing across charts; needs invalidation
// see https://observablehq.com/@observablehq/plot-brush-crossfilter-1653#analyzer for a technique

return Object.assign(
function apply(options) {
return {
...options,
render(index, scales, values, dimensions, context, next) {
// const data = kyiv; // needs https://github.com/observablehq/plot/issues/1857
const { data } = values;

const filter = (test) => typeof test === "function" ? index.filter((i) => test(data[i], i, data)) : test ? index : []; // prettier-ignore
let g = next(
filter(initialTest),
scales,
values,
dimensions,
context
);
updates.push((test) => {
const transform = g.getAttribute("transform");
g.replaceWith(
(g = next(filter(test), scales, values, dimensions, context))
);
g.setAttribute("transform", transform); // facet translate
});
return g;
}
};
},
{
update(test) {
return updates.map((update) => update?.(test));
}
}
);
}
Insert cell
vis2 = {
d3.timeFormatDefaultLocale({
dateTime: "%A, %e %B %Y р. %X",
date: "%d.%m.%Y",
time: "%H:%M:%S",
periods: ["дп", "пп"],
days: [
"неділя",
"понеділок",
"вівторок",
"середа",
"четвер",
"п'ятниця",
"субота"
],
shortDays: ["нд", "пн", "вт", "ср", "чт", "пт", "сб"],
months: [
"січня",
"лютого",
"березня",
"квітня",
"травня",
"червня",
"липня",
"серпня",
"вересня",
"жовтня",
"листопада",
"грудня"
],
shortMonths: [
"січ",
"лют",
"бер",
"квіт",
"трав",
"черв",
"лип",
"серп",
"вер",
"жов",
"лист",
"груд"
]
});

return Plot.plot({
subtitle: "Зміна цін на 47 популярних ліків, які не увійшли в перелік",
style: { fontSize: 14, fontWeight: 400 },
marginTop: 40,
marginBottom: 40,
marginRight: 100,
marginLeft: 45,
y: {
percent: true,
tickFormat: (d) => d - 100 + "%",
label: null,
labelArrow: false,
ticks: 8,
grid: true,
nice: true
// domain: [0.5, 3]
},
x: { nice: true, ticks: width > 650 ? 8 : 4 },
width: width > 650 ? 750 : 350,
// marginLeft: 80,
marks: [
Plot.text(
[
`Ціни на 47 препаратів, які не увійшли в перелік на зниження, поки без змін.`
],
{
x: new Date("2025-01-19"),
y: 1.31,
textAnchor: "start",
lineWidth: 9,
dx: width > 650 ? 70 : 30
}
),
Plot.lineY(
data_feb,
Plot.normalizeY("first", {
x: "date",
y: "value",
z: "name",
stroke: "#ddd",
tip: true,
title: (d) => `${d.name.replace(/_.+/, "")}`,
opacity: 0.7
// strokeWidth: 1
})
),

Plot.ruleY([1], { strokeDasharray: "2 2" }),

Plot.lineY(
data_feb,
Plot.normalizeY(
"first",
Plot.binX(
{ y: "mean" },
{
x: "date",
y: "value",
// z: "name",
interval: "months",
stroke: "red",
strokeWidth: 2.5,
dx: width > 650 ? 25 : 10
}
)
)
),
Plot.dot(
data_feb,
Plot.normalizeY(
"first",
Plot.binX(
{ y: "mean" },
{
x: "date",
y: "value",
// z: "name",
interval: "months",
stroke: "red",
strokeWidth: 2.5,
dx: width > 650 ? 25 : 10,
r: 2
}
)
)
),
Plot.arrow([0], {
x1: new Date("2025-03-21"),
x2: new Date("2025-03-08"),
y1: 1.2,
y2: 1.14,
bend: 30,
dx: width > 650 ? 0 : 5,
strokeWidth: 1
})
]
// caption: html`<small style="color:grey">Дані: Сайт https://tabletki.ua/uk/category/256/. Назви препаратів вказані російською, оскільки в такому форматі вони записані в назві url-строки</small>`
});
}
Insert cell
vis3 = {
d3.timeFormatDefaultLocale({
dateTime: "%A, %e %B %Y р. %X",
date: "%d.%m.%Y",
time: "%H:%M:%S",
periods: ["дп", "пп"],
days: [
"неділя",
"понеділок",
"вівторок",
"середа",
"четвер",
"п'ятниця",
"субота"
],
shortDays: ["нд", "пн", "вт", "ср", "чт", "пт", "сб"],
months: [
"січня",
"лютого",
"березня",
"квітня",
"травня",
"червня",
"липня",
"серпня",
"вересня",
"жовтня",
"листопада",
"грудня"
],
shortMonths: [
"січ",
"лют",
"бер",
"квіт",
"трав",
"черв",
"лип",
"серп",
"вер",
"жов",
"лист",
"груд"
]
});

return Plot.plot({
title: "Зміна відносних цін на 51 препарат",
style: { fontSize: 14 },
marginBottom: 40,
marginTop: 100,
marginLeft: 45,
y: {
percent: true,
tickFormat: (d) => d + "%",
label: "Порівняно з березнем 2024 року",
labelArrow: false,
ticks: 8,
grid: true
},
x: { nice: true },
width: width > 650 ? 1150 : 350,
// marginLeft: 80,
marks: [
Plot.axisFx({ lineWidth: 8 }),
Plot.lineY(
data2,
Plot.normalizeY("first", {
x: "date",
y: "value",
z: "name",
stroke: "#ddd",
tip: true,
title: (d) => `${d.name}`,
opacity: 0.7,
fx: "company"
})
),
Plot.ruleY([1], { strokeDasharray: "2 2" }),

Plot.lineY(
data2,
Plot.normalizeY(
"first",
Plot.binX(
{ y: "mean" },
{
x: "date",
y: "value",
// tip: true,
interval: "months",
stroke: "red",
strokeWidth: 2.5,
dx: 5,
fx: "company"
}
)
)
)
],
caption: html`<small style="color:grey">Дані: Сайт https://tabletki.ua/uk/category/256/. Назви препаратів вказані російською, оскільки в такому форматі вони записані в назві url-строки</small>`
});
}

Insert cell
distr = (await FileAttachment("drug_distr@1.csv").csv())
.map((d) => {
return {
...d,
per: +d.per * 100,
status2: d.status == "yes" ? "В списку на зниження" : "Не в списку"
};
})
.filter((d) => d.per < 200)
Insert cell
vis31 = {
let yes = d3.mean(
distr.filter((d) => d.status == "yes"),
(d) => d.per
);
let no = d3.mean(
distr.filter((d) => d.status == "no"),
(d) => d.per
);
return Plot.plot({
title: "Розкид цін в аптеках Києва",
style: { fontSize: 14 },
x: {
tickFormat: (d) => d + "%",
label: "Розкид цін в аптеках Києва",
nice: true
},
height: 350,
width: 650,
marginRight: 100,
fy: { padding: 0.3 },
marks: [
Plot.axisFy({
fontSize: 16,
label: null,
// lineWidth: 8,
textAnchor: "middle",
frameAnchor: "top"
}),
Plot.ruleX([yes], { fy: ["В списку на зниження"] }),
Plot.ruleX([no], { fy: ["Не в списку"] }),
Plot.dotX(
distr,
Plot.dodgeY({
x: "per",
title: "name",
fill: "currentColor",
anchor: "bottom",
fy: "status2",
r: 4,
tip: true
})
)
]
});
}
Insert cell
Plot.plot({
r: { range: [0, 15] },
height: 150,
marks: [
Plot.dot(
distr,
Plot.binX({ r: "count" }, { x: "per", interval: 5, fy: "status" })
)
]
})
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