From 236b32cca3f64bc5749cdf8513d4b305310b8323 Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Fri, 26 Jul 2024 13:40:53 +0330 Subject: [PATCH 01/27] Fix md file --- README.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cd45cf6..9914192 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,15 @@ AnoGraph and AnoGraph-K detect graph anomalies by first mapping the graph to a h ## Demo -1. To run on DARPA dataset `bash demo.sh DARPA` -2. To run on ISCX dataset `bash demo.sh ISCX` +1. To run on DARPA dataset +``` +bash demo.sh DARPA +``` + +2. To run on ISCX dataset +``` +bash demo.sh ISCX +``` ## Datasets 1. [DARPA](http://kdd.ics.uci.edu/databases/kddcup99/kddcup99.html) From 7c7369fd9041f7ebe178021ca8a17ce77c844fa3 Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Fri, 26 Jul 2024 13:45:53 +0330 Subject: [PATCH 02/27] #include --- code/anograph.cpp | 1 + code/anographk.cpp | 1 + code/hcms.cpp | 1 + 3 files changed, 3 insertions(+) diff --git a/code/anograph.cpp b/code/anograph.cpp index 89b8593..9237043 100644 --- a/code/anograph.cpp +++ b/code/anograph.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "anograph.hpp" #include "hcms.hpp" diff --git a/code/anographk.cpp b/code/anographk.cpp index 22ff76a..9df85df 100644 --- a/code/anographk.cpp +++ b/code/anographk.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "anographk.hpp" #include "hcms.hpp" diff --git a/code/hcms.cpp b/code/hcms.cpp index 57a45b7..052130e 100644 --- a/code/hcms.cpp +++ b/code/hcms.cpp @@ -1,4 +1,5 @@ #include +#include #include "hcms.hpp" #include "anoedgeglobal.hpp" From 20ff7bf7a78c2d2215e2e5153d15e0dbdf045f09 Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Fri, 26 Jul 2024 13:47:13 +0330 Subject: [PATCH 03/27] Fix warning --- code/utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/utils.cpp b/code/utils.cpp index 48c2ea7..9bbed8e 100644 --- a/code/utils.cpp +++ b/code/utils.cpp @@ -8,7 +8,7 @@ using namespace std; void WriteUtils::writeScoresAndLabels(vector scores, vector labels, string output_file) { assert (scores.size() == labels.size()); FILE* output_file_ptr = fopen(output_file.c_str(), "w"); - for (int i = 0; i < scores.size(); i++) { + for (unsigned long int i = 0; i < scores.size(); i++) { fprintf(output_file_ptr, "%.4f %d\n", scores[i], labels[i]); } fclose(output_file_ptr); From 05600f13e62a1cf707b8bb6ac2a209bc0eee78ca Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Fri, 26 Jul 2024 14:13:51 +0330 Subject: [PATCH 04/27] Fix type --- code/utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/utils.cpp b/code/utils.cpp index 9bbed8e..0ce632b 100644 --- a/code/utils.cpp +++ b/code/utils.cpp @@ -8,7 +8,7 @@ using namespace std; void WriteUtils::writeScoresAndLabels(vector scores, vector labels, string output_file) { assert (scores.size() == labels.size()); FILE* output_file_ptr = fopen(output_file.c_str(), "w"); - for (unsigned long int i = 0; i < scores.size(); i++) { + for (size_t i = 0; i < scores.size(); i++) { fprintf(output_file_ptr, "%.4f %d\n", scores[i], labels[i]); } fclose(output_file_ptr); From e71130cb2d8eff4695e44809ec7cd9911f2f588e Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Fri, 26 Jul 2024 14:22:22 +0330 Subject: [PATCH 05/27] add results folder --- results/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 results/.gitkeep diff --git a/results/.gitkeep b/results/.gitkeep new file mode 100644 index 0000000..e69de29 From 14b5bd42af0cfb26c35af5ef48f961d11e6ad1ec Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Fri, 26 Jul 2024 14:23:20 +0330 Subject: [PATCH 06/27] add file opening validation --- code/utils.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/code/utils.cpp b/code/utils.cpp index 0ce632b..1324196 100644 --- a/code/utils.cpp +++ b/code/utils.cpp @@ -8,6 +8,13 @@ using namespace std; void WriteUtils::writeScoresAndLabels(vector scores, vector labels, string output_file) { assert (scores.size() == labels.size()); FILE* output_file_ptr = fopen(output_file.c_str(), "w"); + + if (output_file_ptr == NULL) { + cerr << "file path is : " << output_file << endl; + cerr << "Can not open file to write" << endl; + return; + } + for (size_t i = 0; i < scores.size(); i++) { fprintf(output_file_ptr, "%.4f %d\n", scores[i], labels[i]); } From 8789f47ce8cae31a4d8aca0c38eed4a9fca5b449 Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Fri, 26 Jul 2024 14:38:56 +0330 Subject: [PATCH 07/27] Add installing python dependencies in bash script --- code/demo.sh | 4 ++++ code/requirements.txt | 1 + 2 files changed, 5 insertions(+) create mode 100644 code/requirements.txt diff --git a/code/demo.sh b/code/demo.sh index 16b483b..73c11aa 100644 --- a/code/demo.sh +++ b/code/demo.sh @@ -18,6 +18,10 @@ if [ $1 == "DARPA" ]; then echo "Running AnoEdge-L" ./main anoedge_l DARPA 2 32 0.9 + echo "Installing python dependencies" + pip3 install -r requirements.txt + + echo "Running python metrics" python3 metrics.py --dataset DARPA --time_window 30 --edge_threshold 50 fi diff --git a/code/requirements.txt b/code/requirements.txt new file mode 100644 index 0000000..f5c56c9 --- /dev/null +++ b/code/requirements.txt @@ -0,0 +1 @@ +scikit-learn==1.3.2 From f615a6b332282bb1ebfb8b68a7e211727c6c33e8 Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Fri, 26 Jul 2024 14:41:33 +0330 Subject: [PATCH 08/27] add IDEA files --- .idea/.gitignore | 8 ++++++++ .idea/misc.xml | 20 ++++++++++++++++++++ .idea/vcs.xml | 6 ++++++ 3 files changed, 34 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/misc.xml create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..7859412 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,20 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file From 2f11792e0a55165ad245e23706b60d686eb56436 Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Sat, 27 Jul 2024 20:37:00 +0330 Subject: [PATCH 09/27] Fix data type --- code/utils.cpp | 2 +- code/utils.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/utils.cpp b/code/utils.cpp index 1324196..9852b3b 100644 --- a/code/utils.cpp +++ b/code/utils.cpp @@ -21,7 +21,7 @@ void WriteUtils::writeScoresAndLabels(vector scores, vector labels, fclose(output_file_ptr); } -void WriteUtils::writeTime(double total_time, int num_records, string output_file) { +void WriteUtils::writeTime(double total_time, size_t num_records, string output_file) { FILE* output_file_ptr = fopen(output_file.c_str(), "w"); fprintf(output_file_ptr, "Average time taken: %.4fs\n", (1.0f*total_time)/num_records); fprintf(output_file_ptr, "Total time taken: %.4fs\n", 1.0f*total_time); diff --git a/code/utils.hpp b/code/utils.hpp index 48f8848..e958a3c 100644 --- a/code/utils.hpp +++ b/code/utils.hpp @@ -15,7 +15,7 @@ const string TIME_FILE_SUFFIX = "_time.csv"; class WriteUtils { public: static void writeScoresAndLabels(vector scores, vector labels, string output_file); - static void writeTime(double total_time, int num_records, string output_file); + static void writeTime(double total_time, size_t num_records, string output_file); }; class ReadUtils { From eed41bd64e2bab49ae84ed4bae99381865f7a932 Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Sat, 27 Jul 2024 20:41:39 +0330 Subject: [PATCH 10/27] Fix size_t --- code/anoedgeglobal.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/code/anoedgeglobal.cpp b/code/anoedgeglobal.cpp index 4df3fc4..228794a 100644 --- a/code/anoedgeglobal.cpp +++ b/code/anoedgeglobal.cpp @@ -23,10 +23,10 @@ vector AnoedgeGlobal::getScores() { vector scores; Hcms count(rows, buckets); - int num_records = src.size(); + size_t num_records = src.size(); int last_time = 0; - for (int i = 0; i < num_records; i++) { + for (size_t i = 0; i < num_records; i++) { if (times[i] - last_time > 0) { count.decay(decay_factor); } @@ -51,8 +51,8 @@ void AnoedgeGlobal::run() { } double AnoedgeGlobal::getAnoedgeglobalDensity(vector>& mat, int src, int dst) { - int num_rows = mat.size(); - int num_cols = mat[0].size(); + size_t num_rows = mat.size(); + size_t num_cols = mat[0].size(); bool row_flag[num_rows]; bool col_flag[num_cols]; @@ -94,7 +94,7 @@ double AnoedgeGlobal::getAnoedgeglobalDensity(vector>& mat, int s double cur_mat_sum = mat[src][dst]; double output = cur_mat_sum/sqrt(marked_rows*marked_cols); - int ctr = num_rows + num_cols - 2; + size_t ctr = num_rows + num_cols - 2; while (ctr--) { if (max_row.second >= max_col.second) { row_flag[max_row.first] = true; From e3d61fef3e2b4c53f604144799bbe1974a66f9b4 Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Sun, 28 Jul 2024 06:27:44 +0330 Subject: [PATCH 11/27] add AnoGraph_EDA_nodes.ipynb --- notebooks/AnoGraph_EDA_nodes.ipynb | 3000 ++++++++++++++++++++++++++++ 1 file changed, 3000 insertions(+) create mode 100644 notebooks/AnoGraph_EDA_nodes.ipynb diff --git a/notebooks/AnoGraph_EDA_nodes.ipynb b/notebooks/AnoGraph_EDA_nodes.ipynb new file mode 100644 index 0000000..c08f66e --- /dev/null +++ b/notebooks/AnoGraph_EDA_nodes.ipynb @@ -0,0 +1,3000 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + } + }, + "cells": [ + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "21Ut0P4sQXrU", + "outputId": "ba909905-f0d4-4ba5-fc2a-027b90cc0a65" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Cloning into 'AnoGraph'...\n", + "remote: Enumerating objects: 105, done.\u001b[K\n", + "remote: Counting objects: 100% (105/105), done.\u001b[K\n", + "remote: Compressing objects: 100% (70/70), done.\u001b[K\n", + "remote: Total 105 (delta 47), reused 77 (delta 31), pack-reused 0\u001b[K\n", + "Receiving objects: 100% (105/105), 5.13 MiB | 2.94 MiB/s, done.\n", + "Resolving deltas: 100% (47/47), done.\n", + "Updating files: 100% (30/30), done.\n" + ] + } + ], + "source": [ + "!git clone https://github.com/MKasaei00/AnoGraph.git" + ] + }, + { + "cell_type": "code", + "source": [ + "%cd AnoGraph/code" + ], + "metadata": { + "id": "VOmuROnNQlj-", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "1aa27db2-51de-44c1-ad7f-992700ecd821" + }, + "execution_count": 54, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "/content/AnoGraph/code/AnoGraph/code\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "# Import necessary libraries\n", + "import pandas as pd\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ], + "metadata": { + "id": "RpwDWpYUQtY0", + "collapsed": true + }, + "execution_count": 55, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Load the CSV file\n", + "dataset_name = 'DARPA'\n", + "file_path = f\"../data/{dataset_name}/Data.csv\"\n", + "data = pd.read_csv(file_path,header=None, names=['u', 'v', 'time_stamp'])" + ], + "metadata": { + "id": "jWnsVy60Gz5D" + }, + "execution_count": 56, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Convert 'u' and 'v' to integers\n", + "data['u'] = data['u'].astype(int)\n", + "data['v'] = data['v'].astype(int)" + ], + "metadata": { + "id": "CwiZyGNuHEwS" + }, + "execution_count": 57, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "data" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 423 + }, + "id": "ujikBoknHxRZ", + "outputId": "2d9318cb-8217-4528-e501-2036ceeb6951" + }, + "execution_count": 58, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " u v time_stamp\n", + "0 7577 9765 1\n", + "1 7577 9765 1\n", + "2 9764 9763 2\n", + "3 9764 9763 2\n", + "4 9765 7577 3\n", + "... ... ... ...\n", + "4554339 10215 9763 46571\n", + "4554340 10215 9763 46571\n", + "4554341 10215 9763 46571\n", + "4554342 10215 9763 46571\n", + "4554343 9763 9764 46572\n", + "\n", + "[4554344 rows x 3 columns]" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
uvtime_stamp
0757797651
1757797651
2976497632
3976497632
4976575773
............
455433910215976346571
455434010215976346571
455434110215976346571
455434210215976346571
45543439763976446572
\n", + "

4554344 rows × 3 columns

\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe", + "variable_name": "data" + } + }, + "metadata": {}, + "execution_count": 58 + } + ] + }, + { + "cell_type": "code", + "source": [ + "# Display basic information about the dataset\n", + "data.info()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "5bp9WQINHtA4", + "outputId": "653794ba-1b9d-4bc5-e23e-c31eaa92520e" + }, + "execution_count": 59, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "RangeIndex: 4554344 entries, 0 to 4554343\n", + "Data columns (total 3 columns):\n", + " # Column Dtype\n", + "--- ------ -----\n", + " 0 u int64\n", + " 1 v int64\n", + " 2 time_stamp int64\n", + "dtypes: int64(3)\n", + "memory usage: 104.2 MB\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "# Descriptive statistics for u, v, and time_stamp\n", + "data.describe()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 300 + }, + "id": "gkBTfCQ2H1D_", + "outputId": "ae1ddcd5-778d-481c-e7eb-c44bcb1cb0d7" + }, + "execution_count": 60, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " u v time_stamp\n", + "count 4.554344e+06 4.554344e+06 4.554344e+06\n", + "mean 9.716706e+03 9.768405e+03 2.794812e+04\n", + "std 8.326770e+03 4.535230e+03 1.160524e+04\n", + "min 3.000000e+00 0.000000e+00 1.000000e+00\n", + "25% 2.538000e+03 7.607000e+03 2.236800e+04\n", + "50% 8.409000e+03 7.863000e+03 2.794500e+04\n", + "75% 1.139400e+04 1.013900e+04 3.785600e+04\n", + "max 2.552400e+04 2.552400e+04 4.657200e+04" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
uvtime_stamp
count4.554344e+064.554344e+064.554344e+06
mean9.716706e+039.768405e+032.794812e+04
std8.326770e+034.535230e+031.160524e+04
min3.000000e+000.000000e+001.000000e+00
25%2.538000e+037.607000e+032.236800e+04
50%8.409000e+037.863000e+032.794500e+04
75%1.139400e+041.013900e+043.785600e+04
max2.552400e+042.552400e+044.657200e+04
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe", + "summary": "{\n \"name\": \"data\",\n \"rows\": 8,\n \"fields\": [\n {\n \"column\": \"u\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 1606892.581859376,\n \"min\": 3.0,\n \"max\": 4554344.0,\n \"num_unique_values\": 8,\n \"samples\": [\n 9716.70583205836,\n 8409.0,\n 4554344.0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"v\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 1606915.5274845983,\n \"min\": 0.0,\n \"max\": 4554344.0,\n \"num_unique_values\": 8,\n \"samples\": [\n 9768.405486937307,\n 7863.0,\n 4554344.0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"time_stamp\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 1601465.603446348,\n \"min\": 1.0,\n \"max\": 4554344.0,\n \"num_unique_values\": 8,\n \"samples\": [\n 27948.12267430831,\n 27945.0,\n 4554344.0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}" + } + }, + "metadata": {}, + "execution_count": 60 + } + ] + }, + { + "cell_type": "code", + "source": [ + "# Calculate additional statistics\n", + "percentiles = [0.1, 0.25, 0.5, 0.75, 0.9, 0.95, 0.99, 0.999]\n", + "additional_stats = data.describe(percentiles=percentiles)\n", + "additional_stats" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 457 + }, + "id": "p-590scHIESw", + "outputId": "9d0955b7-1744-47d2-ec12-df46daeea96e" + }, + "execution_count": 61, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " u v time_stamp\n", + "count 4.554344e+06 4.554344e+06 4.554344e+06\n", + "mean 9.716706e+03 9.768405e+03 2.794812e+04\n", + "std 8.326770e+03 4.535230e+03 1.160524e+04\n", + "min 3.000000e+00 0.000000e+00 1.000000e+00\n", + "10% 2.330000e+02 7.607000e+03 1.165400e+04\n", + "25% 2.538000e+03 7.607000e+03 2.236800e+04\n", + "50% 8.409000e+03 7.863000e+03 2.794500e+04\n", + "75% 1.139400e+04 1.013900e+04 3.785600e+04\n", + "90% 2.552300e+04 1.816400e+04 4.306300e+04\n", + "95% 2.552300e+04 2.054300e+04 4.429300e+04\n", + "99% 2.552300e+04 2.401500e+04 4.577300e+04\n", + "99.9% 2.552300e+04 2.544200e+04 4.647500e+04\n", + "max 2.552400e+04 2.552400e+04 4.657200e+04" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
uvtime_stamp
count4.554344e+064.554344e+064.554344e+06
mean9.716706e+039.768405e+032.794812e+04
std8.326770e+034.535230e+031.160524e+04
min3.000000e+000.000000e+001.000000e+00
10%2.330000e+027.607000e+031.165400e+04
25%2.538000e+037.607000e+032.236800e+04
50%8.409000e+037.863000e+032.794500e+04
75%1.139400e+041.013900e+043.785600e+04
90%2.552300e+041.816400e+044.306300e+04
95%2.552300e+042.054300e+044.429300e+04
99%2.552300e+042.401500e+044.577300e+04
99.9%2.552300e+042.544200e+044.647500e+04
max2.552400e+042.552400e+044.657200e+04
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe", + "variable_name": "additional_stats", + "summary": "{\n \"name\": \"additional_stats\",\n \"rows\": 13,\n \"fields\": [\n {\n \"column\": \"u\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 1259301.3872468325,\n \"min\": 3.0,\n \"max\": 4554344.0,\n \"num_unique_values\": 10,\n \"samples\": [\n 25523.0,\n 9716.70583205836,\n 2538.0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"v\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 1259450.2323463897,\n \"min\": 0.0,\n \"max\": 4554344.0,\n \"num_unique_values\": 12,\n \"samples\": [\n 25442.0,\n 24015.0,\n 4554344.0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"time_stamp\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 1254794.221144536,\n \"min\": 1.0,\n \"max\": 4554344.0,\n \"num_unique_values\": 13,\n \"samples\": [\n 46475.0,\n 44293.0,\n 4554344.0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}" + } + }, + "metadata": {}, + "execution_count": 61 + } + ] + }, + { + "cell_type": "code", + "source": [ + "# @title Most repeated values\n", + "\n", + "# Display most repeated values\n", + "most_repeated_values_u = data['u'].value_counts().head()\n", + "most_repeated_values_v = data['v'].value_counts().head()\n", + "most_repeated_values_time = data['time_stamp'].value_counts().head()\n", + "\n", + "# Convert the most repeated values to dataframes for better display\n", + "most_repeated_u_df = pd.DataFrame(most_repeated_values_u).reset_index()\n", + "most_repeated_u_df.columns = ['Value', 'Count']\n", + "\n", + "most_repeated_v_df = pd.DataFrame(most_repeated_values_v).reset_index()\n", + "most_repeated_v_df.columns = ['Value', 'Count']\n", + "\n", + "most_repeated_time_df = pd.DataFrame(most_repeated_values_time).reset_index()\n", + "most_repeated_time_df.columns = ['Value', 'Count']" + ], + "metadata": { + "cellView": "form", + "id": "0ZI4OF7ZIWpv" + }, + "execution_count": 62, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "display(most_repeated_u_df)\n", + "display(most_repeated_v_df)\n", + "display(most_repeated_time_df)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 584 + }, + "id": "POs5BkstI8Q_", + "outputId": "8d3d27b3-e549-4f7e-94bc-7a7ff03215b0" + }, + "execution_count": 63, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + " Value Count\n", + "0 233 1024185\n", + "1 25523 499613\n", + "2 9763 281555\n", + "3 10215 250796\n", + "4 2538 224542" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
ValueCount
02331024185
125523499613
29763281555
310215250796
42538224542
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe", + "variable_name": "most_repeated_u_df", + "summary": "{\n \"name\": \"most_repeated_u_df\",\n \"rows\": 5,\n \"fields\": [\n {\n \"column\": \"Value\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 9893,\n \"min\": 233,\n \"max\": 25523,\n \"num_unique_values\": 5,\n \"samples\": [\n 25523,\n 2538,\n 9763\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Count\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 335726,\n \"min\": 224542,\n \"max\": 1024185,\n \"num_unique_values\": 5,\n \"samples\": [\n 499613,\n 224542,\n 281555\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}" + } + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + " Value Count\n", + "0 7607 1639430\n", + "1 7863 646164\n", + "2 9763 347190\n", + "3 8117 233892\n", + "4 10215 201902" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
ValueCount
076071639430
17863646164
29763347190
38117233892
410215201902
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe", + "variable_name": "most_repeated_v_df", + "summary": "{\n \"name\": \"most_repeated_v_df\",\n \"rows\": 5,\n \"fields\": [\n {\n \"column\": \"Value\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 1189,\n \"min\": 7607,\n \"max\": 10215,\n \"num_unique_values\": 5,\n \"samples\": [\n 7863,\n 10215,\n 9763\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Count\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 599590,\n \"min\": 201902,\n \"max\": 1639430,\n \"num_unique_values\": 5,\n \"samples\": [\n 646164,\n 201902,\n 347190\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}" + } + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + " Value Count\n", + "0 11654 65009\n", + "1 14975 64169\n", + "2 14974 26548\n", + "3 14976 22054\n", + "4 27963 13252" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
ValueCount
01165465009
11497564169
21497426548
31497622054
42796313252
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe", + "variable_name": "most_repeated_time_df", + "summary": "{\n \"name\": \"most_repeated_time_df\",\n \"rows\": 5,\n \"fields\": [\n {\n \"column\": \"Value\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 6344,\n \"min\": 11654,\n \"max\": 27963,\n \"num_unique_values\": 5,\n \"samples\": [\n 14975,\n 27963,\n 14974\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Count\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 24555,\n \"min\": 13252,\n \"max\": 65009,\n \"num_unique_values\": 5,\n \"samples\": [\n 64169,\n 13252,\n 26548\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}" + } + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "# @title Scatter plot for u(source nodes) , v(destination nodes)\n", + "\n", + "from matplotlib import pyplot as plt\n", + "\n", + "# Create the scatter plot\n", + "plt.figure(figsize=(10, 6))\n", + "\n", + "# Plot data from most_repeated_u_df in blue\n", + "plt.scatter(most_repeated_u_df['Value'], most_repeated_u_df['Count'], color='blue', s=32, alpha=0.8, label='U Data')\n", + "\n", + "# Plot data from most_repeated_v_df in orange\n", + "plt.scatter(most_repeated_v_df['Value'], most_repeated_v_df['Count'], color='orange', s=32, alpha=0.8, label='V Data')\n", + "\n", + "# Customize the plot\n", + "plt.xlabel('Value')\n", + "plt.ylabel('Count')\n", + "plt.title('Value vs Count of Source Node')\n", + "plt.legend()\n", + "plt.gca().spines[['top', 'right']].set_visible(False)\n", + "\n", + "# Show the plot\n", + "plt.show()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 564 + }, + "cellView": "form", + "id": "rRMEIbynLyD8", + "outputId": "b1f18d86-d561-4311-9a5e-a746169fc323" + }, + "execution_count": 64, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "
" + ], + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA04AAAIjCAYAAAA0vUuxAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABXHklEQVR4nO3deXxU1f3/8fdkHUKYAGYDDPsOYRcERKBEA2oE0QJKBRQVBCmIuKBCQFupBREXKGoVtJXVAlrlxxZFEFEqskgFZAeRJCySIUBIyJzfH/Nl6pCEm0CSyfJ6Ph7zGOfcc+987uSazJt77rk2Y4wRAAAAACBPfr4uAAAAAABKOoITAAAAAFggOAEAAACABYITAAAAAFggOAEAAACABYITAAAAAFggOAEAAACABYITAAAAAFggOAEAAACABYITABSygwcPymazae7cub4uBUUgJSVF99xzj6677jrZbDbNmDHD1yWVe2vXrpXNZtPatWt9XQqAMozgBKBcu/POOxUSEqIzZ87k2WfgwIEKCgrSyZMni7Gykm3t2rXq27evoqOjFRQUpMjISCUkJGjJkiW+Lk2SdO7cOU2aNKlIvkg//vjjWrlypcaPH69//OMf6tmzZ55909PTlZiYqObNm6tixYq67rrr1KpVK40ePVq//PJLoddWUnTr1k02m00JCQk5ll36h4Vp06b5oDIAuHoEJwDl2sCBA3X+/HktXbo01+Xnzp3Txx9/rJ49e+q6664r5upKpsTERHXv3l07duzQsGHDNHv2bD355JNKT0/X3XffrXnz5vm6RJ07d06TJ08ukuD0+eefq3fv3ho3bpz+8Ic/qHHjxrn2y8rK0s0336ypU6eqS5cumj59up599lm1adNG8+bN008//VTotZU0n376qTZv3uzrMgCgUAT4ugAA8KU777xTlSpV0rx58zRo0KAcyz/++GOdPXtWAwcO9EF1Jc9HH32kF154Qffcc4/mzZunwMBAz7Inn3xSK1euVFZWlg8rLHqpqamqXLmyZb9ly5Zpy5Yt+vDDD3Xfffd5LcvIyFBmZmYRVZg7l8ulzMxM2e32Ynm/mjVr6syZM5o8ebI++eSTYnlPAChKnHECUK5VqFBBffv2VVJSklJTU3MsnzdvnipVqqQ777xTp06d0rhx4xQbG6vQ0FA5HA716tVL27Zts3yfbt26qVu3bjnahwwZotq1a3u1uVwuzZgxQ82aNZPdbldUVJSGDRumX3/99YrvMW3aNNlsNh06dCjHsvHjxysoKMizjT179ujuu+9WdHS07Ha7rr/+eg0YMEBpaWlXfI8JEyaoatWqeu+997xC0yXx8fG64447PK9TU1M1dOhQRUVFyW63q2XLlnr//fe91snr+pTcrhUbMmSIQkNDdfToUfXp00ehoaGKiIjQuHHjlJ2d7VkvIiJCkjR58mTZbDbZbDZNmjTpivu2f/9+/f73v1fVqlUVEhKiG2+8UZ999pln+dy5c2Wz2WSM0cyZMz3bzcu+ffskSZ07d86xzG63y+FweLV9/vnn6tKliypWrKjKlSurd+/e2rlzp1ef3I4XSZo0aVKOWmw2mx577DF9+OGHatasmYKDg7VixQpJ0tGjRzV06FBVr15dwcHBqlOnjh599FGvMHf69GmNGTNGMTExCg4OVv369fXyyy/L5XLluc+/ValSJT3++OP697//re+//96yv9Xnf8nPP/+sPn36qGLFioqMjNTjjz+uCxcu5LrNb7/9Vj179lRYWJhCQkLUtWtXbdiwIV/1A8DlCE4Ayr2BAwfq4sWLWrRokVf7qVOntHLlSt11112qUKGC9u/fr2XLlumOO+7Q9OnT9eSTT+qHH35Q165dC/V6lWHDhunJJ59U586d9dprr+mBBx7Qhx9+qPj4+CuezenXr59sNluO/ZCkRYsW6dZbb1WVKlWUmZmp+Ph4ffPNNxo1apRmzpypRx55RPv379fp06fz3P6ePXu0a9cu9enTR5UqVbLcj/Pnz6tbt276xz/+oYEDB2rq1KkKCwvTkCFD9Nprr+Xrs8hNdna24uPjdd1112natGnq2rWrXnnlFb399tuSpIiICP3tb3+TJN111136xz/+oX/84x/q27dvnttMSUlRp06dtHLlSo0YMUJ//vOflZGRoTvvvNMzjPPmm2/WP/7xD0nSLbfc4tluXmrVqiVJ+uCDD2SMueI+rVmzRvHx8UpNTdWkSZM0duxYff311+rcubMOHjyY78/mcp9//rkef/xx9e/fX6+99ppq166tX375Re3bt9eCBQvUv39/vf7667r//vv15Zdf6ty5c5LcQx27du2qf/7znxo0aJBef/11de7cWePHj9fYsWPz/f6jR49WlSpVLENrfj5/yX1M9ejRQytX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cnKyiYqKMmFhYWbChAnm1VdfNa1atTI2m80sWbLEq6/yOave1KlTTUhIiBkwYICZMWOG+fvf/26effZZEx0dbfz8/Ly2u3r1ahMQEGAaN25spk6dal544QUTERFhqlSpYvbv3+/pd+LECVOxYkVTt25dM2PGDPPSSy+ZmJgY06ZNm1xn1cutzp9//tlER0ebkJAQM2bMGPPWW2+ZSZMmmWbNmplff/3VGOOeqa5NmzYmICDAPPTQQ+Zvf/ubmTZtmufzP378+BX3/bez6v3WpZ+TLptVL7+f/6UZ9Ox2u3n66afNjBkzTNu2bU2LFi1yzMz4xRdfGLvdbmrWrGkSExPN22+/bRITE83NN99s7rjjjivWDwC5ITj9n8uD06effmokmYoVK3o9AgICTL9+/Ywxxjz88MNGktm9e7dnvc2bNxtJZteuXcW9CwAKwcyZM40k0759+xzLMjIyzBNPPGGqVatmKlSoYDp37mw2btyYY6rx3IKTMcb885//NHXr1jVBQUGmVatWZuXKlXlOL/3222+btm3bmgoVKphKlSqZ2NhY89RTT5lffvklX/vxzjvvGEmmUqVK5vz5817L9u/fbx588EFTr149Y7fbTdWqVU337t3NmjVr8rVtY4xJSkoyvXv3NpGRkSYgIMBERESYhIQE8/HHH3v1S0lJMQ888IAJDw83QUFBJjY2NsfnYox7+vC7777bhISEmCpVqphhw4aZHTt2XFNw+vrrr03btm1NUFBQvqYm37dvn7nnnntM5cqVjd1uN+3btzeffvppjn75DU779+83EydONDfeeKPX53T77bebzz//PEf/NWvWmM6dO5sKFSoYh8NhEhISzI8//pij36pVq0zz5s1NUFCQadSokfnnP/+Z53TkedV56NAhM2jQIBMREWGCg4NN3bp1zciRI82FCxc8fc6cOWPGjx9v6tevb4KCgkx4eLjp1KmTmTZtmsnMzLzivucVnH799VcTFhaWIzgZk//P/9ChQ+bOO+80ISEhJjw83IwePdozXf/lU9pv2bLF9O3b11x33XUmODjY1KpVy/Tr188kJSVdsX4AyI3NGIu78pUTNptNS5cuVZ8+fSS5p+8dOHCg/vvf/+a4EDk0NFTR0dGeG+r99oaU58+fV0hIiFatWqVbbrmlOHcBAAAAQBEp15NDXEnr1q2VnZ2t1NRUdenS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9522bNmixx57TJLkcrlkjFFAQIBWrVql3/3udznWCw4OVnBwcHGXCwAAAKAMKTXByeFw6IcffvBqmzVrlj7//HN99NFHqlOnjo8qAwAAAFDW+TQ4paena+/evZ7XBw4c0NatW1W1alXVrFlT48eP19GjR/XBBx/Iz89PzZs391o/MjJSdrs9RzsAAAAAFCafBqfvvvtO3bt397weO3asJGnw4MGaO3eujh07psOHD/uqPAAAAACQJNmMMcbXRRQnp9OpsLAwpaWlyeFw+LocAAAAAKVAqZlVDwAAAAB8heAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABgwafBad26dUpISFD16tVls9m0bNmyK/ZfsmSJbrnlFkVERMjhcKhjx45auXJl8RQLAAAAoNzyaXA6e/asWrZsqZkzZ+ar/7p163TLLbdo+fLl2rx5s7p3766EhARt2bKliCsFAAAAUJ7ZjDHG10VIks1m09KlS9WnT58CrdesWTP1799fEydOzFd/p9OpsLAwpaWlyeFwXEWlAAAAAMqbAF8XcC1cLpfOnDmjqlWr5tnnwoULunDhgue10+ksjtIAAAAAlCGlenKIadOmKT09Xf369cuzz5QpUxQWFuZ5xMTEFGOFAAAAAMqCUhuc5s2bp8mTJ2vRokWKjIzMs9/48eOVlpbmeRw5cqQYqwQAAABQFpTKoXoLFizQQw89pMWLFysuLu6KfYODgxUcHFxMlQEAAAAoi0rdGaf58+frgQce0Pz583X77bf7uhwAAAAA5YBPzzilp6dr7969ntcHDhzQ1q1bVbVqVdWsWVPjx4/X0aNH9cEHH0hyD88bPHiwXnvtNXXo0EHJycmSpAoVKigsLMwn+wAAAACg7PPpdORr165V9+7dc7QPHjxYc+fO1ZAhQ3Tw4EGtXbtWktStWzd9+eWXefbPD6YjBwAAAFBQJeY+TsWF4AQAAACgoErdNU4AAAAAUNwITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITgAAAABggeAEAAAAABYITj6WkSGlprqfAQAAAJRMAb4uoLxKSZFmzZKWLpXOnpUqVpT69pVGjJAiI31dHQAAAIDfshljjK+LKE5Op1NhYWFKS0uTw+HwSQ0pKdKAAdLOnZLd7n5kZLgfTZpICxcSngAAAICShKF6PjBrljs0RUVJ4eFSaKj7OSrK3T5rlq8rBAAAAPBbBKdilpHhHp5nt0uBgd7LAgPd7UuWcM0TAAAAUJIQnIqZ0+m+psluz3253e5e7nQWb10AAAAA8kZwKmYOh3siiLzOKGVkuJf76PIrAAAAALkgOBUzu1266y53QMrK8l6WleVu79s37zNSAAAAAIof05H7wMiR0rp1uc+q17Spe0pyAAAAACWHT884rVu3TgkJCapevbpsNpuWLVtmuc7atWvVpk0bBQcHq379+po7d26R11nYIiPdU44PH+4ekpeV5X4ePlxasICpyAEAAICSxqfB6ezZs2rZsqVmzpyZr/4HDhzQ7bffru7du2vr1q0aM2aMHnroIa1cubKIKy18kZHSpEnSpk3Sxo3u50mTCE0AAABASVRiboBrs9m0dOlS9enTJ88+Tz/9tD777DPt2LHD0zZgwACdPn1aK1asyNf7lIQb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\n" + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "# @title Visualizing Source and Destination Node Distributions with Multi-Bar Histograms\n", + "\n", + "def plot_multi_bar_distribution(u_column, v_column, bins=50):\n", + " plt.figure(figsize=(14, 8))\n", + "\n", + " u_counts, u_bins = np.histogram(u_column, bins=bins)\n", + " v_counts, v_bins = np.histogram(v_column, bins=bins)\n", + "\n", + " width = (u_bins[1] - u_bins[0]) / 3\n", + " plt.bar(u_bins[:-1] - width/2, u_counts, width=width, color='blue', alpha=0.7, label='u')\n", + " plt.bar(v_bins[:-1] + width/2, v_counts, width=width, color='orange', alpha=0.7, label='v')\n", + "\n", + " plt.title('Distribution of u and v')\n", + " plt.xlabel('Value')\n", + " plt.ylabel('Frequency')\n", + " plt.legend()\n", + " plt.grid(True)\n", + " plt.show()\n", + "\n", + "# Plot multi-bar distributions for u and v\n", + "plot_multi_bar_distribution(data['u'], data['v'])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 718 + }, + "id": "uRg2cjKKLQaF", + "outputId": "9a0c7f95-97cf-46a5-deb1-5c75fa2548ff" + }, + "execution_count": 65, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "
" + ], + "image/png": "iVBORw0KGgoAAAANSUhEUgAABIQAAAK9CAYAAABVd7dpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABX4klEQVR4nO3deXgV5fk/4CeBEIgYEdkRARW3qoBQKVXrBiIqau1CRWVxaa3SanFpaRWktmJdKF20dBO01or6tS7VIhRFXKhWK1qXoiiIyiIVNSw1BDK/P/glNWYRwklOkrnv68p1cd55Z+aZkzwnJx9m5uQkSZIEAAAAAKmRm+0CAAAAAKhfAiEAAACAlBEIAQAAAKSMQAgAAAAgZQRCAAAAACkjEAIAAABIGYEQAAAAQMoIhAAAAABSRiAEAAAAkDICIQBgq1xxxRWRk5NTL/s64ogj4ogjjih/PG/evMjJyYm77rqrXvY/evTo6NGjR73sq7bWrVsXZ599dnTq1ClycnLiwgsvzHZJ260xPO8A0FQIhAAghWbMmBE5OTnlXy1btowuXbrEkCFD4uc//3msXbs2I/tZvnx5XHHFFbFw4cKMbC+TGnJtW+Oqq66KGTNmxDe/+c34wx/+EGeccUa2SwIAGpHm2S4AAMieH/7wh9GzZ88oKSmJlStXxrx58+LCCy+MKVOmxH333RcHHnhg+dzLLrssvve9723T9pcvXx6TJk2KHj16RJ8+fbZ6vdmzZ2/Tfmqjptp++9vfRmlpaZ3XsD0efvjh+NznPhcTJ07MdikAQCMkEAKAFBs6dGj079+//PH48ePj4YcfjhNOOCFOPPHEeOWVV6JVq1YREdG8efNo3rxu3zps2LAhCgoKokWLFnW6n0+Tl5eX1f1vjXfffTf222+/bJcBADRSLhkDACo46qij4vLLL48333wzbr311vLxqu4hNGfOnDj00EOjTZs20bp169h7773j+9//fkRsue/PZz/72YiIGDNmTPnlaTNmzIiILfcJ2n///ePZZ5+NL3zhC1FQUFC+7ifvIVRm8+bN8f3vfz86deoUO+ywQ5x44onx1ltvVZjTo0ePGD16dKV1P77NT6utqnvZrF+/Pi666KLo1q1b5Ofnx9577x3XXXddJElSYV5OTk6MHTs27rnnnth///0jPz8/PvOZz8SsWbOqfsI/4d13342zzjorOnbsGC1btozevXvHzTffXL687H5KS5YsiQceeKC89qVLl1a5vaVLl1Y4tk/WesUVV9RYz8aNG2PChAnRr1+/2GmnnWKHHXaIww47LB555JEq93PdddfFb37zm9hjjz0iPz8/PvvZz8Y//vGPStste35atmwZ+++/f/z5z3/+1OcmIuKEE06I3XffvcplAwcOrBBwAgDVc4YQAFDJGWecEd///vdj9uzZcc4551Q556WXXooTTjghDjzwwPjhD38Y+fn5sXjx4njiiSciImLfffeNH/7whzFhwoT4+te/HocddlhERHz+858v38Z7770XQ4cOja997Wtx+umnR8eOHWus68c//nHk5OTEd7/73Xj33Xdj6tSpMWjQoFi4cGH5mUxbY2tq+7gkSeLEE0+MRx55JM4666zo06dPPPTQQ3HJJZfEO++8Ez/96U8rzH/88cfj7rvvjvPOOy923HHH+PnPfx5f+tKXYtmyZbHLLrtUW9d///vfOOKII2Lx4sUxduzY6NmzZ9x5550xevTo+OCDD+KCCy6IfffdN/7whz/Ed77zndh1113joosuioiI9u3bb/Xxb4uioqL43e9+F6eeemqcc845sXbt2vj9738fQ4YMiaeffrrS5Xa33XZbrF27Nr7xjW9ETk5OXHPNNXHKKafEG2+8UX7m1ezZs+NLX/pS7LfffjF58uR47733YsyYMbHrrrt+aj3Dhw+PkSNHxj/+8Y/yUC8i4s0334y///3vce2112b0+AGgyUoAgNSZPn16EhHJP/7xj2rn7LTTTknfvn3LH0+cODH5+FuHn/70p0lEJKtXr652G//4xz+SiEimT59eadnhhx+eREQybdq0Kpcdfvjh5Y8feeSRJCKSrl27JkVFReXjd9xxRxIRyc9+9rPyse7duyejRo361G3WVNuoUaOS7t27lz++5557kohIfvSjH1WY9+UvfznJyclJFi9eXD4WEUmLFi0qjD3//PNJRCS/+MUvKu3r46ZOnZpERHLrrbeWj23cuDEZOHBg0rp16wrH3r179+T444+vcXtJkiRLliyp9jgjIpk4cWKN62/atCkpLi6uMPb+++8nHTt2TM4888xK+9lll12SNWvWlI/fe++9SUQk999/f/lYnz59ks6dOycffPBB+djs2bOTiKjwvFflww8/TPLz85OLLrqowvg111yT5OTkJG+++WaN6wMAW7hkDACoUuvWrWv8tLE2bdpERMS9995b6xsw5+fnx5gxY7Z6/siRI2PHHXcsf/zlL385OnfuHA8++GCt9r+1HnzwwWjWrFl8+9vfrjB+0UUXRZIk8de//rXC+KBBg2KPPfYof3zggQdGYWFhvPHGG5+6n06dOsWpp55aPpaXlxff/va3Y926dfHoo49m4Gi2TbNmzcrv6VRaWhpr1qyJTZs2Rf/+/eOf//xnpfnDhw+PnXfeufxx2dlXZce+YsWKWLhwYYwaNSp22mmn8nmDBw/eqnsiFRYWxtChQ+OOO+6ocLnezJkz43Of+1zstttutTtQAEiZVAdC8+fPj2HDhkWXLl0iJycn7rnnnm3eRpIkcd1118Vee+0V+fn50bVr1/jxj3+c+WIBoJ6tW7euQvjyScOHD49DDjkkzj777OjYsWN87WtfizvuuGObwqGuXbtu0w2ke/XqVeFxTk5O7LnnntXePydT3nzzzejSpUul52PfffctX/5xVYUSO++8c7z//vufup9evXpFbm7Ft2jV7ae+3HzzzXHggQdGy5YtY5dddon27dvHAw88EB9++GGluZ889rJwqOzYy47hk9/LiIi99957q+oZPnx4vPXWW7FgwYKIiHj99dfj2WefjeHDh2/9QQFAyqU6EFq/fn307t07brjhhlpv44ILLojf/e53cd1118W///3vuO++++Lggw/OYJUAUP/efvvt+PDDD2PPPfesdk6rVq1i/vz58be//S3OOOOMeOGFF2L48OExePDg2Lx581btZ1vu+7O1Pnnj6zJbW1MmNGvWrMrx5BM3oK4P2/t83HrrrTF69OjYY4894ve//33MmjUr5syZE0cddVSV4V99HPuwYcOioKAg7rjjjoiIuOOOOyI3Nze+8pWvZGwfANDUpToQGjp0aPzoRz+KL37xi1UuLy4ujosvvji6du0aO+ywQwwYMCDmzZtXvvyVV16JX/3qV3HvvffGiSeeGD179ox+/frF4MGD6+kIAKBu/OEPf4iIiCFDhtQ4Lzc3N44++uiYMmVKvPzyy/HjH/84Hn744fJPoKoujKit1157rcLjJEli8eLFFT4RbOedd44PPvig0rqfPLtmW2rr3r17LF++vNIldP/+97/Ll2dC9+7d47XXXqsUtGzPfsrO0Pnkc7K1Zxvdddddsfvuu8fdd98dZ5xxRgwZMiQGDRoUH3300TbXEvG/Y/jk9zIiYtGiRVu1jR122CFOOOGEuPPOO6O0tDRmzpwZhx12WHTp0qVWNQFAGqU6EPo0Y8eOjQULFsTtt98eL7zwQnzlK1+JY489tvwNzP333x+77757/OUvf4mePXtGjx494uyzz441a9ZkuXIAqL2HH344rrzyyujZs2ecdtpp1c6r6vdd2SdOFRcXR8SWP9wjKocRtXXLLbdUCGXuuuuuWLFiRQwdOrR8bI899oi///3vsXHjxvKxv/zlL5U+nn5bajvuuONi8+bN8ctf/rLC+E9/+tPIycmpsP/tcdxxx8XKlStj5syZ5WObNm2KX/ziF9G6des4/PDDt3mbhYWF0a5du5g/f36F8RtvvHGr1i874+fjZ/g89dRT5ZdrbavOnTtHnz594uabb65wydmcOXPi5Zdf3urtDB8+PJYvXx6/+93v4vnnn3e5GABsIx87X41ly5bF9OnTY9myZeX/23TxxRfHrFmzYvr06XHVVVfFG2+8EW+++Wbceeedccstt8TmzZvjO9/5Tnz5y1+Ohx9+OMtHAACf7q9//Wv8+9//jk2bNsWqVavi4Ycfjjlz5kT37t3jvvvui5YtW1a77g9/+MOYP39+HH/88dG9e/d4991348Ybb4xdd901Dj300IjYEs60adMmpk2bFjvuuGP5Gbc9e/asVb1t27aNQw89NMaMGROrVq2KqVOnxp577hnnnHNO+Zyzzz477rrrrjj22GPjq1/9arz++utx6623VrjJ87bWNmzYsDjyyCPjBz/4QSxdujR69+4ds2fPjnvvvTcuvPDCStuura9//evx61//OkaPHh3PPvts9OjRI+6666544oknYurUqTXe06kmZ599dlx99dVx9tlnR//+/WP+/Pnx6quvbtW6J5xwQtx9993xxS9+MY4//vhYsmRJTJs2Lfbbb79Yt25dreqZPHlyHH/88XHooYfGmWeeGWvWrIlf/OIX8ZnPfGart3ncccfFjjvuGBdffHE0a9YsvvSlL9WqFgBIK4FQNf71r3/F5s2bY6+99qowXlxcHLvssktEbPmkjeLi4rjlllvK5/3+97+Pfv36xaJFi7b6xogAkC0TJkyIiIgWLVpE27Zt44ADDoipU6fGmDFjPjV8OPHEE2Pp0qVx0003xX/+859o165d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3377xUcffRQjRoyI1157Ldq1axd/+tOfMl0jAAAAABlUq0Bo1113jeeffz5uv/32eOGFF2LdunVx1llnxWmnnVbhJtMAAAAANDy1CoQiIpo3bx6nn356JmsBAAAAoB7UKhC65ZZbalw+cuTIWhUDAAAAQN2rVSB0wQUXVHhcUlISGzZsiBYtWkRBQYFACAAAAKABq9WnjL3//vsVvtatWxeLFi2KQw891E2lAQAAABq4WgVCVenVq1dcffXVlc4eAgAAAKBhyVggFLHlRtPLly/P5CYBAAAAyLBa3UPovvvuq/A4SZJYsWJF/PKXv4xDDjkkI4UBAAAAUDdqFQidfPLJFR7n5ORE+/bt46ijjorrr78+E3UBAAAAUEdqFQiVlpZmug4AAAAA6klG7yEEAAAAQMNXqzOExo0bt9Vzp0yZUptdAAAAAFBHahUIPffcc/Hcc89FSUlJ7L333hER8eqrr0azZs3ioIMOKp+Xk5OTmSoBAAAAyJhaBULDhg2LHXfcMW6++ebYeeedIyLi/fffjzFjxsRhhx0WF110UUaLBAAAACBzanUPoeuvvz4mT55cHgZFROy8887xox/9yKeMAQAAADRwtQqEioqKYvXq1ZXGV69eHWvXrt3uogAAAACoO7UKhL74xS/GmDFj4u67746333473n777fi///u/OOuss+KUU07JdI0AAAAAZFCt7iE0bdq0uPjii2PEiBFRUlKyZUPNm8dZZ50V1157bUYLBAAAACCzahUIFRQUxI033hjXXnttvP766xERsccee8QOO+yQ0eIAAAAAyLxaXTJWZsWKFbFixYro1atX7LDDDpEkSabqAgAAAKCO1CoQeu+99+Loo4+OvfbaK4477rhYsWJFREScddZZPnIeAAAAoIGrVSD0ne98J/Ly8mLZsmVRUFBQPj58+PCYNWtWxooDAAAAIPNqdQ+h2bNnx0MPPRS77rprhfFevXrFm2++mZHCAAAAAKgbtTpDaP369RXODCqzZs2ayM/P3+6iAAAAAKg7tQqEDjvssLjlllvKH+fk5ERpaWlcc801ceSRR27z9m644Ybo0aNHtGzZMgYMGBBPP/10jfOnTp0ae++9d7Rq1Sq6desW3/nOd+Kjjz7a5v0CAAAApFGtLhm75ppr4uijj45nnnkmNm7cGJdeemm89NJLsWbNmnjiiSe2aVszZ86McePGxbRp02LAgAExderUGDJkSCxatCg6dOhQaf5tt90W3/ve9+Kmm26Kz3/+8/Hqq6/G6NGjIycnJ6ZMmVKbwwEAAABIlVqdIbT//vvHq6++GoceemicdNJJsX79+jjllFPiueeeiz322GObtjVlypQ455xzYsyYMbHffvvFtGnToqCgIG666aYq5z/55JNxyCGHxIgRI6JHjx5xzDHHxKmnnvqpZxUBAAAAsMU2nyFUUlISxx57bEybNi1+8IMfbNfON27cGM8++2yMHz++fCw3NzcGDRoUCxYsqHKdz3/+83HrrbfG008/HQcffHC88cYb8eCDD8YZZ5xR5fzi4uIoLi4uf1xUVFR+HCUlJdtVf7bl5VU1tuWYGvuxwfYq6wG9kCFJFS84ZWrzHGd6e1RJH8AWegH0AZRp6r2wLceVkyRJsq07aN++fTz55JPRq1evbV21guXLl0fXrl3jySefjIEDB5aPX3rppfHoo4/GU089VeV6P//5z+Piiy+OJEli06ZNce6558avfvWrKudeccUVMWnSpErjt912W5U3xgYAAABojDZs2BAjRoyIDz/8MAoLC2ucW6t7CJ1++unx+9//Pq6++upaFbg95s2bF1dddVXceOONMWDAgFi8eHFccMEFceWVV8bll19eaf748eNj3Lhx5Y+LioqiW7duccwxx3zqk9PQDR9eeSwvryRGjJgTgwcPjryqTiGClCgpKYk5c/RCxjxexQtOmUNnZn97VEkfwBZ6AfQBlGnqvVB2VdTWqFUgtGnTprjpppvib3/7W/Tr1y922GGHCsu39ubO7dq1i2bNmsWqVasqjK9atSo6depU5TqXX355nHHGGXH22WdHRMQBBxwQ69evj69//evxgx/8IHJzK94WKT8/P/Lz8yttJy8vr9F/82s6E6wpHB9kgl7IkJwaX3Cyvz1qpA9gC70A+gDKNNVe2JZj2qZA6I033ogePXrEiy++GAcddFBERLz66qsV5uTk5Gz19lq0aBH9+vWLuXPnxsknnxwREaWlpTF37twYO3Zslets2LChUujTrFmziIioxdVvAAAAAKmzTYFQr169YsWKFfHII49ERMTw4cPj5z//eXTs2LHWBYwbNy5GjRoV/fv3j4MPPjimTp0a69evjzFjxkRExMiRI6Nr164xefLkiIgYNmxYTJkyJfr27Vt+ydjll18ew4YNKw+GAAAAAKjeNgVCnzwD569//WusX79+uwoYPnx4rF69OiZMmBArV66MPn36xKxZs8pDpmXLllU4I+iyyy6LnJycuOyyy+Kdd96J9u3bx7Bhw+LHP/7xdtUBAAAAkBa1uodQmUxdojV27NhqLxGbN29ehcfNmzePiRMnxsSJEzOybwAAAIC0yf30Kf+Tk5NT6R5B23LPIAAAAACyb5svGRs9enT5p3Z99NFHce6551b6lLG77747cxUCAAAAkFHbFAiNGjWqwuPTTz89o8UAAAAAUPe2KRCaPn16XdUBAAAAQD3ZpnsIAQAAAND4CYQAAAAAUkYgBAAAAJAyAiEAAACAlBEIAQAAAKSMQAgAAAAgZQRCAAAAACkjEAIAAABIGYEQAAAAQMoIhAAAAABSRiAEAAAAkDICIQAAAICUEQgBAAAApIxACAAAACBlBEIAAAAAKSMQAgAAAEgZgRAAAABAygiEAAAAAFJGIAQAAACQMgIhAAAAgJQRCAEAAACkjEAIAAAAIGUEQgAAAAApIxACAAAASBmBEAAAAEDKCIQAAAAAUkYgBAAAAJAyAiEAAACAlBEIAQAAAKSMQAgAAAAgZQRCAAAAACkjEAIAAABIGYEQAAAAQMoIhAAAAABSRiAEAAAAkDICIQAAAICUEQgBAAAApIxACAAAACBlBEIAAAAAKSMQAgAAAEgZgRAAAABAygiEAAAAAFJGIAQAAACQMgIhAAAAgJQRCAEAAACkjEAIAAAAIGUEQgAAAAApIxACAAAASBmBEAAAAEDKCIQAAAAAUkYgBAAAAJAyAiEAAACAlBEIAQAAAKSMQAgAAAAgZQRCAAAAACnTIAKhG264IXr06BEtW7aMAQMGxNNPP13j/A8++CDOP//86Ny5c+Tn58dee+0VDz74YD1VCwAAANC4Nc92ATNnzoxx48bFtGnTYsCAATF16tQYMmRILFq0KDp06FBp/saNG2Pw4MHRoUOHuOuuu6Jr167x5ptvRps2beq/eAAAAIBGKOuB0JQpU+Kcc86JMWPGRETEtGnT4oEHHoibbropvve971Waf9NNN8WaNWviySefjLy8vIiI6NGjR32WDAAAANCoZTUQ2rhxYzz77LMxfvz48rHc3NwYNGhQLFiwoMp17rvvvhg4cGCcf/75ce+990b79u1jxIgR8d3vfjeaNWtWaX5xcXEUFxeXPy4qKoqIiJKSkigpKcnwEdWv/5+HfWJsyzE19mOD7VXWA3ohQ5IqXnDK1OY5zvT2qJI+gC30AugDKNPUe2FbjisnSZKkDmup0fLly6Nr167x5JNPxsCBA8vHL7300nj00UfjqaeeqrTOPvvsE0uXLo3TTjstzjvvvFi8eHGcd9558e1vfzsmTpxYaf4VV1wRkyZNqjR+2223RUFBQWYPCAAAACBLNmzYECNGjIgPP/wwCgsLa5yb9UvGtlVpaWl06NAhfvOb30SzZs2iX79+8c4778S1115bZSA0fvz4GDduXPnjoqKi6NatWxxzzDGf+uQ0dMOHVx7LyyuJESPmxODBg8svqYM0KikpiTlz9ELGPF7FC06ZQ2dmf3tUSR/AFnoB9AGUaeq9UHZV1NbIaiDUrl27aNasWaxatarC+KpVq6JTp05VrtO5c+fIy8urcHnYvvvuGytXroyNGzdGixYtKszPz8+P/Pz8StvJy8tr9N/8ms4EawrHB5mgFzIkp8YXnOxvjxrpA9hCL4A+gDJNtRe25Ziy+rHzLVq0iH79+sXcuXPLx0pLS2Pu3LkVLiH7uEMOOSQWL14cpaWl5WOvvvpqdO7cuVIYBAAAAEBlWQ2EIiLGjRsXv/3tb+Pmm2+OV155Jb75zW/G+vXryz91bOTIkRVuOv3Nb34z1qxZExdccEG8+uqr8cADD8RVV10V559/frYOAQAAAKBRyfo9hIYPHx6rV6+OCRMmxMqVK6NPnz4xa9as6NixY0RELFu2LHJz/5dbdevWLR566KH4zne+EwceeGB07do1Lrjggvjud7+brUMAAAAAaFSyHghFRIwdOzbGjh1b5bJ58+ZVGhs4cGD8/e9/r+OqAAAAAJqmrF8yBgAAAED9EggBAAAApIxACAAAACBlBEIAAAAAKSMQAgAAAEgZgRAAAABAygiEAAAAAFJGIAQAAACQMgIhAAAAgJQRCAEAAACkjEAIAAAAIGUEQgAAAAApIxACAAAASBmBEAAAAEDKCIQAAAAAUkYgBAAAAJAyAiEAAACAlBEIAQAAAKSMQAgAAAAgZQRCAAAAACkjEAIAAABIGYEQAAAAQMoIhAAAAABSRiAEAAAAkDICIQAAAICUEQgBAAAApEzzbBcAAAAAkGnDhlUey8uLGDWq/mtpiJwhBAAAAJAyAiEAAACAlBEIAQAAAKSMQAgAAAAgZQRCAAAAACkjEAIAAABIGYEQAAAAQMoIhAAAAABSRiAEAAAAkDICIQAAAICUEQgBAAAApIxACAAAACBlBEIAAAAAKSMQAgAAAEgZgRAAAABAyjTPdgEAsDWGDat+2f33118dAADQFDhDCAAAACBlBEIAAAAAKSMQAgAAAEgZgRAAAABAyripNAA0NvOqucP2Ee6uDQDA1nGGEAAAAEDKCIQAAAAAUkYgBAAAAJAyAiEAAACAlBEIAQAAAKSMQAgAAAAgZQRCAAAAACkjEAIAAABIGYEQAAAAQMoIhAAAAABSRiAEAAAAkDINIhC64YYbokePHtGyZcsYMGBAPP3001u13u233x45OTlx8skn122BAAAAAE1I1gOhmTNnxrhx42LixInxz3/+M3r37h1DhgyJd999t8b1li5dGhdffHEcdthh9VQpAAAAQNOQ9UBoypQpcc4558SYMWNiv/32i2nTpkVBQUHcdNNN1a6zefPmOO2002LSpEmx++6712O1AAAAAI1f82zufOPGjfHss8/G+PHjy8dyc3Nj0KBBsWDBgmrX++EPfxgdOnSIs846Kx577LEa91FcXBzFxcXlj4uKiiIioqSkJEpKSrbzCLIrL6+qsS3H1NiPDbZXWQ/ohQxJqnjBKVOb57gW26vqNW97SmjUqnv+PvFE6APYQi+APiCd0vg387YcV06SJEkd1lKj5cuXR9euXePJJ5+MgQMHlo9feuml8eijj8ZTTz1VaZ3HH388vva1r8XChQujXbt2MXr06Pjggw/innvuqXIfV1xxRUyaNKnS+G233RYFBQUZOxYAAACAbNqwYUOMGDEiPvzwwygsLKxxblbPENpWa9eujTPOOCN++9vfRrt27bZqnfHjx8e4cePKHxcVFUW3bt3imGOO+dQnp6EbPrzyWF5eSYwYMScGDx4ceTX9dzo0cSUlJTFnjl7ImMereMEpc+jMetleVa95ZWbWooRGrbrn7xPPnT6ALfQC6APSKY1/M5ddFbU1shoItWvXLpo1axarVq2qML5q1aro1KlTpfmvv/56LF26NIYNG1Y+VlpaGhERzZs3j0WLFsUee+xRYZ38/PzIz8+vtK28vLxG/82v6UywpnB8kAl6IUNyanzBqZft1fyat+0lNGrVPX/VPBH6ALbQC6APSJc0/s28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VE04B5mR1UBo48aN8eyzz8b48ePLx3Jzc2PQoEGxYMGCKtdZsGBBjBs3rsLYkCFD4p577qlyfnFxcRQXF5c//vDDDyMiYs2aNVFSUsUNpBq9ktiwYUO8N2t45FV1g6yBM6pdc/ToqsdnfKOG3b333jZtq163V822Ps1767Z9e5muL5Pfi5q895dqdhRR489Ktdur7rmLqN33Yzu2V1Ly/3vhvfciLy+v5u3V8mcl49tbMLrq8Vp8L6rdVi23l+nvbaPdXgP5Wdna+j7eBxF5Va6ydmNmj7fRfm8byvaqe12uxe/viMz+DrrwcxF9em/79qpVj9+Lkvfeq/w74dM09J+VBvA6VW/vzxrr96KG7WXjvXJJElv6IDciL6f6bX2ahvCzV516e02OSNf7qUb8d1XVSir2wnZvr2FZu3ZtREQkSfLpk5Mseuedd5KISJ588skK45dcckly8MEHV7lOXl5ectttt1UYu+GGG5IOHTpUOX/ixIlJRPjy5cuXL1++fPny5cuXL1++fKXi66233vrUTCbrl4zVtfHjx1c4o6i0tDTWrFkTu+yyS+TkVBUHNm5FRUXRrVu3eOutt6KwsDDb5UDW6AXQB1BGL4A+gDJNvReSJIm1a9dGly5dPnVuVgOhdu3aRbNmzWLVqlUVxletWhWdOnWqcp1OnTpt0/z8/PzIz8+vMNamTZvaF91IFBYWNskfbthWegH0AZTRC6APoExT7oWddtppq+bl1nEdNWrRokX069cv5s6dWz5WWloac+fOjYEDB1a5zsCBAyvMj4iYM2dOtfMBAAAAqCjrl4yNGzcuRo0aFf3794+DDz44pk6dGuvXry//1LGRI0dG165dY/LkyRERccEFF8Thhx8e119/fRx//PFx++23xzPPPBO/+c1vsnkYAAAAAI1G1gOh4cOHx+rVq2PChAmxcuXK6NOnT8yaNSs6duwYERHLli2L3Nz/ncj0+c9/Pm677ba47LLL4vvf/3706tUr7rnnnth///2zdQgNSn5+fkycOLHSZXKQNnoB9AGU0QugD6CMXvifnCTZms8iAwAAAKCpyOo9hAAAAACofwIhAAAAgJQRCAEAAACkjEAIAAAAIGUEQk3MDTfcED169IiWLVvGgAED4umnn852SVArV1xxReTk5FT42meffcqXf/TRR3H++efHLrvsEq1bt44vfelLsWrVqgrbWLZsWRx//PFRUFAQHTp0iEsuuSQ2bdpUYc68efPioIMOivz8/Nhzzz1jxowZ9XF4UK358+fHsGHDokuXLpGTkxP33HNPheVJksSECROic+fO0apVqxg0aFC89tprFeasWbMmTjvttCgsLIw2bdrEWWedFevWrasw54UXXojDDjssWrZsGd26dYtrrrmmUi133nln7LPPPtGyZcs44IAD4sEHH8z48UJVPq0PRo8eXel3xLHHHlthjj6gKZg8eXJ89rOfjR133DE6dOgQJ598cixatKjCnPp8T+RvDbJha/rgiCOOqPR74dxzz60wRx9UIaHJuP3225MWLVokN910U/LSSy8l55xzTtKmTZtk1apV2S4NttnEiROTz3zmM8mKFSvKv1avXl2+/Nxzz026deuWzJ07N3nmmWeSz33uc8nnP//58uWbNm1K9t9//2TQoEHJc889lzz44INJu3btkvHjx5fPeeONN5KCgoJk3Lhxycsvv5z84he/SJo1a5bMmjWrXo8VPu7BBx9MfvCDHyR33313EhHJn//85wrLr7766mSnnXZK7rnnnuT5559PTjzxxKRnz57Jf//73/I5xx57bNK7d+/k73//e/LYY48le+65Z3LqqaeWL//www+Tjh07Jqeddlry4osvJn/605+SVq1aJb/+9a/L5zzxxBNJs2bNkmuuuSZ5+eWXk8suuyzJy8tL/vWvf9X5cwCf1gejRo1Kjj322Aq/I9asWVNhjj6gKRgyZEgyffr05MUXX0wWLlyYHHfcccluu+2WrFu3rnxOfb0n8rcG2bI1fXD44Ycn55xzToXfCx9++GH5cn1QNYFQE3LwwQcn559/fvnjzZs3J126dEkmT56cxaqgdiZOnJj07t27ymUffPBBkpeXl9x5553lY6+88koSEcmCBQuSJNnyx0Rubm6ycuXK8jm/+tWvksLCwqS4uDhJkiS59NJLk8985jMVtj18+PBkyJAhGT4aqJ1P/iFcWlqadOrUKbn22mvLxz744IMkPz8/+dOf/pQkSZK8/PLLSUQk//jHP8rn/PWvf01ycnKSd955J0mSJLnxxhuTnXfeubwXkiRJvvvd7yZ77713+eOvfvWryfHHH1+hngEDBiTf+MY3MnqM8GmqC4ROOumkatfRBzRV7777bhIRyaOPPpokSf2+J/K3Bg3FJ/sgSbYEQhdccEG16+iDqrlkrInYuHFjPPvsszFo0KDysdzc3Bg0aFAsWLAgi5VB7b322mvRpUuX2H333eO0006LZcuWRUTEs88+GyUlJRV+3vfZZ5/Ybbfdyn/eFyxYEAcccEB07NixfM6QIUOiqKgoXnrppfI5H99G2Rw9Q0O1ZMmSWLlyZYWf25122ikGDBhQ4We/TZs20b9///I5gwYNitzc3HjqqafK53zhC1+IFi1alM8ZMmRILFq0KN5///3yOfqDhmzevHnRoUOH2HvvveOb3/xmvPfee+XL9AFN1YcffhgREW3bto2I+ntP5G8NGpJP9kGZP/7xj9GuXbvYf//9Y/z48bFhw4byZfqgas2zXQCZ8Z///Cc2b95c4Qc8IqJjx47x73//O0tVQe0NGDAgZsyYEXvvvXesWLEiJk2aFIcddli8+OKLsXLlymjRokW0adOmwjodO3aMlStXRkTEypUrq+yHsmU1zSkqKor//ve/0apVqzo6Oqidsp/dqn5uP/5z3aFDhwrLmzdvHm3btq0wp2fPnpW2UbZs5513rrY/yrYB2XTsscfGKaecEj179ozXX389vv/978fQoUNjwYIF0axZM31Ak1RaWhoXXnhhHHLIIbH//vtHRNTbe6L333/f3xo0CFX1QUTEiBEjonv37tGlS5d44YUX4rvf/W4sWrQo7r777ojQB9URCAEN0tChQ8v/feCBB8aAAQOie/fucccddwhqAFLua1/7Wvm/DzjggDjwwANjjz32iHnz5sXRRx+dxcqg7px//vnx4osvxuOPP57tUiBrquuDr3/96+X/PuCAA6Jz585x9NFHx+uvvx577LFHfZfZaLhkrIlo165dNGvWrNInCqxatSo6deqUpaogc9q0aRN77bVXLF68ODp16hQbN26MDz74oMKcj/+8d+rUqcp+KFtW05zCwkKhEw1S2c9uTa/1nTp1infffbfC8k2bNsWaNWsy0h9+p9AQ7b777tGuXbtYvHhxROgDmp6xY8fGX/7yl3jkkUdi1113LR+vr/dE/tagIaiuD6oyYMCAiIgKvxf0QWUCoSaiRYsW0a9fv5g7d275WGlpacydOzcGDhyYxcogM9atWxevv/56dO7cOfr16xd5eXkVft4XLVoUy5YtK/95HzhwYPzrX/+q8AfBnDlzorCwMPbbb7/yOR/fRtkcPUND1bNnz+jUqVOFn9uioqJ46qmnKvzsf/DBB/Hss8+Wz3n44YejtLS0/M3RwIEDY/78+VFSUlI+Z86cObH33nvHzjvvXD5Hf9BYvP322/Hee+9F586dI0If0HQkSRJjx46NP//5z/Hwww9Xusyxvt4T+VuDbPq0PqjKwoULIyIq/F7QB1XI9l2tyZzbb789yc/PT2bMmJG8/PLLyde//vWkTZs2Fe6kDo3FRRddlMybNy9ZsmRJ8sQTTySDBg1K2rVrl7z77rtJkmz5iNXddtstefjhh5NnnnkmGThwYDJw4MDy9cs+WvKYY45JFi5cmMyaNStp3759lR8teckllySvvPJKcsMNN/jYebJu7dq1yXPPPZc899xzSUQkU6ZMSZ577rnkzTffTJJky8fOt2nTJrn33nuTF154ITnppJOq/Nj5vn37Jk899VTy+OOPJ7169arwcdsffPBB0rFjx+SMM85IXnzxxeT2229PCgoKKn3cdvPmzZPrrrsueeWVV5KJEyf6uG3qTU19sHbt2uTiiy9OFixYkCxZsiT529/+lhx00EFJr169ko8++qh8G/qApuCb3/xmstNOOyXz5s2r8HHaGzZsKJ9TX++J/K1BtnxaHyxevDj54Q9/mDzzzDPJkiVLknvvvTfZfffdky984Qvl29AHVRMINTG/+MUvkt122y1p0aJFcvDBByd///vfs10S1Mrw4cOTzp07Jy1atEi6du2aDB8+PFm8eHH58v/+97/Jeeedl+y8885JQUFB8sUvfjFZsWJFhW0sXbo0GTp0aNKqVaukXbt2yUUXXZSUlJRUmPPII48kffr0SVq0aJHsvvvuyfTp0+vj8KBajzzySBIRlb5GjRqVJMmWj56//PLLk44dOyb5+fnJ0UcfnSxatKjCNt57773k1FNPTVq3bp0UFhYmY8aMSdauXVthzvPPP58ceuihSX5+ftK1a9fk6quvrlTLHXfckey1115JixYtks985jPJAw88UGfHDR9XUx9s2LAhOeaYY5L27dsneXl5Sffu3ZNzzjmn0ptxfUBTUFUfRESF9yv1+Z7I3xpkw6f1wbJly5IvfOELSdu2bZP8/Pxkzz33TC655JLkww8/rLAdfVBZTpIkSf2djwQAAABAtrmHEAAAAEDKCIQAAAAAUkYgBAAAAJAyAiEAAACAlBEIAQAAAKSMQAgAAAAgZQRCAAAAACkjEAIAAABIGYEQAEAdOeKII+LCCy/MdhkAAJUIhAAAqjBs2LA49thjq1z22GOPRU5OTrzwwgv1XBUAQGYIhAAAqnDWWWfFnDlz4u233660bPr06dG/f/848MADs1AZAMD2EwgBAFThhBNOiPbt28eMGTMqjK9bty7uvPPOOPnkk+PUU0+Nrl27RkFBQRxwwAHxpz/9qcZt5uTkxD333FNhrE2bNhX28dZbb8VXv/rVaNOmTbRt2zZOOumkWLp0aWYOCgDg/xMIAQBUoXnz5jFy5MiYMWNGJElSPn7nnXfG5s2b4/TTT49+/frFAw88EC+++GJ8/etfjzPOOCOefvrpWu+zpKQkhgwZEjvuuGM89thj8cQTT0Tr1q3j2GOPjY0bN2bisAAAIkIgBABQrTPPPDNef/31ePTRR8vHpk+fHl/60peie/fucfHFF0efPn1i9913j29961tx7LHHxh133FHr/c2cOTNKS0vjd7/7XRxwwAGx7777xvTp02PZsmUxb968DBwRAMAWAiEAgGrss88+8fnPfz5uuummiIhYvHhxPPbYY3HWWWfF5s2b48orr4wDDjgg2rZtG61bt46HHnooli1bVuv9Pf/887F48eLYcccdo3Xr1tG6deto27ZtfPTRR/H6669n6rAAAKJ5tgsAAGjIzjrrrPjWt74VN9xwQ0yfPj322GOPOPzww+MnP/lJ/OxnP4upU6fGAQccEDvssENceOGFNV7alZOTU+Hys4gtl4mVWbduXfTr1y/++Mc/Vlq3ffv2mTsoACD1BEIAADX46le/GhdccEHcdtttccstt8Q3v/nNyMnJiSeeeCJOOumkOP300yMiorS0NF599dXYb7/9qt1W+/btY8WKFeWPX3vttdiwYUP544MOOihmzpwZHTp0iMLCwro7KAAg9VwyBgBQg9atW8fw4cNj/PjxsWLFihg9enRERPTq1SvmzJkTTz75ZLzyyivxjW98I1atWlXjto466qj45S9/Gc8991w888wzce6550ZeXl758tNOOy3atWsXJ510Ujz22GOxZMmSmDdvXnz729+Ot99+uy4PEwBIGYEQAMCnOOuss+L999+PIUOGRJcuXSIi4rLLLouDDjoohgwZEkcccUR06tQpTj755Bq3c/3110e3bt3isMMOixEjRsTFF18cBQUF5csLCgpi/vz5sdtuu8Upp5wS++67b5x11lnx0UcfOWMIAMionOSTF7IDAAAA0KQ5QwgAAAAgZQRCAAAAACkjEAIAAABIGYEQAAAAQMoIhAAAAABSRiAEAAAAkDICIQAAAICUEQgBAAAApIxACAAAACBlBEIAAAAAKSMQAgAAAEiZ/wegIqCJFG/emgAAAABJRU5ErkJggg==\n" + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "data['time_stamp']" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "-b0wmfUiM-YN", + "outputId": "dda9e873-c051-4dff-fdfc-c13e6bfb7f2f" + }, + "execution_count": 66, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "0 1\n", + "1 1\n", + "2 2\n", + "3 2\n", + "4 3\n", + " ... \n", + "4554339 46571\n", + "4554340 46571\n", + "4554341 46571\n", + "4554342 46571\n", + "4554343 46572\n", + "Name: time_stamp, Length: 4554344, dtype: int64" + ] + }, + "metadata": {}, + "execution_count": 66 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Analysis of the graph's final state\n" + ], + "metadata": { + "id": "R4Tc5SsgNvja" + } + }, + { + "cell_type": "code", + "source": [ + "# @title Calculate in-degree and out-degree for every node\n", + "in_degree = data['v'].value_counts()\n", + "out_degree = data['u'].value_counts()\n", + "degree_df['total_degree'] = degree_df['in_degree'] + degree_df['out_degree']" + ], + "metadata": { + "id": "pzfxUpBCNw56" + }, + "execution_count": 67, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Create a DataFrame for degrees\n", + "degree_df = pd.DataFrame({\n", + " 'node': list(set(data['u']).union(set(data['v']))),\n", + " 'in_degree': in_degree,\n", + " 'out_degree': out_degree\n", + "}).fillna(0).astype(int)\n", + "\n", + "# Add a new column that is the sum of in-degree and out-degree\n", + "degree_df['total_degree'] = degree_df['in_degree'] + degree_df['out_degree']\n", + "\n", + "# Display the new DataFrame\n", + "display(degree_df)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 423 + }, + "id": "kHXqyniFN3fq", + "outputId": "cc362a14-0e4b-42c7-e1d7-b1a61ae1231d" + }, + "execution_count": 68, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + " node in_degree out_degree total_degree\n", + "0 0 21 0 21\n", + "1 1 21 0 21\n", + "2 2 84 0 84\n", + "3 3 0 777 777\n", + "4 4 0 1 1\n", + "... ... ... ... ...\n", + "25520 25520 36 0 36\n", + "25521 25521 99 0 99\n", + "25522 25522 0 1001 1001\n", + "25523 25523 0 499613 499613\n", + "25524 25524 1809 1 1810\n", + "\n", + "[25525 rows x 4 columns]" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
nodein_degreeout_degreetotal_degree
0021021
1121021
2284084
330777777
44011
...............
255202552036036
255212552199099
2552225522010011001
25523255230499613499613
2552425524180911810
\n", + "

25525 rows × 4 columns

\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe", + "variable_name": "degree_df", + "summary": "{\n \"name\": \"degree_df\",\n \"rows\": 25525,\n \"fields\": [\n {\n \"column\": \"node\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 7368,\n \"min\": 0,\n \"max\": 25524,\n \"num_unique_values\": 25525,\n \"samples\": [\n 15191,\n 3820,\n 24525\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"in_degree\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 11439,\n \"min\": 0,\n \"max\": 1639430,\n \"num_unique_values\": 681,\n \"samples\": [\n 1873,\n 703,\n 109\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"out_degree\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 8059,\n \"min\": 0,\n \"max\": 1024185,\n \"num_unique_values\": 159,\n \"samples\": [\n 23455,\n 5225,\n 597\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"total_degree\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 14535,\n \"min\": 1,\n \"max\": 1649547,\n \"num_unique_values\": 749,\n \"samples\": [\n 5702,\n 885,\n 274\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}" + } + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "# @title Find the top k nodes by total degree\n", + "k = 20\n", + "top_k_nodes = degree_df.nlargest(k, 'total_degree')\n", + "\n", + "# Plot multi-bar chart\n", + "plt.figure(figsize=(14, 8))\n", + "width = 0.35\n", + "x = np.arange(len(top_k_nodes['node'].astype(str)))\n", + "\n", + "plt.bar(x - width/2, top_k_nodes['in_degree'], width, label='In-Degree', color='blue')\n", + "plt.bar(x + width/2, top_k_nodes['out_degree'], width, label='Out-Degree', color='orange')\n", + "\n", + "plt.xlabel('Node')\n", + "plt.ylabel('Degree')\n", + "plt.title(f'Top {k} Nodes by Total Degree')\n", + "plt.xticks(x, top_k_nodes['node'].astype(str), rotation=90)\n", + "plt.legend()\n", + "plt.grid(True)\n", + "plt.show()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 748 + }, + "id": "EQqA9_UaQXoE", + "outputId": "ce370a3c-2b39-4a5e-f29f-c539a83b6027" + }, + "execution_count": 69, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "
" + ], + "image/png": "iVBORw0KGgoAAAANSUhEUgAABIQAAALbCAYAAACL2lsuAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAACPEklEQVR4nOzdeZyN9f//8eeZxaxm7MwwjLIWWSOULWONVFKNIlsLPrZPlBZD0arSohThQ5SUpZRlyBZK1hSyL1kGWWeGccy8f3/4zfl2msFs5izX4367nVtd63m9ruvM9vS+rstmjDECAAAAAACAZfi4ugAAAAAAAADkLwIhAAAAAAAAiyEQAgAAAAAAsBgCIQAAAAAAAIshEAIAAAAAALAYAiEAAAAAAACLIRACAAAAAACwGAIhAAAAAAAAiyEQAgAAAAAAsBgCIQAA4JWWL18um82m5cuX59t7jhgxQjabTSdPnsy393Slxx9/XNHR0a4uAwAA5ACBEAAALmCz2bL0utFhxqFDhzRy5EjVq1dPhQsXVrFixdS0aVMtWbIk0/XPnDmjJ554QsWLF1dISIiaNWumjRs3Zum9mjZtKpvNpvbt22dYtn//ftlsNo0ZMyZX/Xi7KVOmZOlzk5WQZtu2bRoxYoT2799/w+tOP/c2m00+Pj4KCwtT5cqV9dhjjyk+Pv6Gvz8AAMjIz9UFAABgRdOmTXOanjp1quLj4zPMr1q16g2tY968eXrjjTfUsWNHdevWTZcvX9bUqVMVExOjSZMmqXv37o5109LS1K5dO23ZskVDhgxRsWLF9NFHH6lp06basGGDKlasmKX3nD9/vjZs2KA6dercqLa8VuPGjTN8Rnr16qV69erpiSeecMwLDQ297r62bdumkSNHqmnTpvkyyqdMmTJ67bXXJElJSUnavXu3Zs+erc8//1ydO3fW559/Ln9//xteBwAAuIJACAAAF3j00Uedpn/++WfFx8dnmH+jNWvWTAcPHlSxYsUc85566inVrFlTw4cPdwqEvv76a61Zs0azZs1Sp06dJEmdO3dWpUqVFBcXpxkzZlz3/cqWLavz589r5MiR+vbbb/O+IS9300036aabbnKa99RTT+mmm27K989OdoWHh2eo8fXXX1f//v310UcfKTo6Wm+88Ua+1nTx4kUVKFBAPj4MmgcAWA8//QAAcFNJSUn673//q6ioKAUEBKhy5coaM2aMjDFO69lsNvXr10/Tp09X5cqVFRgYqDp16mjlypXXfY9bb73VKQySpICAALVt21Z//fWXzp8/75j/9ddfq2TJkrr//vsd84oXL67OnTtr3rx5SklJue77FSxYUIMGDdJ3332XpUvN9u7dqwcffFBFihRRcHCw7rjjDn3//fcZ1vvrr7/UsWNHhYSEqESJEho0aNBV6/nll1/UunVrhYeHKzg4WE2aNNHq1aud1jl//rwGDhyo6OhoBQQEqESJEoqJicny5XEnT55U586dFRYWpqJFi2rAgAG6ePGiY3mTJk1Uo0aNTLetXLmyWrVqlaX3uZpNmzapTZs2CgsLU2hoqO6++279/PPPjuVTpkzRgw8+KOlKKPjvSxTnzZundu3aKTIyUgEBAbr55pv1yiuvKDU1NVd1/Zuvr6/ef/993XLLLfrwww919uxZp+Wff/656tSpo6CgIBUpUkQPP/ywDh06lGE/48aN00033aSgoCDVq1dPq1atUtOmTdW0aVPHOun3lPryyy/14osvqnTp0goODta5c+ckZe1zIUmHDx9Wjx49VLJkSQUEBOjWW2/VpEmT8vS4AACQHwiEAABwQ8YYdejQQe+++65at26td955R5UrV9aQIUM0ePDgDOuvWLFCAwcO1KOPPqqXX35Zf//9t1q3bq3ff/89R+9/7NgxBQcHKzg42DFv06ZNql27dobRFPXq1VNycrJ27tyZpX0PGDBAhQsX1ogRI665XkJCgho2bKhFixapT58+Gj16tC5evKgOHTpozpw5jvUuXLigu+++W4sWLVK/fv30wgsvaNWqVRo6dGiGff74449q3Lixzp07p7i4OL366qs6c+aMmjdvrnXr1jnWe+qpp/Txxx/rgQce0EcffaRnnnlGQUFB2r59e5Z67Ny5sy5evKjXXntNbdu21fvvv+90Sddjjz2m3377LcP5+fXXX7Vz585cjfb5448/dNddd2nLli0aOnSoXnrpJe3bt09NmzbVL7/8IunKpWf9+/eXJD3//POaNm2apk2b5rhEccqUKQoNDdXgwYP13nvvqU6dOho+fLiee+65HNd1Nb6+vnrkkUeUnJysn376yTF/9OjR6tq1qypWrKh33nlHAwcO1NKlS9W4cWOdOXPGsd7HH3+sfv36qUyZMnrzzTd11113qWPHjvrrr78yfb9XXnlF33//vZ555hm9+uqrKlCgQJY/FwkJCbrjjju0ZMkS9evXT++9954qVKignj17auzYsXl+bAAAuKEMAABwub59+5p//lieO3eukWRGjRrltF6nTp2MzWYzu3fvdsyTZCSZ9evXO+YdOHDABAYGmvvuuy/btezatcsEBgaaxx57zGl+SEiI6dGjR4b1v//+eyPJLFy48Jr7bdKkibn11luNMcaMHDnSSDIbNmwwxhizb98+I8m89dZbjvUHDhxoJJlVq1Y55p0/f96UL1/eREdHm9TUVGOMMWPHjjWSzFdffeVYLykpyVSoUMFIMsuWLTPGGJOWlmYqVqxoWrVqZdLS0hzrJicnm/Lly5uYmBjHvPDwcNO3b99r9pOZuLg4I8l06NDBaX6fPn2MJLNlyxZjjDFnzpwxgYGB5tlnn3Var3///iYkJMQkJiZm+T1DQkJMt27dHNMdO3Y0BQoUMHv27HHMO3LkiClYsKBp3LixY96sWbOcjs8/JScnZ5j35JNPmuDgYHPx4kXHvG7duply5cpdt8Z/nvvMzJkzx0gy7733njHGmP379xtfX18zevRop/W2bt1q/Pz8HPNTUlJM0aJFze23327sdrtjvSlTphhJpkmTJo55y5YtM5LMTTfd5NRfdj4XPXv2NBEREebkyZNOdT388MMmPDw80+MGAIC7YoQQAABu6IcffpCvr69jFEe6//73vzLGaMGCBU7zGzRo4HST5rJly+ree+/VokWLsnWZT3Jysh588EEFBQXp9ddfd1p24cIFBQQEZNgmMDDQsTyr0kcJjRw58qrr/PDDD6pXr57uvPNOx7zQ0FA98cQT2r9/v7Zt2+ZYLyIiwnFfI0kKDg52GpEjSZs3b9auXbsUGxurv//+WydPntTJkyeVlJSku+++WytXrlRaWpokqVChQvrll1905MiRLPf0T3379nWa/s9//uOoVbpyP517771XX3zxheMSwNTUVM2cOdNx6VtOpKamavHixerYsaPTvYYiIiIUGxurn376yXGJ1LUEBQU5/v/8+fM6efKk7rrrLiUnJ2vHjh05qu1a0m+CnX6J4uzZs5WWlqbOnTs7ztPJkydVqlQpVaxYUcuWLZMkrV+/Xn///bd69+4tP7//uzVmly5dVLhw4Uzfq1u3bk79ZfVzYYzRN998o/bt28sY41RXq1at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t9batWv1+eefKzY2VjVq1HBar1atWqpWrZpmzZqlqlWrqnbt2jk6jv80atQoxcfH684771SfPn3k5+enTz75RCkpKXrzzTcd69WsWVO+vr564403dPbsWQUEBKh58+Zq2LChChcurG7duql///6y2WyaNm1ahmAmu86ePev4fCUnJ2v37t2aPXu29uzZo4cfflivvPKKY92bb75Zo0aN0rBhw7R//3517NhRBQsW1L59+zRnzhw98cQTeuaZZ1SgQAGNGDFC//nPf9S8eXN17txZ+/fv15QpU3TzzTdfdRTUP2X1cyFJr7/+upYtW6b69eurd+/euuWWW3Tq1Clt3LhRS5Ys4R8FAQCexTUPN3M/ksycOXMc0/PnzzeSTEhIiNPLz8/PdO7c2RhjTO/evY0k8+effzq227Bhg5FkduzYkd8tAAA82L8fO2/MlUesDxo0yERGRhp/f39TsWJF89Zbbzk9GtuYKz/D+vbtaz7//HNTsWJFExAQYGrVqpXp48T/Lf0x6Vd7/Xsfp06dMj179jRFixY1wcHBpkmTJubXX3/NUo9Xe/T46dOnTXh4eIbHzhtjzJ49e0ynTp1MoUKFTGBgoKlXr56ZP39+hn0cOHDAdOjQwQQHB5tixYqZAQMGmIULF2baw6ZNm8z9999vihYtagICAky5cuVM586dzdKlS40xVx5lPmTIEFOjRg1TsGBBExISYmrUqGE++uij6/aYfjy3bdtmOnXqZAoWLGgKFy5s+vXrZy5cuJDpNm+++aaRZF599dXr7j8z/37svDHGbNy40bRq1cqEhoaa4OBg06xZM7NmzZoM206YMMHcdNNNxtfX1+lYrV692txxxx0mKCjIREZGmqFDh5pFixZlOJ7Zeez8Pz9XoaGhpmLFiubRRx81ixcvvup233zzjbnzzjsdv4dVqVLF9O3b1+l3L2OMef/99025cuVMQECAqVevnlm9erWpU6eOad26tWOd9MfOz5o1K9P3ut7nIl1CQoLp27eviYqKMv7+/qZUqVLm7rvvNp9++ul1jwMAAO7EZkwu/7nHS9hsNs2ZM0cdO3aUJM2cOVNdunTRH3/8kWGofWhoqEqVKqW4uDi9+uqrstvtjmUXLlxQcHCwFi9erJiYmPxsAQBgUTabTX379s1weRk8w3vvvadBgwZp//79Klu2rKvL8QppaWkqXry4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H3/8Md577z1cvnzZ5vcMGzYMly9fxu7du5GVlYXOnTujT58+uHr16v+6RCIiInIwzqoDEBEREZnBkCFDEBERgblz52LVqlU2bcuXL0efPn0we/ZsAEBoaChOnjyJt99+GwkJCcjNzcXu3btx5MgRREVFAQBWrVqFtm3bWn9HRkYGjhw5gsuXL8PV1RUAsGzZMmzbtg0ff/wxxo8fX0+VEhERkSPgFUJEREREdlq6dCk++ugjnDp1ymb7qVOnEB0dbbMtOjoaeXl5KCsrw6lTp+Ds7IzIyEhre1hYGLy8vKzvs7OzcevWLXh7e6NJkybW15kzZ5Cfn/8/rYuIiIgcD68QIiIiIrJTz5490a9fPyQnJyMhIeFn/d23bt2Cr68vDh48WKnt4YkjIiIiop8DJ4SIiIiI6mDJkiWIiIhAmzZtrNvatm2LQ4cO2fzcoUOHEBoaCicnJ4SFheH+/fvIysqy3jKWk5ODa9euWX++c+fOKCoqgrOzMwIDA+ujFCIiInJgvGWMiIiIqA6eeeYZvPzyy3j33Xet26ZNm4b9+/djwYIFyM3NxUcffYSUlBS8/vrrAIA2bdqgf//+mDBhAg4fPoysrCyMHTsW7u7u1t8RFxeH7t27Iz4+Hnv37sXZs2fxj3/8A2+88QYyMzPrvU4iIiLSGyeEiIiIiOpo/vz5KC8vt77v3LkzNm3ahI0bNyI8PBxz5szB/PnzbW4rW7NmDfz8/BAbG4tf/epXGD9+PFq0aGFtNwwDu3btQs+ePfGb3/wGoaGhGDFiBAoLC9GyZcv6LI+IiIgcgCEiojoEERERERERERHVH14hRERERERERETkYDghRERERERERETkYDghRERERERERETkYDghRERERERERETkYDghRERERERERETkYDghRERERERERETkYDghRERERERERETkYDghRERERERERETkYDghRERERERERETkYDghRERERERERETkYDghRERERERERETkYP4LEk4XDVK8Xu4AAAAASUVORK5CYII=\n" + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [], + "metadata": { + "id": "3eDRjVjKQ1Ll" + }, + "execution_count": 69, + "outputs": [] + } + ] +} \ No newline at end of file From ea85dc4d29ee24fd19ae85e9b9974127dbab28ea Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Mon, 19 Aug 2024 20:24:55 +0330 Subject: [PATCH 12/27] Add .gitignore --- .gitignore | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0a5675b --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# cpp object files +*.o + +# results files +results/*.csv + +# preprocessed data labels +data/*/Label_*_*.csv + +# main executable file +main \ No newline at end of file From cee10929f5643cb72433b91adf41039bd9358b83 Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Mon, 19 Aug 2024 20:31:25 +0330 Subject: [PATCH 13/27] Fix minor size_t types --- code/anoedgeglobal.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/code/anoedgeglobal.cpp b/code/anoedgeglobal.cpp index 228794a..1e278aa 100644 --- a/code/anoedgeglobal.cpp +++ b/code/anoedgeglobal.cpp @@ -60,11 +60,11 @@ double AnoedgeGlobal::getAnoedgeglobalDensity(vector>& mat, int s double row_slice_sum[num_rows]; double col_slice_sum[num_cols]; - for (int i = 0; i < num_rows; i++) { + for (size_t i = 0; i < num_rows; i++) { row_flag[i] = false; row_slice_sum[i] = mat[i][dst]; } - for (int i = 0; i < num_cols; i++) { + for (size_t i = 0; i < num_cols; i++) { col_flag[i] = false; col_slice_sum[i] = mat[src][i]; } @@ -75,14 +75,14 @@ double AnoedgeGlobal::getAnoedgeglobalDensity(vector>& mat, int s col_slice_sum[dst] = mat[src][dst]; pair max_row = {-1, -1.0}; - for (int i = 0; i < num_rows; i++) { + for (size_t i = 0; i < num_rows; i++) { if (!row_flag[i] && (row_slice_sum[i] >= max_row.second)) { max_row = {i, row_slice_sum[i]}; } } pair max_col = {-1, -1.0}; - for (int i = 0; i < num_cols; i++) { + for (size_t i = 0; i < num_cols; i++) { if (!col_flag[i] && (col_slice_sum[i] >= max_col.second)) { max_col = {i, col_slice_sum[i]}; } @@ -101,7 +101,7 @@ double AnoedgeGlobal::getAnoedgeglobalDensity(vector>& mat, int s marked_rows++; max_col = {-1, -1.0}; - for (int i = 0; i < num_cols; i++) { + for (size_t i = 0; i < num_cols; i++) { if (col_flag[i]) { cur_mat_sum = cur_mat_sum + mat[max_row.first][i]; } else { @@ -113,7 +113,7 @@ double AnoedgeGlobal::getAnoedgeglobalDensity(vector>& mat, int s } max_row = {-1, -1.0}; - for (int i = 0; i < num_rows; i++) { + for (size_t i = 0; i < num_rows; i++) { if (!row_flag[i] && (row_slice_sum[i] >= max_row.second)) { max_row = {i, row_slice_sum[i]}; } @@ -123,7 +123,7 @@ double AnoedgeGlobal::getAnoedgeglobalDensity(vector>& mat, int s marked_cols++; max_row = {-1, -1.0}; - for (int i = 0; i < num_rows; i++) { + for (size_t i = 0; i < num_rows; i++) { if (row_flag[i]) { cur_mat_sum = cur_mat_sum + mat[i][max_col.first]; } else { @@ -135,7 +135,7 @@ double AnoedgeGlobal::getAnoedgeglobalDensity(vector>& mat, int s } max_col = {-1, -1.0}; - for (int i = 0; i < num_cols; i++) { + for (size_t i = 0; i < num_cols; i++) { if (!col_flag[i] && (col_slice_sum[i] >= max_col.second)) { max_col = {i, col_slice_sum[i]}; } From 9281682f99dd500cb4363e4621363c1ba61666a6 Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Mon, 19 Aug 2024 20:43:23 +0330 Subject: [PATCH 14/27] Some refactoring in python code --- code/metrics.py | 65 +++++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/code/metrics.py b/code/metrics.py index 2309ade..0ec859f 100644 --- a/code/metrics.py +++ b/code/metrics.py @@ -1,6 +1,8 @@ import pandas as pd import argparse from sklearn import metrics + + parser = argparse.ArgumentParser() parser.add_argument('--dataset', default='DARPA') parser.add_argument('--time_window', type=int, default=30) @@ -15,9 +17,9 @@ def print_anoedge_auc_time(base_path, dataset_name, algorithm): fpr, tpr, _ = metrics.roc_curve(data.label, data.score) auc = metrics.roc_auc_score(data.label, data.score) - print ("%s,%s" % (algorithm, dataset_name)) - print ("AUC: %.3f" % (auc)) - print ("Time: %s\n" % (time_values["total"].iloc[1])) + print("%s,%s" % (algorithm, dataset_name)) + print("AUC: %.3f" % (auc)) + print("Time: %s\n" % (time_values["total"].iloc[1])) def print_anograph_auc_time(base_path, dataset_name, time_window, edge_threshold, algorithm): data = pd.read_csv(base_path + algorithm + "_" + dataset_name + "_" + str(time_window) + "_" + str(edge_threshold) + "_score.csv", names=['score', 'label'], sep=" ") @@ -26,36 +28,45 @@ def print_anograph_auc_time(base_path, dataset_name, time_window, edge_threshold fpr, tpr, _ = metrics.roc_curve(data.label, data.score) auc = metrics.roc_auc_score(data.label, data.score) - print ("%s,%s" % (algorithm, dataset_name)) - print ("AUC: %.3f" % (auc)) - print ("Time: %s\n" % (time_values["total"].iloc[1])) + print("%s,%s" % (algorithm, dataset_name)) + print("AUC: %.3f" % auc) + print("Time: %s\n" % (time_values["total"].iloc[1])) -if __name__ == "__main__": - if args.dataset == 'DARPA': - print_anograph_auc_time("../results/", "DARPA", args.time_window, args.edge_threshold, "anograph") - print_anograph_auc_time("../results/", "DARPA", args.time_window, args.edge_threshold, "anograph_k") +def run_darpa(): + print_anograph_auc_time("../results/", "DARPA", args.time_window, args.edge_threshold, "anograph") + print_anograph_auc_time("../results/", "DARPA", args.time_window, args.edge_threshold, "anograph_k") + print_anoedge_auc_time("../results/", "DARPA", "anoedge_g") + print_anoedge_auc_time("../results/", "DARPA", "anoedge_l") - print_anoedge_auc_time("../results/", "DARPA", "anoedge_g") - print_anoedge_auc_time("../results/", "DARPA", "anoedge_l") - if args.dataset == 'ISCX': - print_anograph_auc_time("../results/", "ISCX", args.time_window, args.edge_threshold, "anograph") - print_anograph_auc_time("../results/", "ISCX", args.time_window, args.edge_threshold, "anograph_k") +def run_iscx(): + print_anograph_auc_time("../results/", "ISCX", args.time_window, args.edge_threshold, "anograph") + print_anograph_auc_time("../results/", "ISCX", args.time_window, args.edge_threshold, "anograph_k") + print_anoedge_auc_time("../results/", "ISCX", "anoedge_g") + print_anoedge_auc_time("../results/", "ISCX", "anoedge_l") - print_anoedge_auc_time("../results/", "ISCX", "anoedge_g") - print_anoedge_auc_time("../results/", "ISCX", "anoedge_l") - if args.dataset == 'IDS2018': - print_anograph_auc_time("../results/", "IDS2018", args.time_window, args.edge_threshold, "anograph") - print_anograph_auc_time("../results/", "IDS2018", args.time_window, args.edge_threshold, "anograph_k") +def run_ids2018(): + print_anograph_auc_time("../results/", "IDS2018", args.time_window, args.edge_threshold, "anograph") + print_anograph_auc_time("../results/", "IDS2018", args.time_window, args.edge_threshold, "anograph_k") + print_anoedge_auc_time("../results/", "IDS2018", "anoedge_g") + print_anoedge_auc_time("../results/", "IDS2018", "anoedge_l") - print_anoedge_auc_time("../results/", "IDS2018", "anoedge_g") - print_anoedge_auc_time("../results/", "IDS2018", "anoedge_l") - if args.dataset == 'DDOS2019': - print_anograph_auc_time("../results/", "DDOS2019", args.time_window, args.edge_threshold, "anograph") - print_anograph_auc_time("../results/", "DDOS2019", args.time_window, args.edge_threshold, "anograph_k") +def run_ddos2019(): + print_anograph_auc_time("../results/", "DDOS2019", args.time_window, args.edge_threshold, "anograph") + print_anograph_auc_time("../results/", "DDOS2019", args.time_window, args.edge_threshold, "anograph_k") + print_anoedge_auc_time("../results/", "DDOS2019", "anoedge_g") + print_anoedge_auc_time("../results/", "DDOS2019", "anoedge_l") - print_anoedge_auc_time("../results/", "DDOS2019", "anoedge_g") - print_anoedge_auc_time("../results/", "DDOS2019", "anoedge_l") \ No newline at end of file + +if __name__ == "__main__": + if args.dataset == 'DARPA': + run_darpa() + elif args.dataset == 'ISCX': + run_iscx() + elif args.dataset == 'IDS2018': + run_ids2018() + elif args.dataset == 'DDOS2019': + run_ddos2019() \ No newline at end of file From 9c440a62e5dc8851d9f932dcf06bc46961dfca8f Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Mon, 19 Aug 2024 20:46:14 +0330 Subject: [PATCH 15/27] reformat python code --- code/metrics.py | 82 ++++++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/code/metrics.py b/code/metrics.py index 0ec859f..d6dd2a5 100644 --- a/code/metrics.py +++ b/code/metrics.py @@ -2,7 +2,6 @@ import argparse from sklearn import metrics - parser = argparse.ArgumentParser() parser.add_argument('--dataset', default='DARPA') parser.add_argument('--time_window', type=int, default=30) @@ -11,62 +10,67 @@ def print_anoedge_auc_time(base_path, dataset_name, algorithm): - data = pd.read_csv(base_path + algorithm + "_" + dataset_name + "_score.csv", names=['score', 'label'], sep=" ") - time_values = pd.read_csv(base_path + algorithm + "_" + dataset_name + "_time.csv", names=['avg', 'total'], sep=" ") + data = pd.read_csv(base_path + algorithm + "_" + dataset_name + "_score.csv", names=['score', 'label'], sep=" ") + time_values = pd.read_csv(base_path + algorithm + "_" + dataset_name + "_time.csv", names=['avg', 'total'], sep=" ") + + fpr, tpr, _ = metrics.roc_curve(data.label, data.score) + auc = metrics.roc_auc_score(data.label, data.score) - fpr, tpr, _ = metrics.roc_curve(data.label, data.score) - auc = metrics.roc_auc_score(data.label, data.score) + print("%s,%s" % (algorithm, dataset_name)) + print("AUC: %.3f" % (auc)) + print("Time: %s\n" % (time_values["total"].iloc[1])) - print("%s,%s" % (algorithm, dataset_name)) - print("AUC: %.3f" % (auc)) - print("Time: %s\n" % (time_values["total"].iloc[1])) def print_anograph_auc_time(base_path, dataset_name, time_window, edge_threshold, algorithm): - data = pd.read_csv(base_path + algorithm + "_" + dataset_name + "_" + str(time_window) + "_" + str(edge_threshold) + "_score.csv", names=['score', 'label'], sep=" ") - time_values = pd.read_csv(base_path + algorithm + "_" + dataset_name + "_" + str(time_window) + "_" + str(edge_threshold) + "_time.csv", names=['avg', 'total'], sep=" ") + data = pd.read_csv( + base_path + algorithm + "_" + dataset_name + "_" + str(time_window) + "_" + str(edge_threshold) + "_score.csv", + names=['score', 'label'], sep=" ") + time_values = pd.read_csv( + base_path + algorithm + "_" + dataset_name + "_" + str(time_window) + "_" + str(edge_threshold) + "_time.csv", + names=['avg', 'total'], sep=" ") - fpr, tpr, _ = metrics.roc_curve(data.label, data.score) - auc = metrics.roc_auc_score(data.label, data.score) + fpr, tpr, _ = metrics.roc_curve(data.label, data.score) + auc = metrics.roc_auc_score(data.label, data.score) - print("%s,%s" % (algorithm, dataset_name)) - print("AUC: %.3f" % auc) - print("Time: %s\n" % (time_values["total"].iloc[1])) + print("%s,%s" % (algorithm, dataset_name)) + print("AUC: %.3f" % auc) + print("Time: %s\n" % (time_values["total"].iloc[1])) def run_darpa(): - print_anograph_auc_time("../results/", "DARPA", args.time_window, args.edge_threshold, "anograph") - print_anograph_auc_time("../results/", "DARPA", args.time_window, args.edge_threshold, "anograph_k") - print_anoedge_auc_time("../results/", "DARPA", "anoedge_g") - print_anoedge_auc_time("../results/", "DARPA", "anoedge_l") + print_anograph_auc_time("../results/", "DARPA", args.time_window, args.edge_threshold, "anograph") + print_anograph_auc_time("../results/", "DARPA", args.time_window, args.edge_threshold, "anograph_k") + print_anoedge_auc_time("../results/", "DARPA", "anoedge_g") + print_anoedge_auc_time("../results/", "DARPA", "anoedge_l") def run_iscx(): - print_anograph_auc_time("../results/", "ISCX", args.time_window, args.edge_threshold, "anograph") - print_anograph_auc_time("../results/", "ISCX", args.time_window, args.edge_threshold, "anograph_k") - print_anoedge_auc_time("../results/", "ISCX", "anoedge_g") - print_anoedge_auc_time("../results/", "ISCX", "anoedge_l") + print_anograph_auc_time("../results/", "ISCX", args.time_window, args.edge_threshold, "anograph") + print_anograph_auc_time("../results/", "ISCX", args.time_window, args.edge_threshold, "anograph_k") + print_anoedge_auc_time("../results/", "ISCX", "anoedge_g") + print_anoedge_auc_time("../results/", "ISCX", "anoedge_l") def run_ids2018(): - print_anograph_auc_time("../results/", "IDS2018", args.time_window, args.edge_threshold, "anograph") - print_anograph_auc_time("../results/", "IDS2018", args.time_window, args.edge_threshold, "anograph_k") - print_anoedge_auc_time("../results/", "IDS2018", "anoedge_g") - print_anoedge_auc_time("../results/", "IDS2018", "anoedge_l") + print_anograph_auc_time("../results/", "IDS2018", args.time_window, args.edge_threshold, "anograph") + print_anograph_auc_time("../results/", "IDS2018", args.time_window, args.edge_threshold, "anograph_k") + print_anoedge_auc_time("../results/", "IDS2018", "anoedge_g") + print_anoedge_auc_time("../results/", "IDS2018", "anoedge_l") def run_ddos2019(): - print_anograph_auc_time("../results/", "DDOS2019", args.time_window, args.edge_threshold, "anograph") - print_anograph_auc_time("../results/", "DDOS2019", args.time_window, args.edge_threshold, "anograph_k") - print_anoedge_auc_time("../results/", "DDOS2019", "anoedge_g") - print_anoedge_auc_time("../results/", "DDOS2019", "anoedge_l") + print_anograph_auc_time("../results/", "DDOS2019", args.time_window, args.edge_threshold, "anograph") + print_anograph_auc_time("../results/", "DDOS2019", args.time_window, args.edge_threshold, "anograph_k") + print_anoedge_auc_time("../results/", "DDOS2019", "anoedge_g") + print_anoedge_auc_time("../results/", "DDOS2019", "anoedge_l") if __name__ == "__main__": - if args.dataset == 'DARPA': - run_darpa() - elif args.dataset == 'ISCX': - run_iscx() - elif args.dataset == 'IDS2018': - run_ids2018() - elif args.dataset == 'DDOS2019': - run_ddos2019() \ No newline at end of file + if args.dataset == 'DARPA': + run_darpa() + elif args.dataset == 'ISCX': + run_iscx() + elif args.dataset == 'IDS2018': + run_ids2018() + elif args.dataset == 'DDOS2019': + run_ddos2019() From c9bf30d009c45f2ffb2b36e31c66160d103105c8 Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Mon, 19 Aug 2024 20:46:52 +0330 Subject: [PATCH 16/27] better python code :) --- code/metrics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/metrics.py b/code/metrics.py index d6dd2a5..6746e17 100644 --- a/code/metrics.py +++ b/code/metrics.py @@ -17,7 +17,7 @@ def print_anoedge_auc_time(base_path, dataset_name, algorithm): auc = metrics.roc_auc_score(data.label, data.score) print("%s,%s" % (algorithm, dataset_name)) - print("AUC: %.3f" % (auc)) + print("AUC: %.3f" % auc) print("Time: %s\n" % (time_values["total"].iloc[1])) From 24234d2ae6b633122b54f901a0088aa400e97ffe Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Mon, 19 Aug 2024 20:50:28 +0330 Subject: [PATCH 17/27] refactoring python code --- code/metrics.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/code/metrics.py b/code/metrics.py index 6746e17..4b48baf 100644 --- a/code/metrics.py +++ b/code/metrics.py @@ -10,8 +10,9 @@ def print_anoedge_auc_time(base_path, dataset_name, algorithm): - data = pd.read_csv(base_path + algorithm + "_" + dataset_name + "_score.csv", names=['score', 'label'], sep=" ") - time_values = pd.read_csv(base_path + algorithm + "_" + dataset_name + "_time.csv", names=['avg', 'total'], sep=" ") + file_name = base_path + algorithm + "_" + dataset_name + data = pd.read_csv(file_name + "_score.csv", names=['score', 'label'], sep=" ") + time_values = pd.read_csv(file_name + "_time.csv", names=['avg', 'total'], sep=" ") fpr, tpr, _ = metrics.roc_curve(data.label, data.score) auc = metrics.roc_auc_score(data.label, data.score) @@ -22,12 +23,9 @@ def print_anoedge_auc_time(base_path, dataset_name, algorithm): def print_anograph_auc_time(base_path, dataset_name, time_window, edge_threshold, algorithm): - data = pd.read_csv( - base_path + algorithm + "_" + dataset_name + "_" + str(time_window) + "_" + str(edge_threshold) + "_score.csv", - names=['score', 'label'], sep=" ") - time_values = pd.read_csv( - base_path + algorithm + "_" + dataset_name + "_" + str(time_window) + "_" + str(edge_threshold) + "_time.csv", - names=['avg', 'total'], sep=" ") + file_name = base_path + algorithm + "_" + dataset_name + "_" + str(time_window) + "_" + str(edge_threshold) + data = pd.read_csv(file_name + "_score.csv", names=['score', 'label'], sep=" ") + time_values = pd.read_csv(file_name + "_time.csv", names=['avg', 'total'], sep=" ") fpr, tpr, _ = metrics.roc_curve(data.label, data.score) auc = metrics.roc_auc_score(data.label, data.score) From b908d51f0d1e092bed23bccd2a8d13e2e10f327d Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Mon, 19 Aug 2024 21:29:46 +0330 Subject: [PATCH 18/27] Change .gitignore --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0a5675b..4d4566f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,7 @@ results/*.csv data/*/Label_*_*.csv # main executable file -main \ No newline at end of file +main + +# idea +.idea \ No newline at end of file From d1036cda4312ef62afa34cbf79db17acf23f2d0d Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Mon, 19 Aug 2024 21:30:37 +0330 Subject: [PATCH 19/27] refactor python --- code/metrics.py | 46 ++++++++++++---------------------------------- 1 file changed, 12 insertions(+), 34 deletions(-) diff --git a/code/metrics.py b/code/metrics.py index 4b48baf..ba01aaa 100644 --- a/code/metrics.py +++ b/code/metrics.py @@ -8,6 +8,8 @@ parser.add_argument("--edge_threshold", type=int, default=50) args = parser.parse_args() +results_path = "../results/" + def print_anoedge_auc_time(base_path, dataset_name, algorithm): file_name = base_path + algorithm + "_" + dataset_name @@ -35,40 +37,16 @@ def print_anograph_auc_time(base_path, dataset_name, time_window, edge_threshold print("Time: %s\n" % (time_values["total"].iloc[1])) -def run_darpa(): - print_anograph_auc_time("../results/", "DARPA", args.time_window, args.edge_threshold, "anograph") - print_anograph_auc_time("../results/", "DARPA", args.time_window, args.edge_threshold, "anograph_k") - print_anoedge_auc_time("../results/", "DARPA", "anoedge_g") - print_anoedge_auc_time("../results/", "DARPA", "anoedge_l") - - -def run_iscx(): - print_anograph_auc_time("../results/", "ISCX", args.time_window, args.edge_threshold, "anograph") - print_anograph_auc_time("../results/", "ISCX", args.time_window, args.edge_threshold, "anograph_k") - print_anoedge_auc_time("../results/", "ISCX", "anoedge_g") - print_anoedge_auc_time("../results/", "ISCX", "anoedge_l") - - -def run_ids2018(): - print_anograph_auc_time("../results/", "IDS2018", args.time_window, args.edge_threshold, "anograph") - print_anograph_auc_time("../results/", "IDS2018", args.time_window, args.edge_threshold, "anograph_k") - print_anoedge_auc_time("../results/", "IDS2018", "anoedge_g") - print_anoedge_auc_time("../results/", "IDS2018", "anoedge_l") - - -def run_ddos2019(): - print_anograph_auc_time("../results/", "DDOS2019", args.time_window, args.edge_threshold, "anograph") - print_anograph_auc_time("../results/", "DDOS2019", args.time_window, args.edge_threshold, "anograph_k") - print_anoedge_auc_time("../results/", "DDOS2019", "anoedge_g") - print_anoedge_auc_time("../results/", "DDOS2019", "anoedge_l") +def run_with_dataset(dataset_name): + print_anograph_auc_time(results_path, dataset_name, args.time_window, args.edge_threshold, "anograph") + print_anograph_auc_time(results_path, dataset_name, args.time_window, args.edge_threshold, "anograph_k") + print_anoedge_auc_time(results_path, dataset_name, "anoedge_g") + print_anoedge_auc_time(results_path, dataset_name, "anoedge_l") if __name__ == "__main__": - if args.dataset == 'DARPA': - run_darpa() - elif args.dataset == 'ISCX': - run_iscx() - elif args.dataset == 'IDS2018': - run_ids2018() - elif args.dataset == 'DDOS2019': - run_ddos2019() + datasets = ["DARPA", "ISCX", "IDS2018", "DDOS2019"] + if args.dataset in datasets: + run_with_dataset(args.dataset) + else: + print(f"Could not detect dataset {args.dataset}") From 367a9458265468320f2ba2a95b5c543f9de0b0cf Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Mon, 19 Aug 2024 21:35:04 +0330 Subject: [PATCH 20/27] extract print_auc_time --- code/metrics.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/code/metrics.py b/code/metrics.py index ba01aaa..ea0560a 100644 --- a/code/metrics.py +++ b/code/metrics.py @@ -13,19 +13,15 @@ def print_anoedge_auc_time(base_path, dataset_name, algorithm): file_name = base_path + algorithm + "_" + dataset_name - data = pd.read_csv(file_name + "_score.csv", names=['score', 'label'], sep=" ") - time_values = pd.read_csv(file_name + "_time.csv", names=['avg', 'total'], sep=" ") - - fpr, tpr, _ = metrics.roc_curve(data.label, data.score) - auc = metrics.roc_auc_score(data.label, data.score) - - print("%s,%s" % (algorithm, dataset_name)) - print("AUC: %.3f" % auc) - print("Time: %s\n" % (time_values["total"].iloc[1])) + print_auc_time(algorithm, dataset_name, file_name) def print_anograph_auc_time(base_path, dataset_name, time_window, edge_threshold, algorithm): file_name = base_path + algorithm + "_" + dataset_name + "_" + str(time_window) + "_" + str(edge_threshold) + print_auc_time(algorithm, dataset_name, file_name) + + +def print_auc_time(algorithm, dataset_name, file_name): data = pd.read_csv(file_name + "_score.csv", names=['score', 'label'], sep=" ") time_values = pd.read_csv(file_name + "_time.csv", names=['avg', 'total'], sep=" ") From 3553c02d1db5f72235bab037437b65dad6669f34 Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Mon, 19 Aug 2024 21:44:13 +0330 Subject: [PATCH 21/27] refactor process_data.py --- code/process_data.py | 82 +++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 36 deletions(-) diff --git a/code/process_data.py b/code/process_data.py index 4ae62cb..fa9a9f8 100644 --- a/code/process_data.py +++ b/code/process_data.py @@ -4,39 +4,49 @@ from numpy import savetxt -def process_dataset(base_path, dataset_name, time_param, edge_thershold): - records = [] - with open(base_path + dataset_name + "/Data.csv", "r") as f: - for line in f: - if len(line) <= 1: - continue - src, dst, time = line.split("\n")[0].split(",") - records.append((int(src), int(dst), int(time))) - - labels = [] - with open(base_path + dataset_name + "/Label.csv", "r") as f: - for line in f: - if len(line) <= 1: - continue - label = line.split("\n")[0] - labels.append(int(label)) - - assert len(records) == len(labels) - - record_labels = [(record[0], record[1], record[2], label) for record, label in zip(records, labels)] - - write_format = str(time_param) + "_" + str(edge_thershold) - - data = pd.DataFrame(np.array(record_labels)) - - labels = [] - data[2] = (data[2]/time_param).astype(int) - for i in pd.unique(data[2]): - labels.append(sum(data[data[2]==i][3])) - - labels = np.array(labels) - labels = labels >= edge_thershold - labels = labels * 1 - savetxt(base_path + dataset_name + "/Label_" + write_format + ".csv", labels, delimiter='\n',fmt="%d") - -process_dataset("../data/", str(sys.argv[1]), int(sys.argv[2]), int(sys.argv[3])) + +def process_dataset(base_path, dataset_name, time_param, edge_threshold): + records = [] + with open(base_path + dataset_name + "/Data.csv", "r") as f: + for line in f: + if len(line) <= 1: + continue + src, dst, time = line.split("\n")[0].split(",") + records.append((int(src), int(dst), int(time))) + + labels = [] + with open(base_path + dataset_name + "/Label.csv", "r") as f: + for line in f: + if len(line) <= 1: + continue + label = line.split("\n")[0] + labels.append(int(label)) + + assert len(records) == len(labels) + + record_labels = [(record[0], record[1], record[2], label) for record, label in zip(records, labels)] + + write_format = str(time_param) + "_" + str(edge_threshold) + + data = pd.DataFrame(np.array(record_labels)) + + labels = [] + data[2] = (data[2] / time_param).astype(int) + for i in pd.unique(data[2]): + labels.append(sum(data[data[2] == i][3])) + + labels = np.array(labels) + labels = labels >= edge_threshold + labels = labels * 1 + savetxt(base_path + dataset_name + "/Label_" + write_format + ".csv", labels, delimiter='\n', fmt="%d") + + +def main(): + dataset_name = str(sys.argv[1]) + time_param = int(sys.argv[2]) + edge_threshold = int(sys.argv[3]) + process_dataset("../data/", dataset_name, time_param, edge_threshold) + + +if __name__ == "__main__": + main() From 560bac841ab48af887832f2ae51e72cc9886f900 Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Tue, 20 Aug 2024 17:00:18 +0330 Subject: [PATCH 22/27] use os.path.join --- code/process_data.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/code/process_data.py b/code/process_data.py index fa9a9f8..88ee80a 100644 --- a/code/process_data.py +++ b/code/process_data.py @@ -1,13 +1,15 @@ import pandas as pd import numpy as np import sys +import os from numpy import savetxt def process_dataset(base_path, dataset_name, time_param, edge_threshold): records = [] - with open(base_path + dataset_name + "/Data.csv", "r") as f: + data_path = os.path.join(base_path, dataset_name, "Data.csv") + with open(data_path, "r") as f: for line in f: if len(line) <= 1: continue @@ -15,7 +17,8 @@ def process_dataset(base_path, dataset_name, time_param, edge_threshold): records.append((int(src), int(dst), int(time))) labels = [] - with open(base_path + dataset_name + "/Label.csv", "r") as f: + labels_path = os.path.join(base_path, dataset_name, "Label.csv") + with open(labels_path, "r") as f: for line in f: if len(line) <= 1: continue From f5a1ec6cf8feaae64c3c5763451eba2525f5d6f9 Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Tue, 20 Aug 2024 17:09:54 +0330 Subject: [PATCH 23/27] refactor process_data.py --- code/process_data.py | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/code/process_data.py b/code/process_data.py index 88ee80a..50d0ac6 100644 --- a/code/process_data.py +++ b/code/process_data.py @@ -7,6 +7,29 @@ def process_dataset(base_path, dataset_name, time_param, edge_threshold): + record_labels = generate_record_labels(base_path, dataset_name) + labels = generate_final_labels(edge_threshold, record_labels, time_param) + write_to_file(base_path, dataset_name, edge_threshold, labels, time_param) + + +def write_to_file(base_path, dataset_name, edge_threshold, labels, time_param): + file_path = os.path.join(base_path, dataset_name, f"Label_{str(time_param)}_{str(edge_threshold)}.csv") + savetxt(file_path, labels, delimiter='\n', fmt="%d") + + +def generate_final_labels(edge_threshold, record_labels, time_param): + data = pd.DataFrame(np.array(record_labels)) + labels = [] + data[2] = (data[2] / time_param).astype(int) + for i in pd.unique(data[2]): + labels.append(sum(data[data[2] == i][3])) + labels = np.array(labels) + labels = labels >= edge_threshold + labels = labels * 1 + return labels + + +def generate_record_labels(base_path, dataset_name): records = [] data_path = os.path.join(base_path, dataset_name, "Data.csv") with open(data_path, "r") as f: @@ -29,19 +52,7 @@ def process_dataset(base_path, dataset_name, time_param, edge_threshold): record_labels = [(record[0], record[1], record[2], label) for record, label in zip(records, labels)] - write_format = str(time_param) + "_" + str(edge_threshold) - - data = pd.DataFrame(np.array(record_labels)) - - labels = [] - data[2] = (data[2] / time_param).astype(int) - for i in pd.unique(data[2]): - labels.append(sum(data[data[2] == i][3])) - - labels = np.array(labels) - labels = labels >= edge_threshold - labels = labels * 1 - savetxt(base_path + dataset_name + "/Label_" + write_format + ".csv", labels, delimiter='\n', fmt="%d") + return record_labels def main(): From 69d14a20fe441e45e09d67b1772ec6b3a20ff000 Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Tue, 20 Aug 2024 17:43:11 +0330 Subject: [PATCH 24/27] Add code README.MD --- code/README.MD | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 code/README.MD diff --git a/code/README.MD b/code/README.MD new file mode 100644 index 0000000..1751a32 --- /dev/null +++ b/code/README.MD @@ -0,0 +1,11 @@ +# توضیح بخش‌های مختلف کد + +## process_data.py + +در این فایل پردازش داده‌ه‌ها به صورت زیر انجام می‌شود. + +داده‌های زمانی براساس پارامتر مشخصی گسسته‌سازی می‌شوند و به ازای هر +time_stamp +زمان یک برچسب مشخص می‌شود. + +روش تضمیم‌گیری برای برچسب به این‌صورت است که اگر بیش از تعداد مشخصی از یال‌های آن دسته‌ی زمانی برچسب مثبت داشته باشند آن لحظه‌ی زمانی به صورت کامل مثبت اعلام می‌شود. \ No newline at end of file From 5daf0561c46b896719b680286884f6674153b376 Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Tue, 20 Aug 2024 17:50:38 +0330 Subject: [PATCH 25/27] complete README.MD --- code/README.MD | 17 ++++++++++++++++- code/process_data.py | 2 ++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/code/README.MD b/code/README.MD index 1751a32..1e43527 100644 --- a/code/README.MD +++ b/code/README.MD @@ -8,4 +8,19 @@ time_stamp زمان یک برچسب مشخص می‌شود. -روش تضمیم‌گیری برای برچسب به این‌صورت است که اگر بیش از تعداد مشخصی از یال‌های آن دسته‌ی زمانی برچسب مثبت داشته باشند آن لحظه‌ی زمانی به صورت کامل مثبت اعلام می‌شود. \ No newline at end of file +روش تضمیم‌گیری برای برچسب به این‌صورت است که اگر بیش از تعداد مشخصی از یال‌های آن دسته‌ی زمانی برچسب مثبت داشته باشند آن لحظه‌ی زمانی به صورت کامل مثبت اعلام می‌شود. + +```python +def generate_final_labels(edge_threshold, record_labels, time_param): + data = pd.DataFrame(np.array(record_labels)) + labels = [] + data[2] = (data[2] / time_param).astype(int) + + for i in pd.unique(data[2]): + labels.append(sum(data[data[2] == i][3])) + + labels = np.array(labels) + labels = labels >= edge_threshold + labels = labels * 1 + return labels +``` \ No newline at end of file diff --git a/code/process_data.py b/code/process_data.py index 50d0ac6..03e0696 100644 --- a/code/process_data.py +++ b/code/process_data.py @@ -21,8 +21,10 @@ def generate_final_labels(edge_threshold, record_labels, time_param): data = pd.DataFrame(np.array(record_labels)) labels = [] data[2] = (data[2] / time_param).astype(int) + for i in pd.unique(data[2]): labels.append(sum(data[data[2] == i][3])) + labels = np.array(labels) labels = labels >= edge_threshold labels = labels * 1 From 3af75e252364c49c4e974659a44a755a8b6dcfcf Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Tue, 20 Aug 2024 17:51:16 +0330 Subject: [PATCH 26/27] add python configs --- .idea/misc.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.idea/misc.xml b/.idea/misc.xml index 7859412..48b747e 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,5 +1,8 @@ + + \ No newline at end of file From c9cee155ca8471558fb821f7048cf333ecb61f5b Mon Sep 17 00:00:00 2001 From: Mohammad Kasaei Date: Tue, 20 Aug 2024 17:55:52 +0330 Subject: [PATCH 27/27] add comments to .sh file --- code/demo.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/code/demo.sh b/code/demo.sh index 73c11aa..1dc894b 100644 --- a/code/demo.sh +++ b/code/demo.sh @@ -13,9 +13,19 @@ if [ $1 == "DARPA" ]; then ./main anograph_k DARPA 30 50 2 32 5 echo "Running AnoEdge-G" + # Algorithm => anoedge_g + # Dataset => DARPA + # Rows => 2 + # Buckets => 32 + # Decay factor => 0.9 ./main anoedge_g DARPA 2 32 0.9 echo "Running AnoEdge-L" + # Algorithm => anoedge_g + # Dataset => DARPA + # Rows => 2 + # Buckets => 32 + # Decay factor => 0.9 ./main anoedge_l DARPA 2 32 0.9 echo "Installing python dependencies"