Book Image

Building Data-Driven Applications with Danfo.js

By : Rising Odegua, Stephen Oni
Book Image

Building Data-Driven Applications with Danfo.js

By: Rising Odegua, Stephen Oni

Overview of this book

Most data analysts use Python and pandas for data processing for the convenience and performance these libraries provide. However, JavaScript developers have always wanted to use machine learning in the browser as well. This book focuses on how Danfo.js brings data processing, analysis, and ML tools to JavaScript developers and how to make the most of this library to build data-driven applications. Starting with an overview of modern JavaScript, you’ll cover data analysis and transformation with Danfo.js and Dnotebook. The book then shows you how to load different datasets, combine and analyze them by performing operations such as handling missing values and string manipulations. You’ll also get to grips with data plotting, visualization, aggregation, and group operations by combining Danfo.js with Plotly. As you advance, you’ll create a no-code data analysis and handling system and create-react-app, react-table, react-chart, Draggable.js, and tailwindcss, and understand how to use TensorFlow.js and Danfo.js to build a recommendation system. Finally, you’ll build a Twitter analytics dashboard powered by Danfo.js, Next.js, node-nlp, and Twit.js. By the end of this app development book, you’ll be able to build and embed data analytics, visualization, and ML capabilities into any JavaScript app in server-side Node.js or the browser.
Table of Contents (18 chapters)
1
Section 1: The Basics
3
Section 2: Data Analysis and Manipulation with Danfo.js and Dnotebook
10
Section 3: Building Data-Driven Applications

Object-oriented programming and JavaScript classes

OOP is a common programming paradigm supported by most high-level languages. In OOP, you typically program an application using the concept of objects, which can be a combination of data and code.

Data represents information about the object, while code represents attributes, properties, and behaviors that can be carried out on objects.

OOP opens up a whole new world of possibilities as many problems can be simulated or designed as the interaction between different objects, thereby making it easier to design complex programs, as well as maintain and scale them.

JavaScript, like other high-level languages, provides support for OOP concepts, although not fully (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes), but in essence, most of the important concepts of OOP, such as objects, classes, and inheritance, are supported, and these are mostly enough to solve many problems you wish to model using OOP. In the following section, we will briefly look at classes and how these are related to OOP in JavaScript.

Classes

Classes in OOP act like a blueprint for an object. That is, they define a template of an abstract object in such a way that multiple copies can be made by following that blueprint. Copies here are officially called instances. So, in essence, if we define a class, then we can easily create multiple instances of that class.

In ECMA 2015, the class keyword was introduced to JavaScript, and this greatly simplified the creation of classes in the language. The following code snippet shows how to model a User object using the ES16 class keyword:

class User {
    constructor(firstName, lastName, email) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
    }
    getFirstName() {
        return this.firstName;
    }
    getLastName() {
        return this.lastName;
    }
    getFullName() {
        return `${this.firstName} ${this.lastName}`;
    }
    getEmail() {
        return this.email;
    }
    setEmail(email) {
        this.email = email;
    }
}
let Person1 = new User("John", "Benjamin", "[email protected]")
console.log(Person1.getFullName());
console.log(Person1.getEmail());
// outputs 
// "John Benjamin"
// "[email protected]"

By using the class keyword, you can wrap both data (names and email) with functionality (functions/methods) in a cleaner way that aids easy maintenance as well as understanding.

Before we move on, let's break down the class template in more detail for a better understanding.

The first line starts with the class keyword and is usually followed by a class name. A class name, by convention, is written in camel case, for instance, UserModel or DatabaseModel.

An optional constructor can be added inside a class definition. A constructor class is an initialization function that runs every time a new instance is created from a class. Here, you'll normally add code that initializes each instance with specific properties. For instance, in the following code snippet, we create two instances from the User class, and initialize them with specific properties:

let Person2 = new User("John", "Benjamin", "[email protected]")
let Person3 = new User("Hannah", "Joe", "[email protected]")
console.log(Person2.getFullName());
console.log(Person3.getFullName());
//outputs 
// "John Benjamin"
// "Hannah Montanna"

The next important part of a class is the addition of functions. Functions act as class methods and generally add a specific behavior to the class. Functions are also available to every instance created from the class. In our User class, methods such as getFirstName, getLastName, getEmail, and setEmail are added to perform different functions based on their implementation. To call functions on class instances, you typically use a dot notation, as you would when accessing an object's property. For example, in the following code, we return the full name of the Person1 instance:

Person1.getFullName()

With classes out of the way, we now move to the next concept in OOP, called inheritance.

Inheritance

Inheritance in OOP is the ability of one class to use properties/methods of another class. It is an easy way of extending the characteristics of one class (subclass/child class) using another class (superclass/parent class). In that way, the child class inherits all the characteristics of the parent class and can either extend or change these properties. Let's use an example to better understand this concept.

In our application, let's assume we already have the User class defined in the previous section, but we want to create a new set of users called Teachers. Teachers are also a class of users, and they will also require basic properties, such as the name and email that the User class already has. So, instead of creating a new class with these existing properties and methods, we can simply extend it, as shown in the following code snippet:

class Teacher extends User {
}

Note that we use the extends keyword. This keyword simply makes all the properties in the parent class (User) available to the child class (Teacher). With just the basic setup, the Teacher class automatically has access to all the properties and methods of the User class. For instance, we can instantiate and create a new Teacher in the same way we created a User value:

let teacher1 = new Teacher("John", "Benjamin", "[email protected]")
console.log(teacher1.getFullName());
//outputs
// "John Benjamin"

After extending a class, we basically want to add new features. We can do this by simply adding new functions or properties inside the child class template, as shown in the following code:

class Teacher extends User {
  getUserType(){
    return "Teacher"
  }
}

In the preceding code snippet, we added a new method, getUserType, which returns a string of the user type. In this way, we can add more features that were not originally in the parent class.

It is worth mentioning that you can replace parent functions in the child class by creating a new function in the child class with the same name. This process is called method overriding. For instance, to override the getFullName function in the Teacher class, we can do the following:

class User {
    constructor(firstName, lastName, email) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
    }
    getFirstName() {
        return this.firstName;
    }
    getLastName() {
        return this.lastName;
    }
    getFullName() {
        return `${this.firstName} ${this.lastName}`;
    }
    getEmail() {
        return this.email;
    }
    setEmail(email) {
        this.email = email;
    }
}
class Teacher extends User { 
    getFullName(){ 
      return `Teacher: ${this.firstName} ${this.lastName}`; 
    } 
    getUserType(){ 
      return "Teacher" 
    } 
  } 
let teacher1 = new Teacher("John", "Benjamin", "[email protected]") 
console.log(teacher1.getFullName()); 
//output 
// "Teacher: John Benjamin"

A question may arise here: What if we want to initialize the Teacher class with additional instances besides firstname, lastname, and email? This is achievable, and we can easily extend the constructor function by using a new keyword, super. We demonstrate how to do this in the following code:

// class User{
// previous User class goes here
//     ... 
// }
 
class Teacher extends User {
    constructor(firstName, lastName, email, userType, subject) {
        super(firstName, lastName, email) //calls parent class constructor 
        this.userType = userType
        this.subject = subject
    }
    getFullName() {
        return `Teacher: ${this.firstName} ${this.lastName}`;
    }
    getUserType() {
        return "Teacher"
    }
}
let teacher1 = new Teacher("Johnny", "Benjamin", "[email protected]", "Teacher", "Mathematics")
console.log(teacher1.getFullName());
console.log(teacher1.userType);
console.log(teacher1.subject);
//outputs 
// "Teacher: Johnny Benjamin"
// "Teacher"
// "Mathematics"

In the preceding code, we are performing two new things. First, we add two new instance properties (userType and subject) to the Teacher class, and then we are calling the super function. The super function simply calls the parent class (User), and performs the instantiation, and immediately after, we initialize the new properties of the Teacher class.

In this way, we are able to first initialize the parent properties before initializing the class properties.

Classes are very useful in OOP and the class keyword provided in JavaScript makes working with OOP easy. It is worth mentioning that under the hood, JavaScript converts the classes template to an object, as it does not have first-class support for classes. This is because JavaScript, by default, is a prototype-based, object-oriented language. Hence, the class interface provided is called syntactic sugar over the underlying prototype-based model, which JavaScript calls under the hood. You can read more about this at the following link: http://es6-features.org/#ClassDefinition.

Now that we have a basic understanding of OOP in JavaScript, we are ready to create complex applications that can be easily maintained. In the next section, we will discuss another important aspect of JavaScript development, which is setting up a development environment with modern JavaScript support.