Monday, 29 November 2021

Discrete And Continuous Data Defined

When dealing with data, one of the things we should take note of is the type of the data. Is it numeric or string? Boolean? Date, even?

If the data is numeric, we would then have to consider whether the data in a dataset is discrete or continuous. It is only after such a classification, that an analyst would be able to decide on the best way to visualize this data.

Discrete data

Data is said to be discrete if there the data points are distinct and separate from other data points in the dataset. There are restrictions to the value that these data points can have. Usually, these restrictions take the form of the value being a whole number and not a fraction.

Take for example the number of silver cars in a showroom, the number of people attending a Rolling Stones concert, or the number of eggs sold in a supermart on weekends. You can't have a fraction of a car, or a person. And definitely not half an egg, at least, in raw form.

Number of silver cars in
a showroom.

Think of discrete values as countable data. Numbers that have to be counted.

Discrete data is easy to visualize. Bar charts, line charts, etc, all these handle discrete data quite readily.

Continuous data

Data is said to be continuous if the values of data points can take on any value within a certain range. This usually means that the data can be from an infinitely precise measurement.

Take for example the temperatures taken on a certain day of the week, the speed of the cars on the road, or the heights of different buildings on a street. The values are not restricted to whole numbers - a temperature can be a value like 30.01657 degrees Celcius, for instance. Depending on how precise you wish to be, the figure could go on even longer.

Measurable values.

Think of continuous data as measurable values. Values that have to be measured by an instrument such as a thermometer or gauge.

Continuous data is best represented by line charts. If grouped into discrete categories of ranges, they can also be represented much the same as discrete data.

What about monetary figures, such as profits?

One might be tempted to think of this as a continuous measure, because profits is a measure of money, which uses decimals. For example, the figure 1,400,450.25. However, the decimals are actually representations of cents. So in essence, the figure means 140,045,025 cents. It's not a fraction, exactly.

Money can be counted.

Also, remember, discrete figures are counted. Money is counted. Therefore, monetary figures are discrete.

In a nutshell

The difference between discrete and continuous data is straightforward in most cases. It is usually when decimal places are involved, where confusion sets in.

See you lata, alli-data!
T___T

Wednesday, 24 November 2021

When Zuckerberg Met(a) Facebook

What in the world just happened? Facebook is changing names!

Last month, in what feels like it should have been more of an earthshaking moment, Mark Zuckerberg announced in a series of public conferences, that Facebook would now be named Meta Platforms, Inc. It was not to be just a cosmetic name change. The change went deeper than just a name and logo.


The existing Social Media and communications platforms - Facebook, Instagram and WhatsApp - will take up half of Meta. The other half will be dedicated to the construction of a metaverse. Just what in the heck is a "metaverse", you may ask? Tt is a virtual world where, if Zuckerberg achieves his vision, users can interact with an augmented or virtual reality environment in real-time. Hence, "Meta".

A metaverse!

Cynically, of course, one could see this as a matter of branding for Zuckerberg, nothing more. If he's to be believed, this was the direction they were heading all along and the name change is just meant to reflect that. The problem, according to him, is that Facebook is currently seen a a Social Media company. (No shit, really?!)



This move might prove to be worthwhile for Zuckerberg. After all, the abovementioned platforms all depend on mobile technology for propagation. That makes Facebook, as a company, vulnerable to the rules imposed by Google and Apple. And we all know Facebook has faced strong opposition to both entities in the recent past.

But what if Zuckerberg were able to pivot Facebook to a new kind of platform, one which wasn't dominated by Google and Apple? I'm not even gonna front - the possibilities are, to say the least, intriguing.

The Meta-conclusion

Honestly, I can't see the name change sticking. We're talking about over ten years' worth of inertia at this point. And let's be real here - "Meta" isn't exactly catchy or original. And the less said about the sheer lameness of the logo, the better. Seriously, were they maybe hoping to be as unimpressive as possible?

What's more pertinent, however, is the nature of Facebook's - ahem, excuse me, Meta's - business model. If they are truly pivoting to virtual reality, that is a huge deal if they manage to pull it off. Will they? The technology is there, but how much of it could truly permeate a market where internet strength can be spotty at best? Your guess is as good as mine.

It's been real,
T___T

Friday, 19 November 2021

Thoughts On The Ongoing Millennial-Boomer War (Part 2/2)

OK! We've seen the millenial point of view, agreed with some of it and ripped into the rest. Boomers are going under the microscope next.

What Boomers say about Millennials

The younger generations are soft and spoilt. They're irresponsible, whiny snowflakes. They have no idea how hard things were back in the day. They're idealistic and self-righteous, and think that they can change the world by undoing the past. But their arrogance, combined with naivete, is their undoing. The world is what it is. What makes them think they can change it? They have no consistency. They're so entitled and don't believe in the values of hard work and loyalty, changing jobs on a whim. Young people just don't know what they want in life.

Why Boomers are right

Some things are not era-specific. Some principles apply regardless of the times we are currently living in. And it's these principles that make Boomers valuable, if they have been consistently applying them to their lives. Millennials would do well not to dismiss Boomers out of hand just because their words and experiences come from a different era. Not only is that lazy, it is foolhardy.

The Boomers also have a point about this overdoing of Woke Culture that seems to be a hallmark of this generation. It's one thing to advocate for awareness of Climate Change, Minority Rights, Social Justice, and so on. But digging through someone's tweets from ten years ago, and holding them accountable for those? Going way back into ancient history to attack long-dead personalities? That is so banal. Shouldn't the leaders of the future be more concerned with, y'know, the future?

Plant a seed of change,
not a tree.

Change is a natural aspect of our reality. Change occurs regardless of anyone's attempts to stop it. It also occurs at its own speed regardless of attempts to speed it up. So trying to effect change for the sake of change itself, is unproductive. If the younger generation pushes for change and encounters pushback, maybe it's time to consider that this isn't the time for that particular change. The overemphasis on things like Affirmative Action and erasure of potentially offensive terms in tech is what I call "trying too hard", and it's counter-productive.

Get the seeds of change right, and change will happen. Millennials need to stop trying to produce an entire tree overnight. There's a process to this shit!

Why Boomers are wrong

No matter what you may think of Millennials, remember that they are now the dominant majority demographic where the workforce is concerned. They will, by default, be the ones charting the course for the future. Who else is gonna do it? You, Boomers? Face it; your time was over ten years back. Whatever mistakes Millennials may or may not make, they are the ones who are going to have to live with it. Statistically, you are going to be long gone by then. You have no skin in the game. So stop trying to exercise influence over a world which you won't have to be responsible for.

You probably think Millennials change their minds way too often, as compared to the days where you stuck to one course your entire lives and never deviated? It's frightening that this lack of flexibility is actually considered a virtue.

People change their minds more often now because they can. It's called having options. Let's be real - if you had that many options back in the day, would you be doing what you're doing now? Not having options is not the same as being steadfast. The internet has opened up a multitude of options. There are multiple paths to success, and as the conditions for success change, so too, must the course you are on.

Agility is a mindset that is encouraged in the workforce today. The Agile Methodology is a concept bred in software and now business - that one must constantly make adjustments to work plans based on ever-evolving conditions. If people with static mindsets were in charge, we would not be seeing the great leaps in progress that we're seeing today.

Are Millennials really lazy?

As for knowing the value of hard work, quit the bullshit myth that Milllennials are lazy. They are just as capable of working hard... except that unlike you, apparently, they want their hard work to mean something. Hard work for the sake of hard work is even dumber than change for the sake of change. In your day, hard work was the only value you brought to the table. But in this day and age, any idiot can be hardworking. So if you want to get ahead, you better have a hell lot more to offer than your sweat, blood and tears.

And finally...

"In my day..." doesn't help your case at all. You're trying to make a point as to why things should be done a certain way. Even if people were interested in a history lesson, a history lesson from your personal point of view is pretty fucking useless except for entertainment purposes.

Also, stop with the unsolicited advice. If your advice was so valuable, people would be paying you to talk. Are people paying you to talk? No? Well, take that as a sign from the universe.

The war continues!

Nothing I've said here is going to change anything. People are going to think what they think and do what they do, even if it is absolutely stupid. It's a very human thing.

Did I mention I'm taking no sides here? Millennials and Boomers both suck. Seriously. Honestly, what kind of idiot thinks that simply being born in a particular era makes them special?

The world continues to spin no matter how important you think your perspectives are. Millennials, one day you will be those Boomers you love to shit on. Boomers, one day you will be corpses and distant memories. Get over yourselves, guys.

Tale as old as time!
T___T

Wednesday, 17 November 2021

Thoughts On The Ongoing Millennial-Boomer War (Part 1/2)

There's been a low-key war between Millennials and Baby Boomers (or "Boomers", for short) that has been simmering for years, and while other tiny petty wars have broken out, this particular one shows no signs of going away. Every other day, I see people dismissing the opinions of others citing the disparity of age as a reason. Both parties are equally guilty of this.

As a member of Generation Y, born in 1997, I fall fairly in the middle of these two sides and I've had to suffer douchey behavior eccentricities from both camps. And this is what I think. I think both sides are right... and also wrong.

This is what I have to say about it, and unsurprisingly, much of it will be from a software point of view.

What Millennials say about Boomers

They're always going on about how things should be, and are stubbornly against accepting new ideas. They refuse to accept that some things that were OK in the past are not OK now, and they don't understand the value of social justice. They think we're naive and idealistic, but we actually know more shit because we have the internet now. We keep ourselves informed; they go around spreading conspiracy theories. They're out of touch and just don't get it. They're preachy and think they know better simply because they're older. Simply put, they resist change and are afraid of it.

Why Millennials are right

Change is inevitable. Change takes place all the time. Tradition has its own undeniable charm, but it shouldn't be a ball and chain. Tradition has its place and more importantly, tradition must know its place.

What would the world be like if people insisted on doing things the way they've always been done? For one, the software industry, as we know it now, would not have made the advances it has made today or made such great leaps. Software development, by itself, is something that defies all tradition. It has automated jobs that once were thought to be the sole province of lifelong practitioners. Software development is the quintessential example of constant evolution.

The assertion that older people know better is laughable at best. You do not know better simply because you've experienced more. What you've experienced matters. Relevance of experience, rather than quantity, matters. You do not ask an experienced farmer to do the work of a chef, or vice versa. Growing corn and cooking corn are two entirely different skillsets.

You don't ask a chef
to grow corn.

If I were to apply for a job in Java despite having no professional experience in Java at all, and tried to use my decade of experience in JavaScript as a bargaining chip, I would be laughed out of the interview room. And I would deserve it.

Same with Boomers. Boomers need to stop using quantity of experience as a flex. If they must flex, they need to use both relevance and quantity. Unfortunately, relevance of their experience can be in short supply precisely because the world has changed a lot since, and will keep changing.

Why Millennials are wrong

On the other hand, while it's true that the younger generation is more tech savvy and thus knows more about gathering information online, the same logic - relevance over quantity - can and should be applied. In an age where online misinformation reigns supreme and there is no minimum I.Q requirement to post content, more information does not mean better. 10,000,000 or 100 times 0 is still 0.

And before people start yapping about how authentic their information is, stop right there. Everyone thinks their info is authentic, unbiased and the objective source of truth. You're not special in that regard.

And sure... Boomers can be preachy AF. They like to do this big song and dance about how things used to be, and how everything has gone to shit now. The thing is, Millennials don't exactly have a leg to stand on where preachiness is concerned. Climate Change, anyone? Gender Equality? Gay Rights? Oh yeah, you probably think you have a free pass to pontificate about how people should live their lives because your cause is more noble, right?

Get off your soapbox.
Nobody's impressed.

Please, fuck off that particular high horse. Preaching is preaching.

And finally...

"OK Boomer" sounds cool, you think? A nice pithy comeback to display your contempt without needing to resort to a lot of words, or worse, having to formulate a thoughtful response?

No, it's lazy. It means a whole lot of nothing. And overuse makes you look like a petulant child. It reinforces the harmful stereotype that young people have nothing but empty sarcasm as a response. You should be better than this. Either don't respond, or say something intelligent.

Next

Let's examine those goddamn Boomers.

Friday, 12 November 2021

Eyes Off The Prize!

The state of the world in recent years had led to some reflection. And I realized, with some surprise, that I was doing well. Like, really well. I had my own place with no debt, I was somewhat happily married, I had a career drawing a decent paycheck that I didn't even need.

And that led to another spate of reflection. How did a software developer of middling skill and mediocre experience ever manage to pull it off, especially considering all the bad decisions I had made in my youth? One could always say I got lucky, but that's really the lazy answer. After some more reflection, I had an even better answer. All this good fortune had taken place because I did one thing.

This is going to sound really counterintuitive, but here goes: take your eyes off the prize.

What I wanted from life

There were some really specific KPIs to meet.

Firstly, I measured my income against the national median, and vowed to meet and exceed that. I wanted a comfortable income to enjoy myself. Have fun.

Being a beefcake.

Secondly, I wanted to be buff. I wanted to be in the kind of shape I had been in my teens and during my days in the military.

Thirdly, like many young men, I obsessed over the fact that no women seemed interested in me and wondered how I would ever get a girlfriend. Pathetic? Yes, very. But again, I was young.

What I ended up doing

The story of how I eventually became a web developer has already been throughly explored. Suffice to say, I went through ups and downs - job losses, bad decisions and lots of hard work. I worked for tiny startups and shitty employers. In between, I kept going back to school and learning new things because no matter how much I learned, it never seemed to be enough. It was years of struggle, and what kept me sane was that I was actually doing things I enjoyed.

As I grew older, I found that going without some kind of exercise at least once every forty-eight hours would give me headaches. My blood circulation was poor and sitting still for long hours would make my back and knees hurt. To combat this, I got into the habit of swimming laps.

A plan to die alone.

As for women, after a long string of failed relationships early in my thirties, I gave up on the idea altogether and just bought my own place, intending to live and die alone, bachelor-style.

Ten years later

After taking stock, it appears that I succeeded beyond my wildest dreams, mostly without even noticing until recently.

Exceeding Singapore's
median income.

I took a look at my income recently and tabulated it, formulating a chart (see above) to compare it against the national median income of Singapore. And the result was staggering. I had surpassed it eight years ago without realizing it. Along the way, I had stopped trying to get rich and just wanted to make an honest living, and this was the result. It certainly did not hurt that the years spent being broke had conditioned me to lead a very low-maintenance lifestyle. My idea of "fun" now involved going to the library and watching the occasional movie in a theater. Not only was I earning more, I was spending less.

The amount of exercise I had done over the years, averaging about five days a week, has done wonders for my physique. While I'm no Mr Universe, I look pretty good with my shirt off, even at my age. And I did it all without checking myself out in the mirror every morning, obsessing over my measurements or counting calories. The purpose of all that exercise had been to stave off the aches and pains of the life of a computer geek, not to look good. But I do look good... at least from the neck down.

And out of nowhere, I'm married. Apparently, the fact that I'm so boring and live such a simple lifestyle is extremely attractive to women of a certain demographic... specifically women who have no use for pretty boys and honeyed words, who appreciate men that they don't have to babysit. Men who can take care of their own shit. Because if a man can't take care of himself, the chances of him stepping up to take care of someone else, are slim to none.

Finally...

Perhaps it's not really accurate to say I took my eyes off the prize. It's more like I redefined what the prize was, and ended up living my life a certain way in accordance to the new definitions. Whatever I achieved were side-effects that closely matched what my original goals were.

Still, food for thought!

Stay eye-dealistic!
T___T

Monday, 8 November 2021

Web Tutorial: The Highcharts Column Chart

In recent years, I've been doing quite a bit of work on data visualization. One of the nicer libraries out there on the market, from the few sampes I've seen, is Highcharts.

Today, I will be attempting to replicate what I previously did for bar charts in D3 and the old-fashioned way, but this time, using Highcharts. Let's explore what crazy new features we can unlock!

The data I will be using today is, again, from Liverpool Football Club. I am using updated statistics appearance and goal statistics for certain players, up to the 2020/2021 season.

On to the code!

As usual, we begin with some HTML. There should be a link to the Highcharts JavaScript file.
<!DOCTYPE html>
<html>
    <head>
        <title>Bar Chart</title>

        <style>

        </style>

        <script src="https://code.highcharts.com/highcharts.js"></script>

        <script>

        </script>
    </head>

    <body>

    </body>
</html>


Here, let's make sure we have a placeholder for the chart and the controls. These are divs with appropriate ids.
<body>
    <div id="container">

    </div>

    <div id="dashboard">

    </div>

</body>


The CSS styles show that both these divs will take up 100% width of the screen. container will have a height of 600 pixels while dashboard will be 100 pixels tall, and text will be aligned center.
<style>
    #container
    {
        width: 100%;
        height: 600px;
    }

    #dashboard
    {
        width: 100%;
        height: 100px;
        text-align: center;
    }

</style>


In the dashboard div, add label tags with select tags within. They will have ids ddlSeason and ddlStat respectively.
<div id="dashboard">
    <label for="ddlSeason">
        SEASON
        <select id="ddlSeason">

        </select>
    </label>

    <label for="ddlStat">
        STATISTICS
        <select id="ddlStat">
            <option value="appearances" selected>Appearances</option>
            <option value="goals">Goals</option>
        </select>
    </label>

</div>


Add two options for ddlStat. We are adding them manually since there are only two. They are appearances and goals.
<label for="ddlStat">
    STATISTICS
    <select id="ddlStat">
        <option value="appearances" selected>Appearances</option>
        <option value="goals">Goals</option>

    </select>
</label>


Now add styling for label tags. We set the display property to inline-block because we are going to set the widths as well. Color and font is up to you, though I'm going with the Liverpool red.
<style>
    #container
    {
        width: 100%;
        height: 600px;
    }

    #dashboard
    {
        width: 100%;
        height: 100px;
        text-align: center;
    }

    label
    {
        display: inline-block;
        width: 20em;
        font-family: verdana;
        color: rgba(200, 0, 0, 1);
    }

</style>


Now you see the controls at the dashboard area.


The Data

Now in the script tag, add an event listener to fire off when the page loads. Then outside of it, declare a constant, data. It will be an array.
<script>
    document.addEventListener("DOMContentLoaded", function () {
                
    });

    const data = [];

</script>


Here, we push an object into the data array with a key, "2016/2017". That is the name of the season and we will be using the season names for keys. Look at how the data is structured. The object has categories, appearances and goals as arrays. Highcharts will be using these as data series.
document.addEventListener("DOMContentLoaded", function () {
    data["2016/2017"] =
    {
        "categories": ["Roberto Firminho", "Jordan Henderson", "Sadio Mané", "Danny Ings"],
        "appearances": [41, 27, 29, 2],
        "goals": [12, 1, 13, 0]
    };    
            
});


Here, we input the rest of the seasons.
document.addEventListener("DOMContentLoaded", function () {
    data["2016/2017"] =
    {
        "categories": ["Roberto Firminho", "Jordan Henderson", "Sadio Mané", "Danny Ings"],
        "appearances": [41, 27, 29, 2],
        "goals": [12, 1, 13, 0]
    };

    data["2017/2018"] =
    {
        "categories": ["Roberto Firminho", "Jordan Henderson", "Sadio Mané", "Danny Ings", "Alex Oxlade-Chamberlain", "Mohd Salah"],
        "appearances": [54, 20, 44, 14, 42, 52],
        "goals": [27, 1, 20, 1, 5, 44]
    };

    data["2018/2019"] =
    {
        "categories": ["Roberto Firminho", "Jordan Henderson", "Sadio Mané", "Alex Oxlade-Chamberlain", "Mohd Salah", "Fabinho"],
        "appearances": [48, 44, 50, 2, 52, 41],
        "goals": [16, 1, 26, 0, 27, 1]
    };

    data["2019/2020"] =
    {
        "categories": ["Roberto Firminho", "Jordan Henderson", "Sadio Mané", "Alex Oxlade-Chamberlain", "Mohd Salah", "Fabinho"],
        "appearances": [52, 40, 47, 43, 48, 39],
        "goals": [12, 4, 22, 8, 23, 2]
    };

    data["2020/2021"] =
    {
        "categories": ["Roberto Firminho", "Jordan Henderson", "Sadio Mané", "Alex Oxlade-Chamberlain", "Mohd Salah", "Fabinho"],
        "appearances": [48, 28, 48, 17, 51, 42],
        "goals": [9, 1, 16, 1, 21, 0]
    }; 
               
});


Declare ddlSeason. We are going to set it to the element in the DOM. Then we declare keys and use it to get all the keys that we have entered into the data array so far.
document.addEventListener("DOMContentLoaded", function () {
    data["2016/2017"] =
    {
        "categories": ["Roberto Firminho", "Jordan Henderson", "Sadio Mané", "Danny Ings"],
        "appearances": [41, 27, 29, 2],
        "goals": [12, 1, 13, 0]
    };

    data["2017/2018"] =
    {
        "categories": ["Roberto Firminho", "Jordan Henderson", "Sadio Mané", "Danny Ings", "Alex Oxlade-Chamberlain", "Mohd Salah"],
        "appearances": [54, 20, 44, 14, 42, 52],
        "goals": [27, 1, 20, 1, 5, 44]
    };

    data["2018/2019"] =
    {
        "categories": ["Roberto Firminho", "Jordan Henderson", "Sadio Mané", "Alex Oxlade-Chamberlain", "Mohd Salah", "Fabinho"],
        "appearances": [48, 44, 50, 2, 52, 41],
        "goals": [16, 1, 26, 0, 27, 1]
    };

    data["2019/2020"] =
    {
        "categories": ["Roberto Firminho", "Jordan Henderson", "Sadio Mané", "Alex Oxlade-Chamberlain", "Mohd Salah", "Fabinho"],
        "appearances": [52, 40, 47, 43, 48, 39],
        "goals": [12, 4, 22, 8, 23, 2]
    };

    data["2020/2021"] =
    {
        "categories": ["Roberto Firminho", "Jordan Henderson", "Sadio Mané", "Alex Oxlade-Chamberlain", "Mohd Salah", "Fabinho"],
        "appearances": [48, 28, 48, 17, 51, 42],
        "goals": [9, 1, 16, 1, 21, 0]
    };

    var ddlSeason = document.getElementById("ddlSeason");
    var keys = Object.keys(data);     
           
});


And then we use a For loop to iterate through the keys array. And within the For loop, we append options to the drop-down list ddlSeason.
var ddlSeason = document.getElementById("ddlSeason");
var keys = Object.keys(data);

for(let i = 0; i < keys.length; i++)
{
    var option = document.createElement("option");
    option.value = keys[i];
    option.innerHTML = keys[i];
    ddlSeason.appendChild(option);
}


And here we see that the seasons are now in the first drop-down list.


Visualizing the data

Now we're going to weave some Highcharts magic! You will see firsthand how Highcharts generates a chart using just the data and configuration options you put in.

Add a call to function renderBarChart().
for(let i = 0; i < keys.length; i++)
{
    var option = document.createElement("option");
    option.value = keys[i];
    option.innerHTML = keys[i];
    ddlSeason.appendChild(option);
}

renderBarChart();


We'll declare renderBarChart() here.
    renderBarChart();                
});

function renderBarChart()
{

}


const data = [];


We start off the function by declaring season and stat, and setting them to the currently selected values of ddlSeason and ddlStat respectively.
function renderBarChart()
{
    var season = document.getElementById("ddlSeason").value;
    var stat = document.getElementById("ddlStat").value;

}


Now this next line is what renders the bar chart into the container div.
function renderBarChart()
{
    var season = document.getElementById("ddlSeason").value;
    var stat = document.getElementById("ddlStat").value;

    const chart = Highcharts.chart("container", {

    });

}


We are going to pass in some values into the configuration object. We then pass in the chart property, which is in turn an object. We have to specify the property type, which is "column". The next few are entirely optional. I have opted to give my chart a rounded red border.
const chart = Highcharts.chart("container", {
    chart:
    {
        type: "column",
        borderColor: "rgba(200, 0, 0, 1)",
        borderRadius: 10,
        borderWidth: 2,
    }

});


You will see that the default title, since we have not yet specified it, is "Chart title".


Editor's Note: The screenshots provided for the title are inaccurate due to an error in the code, retroactively detected.

Let's specify this using the title property. Again, it's an object and I have opted to provide some (entirely optional) styling.
const chart = Highcharts.chart("container", {
    chart:
    {
        type: "column",
        borderColor: "rgba(200, 0, 0, 1)",
        borderRadius: 10,
        borderWidth: 2,
    },
    title:
    {
        text: "Liverpool FC",
        style: { "color": "rgba(200, 0, 0, 1)", "font-size": "2.5em", "font-weight": "bold" }
    }

});


There you go!


Let's add a subtitle.
title:
{
    text: "Liverpool FC",
    style: { "color": "rgba(200, 0, 0, 1)", "font-size": "2.5em", "font-weight": "bold" }
},
subtitle:
{
    text: "Football statistics by TeochewThunder",
    style: { "color": "rgba(200, 0, 0, 0.8)", "font-size": "0.8em" }
}


Here's how it looks.


Now let's add the xAxis and series properties. These are what creates the bars. xAxis is an object which contains the categories property - it's an array and we will use the categories property of the currently selected season of the data array. That is in turn another array, and will contain the player names.
subtitle:
{
    text: "Football statistics by TeochewThunder",
    style: { "color": "rgba(200, 0, 0, 0.8)", "font-size": "0.8em" }
},
xAxis:
{
    categories: data[season]["categories"]
}


The series property is an object which contains the name and data properties. name is just a label - we will use season and stat to create it. For data, we will use the stat property of the currently selected season of the data array.
subtitle:
{
    text: "Football statistics by TeochewThunder",
    style: { "color": "rgba(200, 0, 0, 0.8)", "font-size": "0.8em" }
},
xAxis:
{
    categories: data[season]["categories"]
},
series:
[                        
    {
        name: season + " " + stat,
        data: data[season][stat]
    }
]


Here are the bars! There's a nice animation too. And we didn't even have to code it! The default color is a light blue.


We add the specification for colors. This time, it's just an array, not an object.
subtitle:
{
    text: "Football statistics by TeochewThunder",
    style: { "color": "rgba(200, 0, 0, 0.8)", "font-size": "0.8em" }
},
colors: ["rgba(200, 0, 0, 1)"],
xAxis:
{
    categories: data[season]["categories"]
},


Presto!


Hey we can pretty this up a bit by adding a rounded yellow border.
{
    name: season + " " + stat,
    data: data[season][stat],
    borderRadius: 5,
    borderColor: "rgba(255, 200, 0, 1)",
    borderWidth: 3

}


Nice! Also see that when you mouse over one of the bars, there's additional info about it.


Now, we don't want the word "values" at the side. It's redundant. So do this. Add the yAxis property. It's an object that contains the title property. Here you can specify the text you want in place of "values", and I'm just going to specify an empty string. I also specify gridLineColor and tickColor to use a translucent red, but this is purely cosmetic.
xAxis:
{
    categories: data[season]["categories"]
},
yAxis:
{
    title:
    {
        text: ""
    },
    gridLineColor: "rgba(200, 0, 0, 0.2)",
    tickColor: "rgba(200, 0, 0, 0.2)"
},

series:
[                        
    {
        name: season + " " + stat,
        data: data[season][stat],
        borderRadius: 5,
        borderColor: "rgba(255, 200, 0, 1)",
        borderWidth: 3
    }
]


Yep.


Adding an average line

The easiest way to implement this in Highcharts, is by adding a spline to the chart. But before that, we need to calculate the data. First, declare an empty array, arrAverage and total, which is initialized to 0.
var season = document.getElementById("ddlSeason").value;
var stat = document.getElementById("ddlStat").value;

var arrAverage = [];
var total = 0;


const chart = Highcharts.chart("container", {


Then use a For loop to iterate through the selected dataset. Add all the values found to total.
var arrAverage = [];
var total = 0;

for(let i = 0; i < data[season][stat].length; i++)
{
    total += data[season][stat][i];
}


Iterate through the dataset again. This time we will push the calculated average into arrAverage. This means you will get an array full of identical values. Fear not, this is exactly what we want.
var arrAverage = [];
var total = 0;

for(let i = 0; i < data[season][stat].length; i++)
{
    total += data[season][stat][i];
}

for(let i = 0; i < data[season][stat].length; i++)
{
    arrAverage.push(parseFloat((total / data[season][stat].length).toFixed(2)));
}


Now add one more object in the series array, just before the part which renders the bars (or "columns", as Highcharts likes to call them). Here, we specify the name, type (use "spline" to ensure you get a line), and data which is arrAverage.
series:
[                        
    {
        name: "Average " + stat + " for " + season,
        type: "spline",
        data: arrAverage
    }, 
                       
    {
        name: season + " " + stat,
        data: data[season][stat],
        borderRadius: 5,
        borderColor: "rgba(255, 200, 0, 1)",
        borderWidth: 3
    }
]


Now the line appears!


I kind of want the line to be a different color and use a dotted line, so I'm going to add these specifications. These are optional, by the way.
series:
[                        
    {
        name: "Average " + stat + " for " + season,
        type: "spline",
        data: arrAverage,
        lineColor: "rgba(255, 200, 0, 1)",
        lineWidth: 3,
        dashStyle: "Dot",
        marker:
        {
            fillColor: "rgba(255, 200, 0, 1)"
        }

    },                        
    {
        name: season + " " + stat,
        data: data[season][stat],
        borderRadius: 5,
        borderColor: "rgba(255, 200, 0, 1)",
        borderWidth: 3
    }
]


Now you see that the line looks different.


One last thing...

Add a change handler to the drop-down lists. This ensures that every time you change a value, the chart re-renders.
<label for="ddlSeason">
    SEASON
    <select id="ddlSeason" onchange="renderBarChart()">

    </select>
</label>

<label for="ddlStat">
    STATISTICS
    <select id="ddlStat" onchange="renderBarChart()">
        <option value="appearances" selected>Appearances</option>
        <option value="goals">Goals</option>
    </select>
</label>


We're done!

This was awesome. And so little effort too.

Highcharts does make a lot of things easier with their sensible defaults. I hope to bring more web tutorials like this, to this blog soon.

Keep column and carry on,
T___T

Tuesday, 2 November 2021

Writing Software and Fiction

Lately, I have been pondering differences between writing - specifically, between writing software and writing fiction. And the conclusions I came to were interesting. But before I continue, allow me to provide some context.

Background

Earlier this year, on the Clubhouse app, I had the pleasure of attending reading rooms where the Moderator would read aloud some fiction written by author Nathalie Taghaboni. It was a trilogy set in Trinidad and Tabago, The Savanoy Series.

The Savanoy Series.

After each session, speakers on stage would discuss what they had just listened to. Sometimes the author herself would be present. On one such occasion, one speaker was opining as to what changes he would make to the story, and I cheekily suggested a fan-fiction site where he could do just that. Ms Taghaboni was flabbergasted upon discovering the concept of fan-fiction, and seemed highly disturbed. I think she even described it as "disrespectful".

I had a hard time wrapping my head around why she would think so. Wasn't it good that fans of an author's work were so enamored of it that they would expend time and effort on expanding that universe?

But after a bit of rumination, I realized exactly where the problem was. You see, as a software developer, I have a bit of a blind spot.

The difference

Writing software is not the same as writing fiction. It's still writing; it's still an act of creation, but there are fundamental differences.

Software is rarely written by one person alone. It is a collaborative effort by a team. As time passes, new members may join the team and the original members may leave. The software continues to be modified, expanded and corrected. As requirements evolve, so too, does software.

Software is a
team effort.

Software that fails to evolve, dies. There's almost no such thing as a current piece of software that has survived from its point of creation ten years ago till today, without change. More importantly, no one person can lay claim to sole authorship of the software. It is made out of contributions from multiple people... much like fan-fiction.

This is not the case with fiction. When creating a work of fiction, once the author is done with the story, he or she is done. Nothing further should be done with the story unless the owner of the work decides otherwise.

What a faux pas!

While I did not invent the concept of fan-fiction or perpetuate it, I probably owe Ms Taghaboni an apology for my insensitivity. I did enjoy her fiction, and it was my mistake to think of that act of creativity as akin to writing software.

Do the write thing,
T___T