Electricity Data Visualisation Experiments

I have produced a number of visualisations of our households electricity data for other projects. This note is just a reference note with all the visualisations that I produced to become familar with the data and visualisation frameworks. They are based on data on electricity consumption from from El Overblik.

Household electricity consumption from 2018 to 2022

Show the code
// Loading our data file
months = FileAttachment("/public/data/el-consumption-months-all.csv").csv({typed:true}).then(raw => {
    //We want to simplify our objects and translate the values into appropriate formats.
    //We keep the date and the value
    raw = raw.filter(o => {
        var str = o["Fra_dato"].substring(6,10)
        return o["Type"] === "Tidsserie" && str != "2017"
    })
    raw = raw.map(o => {
            var d_str = o["Fra_dato"].replace(" 00:00:00", "").split("-")
            var d = new Date(d_str[2],parseInt(d_str[1])-1)
            var v = typeof o["Mængde"] == "string" ? parseInt(o["Mængde"]) : o["Mængde"]
            // We add a couple of things. The fakedate allow us to place all the values in the same year (to wrangle )
            return {date: d, kwhs: v, year: d.getFullYear(), month:d.getMonth(), fakedate: new Date(2022,d.getMonth())}
    })

    return raw.sort((a,b) => {
        return a["date"] > b["date"]
    })
})
Show the code
Plot.plot({
    width:860,
    height:400,
    marginLeft: 50,
    legend: true,
    x: {
        ticks: 20,
        grid: true,
        label: "Months",
        domain: [months[0]["date"],months[months.length-1]["date"]]
    },
    y: {
        label: "Consumption (kwhs)",
        grid: true,
        domain: [200, 550]
    },
    //caption: html`Figure 1. Household electricity consumption from 2018 to 2022 per month`,
    marks: [
        Plot.line(months, {x: "date", y: "kwhs", stroke: "darkblue", strokeWidth: 1, curve: "natural"}),
    ]
})

Figure 1: Household electricity consumption from 2018 to 2022 per month

Yearly household electricity consumption per year

Show the code
Plot.plot({
    width:860,
    height:400,
    marginLeft: 50,
    color: {
        type: "categorical",
        scheme: "Dark2",
        legend: true
    },
    x: {
        ticks: 12,
        grid: true,
        label: "Months",
        type: "time",
        tickFormat: "%B",
        domain: [new Date(2022,0,1),new Date(2022,11,1)]
    },
    y: {
        label: "Consumption (kwhs)",
        grid: true,
        domain: [200, 550]
    },
    //caption: html`Figure 2. Yearly household electricity consumption with monthly data points.`,
    marks: [
        Plot.line(months, {filter: d => d.year == 2018, x: "fakedate", y: "kwhs", stroke: "year", strokeWidth: 2, curve: "natural"}),
        Plot.line(months, {filter: d => d.year == 2019, x: "fakedate", y: "kwhs", stroke: "year", strokeWidth: 2, curve: "natural"}),
        Plot.line(months, {filter: d => d.year == 2020, x: "fakedate", y: "kwhs", stroke: "year", strokeWidth: 2, curve: "natural"}),
        Plot.line(months, {filter: d => d.year == 2021, x: "fakedate", y: "kwhs", stroke: "year", strokeWidth: 2, curve: "natural"}),
        Plot.line(months, {filter: d => d.year == 2022, x: "fakedate", y: "kwhs", stroke: "year", strokeWidth: 2, curve: "natural"})
    ]
})

Figure 2: Yearly household electricity consumption with monthly data points.

Daily electricity consumption with median

Show the code
// Loading our data file
days_and_median = FileAttachment("/public/data/el-consumption-days-all.csv").csv({typed:true}).then(raw => {
    //We want to simplify our objects and translate the values into appropriate formats.
    //We keep the date and the value
    raw = raw.filter(o => {
        var str = o["Fra_dato"].substring(6,10)
        return o["Type"] === "Tidsserie" && str != "2017"
    })

    var median = {}

    raw = raw.map(o => {
            var d_str = o["Fra_dato"].replace(" 00:00:00", "").split("-")
            var d = new Date(d_str[2],parseInt(d_str[1])-1, d_str[0])
            var f = new Date(2022,parseInt(d_str[1])-1, d_str[0])
            var v = typeof o["Mængde"] == "string" ? parseInt(o["Mængde"]) : o["Mængde"]

            if(!median[f]){
                median[f] = {date:f, values: [], median:0}
            }
            median[f].values.push(v)

            // We add a couple of things. The fakedate allow us to place all the values in the same year (to wrangle )
            return {date: d, kwhs: v, year: d.getFullYear(), month:d.getMonth(), fakedate: f}
    })

    raw.sort((a,b) => {
        return a["date"] > b["date"]
    })

    median = Object.values(median)

    median.sort((a,b) => {
        return a["date"] > b["date"]
    })

    median.map(o => {
        o.median = d3.median(o.values)
    })

    return [raw, median]
})
Show the code
Plot.plot({
    width:860,
    height:400,
    marginLeft: 50,
    color: {
        type: "categorical",
        scheme: "Dark2",
        legend: true
    },
    x: {
        ticks: 12,
        grid: true,
        label: "Months",
        type: "time",
        tickFormat: "%B",
        domain: [new Date(2022,0,1),new Date(2022,11,31)]
    },
    y: {
        label: "Consumption (kwhs)",
        grid: true,
        domain: [0, 40]
    },
    //caption: html`Figure 3. Yearly household electricity consumption with median (green)`,
    marks: [
        Plot.line(days_and_median[0], {filter: d => d.year == 2018, x: "fakedate", y: "kwhs", stroke: "lightgrey", strokeWidth: 2}),
        Plot.line(days_and_median[0], {filter: d => d.year == 2019, x: "fakedate", y: "kwhs", stroke: "lightgrey", strokeWidth: 2}),
        Plot.line(days_and_median[0], {filter: d => d.year == 2020, x: "fakedate", y: "kwhs", stroke: "lightgrey", strokeWidth: 2}),
        Plot.line(days_and_median[0], {filter: d => d.year == 2021, x: "fakedate", y: "kwhs", stroke: "lightgrey", strokeWidth: 2}),
        Plot.line(days_and_median[0], {filter: d => d.year == 2022, x: "fakedate", y: "kwhs", stroke: "lightgrey", strokeWidth: 2}),
        Plot.line(days_and_median[1], {x: "date", y: "median", stroke: "green", strokeWidth: 2, curve: "natural"})
    ]
})

Figure 3: Yearly household electricity consumption with median (green)

Weekly household electricity consumption per year

Show the code
// Loading our data file

weeks = FileAttachment("/public/data/el-consumption-days-all.csv").csv({typed:true}).then(raw => {
    //We want to simplify our objects and translate the values into appropriate formats.
    //We keep the date and the value
    raw = raw.filter(o => {
        var str = o["Fra_dato"].substring(6,10)
        return o["Type"] === "Tidsserie" && str != "2017"
    })

    var weeks = {}

    raw = raw.map(o => {
        var d_str = o["Fra_dato"].replace(" 00:00:00", "").split("-")
        var d = new Date(d_str[2],parseInt(d_str[1])-1, d_str[0])
        var firstday = new Date(d.getFullYear(), 0, 1)
        var pastdays = (d - firstday) / 86400000;
        var week = Math.ceil((pastdays + firstday.getDay() + 1) / 7);
        var v = typeof o["Mængde"] == "string" ? parseInt(o["Mængde"]) : o["Mængde"]
        var year = d_str[2]

        if(!weeks[week]){
            weeks[week] = {2018:0,2019:0,2020:0,2021:0,2022:0, week:week}
        }
        
        weeks[week][year] += v
    })

    raw = Object.values(weeks)

    raw.sort((a,b) => {
        return a["week"] > b["week"]
    })
    
    raw = raw.map(o => {
        var inner = []
        var vals = []
        for(var key in o){
            if(key == "week"){
                continue
            }
            vals.push(o[key])
            inner.push({year:key, kwhs:o[key], week: o["week"]})
        }
        inner.push({year:"median",median:d3.median(vals), week: o["week"]})
        return inner
    }).flat()

    return raw
})
Show the code
Plot.plot({
    width:860,
    height:400,
    marginLeft: 50,
    color: {
        type: "categorical",
        scheme: "Dark2",
        legend: true
    },
    x: {
        ticks: 26,
        grid: true,
        label: "Weeks",
        domain: [1,53]
    },
    y: {
        label: "Consumption (kwhs)",
        grid: true,
        domain: [0, 150]
    },
    //caption: html`Figure 4. Weekly household electricity consumption per year`,
    marks: [
        Plot.line(weeks, {filter: d => d.year == 2018,x: "week", y: "kwhs", stroke: "year", strokeWidth: 1, curve: "natural"}),
        Plot.line(weeks, {filter: d => d.year == 2019,x: "week", y: "kwhs", stroke: "year", strokeWidth: 1, curve: "natural"}),
        Plot.line(weeks, {filter: d => d.year == 2020,x: "week", y: "kwhs", stroke: "year", strokeWidth: 1, curve: "natural"}),
        Plot.line(weeks, {filter: d => d.year == 2021,x: "week", y: "kwhs", stroke: "year", strokeWidth: 1, curve: "natural"}),
        Plot.line(weeks, {filter: d => d.year == 2022,x: "week", y: "kwhs", stroke: "year", strokeWidth: 1, curve: "natural"}),
        Plot.line(weeks, {filter: d => d.year == "median",x: "week", y: "median", stroke: "darkgreen", strokeWidth:2, curve: "natural"}),
    ]
})

Figure 4: Weekly household electricity consumption per year

Scatterplot comparing electricity consumption and daylight

Correlation between electricity consumption and daylight is weak with a correlation coefficient of -0.24

Show the code
// Loading our data file
days_daylight = FileAttachment("/public/data/el-consumption-days-all.csv").csv({typed:true}).then(raw => {
    var lat = 56.16, long=10.20
    
    //We want to simplify our objects and translate the values into appropriate formats.
    //We keep the date and the value
    raw = raw.filter(o => {
        var str = o["Fra_dato"].substring(6,10)
        return o["Type"] === "Tidsserie" && str != "2017"
    })

    raw = raw.map(o => {
        // Create a data object
        var dstr = o["Fra_dato"].replace(" 00:00:00", "").split("-")
        var date = new Date(`${dstr[2]}-${dstr[1]}-${dstr[0]}`)
      
        //Create object for date
            //Including data on the hours of the day
        var sun = SunCalc.getTimes(new Date(date), lat,long)
        var daylight = (sun.sunset - sun.sunrise) / 1000 / 60

        var v = typeof o["Mængde"] == "string" ? parseInt(o["Mængde"]) : o["Mængde"]

        return {date: date, kwhs:v, daylight:daylight}
    
    })

    //Sorting dates because the order is messy after conversion from object to array
    raw.sort((a,b) => {
        return a["date"] > b["date"]
    })

    return raw
})
Show the code
simplestats = require("simple-statistics@7")
Show the code
scatterplot = {

    const margin = {top: 30, right: 30, bottom: 50, left: 50}
    const width = 800
    const height = 600

    const x = d3.scaleLinear().domain([0,40]).range([margin.left,width-margin.right])
    const y = d3.scaleLinear().domain([400,1100]).range([height-margin.bottom,margin.top])

    const line = d3.line()
         .x(d => x(d.x))
         .y(d => y(d.y))

    const svg = d3.create("svg")
        .attr("viewBox", [0, 0, width, height]);
        
    svg.append("g")
        .attr("transform", `translate(0,${height - margin.bottom})`)
        .call(d3.axisBottom(x));

    svg.append("g")
        .attr("transform", `translate(${margin.left},0)`)
        .call(d3.axisLeft(y));

    svg.append("g")
        .selectAll(".dot")
        .data(days_daylight)
        .enter()
        .append("circle")
            .attr("cx", (d) => { 
                return x(d.kwhs)
            })
            .attr("cy", (d) => {
                return y(d.daylight)
            })
            .attr("r", 2)
            .style("fill", "steelblue");
            
    svg.append("text")
        .attr("font-family", "sans-serif")
        .attr("font-size", 12)
        .attr("text-anchor", "start")
        .attr("y", 0)
        .attr("dy", ".75em")
        .style("fill", "grey")
        .text("↓ Daylight in minutes");
    
    svg.append("text")
        .attr("font-family", "sans-serif")
        .attr("font-size", 12)
        .attr("text-anchor", "end")
        .attr("y", height-margin.bottom/2)
        .attr("x", width-margin.right)
        .attr("dy", ".75em")
        .style("fill", "grey")
        .text("← Consumption in Kilowatt hours");
    return svg.node()
}

Figure 5: Electricity consumption (kwhs) per day compared to daylight (minutes)