Ny visualisering: «Det er dyrt å være fattig» — boutgifter etter inntekt
SSB-data (tabeller 14061 og 14067) som viser at: - 41 % av fattigste kvartil bruker >40 % av inntekten på bolig (vs 7 % rikeste) - 17 % av fattigste bruker 60 %+ av inntekten bare på bolig - 82 % av sosialhjelpsmottakere leier — bygger ingen formue - Byrden har økt kraftig for alle grupper etter rentehoppet i 2022 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
55e2cd7a13
commit
2eef9d9f31
5 changed files with 503 additions and 0 deletions
262
public/boutgifter/app.js
Normal file
262
public/boutgifter/app.js
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
// SSB data: Boutgifter og boligeierskap etter inntekt
|
||||
// Tabell 14061: Boligøkonomi etter inntektsgruppe (boutgifter som andel av inntekt)
|
||||
// Tabell 14067: Boligeierskap etter inntektsgruppe
|
||||
// Hentet 2026-03-16 via SSB PxWeb API v2
|
||||
|
||||
// --- Data ---
|
||||
|
||||
const YEARS = ['2015', '2016', '2017', '2018', '2019', '2020', '2021', '2022', '2023', '2024'];
|
||||
|
||||
// Boutgifter som andel av inntekt per kvartil, 2024 (tabell 14061)
|
||||
const QUARTILE_LABELS = ['Laveste kvartil', 'Andre kvartil', 'Tredje kvartil', 'Høyeste kvartil'];
|
||||
|
||||
const HOUSING_BURDEN = {
|
||||
under10: [ 5.2, 10.8, 16.6, 22.0],
|
||||
from10to19: [12.4, 26.1, 22.8, 24.6],
|
||||
from20to29: [19.3, 28.2, 29.0, 28.2],
|
||||
from30to39: [22.4, 19.5, 18.9, 17.7],
|
||||
from40to59: [23.6, 12.7, 11.5, 6.4],
|
||||
over60: [17.1, 2.8, 1.2, 1.0]
|
||||
};
|
||||
|
||||
// Andel med boutgifter >40% av inntekt (40-59% + 60%+) over tid (tabell 14061)
|
||||
const HEAVY_BURDEN = {
|
||||
q1: [33.6, 33.4, 32.9, 34.5, 33.8, 33.5, 35.6, 41.8, 42.9, 40.7],
|
||||
q2: [ 7.3, 7.0, 7.0, 6.3, 8.3, 6.0, 7.6, 12.3, 12.5, 15.5],
|
||||
q3: [ 4.2, 3.1, 4.2, 3.9, 3.2, 3.6, 3.6, 6.0, 9.3, 12.7],
|
||||
q4: [ 2.5, 1.2, 1.9, 2.5, 2.5, 3.5, 2.5, 4.6, 5.9, 7.4]
|
||||
};
|
||||
|
||||
// Eierskap etter inntektsgruppe og utsatte grupper, 2024 (tabell 14067)
|
||||
const OWNERSHIP_GROUPS = [
|
||||
{ name: 'Sosialhjelpsmottakere', selveier: 13.9, andel: 4.6, leier: 81.5 },
|
||||
{ name: 'Lavinntekt (EU 60 %)', selveier: 21.2, andel: 10.8, leier: 68.0 },
|
||||
{ name: 'Laveste kvartil', selveier: 33.2, andel: 12.6, leier: 54.3 },
|
||||
{ name: 'Barnefamilier lav inntekt', selveier: 39.0, andel: 8.5, leier: 52.5 },
|
||||
{ name: 'Aleneboende eldre lav innt.', selveier: 52.4, andel: 23.9, leier: 23.7 },
|
||||
{ name: 'Uføretrygdede', selveier: 55.6, andel: 13.7, leier: 30.7 },
|
||||
{ name: 'Andre kvartil', selveier: 60.3, andel: 18.2, leier: 21.5 },
|
||||
{ name: 'Tredje kvartil', selveier: 70.2, andel: 15.6, leier: 14.2 },
|
||||
{ name: 'Høyeste kvartil', selveier: 79.7, andel: 13.6, leier: 6.7 },
|
||||
];
|
||||
|
||||
// --- Formattering ---
|
||||
|
||||
function fmtPct(n) {
|
||||
return n.toLocaleString('nb-NO', { minimumFractionDigits: 1, maximumFractionDigits: 1 }) + '\u202f%';
|
||||
}
|
||||
|
||||
const GRID_COLOR = 'rgba(0,0,0,0.06)';
|
||||
const TICK_COLOR = '#8a857e';
|
||||
|
||||
function baseOpts() {
|
||||
return {
|
||||
responsive: true, maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
tooltip: {
|
||||
backgroundColor: '#1a1714',
|
||||
titleColor: '#f5f2eb',
|
||||
bodyColor: 'rgba(245,242,235,0.7)',
|
||||
padding: 10,
|
||||
cornerRadius: 4,
|
||||
titleFont: { family: 'DM Sans', size: 12 },
|
||||
bodyFont: { family: 'DM Sans', size: 12 }
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
ticks: { color: TICK_COLOR, font: { size: 11, family: 'DM Sans' }, maxRotation: 0 },
|
||||
grid: { display: false },
|
||||
border: { color: 'rgba(0,0,0,0.1)' }
|
||||
},
|
||||
y: {
|
||||
ticks: { color: TICK_COLOR, font: { size: 11, family: 'DM Sans' }, callback: v => v + ' %' },
|
||||
grid: { color: GRID_COLOR },
|
||||
border: { dash: [3, 3], color: 'transparent' }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// --- Diagram 1: Boutgifter som andel av inntekt (stablet horisontal) ---
|
||||
|
||||
var chart1Opts = baseOpts();
|
||||
chart1Opts.indexAxis = 'y';
|
||||
chart1Opts.scales = {
|
||||
x: {
|
||||
stacked: true,
|
||||
max: 100,
|
||||
ticks: { color: TICK_COLOR, font: { size: 11, family: 'DM Sans' }, callback: v => v + ' %' },
|
||||
grid: { color: GRID_COLOR },
|
||||
border: { dash: [3, 3], color: 'transparent' }
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
ticks: { color: TICK_COLOR, font: { size: 11, family: 'DM Sans' }, autoSkip: false },
|
||||
grid: { display: false },
|
||||
border: { color: 'rgba(0,0,0,0.1)' }
|
||||
}
|
||||
};
|
||||
chart1Opts.plugins.tooltip = {
|
||||
mode: 'index',
|
||||
callbacks: {
|
||||
label: function(c) {
|
||||
return ' ' + c.dataset.label + ': ' + fmtPct(c.parsed.x);
|
||||
}
|
||||
},
|
||||
backgroundColor: '#1a1714',
|
||||
titleColor: '#f5f2eb',
|
||||
bodyColor: 'rgba(245,242,235,0.7)',
|
||||
padding: 10,
|
||||
cornerRadius: 4,
|
||||
titleFont: { family: 'DM Sans', size: 12 },
|
||||
bodyFont: { family: 'DM Sans', size: 12 }
|
||||
};
|
||||
|
||||
new Chart(document.getElementById('chart1'), {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: QUARTILE_LABELS,
|
||||
datasets: [
|
||||
{ label: 'Under 10 %', data: HOUSING_BURDEN.under10, backgroundColor: '#2c6e49cc', borderColor: '#2c6e49', borderWidth: 1, borderRadius: 1 },
|
||||
{ label: '10–19 %', data: HOUSING_BURDEN.from10to19, backgroundColor: '#27ae60cc', borderColor: '#27ae60', borderWidth: 1, borderRadius: 1 },
|
||||
{ label: '20–29 %', data: HOUSING_BURDEN.from20to29, backgroundColor: '#e67e22cc', borderColor: '#e67e22', borderWidth: 1, borderRadius: 1 },
|
||||
{ label: '30–39 %', data: HOUSING_BURDEN.from30to39, backgroundColor: '#e74c3ccc', borderColor: '#e74c3c', borderWidth: 1, borderRadius: 1 },
|
||||
{ label: '40–59 %', data: HOUSING_BURDEN.from40to59, backgroundColor: '#c0392bcc', borderColor: '#c0392b', borderWidth: 1, borderRadius: 1 },
|
||||
{ label: '60 %+', data: HOUSING_BURDEN.over60, backgroundColor: '#8b1a1acc', borderColor: '#8b1a1a', borderWidth: 1, borderRadius: 1 }
|
||||
]
|
||||
},
|
||||
options: chart1Opts
|
||||
});
|
||||
|
||||
// --- Diagram 2: Tung boutgiftsbyrde over tid (linje) ---
|
||||
|
||||
function buildLegend(containerId, items) {
|
||||
var container = document.getElementById(containerId);
|
||||
items.forEach(function(item) {
|
||||
var span = document.createElement('span');
|
||||
var swatch = document.createElement('span');
|
||||
swatch.className = 'swatch';
|
||||
swatch.style.background = item.color;
|
||||
span.appendChild(swatch);
|
||||
span.appendChild(document.createTextNode(item.name));
|
||||
container.appendChild(span);
|
||||
});
|
||||
}
|
||||
|
||||
var chart2Lines = [
|
||||
{ name: 'Laveste kvartil', data: HEAVY_BURDEN.q1, color: '#c0392b' },
|
||||
{ name: 'Andre kvartil', data: HEAVY_BURDEN.q2, color: '#e67e22' },
|
||||
{ name: 'Tredje kvartil', data: HEAVY_BURDEN.q3, color: '#1a4a8a' },
|
||||
{ name: 'Høyeste kvartil', data: HEAVY_BURDEN.q4, color: '#2c6e49' }
|
||||
];
|
||||
|
||||
buildLegend('legend2', chart2Lines);
|
||||
|
||||
var chart2Opts = baseOpts();
|
||||
chart2Opts.scales.y.max = 50;
|
||||
chart2Opts.plugins.tooltip = {
|
||||
callbacks: {
|
||||
label: function(c) {
|
||||
return ' ' + c.dataset.label + ': ' + fmtPct(c.parsed.y);
|
||||
}
|
||||
},
|
||||
backgroundColor: '#1a1714',
|
||||
titleColor: '#f5f2eb',
|
||||
bodyColor: 'rgba(245,242,235,0.7)',
|
||||
padding: 10,
|
||||
cornerRadius: 4,
|
||||
titleFont: { family: 'DM Sans', size: 12 },
|
||||
bodyFont: { family: 'DM Sans', size: 12 }
|
||||
};
|
||||
|
||||
new Chart(document.getElementById('chart2'), {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: YEARS,
|
||||
datasets: chart2Lines.map(function(line) {
|
||||
return {
|
||||
label: line.name,
|
||||
data: line.data,
|
||||
borderColor: line.color,
|
||||
backgroundColor: line.color + '12',
|
||||
fill: false,
|
||||
tension: 0.3,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 4,
|
||||
borderWidth: 2.5
|
||||
};
|
||||
})
|
||||
},
|
||||
options: chart2Opts
|
||||
});
|
||||
|
||||
// --- Diagram 3: Eierskap etter inntektsgruppe (horisontal stablet) ---
|
||||
|
||||
var chart3Opts = baseOpts();
|
||||
chart3Opts.indexAxis = 'y';
|
||||
chart3Opts.scales = {
|
||||
x: {
|
||||
stacked: true,
|
||||
max: 100,
|
||||
ticks: { color: TICK_COLOR, font: { size: 11, family: 'DM Sans' }, callback: v => v + ' %' },
|
||||
grid: { color: GRID_COLOR },
|
||||
border: { dash: [3, 3], color: 'transparent' }
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
ticks: { color: TICK_COLOR, font: { size: 11, family: 'DM Sans' }, autoSkip: false },
|
||||
grid: { display: false },
|
||||
border: { color: 'rgba(0,0,0,0.1)' }
|
||||
}
|
||||
};
|
||||
chart3Opts.plugins.tooltip = {
|
||||
mode: 'index',
|
||||
callbacks: {
|
||||
label: function(c) {
|
||||
return ' ' + c.dataset.label + ': ' + fmtPct(c.parsed.x);
|
||||
}
|
||||
},
|
||||
backgroundColor: '#1a1714',
|
||||
titleColor: '#f5f2eb',
|
||||
bodyColor: 'rgba(245,242,235,0.7)',
|
||||
padding: 10,
|
||||
cornerRadius: 4,
|
||||
titleFont: { family: 'DM Sans', size: 12 },
|
||||
bodyFont: { family: 'DM Sans', size: 12 }
|
||||
};
|
||||
|
||||
new Chart(document.getElementById('chart3'), {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: OWNERSHIP_GROUPS.map(function(g) { return g.name; }),
|
||||
datasets: [
|
||||
{
|
||||
label: 'Selveier',
|
||||
data: OWNERSHIP_GROUPS.map(function(g) { return g.selveier; }),
|
||||
backgroundColor: '#2c6e49cc',
|
||||
borderColor: '#2c6e49',
|
||||
borderWidth: 1,
|
||||
borderRadius: 2
|
||||
},
|
||||
{
|
||||
label: 'Andelseier',
|
||||
data: OWNERSHIP_GROUPS.map(function(g) { return g.andel; }),
|
||||
backgroundColor: '#1a4a8acc',
|
||||
borderColor: '#1a4a8a',
|
||||
borderWidth: 1,
|
||||
borderRadius: 2
|
||||
},
|
||||
{
|
||||
label: 'Leier',
|
||||
data: OWNERSHIP_GROUPS.map(function(g) { return g.leier; }),
|
||||
backgroundColor: '#c0392bcc',
|
||||
borderColor: '#c0392b',
|
||||
borderWidth: 1,
|
||||
borderRadius: 2
|
||||
}
|
||||
]
|
||||
},
|
||||
options: chart3Opts
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue