K-means clustering flow
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.
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"]]}]