data1 = transpose (data1_) ;
data1_prop = transpose (data1_prop_) ;
data_tot = transpose (data_tot_) ;
data_tot_sex = transpose (data_tot_sex_) ;
data_indigenous = transpose (data_indigenous_)
data_indigenous_prop = transpose (data_indigenous_prop_)
data_cob = transpose (data_cob_)
data_pref_lan = transpose (data_pref_lan_)
years = Array . from ({ length : 2022 - 2008 + 1 } , (value , index) => 2008 + index) ;
years_transition = Array . from ({ length : 2021 - 2008 + 1 } , (value , index) => 2008 + index) ;
data_filtered = data1 . filter (x => x . year == year_selected1 & x . care_type == care_type_selected) ;
max_y = d3 . max (data_tot . filter (x => x . care_type == care_type_selected) , x => x . value ) ;
data1 =
Array(1416) [Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , …]
data1_prop =
Array(1416) [Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , …]
data_tot =
Array(708) [Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , …]
data_tot_sex =
Array(118) [Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , …]
data_indigenous =
Array(708) [Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , …]
data_indigenous_prop =
Array(1416) [Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , …]
data_cob =
Array(236) [Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , …]
data_pref_lan =
Array(177) [Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , …]
years =
Array(15) [2008 , 2009 , 2010 , 2011 , 2012 , 2013 , 2014 , 2015 , 2016 , 2017 , 2018 , 2019 , 2020 , 2021 , 2022 ]
years_transition =
Array(14) [2008 , 2009 , 2010 , 2011 , 2012 , 2013 , 2014 , 2015 , 2016 , 2017 , 2018 , 2019 , 2020 , 2021 ]
data_filtered =
Array(24) [Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , …]
In this article I share some interactive graphs that give insight into the people using aged care services in Australia. My aim was to highlight trends, age structure, and groups of interest. I extracted the data from the GEN website (Australian Institute of Health and Welfare 2023a ) and cleaned it to facilitate longitudinal comparison. For additional context and commentary, readers may find the article on the GEN website (Australian Institute of Health and Welfare 2023b ) useful as well.
Note that the fee structure was changed in 2014, which may explain the lower use of respite residential care that year.
{
const p = document . createElement ( "h3" ) ;
p . appendChild ( document . createTextNode ( "People receiving " + care_type_selected . toLowerCase () + " visualised" )) ;
return p ;
}
People receiving permanent residential care visualised
care_type = function (x = "" , y = "" , padding = true ) {
const p = document . createElement ( "div" ) ;
if (padding) {p . className = "my-title" ; }
p . appendChild ( document . createTextNode (x + care_type_selected . toLowerCase () + y)) ;
return p ;
}
care_type ( "Number of individuals of each age among people receiving " , "" , false )
Number of individuals of each age among people receiving permanent residential care
Plot . plot ({
color : { domain : ages_in_order , legend : true } ,
y : {
label : "Number of people"
} ,
x : { label : "Year" ,
tickFormat : ".0f" } ,
marks : [
Plot . areaY (data_tot . filter (x => x . care_type == care_type_selected) , { x : "year" , y : "value" , fill : "age" }) ,
Plot . ruleY ([ 0 ])
] ,
margin : 45
})
0-49 50-54 55-59 60-64 65-69 70-74 75-79 80-84 85-89 90-94 95-99 100+
0 20,000 40,000 60,000 80,000 100,000 120,000 140,000 160,000 180,000 ↑ Number of people 2008 2010 2012 2014 2016 2018 2020 2022 Year →
care_type ( "Number of people receiving " , " stratified by sex" )
Number of people receiving permanent residential care stratified by sex
Plot . plot ({
color : { legend : true } ,
y : {
label : "Number of people"
} ,
x : { label : "Year" ,
tickFormat : ".0f" } ,
marks : [
Plot . areaY (data_tot_sex . filter (x => x . care_type == care_type_selected) , { x : "year" , y : "value" , fill : "sex" }) ,
Plot . ruleY ([ 0 ])
] ,
margin : 45
})
Female Male
0 20,000 40,000 60,000 80,000 100,000 120,000 140,000 160,000 180,000 ↑ Number of people 2008 2010 2012 2014 2016 2018 2020 2022 Year →
care_type ( "Number of indigenous Australians of each age among people receiving " )
Number of indigenous Australians of each age among people receiving permanent residential care
Plot . plot ({
color : { legend : true } ,
y : {
label : "Number of indigenous Australians"
} ,
x : { label : "Year" ,
tickFormat : ".0f" } ,
marks : [
Plot . areaY (data_indigenous . filter (x => x . care_type == care_type_selected) , { x : "year" , y : "value" , fill : "age" }) ,
Plot . ruleY ([ 0 ])
] ,
margin : 45
})
0-49 100+ 50-54 55-59 60-64 65-69 70-74 75-79 80-84 85-89 90-94 95-99
0 200 400 600 800 1,000 1,200 1,400 1,600 1,800 2,000 ↑ Number of indigenous Australians 2008 2010 2012 2014 2016 2018 2020 2022 Year →
care_type ( "Number of people receiving " , " stratified by country of birth" )
Number of people receiving permanent residential care stratified by country of birth
Plot . plot ({
color : { legend : true } ,
y : {
label : "Number of people"
} ,
x : { label : "Year" ,
tickFormat : ".0f" } ,
marks : [
Plot . areaY (data_cob . filter (x => x . care_type == care_type_selected) , { x : "year" , y : "value" , fill : "country_of_birth" }) ,
Plot . ruleY ([ 0 ])
] ,
margin : 45
})
Australia Non-English-speaking country Not stated country of birth Other main-English-speaking country
0 20,000 40,000 60,000 80,000 100,000 120,000 140,000 160,000 180,000 ↑ Number of people 2008 2010 2012 2014 2016 2018 2020 2022 Year →
care_type ( "Number of people receiving " , " stratified by preferred language" )
Number of people receiving permanent residential care stratified by preferred language
Plot . plot ({
color : { legend : true } ,
y : {
label : "Number of people"
} ,
x : { label : "Year" ,
tickFormat : ".0f" } ,
marks : [
Plot . areaY (data_pref_lan . filter (x => x . care_type == care_type_selected) , { x : "year" , y : "value" , fill : "preferred_language" }) ,
Plot . ruleY ([ 0 ])
] ,
margin : 45
})
English Not stated/inadequately described Other languages
0 20,000 40,000 60,000 80,000 100,000 120,000 140,000 160,000 180,000 ↑ Number of people 2008 2010 2012 2014 2016 2018 2020 2022 Year →
care_type ( "Number of people receiving " , " by age and sex" )
Number of people receiving permanent residential care by age and sex
viewof year_selected1 = Scrubber (care_type_selected == "Transition care" ? years_transition : years , { step : 1 , delay : 250 , loop : false , autoplay : false , label : "Year: " }) ;
ages_in_order = [ '0-49' , '50-54' , '55-59' , '60-64' , '65-69' , '70-74' , '75-79' ,
'80-84' , '85-89' , '90-94' , '95-99' , '100+' ]
Plot . plot ({
marks : [
Plot . barY (data_filtered ,
Plot . groupX ({ y : "sum" } ,
{ x : "age" ,
y : 'value' ,
fill : 'sex' ,
tip : true })) ,
Plot . ruleY ([ 0 ])
] ,
x : { domain : ages_in_order , label : 'Age group' } ,
y : { label : 'Number of people' , domain : [ 0 , max_y]} ,
color : { domain : [ 'M' , 'F' ] , range : [ '#FC3E3E' , '#009486' ] ,
legend : true }
})
ages_in_order =
Array(12) ["0-49" , "50-54" , "55-59" , "60-64" , "65-69" , "70-74" , "75-79" , "80-84" , "85-89" , "90-94" , "95-99" , "100+" ]
M F
0 5,000 10,000 15,000 20,000 25,000 30,000 35,000 40,000 45,000 ↑ Number of people 0-49 50-54 55-59 60-64 65-69 70-74 75-79 80-84 85-89 90-94 95-99 100+ Age group
pyramid_chart = function () {
function me (selection) {
const data = selection . datum () . data ;
const group_by = selection . datum () . group_by
const group1 = selection . datum () . group1 ;
const group1_label = selection . datum () . group1_label ;
const group2_label = selection . datum () . group2_label ;
const max_x = selection . datum () . max_x ;
const margin = ({ top : 10 , right : 0 , bottom : 50 , left : 0 }) ;
const height = data . length / 2 * 25 + margin . top + margin . bottom ;
const x_label = selection . datum () . x_label
const svg = selection . append ( "svg" )
. attr ( "viewBox" , [ 0 , 0 , width , height])
. attr ( "font-family" , "sans-serif" )
. attr ( "font-size" , 15 ) ;
const xM = d3 . scaleLinear ()
. domain ([ 0 , max_x])
. rangeRound ([width / 2 , margin . left ]) ;
const xF = d3 . scaleLinear ()
. domain (xM . domain ())
. rangeRound ([width / 2 , width - margin . right ])
const y = d3 . scaleBand ()
. domain (data . map (d => d . age ))
. rangeRound ([height - margin . bottom , margin . top ])
. padding ( 0.1 )
const xAxis = g => g
. attr ( "transform" , `translate(0, ${ height - margin . bottom } )` )
. call (g => g . append ( "g" ) . call (d3 . axisBottom (xM) . ticks (width / 80 , "s" )))
. call (g => g . append ( "g" ) . call (d3 . axisBottom (xF) . ticks (width / 80 , "s" )))
. call (g => g . selectAll ( ".domain" ) . remove ())
. call (g => g . selectAll ( ".tick:first-of-type" ) . remove ())
const yAxis = g => g
. attr ( "transform" , `translate( ${ xM ( 0 ) } ,0)` )
. call (d3 . axisRight (y) . tickSizeOuter ( 0 ))
. call (g => g . selectAll ( ".tick text" ) . attr ( "fill" , "black" )
. attr ( "font-family" , "sans-serif" )
. attr ( "font-size" , 15 ))
svg . append ( "g" )
. selectAll ( "rect" )
. data (data)
. join ( "rect" )
. attr ( "fill" , d => [selection . datum () . color2 , selection . datum () . color1 ][d[group_by] === group1 ? 1 : 0 ])
. attr ( "x" , d => d[group_by] === group1 ? xM (d . value ) : xF ( 0 ))
. attr ( "y" , d => y (d . age ))
. attr ( "width" , d => d[group_by] === group1 ? xM ( 0 ) - xM (d . value ) : xF (d . value ) - xF ( 0 ))
. attr ( "height" , y . bandwidth ()) ;
svg . append ( "text" )
. attr ( "text-anchor" , "end" )
. attr ( "fill" , "black" )
. attr ( "dy" , "0.35em" )
. attr ( "x" , xM ( 0 ) - 30 )
. attr ( "y" , y (data[ 0 ] . age ) + y . bandwidth () / 2 )
. text (group1_label) ;
svg . append ( "text" )
. attr ( "text-anchor" , "start" )
. attr ( "fill" , "black" )
. attr ( "dy" , "0.35em" )
. attr ( "x" , xF ( 0 ) + 60 )
. attr ( "y" , y (data[ 0 ] . age ) + y . bandwidth () / 2 )
. text (group2_label) ;
svg . append ( "g" )
. call (xAxis) ;
svg . append ( "g" )
. call (yAxis) ;
svg . append ( "text" )
. attr ( 'x' , ( xF ( 0 ) + xM ( 0 )) / 2 )
. attr ( 'y' , height - 10 )
. attr ( "text-anchor" , "middle" )
. text (x_label) ;
return me ;
} ;
return me ;
}
care_type ( "Number of individuals of each age among people receiving " , " stratified by sex" )
Number of individuals of each age among people receiving permanent residential care stratified by sex
viewof year_selected2 = Scrubber (care_type_selected == "Transition care" ? years_transition : years , { step : 1 , delay : 250 , loop : false , autoplay : false , label : "Year: " }) ;
{
const div = d3 . create ( "div" ) ;
const pyramid = pyramid_chart () ;
div . datum ({ color1 : '#FC3E3E' ,
color2 : '#009486' ,
group_by : 'sex' ,
group1 : 'M' ,
group1_label : 'Male' ,
group2_label : 'Female' ,
data : data1 . filter (x => x . care_type == care_type_selected & x . year == year_selected2) ,
max_x : d3 . max (data1 . filter (x => x . care_type == care_type_selected) ,
x => x . value ) ,
x_label : "← number of people →" }) . call (pyramid) ;
return div . node () ;
}
Male Female 5k 10k 15k 20k 25k 30k 5k 10k 15k 20k 25k 30k 0-49 50-54 55-59 60-64 65-69 70-74 75-79 80-84 85-89 90-94 95-99 100+ ← number of people →
care_type ( "Proportion of individuals of each age among people receiving " , " stratified by sex" )
Proportion of individuals of each age among people receiving permanent residential care stratified by sex
viewof year_selected3 = Scrubber (care_type_selected == "Transition care" ? years_transition : years , { step : 1 , delay : 250 , loop : false , autoplay : false , label : "Year: " }) ;
{
const div = d3 . create ( "div" ) ;
const pyramid = pyramid_chart () ;
div . datum ({ color1 : '#FC3E3E' ,
color2 : '#009486' ,
group_by : 'sex' ,
group1 : 'M' ,
group1_label : 'Male' ,
group2_label : 'Female' ,
data : data1_prop . filter (x => x . care_type == care_type_selected & x . year == year_selected3) ,
max_x : d3 . max (data1_prop . filter (x => x . care_type == care_type_selected) , x => x . value ) ,
x_label : "← proportion of people (%) →" }) . call (pyramid) ;
return div . node () ;
}
Male Female 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 0-49 50-54 55-59 60-64 65-69 70-74 75-79 80-84 85-89 90-94 95-99 100+ ← proportion of people (%) →
data_sex = transpose (data_sex_)
data_sex =
Array(765) [Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , Object , …]
care_type ( "Number of individuals in chosen age group among people receiving " , " stratified by sex" )
Number of individuals in chosen age group among people receiving permanent residential care stratified by sex
viewof age_group_selected1 = Scrubber ([
'0-49' , '50-54' , '55-59' , '60-64' , '65-69' , '70-74' , '75-79' , '80-84' , '85-89' , '90-94' , '95-99' , '100+' ] , { step : 1 , delay : 250 , loop : false , autoplay : false , label : "Age: " }) ;
age_group_selected1 = "0-49"
Plot . plot ({
color : { legend : true , domain : [ 'M' , 'F' ] , range : [ '#FC3E3E' , '#009486' ]} ,
y : {
label : "Number of people"
} ,
x : { label : "Year" ,
tickFormat : ".0f" } ,
marks : [
Plot . areaY (data1 . filter (x => x . care_type == care_type_selected &
x . age == age_group_selected1) , { x : "year" , y : "value" , fill : "sex" }) ,
Plot . ruleY ([ 0 ])
] ,
margin : 45
})
M F
0 100 200 300 400 500 600 700 800 ↑ Number of people 2008 2010 2012 2014 2016 2018 2020 2022 Year →
care_type ( "Proportion of individuals that are female in chosen age group among people receiving " )
Proportion of individuals that are female in chosen age group among people receiving permanent residential care
viewof age_group_selected2 = Scrubber ([
'0-49' , '50-54' , '55-59' , '60-64' , '65-69' , '70-74' , '75-79' , '80-84' , '85-89' , '90-94' , '95-99' , '100+' ] , { step : 1 , delay : 250 , loop : false , autoplay : false , label : "Age: " }) ;
Plot . line (
data_sex . filter (x => x . care_type == care_type_selected &
x . age_group == age_group_selected2) ,
{ x : "year" , y : "PercentFemale" }
) . plot ({
x : {
label : "Year" ,
tickFormat : ".0f"
} ,
y : {
label : "Female (%)" ,
domain : [ 0 , 100 ]
}})
age_group_selected2 = "0-49"
0 10 20 30 40 50 60 70 80 90 100 ↑ Female (%) 2008 2010 2012 2014 2016 2018 2020 2022 Year →
care_type ( "Proportion of individuals of each age among people receiving " , " stratified by indigenous status" ) ;
Proportion of individuals of each age among people receiving permanent residential care stratified by indigenous status
viewof year_selected4 = Scrubber (care_type_selected == "Transition care" ? years_transition : years , { step : 1 , delay : 250 , loop : false , autoplay : false , label : "Year: " }) ;
{
const div = d3 . create ( "div" ) ;
const pyramid = pyramid_chart () ;
div . datum ({ color1 : '#0380B8' ,
color2 : '#C6D0CF' ,
group_by : 'indigenous_status' ,
group1 : 'Indigenous' ,
group1_label : 'Indigenous' ,
group2_label : 'Non-indigenous' ,
data : data_indigenous_prop . filter (x => x . care_type == care_type_selected & x . year == year_selected4) ,
max_x : d3 . max (data_indigenous_prop . filter (x => x . care_type == care_type_selected) , x => x . value ) ,
x_label : "← proportion of people (%) →" }) . call (pyramid) ;
return div . node () ;
}
Indigenous Non-indigenous 2 4 6 8 10 12 14 16 18 20 22 24 26 28 2 4 6 8 10 12 14 16 18 20 22 24 26 28 0-49 50-54 55-59 60-64 65-69 70-74 75-79 80-84 85-89 90-94 95-99 100+ ← proportion of people (%) →
function Scrubber (values , {
format = value => value ,
initial = 0 ,
delay = null ,
autoplay = true ,
loop = true ,
loopDelay = null ,
alternate = false ,
label = "Age: "
} = {}) {
values = Array . from (values) ;
const form = html `<form style="font: 8px var(--sans-serif); font-variant-numeric: tabular-nums; display: flex; height: 50px; align-items: center; vertical-align: top;">
<label style="display: flex; align-items: center;">
${ label }
<output name=o style="margin-left: 0.5em;margin-right: 1em;width: 3em"></output>
<input name=i type=range min=0 max= ${ values . length - 1 } value= ${ initial } step=1 style="width: 180px;">
</label>
<button name=b type=button style="margin-left: 1em;"></button>
</form>` ;
let frame = null ;
let timer = null ;
let interval = null ;
let direction = 1 ;
function start () {
form . b . textContent = "Pause" ;
if (delay === null ) frame = requestAnimationFrame (tick) ;
else interval = setInterval (tick , delay) ;
}
function stop () {
form . b . textContent = "Play" ;
if (frame !== null ) cancelAnimationFrame (frame) , frame = null ;
if (timer !== null ) clearTimeout (timer) , timer = null ;
if (interval !== null ) clearInterval (interval) , interval = null ;
}
function running () {
return frame !== null || timer !== null || interval !== null ;
}
function tick () {
if (form . i . valueAsNumber === (direction > 0 ? values . length - 1 : direction < 0 ? 0 : NaN )) {
if ( ! loop) return stop () ;
if (alternate) direction = - direction ;
if (loopDelay !== null ) {
if (frame !== null ) cancelAnimationFrame (frame) , frame = null ;
if (interval !== null ) clearInterval (interval) , interval = null ;
timer = setTimeout (() => ( step () , start ()) , loopDelay) ;
return ;
}
}
if (delay === null ) frame = requestAnimationFrame (tick) ;
step () ;
}
function step () {
form . i . valueAsNumber = (form . i . valueAsNumber + direction + values . length ) % values . length ;
form . i . dispatchEvent ( new CustomEvent ( "input" , { bubbles : true })) ;
}
form . i . oninput = event => {
if ( event && event . isTrusted && running ()) stop () ;
form . value = values[form . i . valueAsNumber ] ;
form . o . value = format (form . value , form . i . valueAsNumber , values) ;
} ;
form . b . onclick = () => {
if ( running ()) return stop () ;
direction = alternate && form . i . valueAsNumber === values . length - 1 ? - 1 : 1 ;
form . i . valueAsNumber = (form . i . valueAsNumber + direction) % values . length ;
form . i . dispatchEvent ( new CustomEvent ( "input" , { bubbles : true })) ;
start () ;
} ;
form . i . oninput () ;
if (autoplay) start () ;
else stop () ;
Inputs . disposal (form) . then (stop) ;
return form ;
}