Αυτό είναι το πρώτο μέρος μιας σειράς 3 πλήρων άρθρων με σχολιασμένους κώδικες σχετικά με την ομαδοποίηση δεδομένων με χρήση Javascript:



Αλγόριθμοι Ομαδοποίησης

Οι αλγόριθμοι μπορούν να ταξινομηθούν σε: ιεραρχικούς, διαμερισματικούς (που είναι οι δύο πιο παραδοσιακές μέθοδοι), βασισμένοι σε μοντέλα, βασισμένοι σε πλέγμα και βασισμένοι σε πυκνότητα (που είναι οι πιο σύγχρονες μέθοδοι). Η επιλογή της μεθόδου που θα χρησιμοποιηθεί στην ανάλυση εξαρτάται από τον τύπο της μεταβλητής και τον σκοπό της εφαρμογής.

Αλγόριθμοι κατάτμησης

Οι αλγόριθμοι διαμερισμάτων αναζητούν απευθείας τη διαίρεση n στοιχείων και χρησιμεύουν αποκλειστικά για την ομαδοποίησή τους σε k συμπλέγματα.

Η πιο γνωστή μέθοδος είναι η k-means, η οποία παρέχει μια ταξινόμηση των πληροφοριών σύμφωνα με τα ίδια τα δεδομένα. Αυτός ο αλγόριθμος βασίζεται στην ανάλυση και συγκρίσεις μεταξύ των αριθμητικών τιμών των δεδομένων. Με αυτό, ο αλγόριθμος θα στείλει αυτόματα μια αυτόματη ταξινόμηση χωρίς να χρειάζεται μια υπάρχουσα προταξινόμηση.

Ο αλγόριθμος k-means είναι αρκετά βαθμωτός και αξιόπιστος, αλλά έχει κάποιες ιδιαιτερότητες, οι δύο βασικές είναι:

1. Οι μεταβλητές πρέπει να είναι αριθμητικές ή δυαδικές. Σε περιπτώσεις που έχουμε κατηγορικά δεδομένα, μια εναλλακτική είναι η μετατροπή σε αριθμητικές τιμές. Υπάρχουν ορισμένες παραλλαγές του διαθέσιμου αλγορίθμου που έχουν προσαρμοστεί για εργασία με μη αριθμητικά δεδομένα, προκειμένου να επεκταθεί η εφαρμογή του στα πιο διαφορετικά προβλήματα.

2. Είναι ένας αλγόριθμος ευαίσθητος σε ακραίες τιμές, μια μεμονωμένη εγγραφή με πολύ ακραία τιμή μπορεί να τροποποιήσει ουσιαστικά τη διανομή των δεδομένων, επομένως είναι σκόπιμο να αντιμετωπίζονται τα δεδομένα για να μην δημιουργούνται απώλειες στη διαδικασία ομαδοποίησης.

Συνοπτικά, ο αλγόριθμος K-means έχει τη ροή ομαδοποίησης μέσω των παρακάτω βημάτων:

  1. Τυχαία επιλογή των αρχικών κεντροειδών;
  2. Κάθε αντικείμενο (σειρά του συνόλου δεδομένων) εκχωρείται στην ομάδα της οποίας το κέντρο έχει τη μεγαλύτερη ομοιότητα με το αντικείμενο.
  3. Υπολογίστε ξανά την τιμή του κέντρου κάθε ομάδας, ως τον μέσο όρο των αντικειμένων της ομάδας,

Επαναλάβετε τα βήματα 2 και 3 μέχρι να σταθεροποιηθούν οι ομάδες ή να επιτευχθεί μια συνθήκη διακοπής που έχει οριστεί στον αλγόριθμο.

Στα παραδείγματά μας θα χρησιμοποιήσουμε την κλασική βάση δεδομένων Iris (https://archive.ics.uci.edu) και ως κύριες βιβλιοθήκες θα χρησιμοποιήσουμε:

  1. simple-statistics: https://www.npmjs.com/package/simple-statistics
  2. σαμάνος: https://www.npmjs.com/package/shaman
  3. data-forge: https://www.npmjs.com/package/data-forge

Θα χρησιμοποιήσουμε τη βιβλιοθήκη Plotly για να δημιουργήσουμε γραφήματα και να αποθηκεύσουμε τα διαπιστευτήρια πρόσβασης μέσα σε ένα αρχείο που ονομάζεται config.js, το οποίο περιέχει δύο μεταβλητές: USERNAME και API_KEY που πρέπει να περιέχει τα διαπιστευτήρια χρήστη για πρόσβαση στο Plotly API.

/**
 * Import Modules
 */
 const stats = require(‘simple-statistics’);
 const KMeans = require(‘shaman’).KMeans;
 const dataForge = require(‘data-forge’);
 const config = require(‘./config’);
 const plotly = require(‘plotly’)(config.USERNAME, config.API_KEY);
 const opn = require(‘opn’);

Για να φορτώσετε τα δεδομένα απλά καλέστε τη συνάρτηση readFileSync του data-forge.

// reading base file and put dataframe on df variable
const df=dataForge.readFileSync(‘../datasets/iris.csv’).parseCSV();

Κατά τη διαδικασία ανάγνωσης των δεδομένων στο .csv είναι πιθανό να μην έχουν μορφοποιηθεί σωστά, επομένως συνιστάται ιδιαίτερα η επεξεργασία αυτών των δεδομένων μετατρέποντάς τα σε float (για αυτό το παράδειγμα).

Για αυτό θα λάβουμε ένα υποσύνολο μόνο με τις στήλες που θα χρησιμοποιήσουμε στη διαδικασία ομαδοποίησης. Χρησιμοποιώντας τη συνάρτηση Select θα επαναλάβουμε κάθε εγγραφή, χρησιμοποιώντας την τυπική συνάρτηση parseFloat() για να μετατρέψουμε τα δεδομένα κάθε στήλης, όπως φαίνεται παρακάτω:

// create a subset and converting all values to float
const subset = df.subset([“sepallength”, “sepalwidth”,“petallength”, “petalwidth”]).select(function (row) {
   return {
      sepallength: parseFloat(row.sepallength),
      sepalwidth: parseFloat(row.sepalwidth),
      petallength: parseFloat(row.petallength),
      petalwidth: parseFloat(row.petalwidth)
   };
});

Είναι σημαντικό να γνωρίζουμε τις ιδιότητες που περιέχονται στα δεδομένα μας. Για αυτό, θα χρησιμοποιήσουμε τη συνάρτηση σύνοψης σε μια υλοποίηση που περιλαμβάνει τις κύριες πληροφορίες σχετικά με το σύνολο δεδομένων, όπως φαίνεται παρακάτω:

/**
 * Summary function return the overview about the data serie
 * @param data array
 */
 function summary(column) {
    return {
       min: stats.min(column),
       max: stats.max(column),
       sum: stats.sum(column),
       median: stats.median(column),
       mode: stats.mode(column),
       mean: stats.mean(column),
       variance: stats.variance(column),
       stdDeviation: stats.standardDeviation(column),
       quantile: {
          q1: stats.quantile(column, 0.25),
          q3: stats.quantile(column, 0.75)
       }
    }
 }
 // invoke and show summary function for sepalwidth serie
 console.log(‘sepallength’);
 console.log(summary(subset.getSeries(‘sepallength’).toArray()));
console.log(‘sepalwidth’);
 console.log(summary(subset.getSeries(‘sepalwidth’).toArray()));
console.log(‘petallength’);
 console.log(summary(subset.getSeries(‘petallength’).toArray()));
console.log(‘petalwidth’);
 console.log(summary(subset.getSeries(‘petalwidth’).toArray()));

Σε αυτό το σημείο, με τα δεδομένα έτοιμα, θα υλοποιήσουμε την ομαδοποίηση μας στιγμιαία ενός αντικειμένου KMeans. Παρατηρήστε ότι μεταβιβάζουμε μια αριθμητική παράμετρο 3, αυτή αντιπροσωπεύει το K μας, δηλαδή, θέλουμε ο αλγόριθμος να ομαδοποιεί τα δεδομένα μας σε τρία συμπλέγματα. Στη συνέχεια, καλούμε τη συνάρτηση συμπλέγματος μεταβιβάζοντας το σύνολο δεδομένων μας και μια ανάκληση.

Το αποτέλεσμα αυτής της διαδικασίας, αν όλα συμβούν σωστά, θα είναι τα συμπλέγματα και κεντροειδείς μεταβλητών επιστροφής, συμπληρωμένα με τα αντίστοιχα δεδομένα τους που προκύπτουν από τη διαδικασία ομαδοποίησης.

// build clustering model
const kmeans = new KMeans(3);
// execute clustering using dataset
kmeans.cluster(subset.toRows(), function (err, clusters, centroids) {
   // show any errors
   console.log(err);
// show the clusters founds
   console.log(clusters);
// show the centroids
   console.log(centroids);
}

Το αποτέλεσμα που αποθηκεύεται στις μεταβλητές cluster και centroids είναι:

  • cluster: Πίνακας ‹Array ‹number›, όπου το πιο εξωτερικό επίπεδο του πίνακα αντιπροσωπεύει την ποσότητα του συμπλέγματος k και ο πιο εσωτερικός αριθμός αντιπροσωπεύει τον αριθμό των μεταβλητών που χρησιμοποιούνται από κάθε ομαδοποιημένο αντικείμενο.
  • κεντροειδείς: Πίνακας ‹Πίνακας ‹αριθμός›, όπου από το εσωτερικό προς το εξώτατο επίπεδο, αντιπροσωπεύει τις τιμές του αντικειμένου, του συμπλέγματος στο οποίο ανήκουν και του συνόλου των συστάδων που βρέθηκαν.

Μπορούμε να δημιουργήσουμε γραφήματα που δείχνουν τον σχηματισμό της ομαδοποίησης, για αυτό, θα χρησιμοποιήσουμε τη βιβλιοθήκη Plotly.

Η σειρά δεδομένων είναι σύμφωνα με το σύνολο δεδομένων, επομένως θα χρησιμοποιήσουμε ένα λεξικό (κλειδί-τιμή) για να βοηθήσουμε στην αντιστοίχιση των συνόλων δεδομένων.

// dictionary for aux the indexes read
 const indexes = {
     sepallength:0,
     sepalwidth:1,
     petallength:2,
     petalwidth:3
 }

Αρχικά, ας χαρτογραφήσουμε τα δεδομένα για να σχεδιάσουμε τα κεντροειδή στο γράφημα. Για αυτό, στον άξονα x, θα χρησιμοποιήσουμε τη συνάρτηση χάρτη για να περάσουμε από όλα τα αντικείμενα του πίνακα κεντροειδών στο ευρετήριο που αναφέρεται στη στήλη μεσομήκους, με τον ίδιο τρόπο, θα αντιστοιχίσουμε τον άξονα y με τα δεδομένα του τα κεντροειδή αντικείμενα που αναφέρονται στη στήλη μήκους πετάλου.

Είναι σημαντικό να σημειωθεί ότι εάν χρειάζεται να δημιουργήσετε γραφήματα με τα άλλα δεδομένα, απλώς αλλάξτε την αντιστοίχιση, επιλέγοντας άλλα σύνολα μεταβλητών.

Για αυτό το γράφημα, επιλέξαμε τον τύπο scatter και ορίσαμε το χαρακτηριστικό δείκτη μόνο για να εγγυηθούμε καλύτερη οπτικοποίηση, σύμφωνα με αυτήν την οδηγία τα κεντροειδή θα είναι μαύρες κουκκίδες στο γράφημα.

// build centroids graph model
const centroidTrace = {
   x: centroids.map(function (c){
     return c[indexes[“sepallength”]]; // 0 — sepallength
   }),
   y: centroids.map(function (c){
     return c[indexes[“petallength”]]; // 2 — petallength
   }),
   mode: ‘markers’,
   type: ‘scatter’,
   name: ‘Centroids’,
   marker: {
     color: ‘#000000’,
     symbol: ‘cross’,
     size: 10
   }
};

Τα δεδομένα που χρησιμοποιούνται για τη σχεδίαση του γραφήματος θα αποθηκευτούν στον πίνακα plotData και, στη συνέχεια, θα χρησιμοποιήσουμε τα δεδομένα από τα συμπλέγματα για να δημιουργήσουμε τα σημεία δεδομένων στο γράφημα.

Θα επαναλάβουμε τη διάταξη των συστάδων και για κάθε αντικείμενο, θα επιλέξουμε τα χαρακτηριστικά με παρόμοιο τρόπο με την εργασία που έγινε με τα κεντροειδή προηγουμένως. Τέλος, κάθε σύνολο δεδομένων κάθε συμπλέγματος θα αποθηκευτεί επίσης στον πίνακα plotData.

// adding centroids data on the plotData array
const plotData = [centroidTrace];
// build clusters graph model
clusters.forEach(function (cluster, index) {
   const trace = {
      x: cluster.map(function © {
         return c[indexes[“sepallength”]];
      }),
      y: cluster.map(function © {
         return c[indexes[“petallength”]];
      }),
      jitter: 0.3, 
      mode: ‘markers’,
      type: ‘scatter’,
      name: ‘Cluster ‘ + index
   }
   // add cluster graph data on plotData
   plotData.push(trace);
});

Τώρα που έχουμε τα δεδομένα γραφήματος, ας ορίσουμε τις ρυθμίσεις διάταξης, εκχωρώντας τις πληροφορίες τίτλου και τις ετικέτες των αξόνων X και Y, το όνομα αρχείου και μια επιλογή που ονομάζεται fileopt που θα λάβει την τιμή αντικατάστασης, κάθε όταν το εκτελείτε, εάν έχετε ήδη αυτό το γράφημα, θα το αντικαταστήσετε.

// set plotly graph layout
const layout = {
   title: ‘Iris Clustering’,
   xaxis: {
    title: ‘sepallength’
   },
   yaxis: {
    title: ‘petallength’
   }
};
//set graph options
var graphOptions = {
   layout: layout, //set layout
   filename: ‘Iris-clustering’, //set filename
   fileopt: ‘overwrite’ // if exists just overwrite
};

Τέλος καλείται η συνάρτηση γραφήματος, μεταβιβάζοντας τα δεδομένα, τις επιλογές και τη συνάρτηση επανάκλησης. Αυτή η συνάρτηση θα περιέχει δύο παραμέτρους, η πρώτη είναι ένα αντικείμενο σφάλματος, το οποίο θα είναι μηδενικό εάν το γράφημα δημιουργηθεί με επιτυχία. Η δεύτερη παράμετρος περιέχει τις βασικές πληροφορίες του γραφήματος ως το όνομα και τον σύνδεσμο όπου μπορεί να προσπελαστεί. Για να το ανοίξετε αυτόματα, χρησιμοποιήστε τη βιβλιοθήκη opn, η οποία θα ανοίξει τη διεύθυνση URL στο προεπιλεγμένο πρόγραμμα περιήγησης του συστήματος.

/**
 * Build Plotly graph 
 */
plotly.plot(plotData, graphOptions, function (err, msg) {
   if (!err) {
     // if build without erros show the message and open browser with graph
   console.log(`Success! The plot ${msg.filename} can be found at ${ msg.url}`);
   opn(msg.url);
   process.exit();
   }
  });
});

Το αποτέλεσμα αυτής της υλοποίησης φαίνεται στο παρακάτω διάγραμμα, τονίζοντας την ποιότητα των συμπλεγμάτων που βρέθηκαν, σε διαφορετικά χρώματα, υποδεικνύοντας ξεκάθαρα την αποτελεσματικότητα της διαδικασίας ομαδοποίησης κατά την ομαδοποίηση αντικειμένων με υψηλή ομοιότητα.

Προαιρετικά μπορούμε να οπτικοποιήσουμε αυτήν την ομαδοποίηση σε ένα τρισδιάστατο γράφημα, η κατασκευή είναι παρόμοια, ωστόσο, αλλάζοντας τον τύπο σε scatter3d και προσθέτοντας μια τρίτη μεταβλητή Z.

Επομένως, στην υλοποίησή μας η κατανομή των μεταβλητών θα είναι: x = sepallength, y = sepalwidth και z = petallength, για κεντροειδή καθώς και για συστάδες, όπως φαίνεται παρακάτω.

// build centroids graph model
var centroidTrace = {
   x: centroids.map(function(c){
      return c[indexes[“sepallength”]]; // 0 — sepallength
   }),
   y: centroids.map(function(c){
      return c[indexes[“sepalwidth”]]; // 2 — petallength
   }),
   z: centroids.map(function(c){
      return c[indexes[“petallength”]]; // 2 — petallength
   }),
   mode: ‘markers’,
   type: ‘scatter3d’,
   name: ‘Centroids’,
   marker: {
     color: ‘#000000’,
     symbol: ‘cross’,
     size: 20
   }
};

Το αποτέλεσμα αυτής της διαδικασίας είναι η οπτικοποίηση της ομαδοποίησης σε τρεις διαστάσεις, αυξάνοντας τις δυνατότητες ανάλυσης των ομάδων που δημιουργούνται και το σχηματισμό τους σύμφωνα με πρόσθετες μεταβλητές στον άξονα Ζ.

Σημειώστε ότι η ομαδοποίηση δεν είναι μια περίπλοκη διαδικασία. Ωστόσο, πρέπει να γνωρίζουμε τις διάφορες διαμορφώσεις που θα κάνουν τη διαδικασία πολύ πιο αποτελεσματική.

Αυτή η σειρά άρθρων είχε ως πρωταρχικό στόχο να δείξει τις δυνατότητες εργασίας με Javascript σε έναν τομέα εφαρμογής διαφορετικό από τον κοινό.

Αν σας άρεσε, ας αλλάξουμε μια ιδέα, αφήστε το σχόλιό σας και προσθέστε με στα κοινωνικά σας δίκτυα: Twitter και Linkedin