Skip to content

Instantly share code, notes, and snippets.

@gkousiouris
Forked from vasKatevas/README.md
Last active May 31, 2023 10:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gkousiouris/1b39a5ce9a98f4e1246846f562693157 to your computer and use it in GitHub Desktop.
Save gkousiouris/1b39a5ce9a98f4e1246846f562693157 to your computer and use it in GitHub Desktop.
K-means clustering flow

Test extension

This is a flow that implements a k-means clustering operation as a service. As such it can be executed inside any Node-RED environment in a service manner. It also implements the Openwhisk API specification so that it can be executed directly as a custom docker action of Openwhisk. The inputs include arrays of objects and their values and the output returns clusters with three centroids for any given input, using the k-means implementation provided by the clusters npm library. clustering_flow

Example Input:

{
   "mode": "multiple",
   "data": [
       {
           "name": "cpu",
           "value": [
               0.015876524497427744,
               0.1902030897354209,
               0.005733681579652651,
               0.030480619519163435,
               0.010371581070262016,
               0.012062255039754171,
               0.015249307746178593

           ]
       },
       {
           "name": "memory",
           "value": [
               27979776,
               38883328,
               23392256,
               22237184,
               26570752,
               24121344,
               22372352
           ]
       }
   ]
}

In the name variable any type of value can be provided and there is no limit for the amount of objects data array can have. Lastly the mode has two options: multiple and single. The option multiple will create separate clusters for each data entry and the option single will generate a single cluster that will include every value inside the data table.

Example Output

{
   "centroids": [
       {
           "cpu": [
               0.018417176700630984,
               0.1902030897354209,
               0.00520766094542967
           ]
       },
       {
           "memory": [
               27275264,
               38883328,
               22822326.85714286
           ]
       }
   ]
}

Running as a container

The image for the flow can be found here. In order to run the flow correctly port 8080 needs to be exposed to host. Once the container is ready the user will need to execute a POST request at http://localhost:1880/run and wrap around the aforementioned input in a value object.

{"value":{
    "mode": "multiple",
    "data": [
        {
            "name": "cpu",
            "value": [
                0.015876524497427744,
                0.1902030897354209,
                0.005733681579652651,
                0.030480619519163435,
                0.010371581070262016,
                0.012062255039754171,
                0.015249307746178593,
                7.264773527105429e-7,
                0.0050459685298976376,
                0.004886347069983335

            ]
        },
        {
            "name": "memory",
            "value": [
                27979776,
                38883328,
                23392256,
                22237184,
                26570752,
                24121344,
                22372352,
                22405120,
                23105536,
                22122496
            ]
        }
    ]
}}

Runninig as an OpenWhisk action

To deploy the flow as an Openwhisk function the user needs to run the following command:

wsk action create clustering --docker vkatevas/node-red_data_clustering -i

To invoke the action the same input json needs to be included from the first example with the url the command wsk action get clusters --url provides

[
{
"id": "5120593091ae3338",
"type": "tab",
"label": "clustering",
"disabled": false,
"info": "",
"env": []
},
{
"id": "1289906ade2b350f",
"type": "http in",
"z": "5120593091ae3338",
"name": "",
"url": "/run",
"method": "post",
"upload": false,
"swaggerDoc": "",
"x": 280,
"y": 420,
"wires": [
[
"5ac44158a96d5520"
]
]
},
{
"id": "2a95fb11530126a5",
"type": "http response",
"z": "5120593091ae3338",
"name": "",
"statusCode": "",
"headers": {},
"x": 830,
"y": 420,
"wires": []
},
{
"id": "a02a06f8db3531b7",
"type": "debug",
"z": "5120593091ae3338",
"name": "debug 1",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 820,
"y": 340,
"wires": []
},
{
"id": "5ac44158a96d5520",
"type": "function",
"z": "5120593091ae3338",
"name": "Clustering function",
"func": "const clusterMaker = global.get('clusters');\nvar centroids = [];\nclusterMaker.k(3);\nclusterMaker.iterations(750)\n\nif (msg.payload.value.mode === \"multiple\"){\n \n //for each resource\n msg.payload.value.data.forEach( element => {\n \n //creating clusterMakerInput\n let clusterMakerInput = [];\n element.value.forEach( value => {\n clusterMakerInput.push([value])\n });\n \n clusterMaker.data(clusterMakerInput);\n \n let temp = clusterMaker.clusters();\n \n //creating the output \n let clusterMakerOutput = [];\n temp.forEach( element => {\n element.centroid.forEach(element2 => {\n clusterMakerOutput.push(element2)\n });\n });\n clusterMakerOutput.sort()\n centroids.push({ [element.name]: clusterMakerOutput});\n });\n} else if (msg.payload.value.mode === \"single\") {\n\n //data.length = m\n //data.element1.lenght = n\n //finding the max length of all data arrays\n let temp = [];\n let n = [];\n\n for (let m = 0; m < msg.payload.value.data.length;m++){\n n.push(msg.payload.value.data[m].value.length)\n }\n\n const nMax = Math.max(...n);\n\n //creating the array for clusterMakerInput\n let clusterMakerInput = []; \n for (let n = 0; n < nMax; n++) {\n let arrTemp = [];\n msg.payload.value.data.forEach( m => {\n\n arrTemp.push(m.value[n]);\n\n });\n clusterMakerInput.push(arrTemp);\n }\n console.log(clusterMakerInput);\n clusterMaker.data(clusterMakerInput);\n centroids = clusterMaker.clusters();\n\n}\nmsg.payload = {centroids};\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 610,
"y": 420,
"wires": [
[
"2a95fb11530126a5",
"a02a06f8db3531b7"
]
]
},
{
"id": "e69e891e32aa374b",
"type": "http in",
"z": "5120593091ae3338",
"name": "",
"url": "/init",
"method": "post",
"upload": false,
"swaggerDoc": "",
"x": 420,
"y": 600,
"wires": [
[
"5f33b7d9c6068eb5"
]
]
},
{
"id": "5f33b7d9c6068eb5",
"type": "http response",
"z": "5120593091ae3338",
"name": "",
"statusCode": "",
"headers": {},
"x": 590,
"y": 600,
"wires": []
},
{
"id": "1845e56a4bfabd26",
"type": "catch",
"z": "5120593091ae3338",
"name": "",
"scope": null,
"uncaught": false,
"x": 420,
"y": 520,
"wires": [
[
"9f2efb572ea57578"
]
]
},
{
"id": "9f2efb572ea57578",
"type": "function",
"z": "5120593091ae3338",
"name": "ADD ERROR INFO",
"func": "var payload=msg.payload;\nmsg.payload={};\n\nmsg.payload.error=msg.error;\nmsg.payload.error.payload=payload;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 630,
"y": 520,
"wires": [
[
"2a95fb11530126a5"
]
]
},
{
"id": "03741f25f3776d69",
"type": "inject",
"z": "5120593091ae3338",
"name": "Inject",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 270,
"y": 340,
"wires": [
[
"80d5b334cfa92e19"
]
]
},
{
"id": "80d5b334cfa92e19",
"type": "function",
"z": "5120593091ae3338",
"name": "Test input",
"func": "msg.payload = {\n \"value\": {\n \"mode\": \"multiple\",\n \"data\": [\n {\n \"name\": \"cpu\",\n \"value\": [\n 0.015876524497427744,\n 0.1902030897354209,\n 0.005733681579652651,\n 0.030480619519163435,\n 0.010371581070262016,\n 0.012062255039754171,\n 0.015249307746178593,\n 7.264773527105429e-7,\n 0.0050459685298976376,\n 0.004886347069983335\n\n ]\n },\n {\n \"name\": \"memory\",\n \"value\": [\n 27979776,\n 38883328,\n 23392256,\n 22237184,\n 26570752,\n 24121344,\n 22372352,\n 22405120,\n 23105536,\n 22122496\n ]\n }\n ]\n }\n}\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 420,
"y": 340,
"wires": [
[
"5ac44158a96d5520"
]
]
}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment