dshield-parser 0.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,508 @@
1
+ import logging
2
+
3
+ import json
4
+ import itertools
5
+ import os
6
+ import sys
7
+ import re
8
+ from datetime import datetime
9
+ from datetime import timezone
10
+ import dshield_parser
11
+ import dshield_parser.firewall_processor
12
+ import dshield_parser.firewall_processor.reports
13
+ import dshield_parser.utils.file_io as file_io
14
+ import pandas as pd
15
+
16
+ default_datespan={'start_date': '1970-01-01', 'end_date': '3000-01-01'}
17
+ default_timespan={"start_time": 0, "end_time": 32516881838}
18
+
19
+ def print_json_pretty(incoming_data):
20
+ for json_data in incoming_data:
21
+ if os.path.exists(json_data):
22
+ for yielded_json in file_io.read_file_json_generator(json_data):
23
+ if "logs" in yielded_json:
24
+ for each_item in yielded_json["logs"]:
25
+ json_data_formatted = json.dumps(yielded_json, indent = 2)
26
+ print(json_data_formatted)
27
+ else:
28
+ json_data_formatted = json.dumps(yielded_json, indent = 2)
29
+ print(json_data_formatted)
30
+ else:
31
+ json_data = json.loads(json_data)
32
+ json_data_formatted = json.dumps(json_data, indent = 2)
33
+ print(json_data_formatted)
34
+
35
+ def print_json_keys(json_data):
36
+ print_list(json.loads(json_data.keys()))
37
+
38
+ def get_json_keys(incoming_data):
39
+ logging.info("Starting JSON key gathering.")
40
+ keys = []
41
+ if not isinstance(incoming_data, list):
42
+ incoming_data = [incoming_data]
43
+ for json_data in incoming_data:
44
+ if os.path.exists(json_data):
45
+ for yielded_json in file_io.read_file_json_generator(json_data):
46
+ if "logs" in yielded_json:
47
+ for each_item in yielded_json["logs"]:
48
+ for each_key in each_item.keys():
49
+ if each_key not in keys:
50
+ keys.append(each_key)
51
+ else:
52
+ for each_key in yielded_json.keys():
53
+ if each_key not in keys:
54
+ keys.append(each_key)
55
+
56
+ else:
57
+ for each_item in json_data:
58
+ for each_key in each_item.keys():
59
+ if each_key not in keys:
60
+ keys.append(each_key)
61
+ ("Completed JSON key gathering.")
62
+ return keys
63
+
64
+ def print_dict(dictionary):
65
+ for key, value in dictionary.items():
66
+ print(value, "\t", key)
67
+
68
+ def print_list(list):
69
+ for each_item in list:
70
+ print(each_item)
71
+
72
+ def sizeof_fmt(num, suffix='B'):
73
+ ''' by Fred Cirera, https://stackoverflow.com/a/1094933/1870254, modified'''
74
+ for unit in ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']:
75
+ if abs(num) < 1024.0:
76
+ return "%3.1f %s%s" % (num, unit, suffix)
77
+ num /= 1024.0
78
+ return "%.1f %s%s" % (num, 'Yi', suffix)
79
+
80
+ def get_json_values(key, incoming_data, timespan=default_timespan, exclusions=None, compress_dates=False):
81
+ day_count = 0
82
+ df = pd.DataFrame()
83
+ #new_df = pd.DataFrame()
84
+ if "start_time" in timespan and "end_time" in timespan:
85
+ start_date = convert_to_date(timespan["start_time"])
86
+ end_date = convert_to_date(timespan["end_time"])
87
+ start_time = timespan["start_time"]
88
+ end_time = timespan["end_time"]
89
+ elif "start_date" in timespan and "end_date" in timespan:
90
+ start_time = convert_to_epoch(timespan["start_date"])
91
+ end_time = convert_to_epoch(timespan["end_date"])
92
+ start_date = timespan["start_date"]
93
+ end_date = timespan["end_date"]
94
+ if not isinstance(incoming_data, list):
95
+ incoming_data = [incoming_data]
96
+ if not isinstance(key, list):
97
+ key = [key]
98
+ values = []
99
+ for file in incoming_data:
100
+ logging.info(f"Starting processing of '{file}'")
101
+ if os.path.exists(file):
102
+ if len(re.findall(r"(\d\d\d\d-\d\d-\d\d)", str(file))) > 0:
103
+ date = re.findall(r"(\d\d\d\d-\d\d-\d\d)", str(file))[0]
104
+ if in_date_range(date, start_date, end_date):
105
+ for yielded_json in file_io.read_file_json_generator(file):
106
+ exclude_log = False
107
+ if isinstance(exclusions, dict):
108
+ for exclusion_key, exclusion_value in exclusions.items():
109
+ if exclusion_key in yielded_json:
110
+ if yielded_json[exclusion_key] == exclusion_value:
111
+ exclude_log = True
112
+ if exclude_log == False:
113
+ log_data = {}
114
+ for each_key in key:
115
+ if "logs" in yielded_json:
116
+ for each_item in yielded_json["logs"]:
117
+ if each_key in each_item:
118
+ #values.append(each_item[each_key])
119
+ log_data[each_key] = each_item[each_key]
120
+ else:
121
+ if each_key in yielded_json:
122
+ #values.append(yielded_json[each_key])
123
+ if isinstance(yielded_json[each_key], str):
124
+ log_data[each_key] = yielded_json[each_key].replace('\x00','') # added .replace('\x00','') on 8/17/24 to replace null bytes for pandas merge issues
125
+ elif isinstance(yielded_json[each_key], dict):
126
+ log_data[each_key] = json.dumps(yielded_json[each_key])
127
+ else:
128
+ log_data[each_key] = yielded_json[each_key]
129
+ if len(log_data) > 0:
130
+ if compress_dates == True:
131
+ #dates = [dshield_parser.utils.json.time_to_day(timestamp) for timestamp in df["time"]
132
+ try:
133
+ # different time field for cowrie honeypot data
134
+ log_data["dates"] = dshield_parser.utils.json.time_to_day(log_data["timestamp"])
135
+ log_data.pop("timestamp")
136
+ except Exception as e:
137
+ # different time field for web or firewall honeypot data
138
+ log_data["dates"] = dshield_parser.utils.json.time_to_day(log_data["time"])
139
+ log_data.pop("time")
140
+ values.append(log_data)
141
+ #df = pd.concat([df, pd.DataFrame([log_data])], ignore_index=True)
142
+ ##new_df = pd.DataFrame([log_data])
143
+ #new_df = pd.concat([new_df, temp_df])
144
+ ##new_df = new_df.groupby(new_df.columns.tolist(),as_index=False, dropna=False).size()
145
+ ##df = pd.concat([df, new_df]).groupby(new_df.columns.tolist(),as_index=False, dropna=False)['size'].sum()
146
+ #new_df = new_df.groupby(new_df.columns.tolist(),as_index=False, dropna=False).size()
147
+ #df = pd.concat([df, new_df]).groupby(new_df.columns.tolist(),as_index=False, dropna=False)['size'].sum()
148
+ #new_df = pd.DataFrame()
149
+
150
+ #summarization will be run at the end of every file processed
151
+ if compress_dates == True:
152
+ if day_count % 50 == 0 or len(values) > 500000: #compress if 50 days of data or more than 500k records
153
+ logging.info(f"50 days of data processed or more than 500k records found. Collapsing dataframe by date (grouping by).")
154
+ file_df = pd.DataFrame(values)
155
+ file_df = file_df.groupby(file_df.columns.tolist(),as_index=False, dropna=False).size()
156
+ df = pd.concat([df, file_df]).groupby(file_df.columns.tolist(),as_index=False, dropna=False)['size'].sum()
157
+ values = []
158
+ elif end_date in file.name or (file.name == incoming_data[-1].name):
159
+ logging.info(f"Last day being processed: {date}")
160
+ file_df = pd.DataFrame(values)
161
+ file_df = file_df.groupby(file_df.columns.tolist(),as_index=False, dropna=False).size()
162
+ df = pd.concat([df, file_df]).groupby(file_df.columns.tolist(),as_index=False, dropna=False)['size'].sum()
163
+ values = []
164
+ day_count += 1
165
+
166
+
167
+ else:
168
+ for yielded_json in file_io.read_file_json_generator(file):
169
+ if "logs" in yielded_json:
170
+ for each_item in yielded_json["logs"]:
171
+ log_data = {}
172
+ for each_key in key:
173
+ if "time" in each_item:
174
+ if each_item["time"] < end_time and each_item["time"] > start_time:
175
+ exclude_log = False
176
+ if isinstance(exclusions, dict):
177
+ for exclusion_key, exclusion_value in exclusions.items():
178
+ if exclusion_key in each_item:
179
+ if each_item[exclusion_key] == exclusion_value:
180
+ exclude_log = True
181
+ if exclude_log == False:
182
+ if each_key in each_item:
183
+ log_data[each_key] = each_item[each_key]
184
+ #values.append(each_item[each_key])
185
+ else:
186
+ logging.debug(f"Filtered out data since {each_item['time']} is not between {timespan['start_time']} and {timespan['end_time']}")
187
+ if len(log_data) > 0:
188
+ if compress_dates == True:
189
+ #dates = [dshield_parser.utils.json.time_to_day(timestamp) for timestamp in df["time"]
190
+ try:
191
+ # different time field for cowrie honeypot data
192
+ log_data["dates"] = dshield_parser.utils.json.time_to_day(log_data["timestamp"])
193
+ log_data.pop("timestamp")
194
+ except Exception as e:
195
+ # different time field for web or firewall honeypot data
196
+ log_data["dates"] = dshield_parser.utils.json.time_to_day(log_data["time"])
197
+ log_data.pop("time")
198
+ values.append(log_data)
199
+ #end of processing firewall log
200
+ logging.info(f"Undated data file finished processing: {file}")
201
+ file_df = pd.DataFrame(values)
202
+ file_df = file_df.groupby(file_df.columns.tolist(),as_index=False, dropna=False).size()
203
+ df = pd.concat([df, file_df]).groupby(file_df.columns.tolist(),as_index=False, dropna=False)['size'].sum()
204
+ values = []
205
+
206
+ else:
207
+ for each_item in file:
208
+ if key in each_item:
209
+ if compress_dates == True:
210
+ #dates = [dshield_parser.utils.json.time_to_day(timestamp) for timestamp in df["time"]
211
+ try:
212
+ # different time field for cowrie honeypot data
213
+ log_data["dates"] = dshield_parser.utils.json.time_to_day(log_data["timestamp"])
214
+ log_data.pop("timestamp")
215
+ except Exception as e:
216
+ # different time field for web or firewall honeypot data
217
+ log_data["dates"] = dshield_parser.utils.json.time_to_day(log_data["time"])
218
+ log_data.pop("time")
219
+ values.append(log_data)
220
+ logging.info(f"Firewall log finished processing: {file}")
221
+ file_df = pd.DataFrame(values)
222
+ file_df = file_df.groupby(file_df.columns.tolist(),as_index=False, dropna=False).size()
223
+ df = pd.concat([df, file_df]).groupby(file_df.columns.tolist(),as_index=False, dropna=False)['size'].sum()
224
+ values = []
225
+
226
+ # print out variable memory usage
227
+ for name, size in sorted(((name, sys.getsizeof(value)) for name, value in list(
228
+ locals().items())), key= lambda x: -x[1])[:10]:
229
+ print("{:>30}: {:>8}".format(name, sizeof_fmt(size)))
230
+
231
+ df = df.groupby(df.columns.tolist(),as_index=False, dropna=False)['size'].sum()
232
+ return df
233
+
234
+ def summarize_values(list):
235
+ summary = {}
236
+ for each_value in list:
237
+ if each_value in summary:
238
+ summary[each_value] += 1
239
+ else:
240
+ summary[each_value] = 1
241
+ return summary
242
+
243
+ def print_json_values(json_data, key):
244
+ print_list(get_json_values(json_data, key))
245
+
246
+ def print_summary_values(list):
247
+ print_list(summarize_values(list))
248
+
249
+ def get_top_10(json_data, key, timespan=default_timespan):
250
+ data = get_summary_values(json_data, key, timespan)
251
+ #the key and json_data seemed to be swapped
252
+ data = sorted(data[json_data].items(), key=lambda x: x[1], reverse=True)
253
+ data = dict(data)
254
+ return dict(itertools.islice(data.items(), 10))
255
+
256
+ def get_summary_values(key, incoming_data, timespan=default_timespan, exclusions=None):
257
+ if "start_time" in timespan and "end_time" in timespan:
258
+ start_date = convert_to_date(timespan["start_time"])
259
+ end_date = convert_to_date(timespan["end_time"])
260
+ start_time = timespan["start_time"]
261
+ end_time = timespan["end_time"]
262
+ elif "start_date" in timespan and "end_date" in timespan:
263
+ start_time = convert_to_epoch(timespan["start_date"])
264
+ end_time = convert_to_epoch(timespan["end_date"])
265
+ start_date = timespan["start_date"]
266
+ end_date = timespan["end_date"]
267
+ list_unpacking_needed = set()
268
+ dict_unpacking_needed = set()
269
+ logging.info(f"Starting summary value processing for '{key}' from '{incoming_data}'")
270
+ if not isinstance(incoming_data, list):
271
+ incoming_data = [incoming_data]
272
+ if not isinstance(key, list):
273
+ key = [key]
274
+ data = {}
275
+ for each_key in key:
276
+ data[each_key] = {}
277
+ for file in incoming_data:
278
+ logging.info(f"Starting processing of '{file}'")
279
+ if os.path.exists(file):
280
+ if len(re.findall(r"(\d\d\d\d-\d\d-\d\d)", str(os.path.basename(file)))) > 0:
281
+ date = re.findall(r"(\d\d\d\d-\d\d-\d\d)", str(file))[0]
282
+ if in_date_range(date, start_date, end_date):
283
+ for yielded_json in file_io.read_file_json_generator(file):
284
+ exclude_log = False
285
+ if isinstance(exclusions, dict):
286
+ for exclusion_key, exclusion_value in exclusions.items():
287
+ if exclusion_key in yielded_json:
288
+ if yielded_json[exclusion_key] == exclusion_value:
289
+ exclude_log = True
290
+ if exclude_log == False:
291
+ for each_key in key:
292
+ if each_key in yielded_json:
293
+ if isinstance(yielded_json[each_key], list):
294
+ #need to do list things
295
+ list_unpacking_needed.add(each_key)
296
+ logging.debug(f"List data needs to be unpacked for key '{each_key}': '{yielded_json}'")
297
+ elif isinstance(yielded_json[each_key], dict):
298
+ #need to do dict things
299
+ dict_unpacking_needed.add(each_key)
300
+ logging.debug(f"List data needs to be unpacked for key '{each_key}': '{yielded_json}'")
301
+ for jsonkey, jsonvalue in yielded_json[each_key].items():
302
+ if isinstance(jsonvalue, dict) or isinstance(jsonvalue, list):
303
+ logging.debug(f"Data needs to be unpacked for key '{jsonkey}': '{jsonvalue}'")
304
+ else:
305
+ if f"{each_key}_{jsonkey}" not in data:
306
+ data[f"{each_key}_{jsonkey}"]= {}
307
+ if jsonvalue in data[f"{each_key}_{jsonkey}"]:
308
+ data[f"{each_key}_{jsonkey}"][jsonvalue] += 1
309
+ else:
310
+ data[f"{each_key}_{jsonkey}"][jsonvalue] = 1
311
+ else:
312
+ if yielded_json[each_key] in data[each_key]:
313
+ data[each_key][yielded_json[each_key]] += 1
314
+ else:
315
+ data[each_key][yielded_json[each_key]] = 1
316
+ else:
317
+ logging.debug(f"Filtered out data since {date} is not between {start_date} and {end_date}")
318
+ else:
319
+ for yielded_json in file_io.read_file_json_generator(file):
320
+ #process for firewall logs
321
+ if "logs" in yielded_json:
322
+ for each_item in yielded_json["logs"]:
323
+ if "time" in each_item:
324
+ if each_item["time"] < end_time and each_item["time"] > start_time:
325
+ exclude_log = False
326
+ if isinstance(exclusions, dict):
327
+ for exclusion_key, exclusion_value in exclusions.items():
328
+ if exclusion_key in each_item:
329
+ if each_item[exclusion_key] == exclusion_value:
330
+ exclude_log = True
331
+ if exclude_log == False:
332
+ for each_key in key:
333
+ if each_key in each_item:
334
+ if each_item[each_key] in data[each_key]:
335
+ data[each_key][each_item[each_key]] += 1
336
+ else:
337
+ data[each_key][each_item[each_key]] = 1
338
+ else:
339
+ logging.debug(f"Filtered out data since {each_item['time']} is not between {timespan['start_time']} and {timespan['end_time']}")
340
+ else:
341
+ for each_line in file:
342
+ for each_entry in each_line:
343
+ if key in each_entry:
344
+ if each_entry[key] in data:
345
+ data[each_entry[key]] += 1
346
+ else:
347
+ data[each_entry[key]] = 1
348
+
349
+ #data = sorted(data.items(), key=lambda x: x[1], reverse=True)
350
+ #data = dict(data)
351
+ logging.info("Finished data summarization.")
352
+ logging.info(f"Dictionaries that need unpacking: {dict_unpacking_needed}")
353
+ logging.info(f"Lists that need unpacking: {list_unpacking_needed}")
354
+ return data
355
+
356
+ def sort_dict_desc(data):
357
+ data = sorted(data.items(), key=lambda x: x[1], reverse=True)
358
+ data = dict(data)
359
+ return data
360
+
361
+ def get_daily_count(incoming_data):
362
+ data = {}
363
+ for json_data in incoming_data:
364
+ if os.path.exists(json_data):
365
+ for yielded_json in file_io.read_file_json_generator(json_data):
366
+ if "logs" in yielded_json:
367
+ for each_item in yielded_json["logs"]:
368
+ if "time" in each_item:
369
+ date = convert_to_day(each_item["time"])
370
+ if date in data:
371
+ data[date] += 1
372
+ else:
373
+ data[date] = 1
374
+ else:
375
+ if "timestamp" in yielded_json:
376
+ date = convert_to_day(yielded_json["timestamp"])
377
+ if date in data:
378
+ data[date] += 1
379
+ else:
380
+ data[date] = 1
381
+ if "time" in yielded_json:
382
+ date = convert_to_day(yielded_json["time"])
383
+ if date in data:
384
+ data[date] += 1
385
+ else:
386
+ data[date] = 1
387
+ else:
388
+ for each_line in json_data:
389
+ for each_entry in each_line:
390
+ if "timestamp" in each_entry:
391
+ date = convert_to_day(each_entry["timestamp"])
392
+ if date in data:
393
+ data[date] += 1
394
+ else:
395
+ data[date] = 1
396
+ if "time" in each_entry:
397
+ date = convert_to_day(each_entry["time"])
398
+ if date in data:
399
+ data[date] += 1
400
+ else:
401
+ data[date] = 1
402
+ return data
403
+
404
+ # used to convert summary "time" or "timestamp" dictionaries so values are "datetime"
405
+ def convert_to_day(dictionary):
406
+ for key, value in dictionary.items():
407
+ if key == "time" or key == "timestamp":
408
+ new_dates[key] = {}
409
+ for time, count in value.items():
410
+ day = time_to_day(time)
411
+ if day in new_dates[key]:
412
+ new_dates[key][day] += count
413
+ else:
414
+ new_dates[key][day] = count
415
+ else:
416
+ raise ValueError(f"Unexpected key: '{key}'")
417
+ return new_dates
418
+
419
+ def time_to_day(time):
420
+ if isinstance(time, int):
421
+ day = datetime.fromtimestamp(time).astimezone(timezone.utc)
422
+ day = day.strftime('%Y-%m-%d')
423
+ elif time[-1:] == "Z":
424
+ day = datetime.fromisoformat(time[:-1]).astimezone(timezone.utc)
425
+ day = day.strftime('%Y-%m-%d')
426
+ else:
427
+ day = datetime.fromisoformat(time).astimezone(timezone.utc)
428
+ day = day.strftime('%Y-%m-%d')
429
+ return day
430
+
431
+ def get_overlapping_timeframes(honeypot_dict):
432
+ consolidated_start_time = ""
433
+ consolidated_end_time = ""
434
+ consolidated_start_date = ""
435
+ consolidated_end_date = ""
436
+
437
+ for key, value in honeypot_dict.items():
438
+ logging.info(f"Reviewing {key} honeypot firewall times")
439
+ for each_file in value["firewall"]:
440
+ logging.info(f"Reviewing file '{each_file}'")
441
+ start_time, end_time = dshield_parser.firewall_processor.reports.get_timspan(each_file)
442
+ if consolidated_start_time == "":
443
+ logging.info(f"{key} caused timing update: Setting start time to {start_time} and end time to {end_time}")
444
+ consolidated_start_time = start_time
445
+ consolidated_end_time = end_time
446
+ elif start_time > consolidated_start_time:
447
+ logging.info(f"{key} caused timing update: Updating start time from {consolidated_start_time} to {start_time}")
448
+ consolidated_start_time = start_time
449
+ elif end_time < consolidated_end_time:
450
+ logging.info(f"{key} caused timing update: Updating end time from {consolidated_end_time} to {end_time}")
451
+ consolidated_end_time = end_time
452
+ consolidated_start_date = dshield_parser.utils.json.convert_to_day(consolidated_start_time)
453
+ consolidated_end_date = dshield_parser.utils.json.convert_to_day(consolidated_end_time)
454
+ return consolidated_start_time, consolidated_end_time, consolidated_start_date, consolidated_end_date
455
+
456
+ def in_date_range(date, start_date, end_date):
457
+ if datetime(int(date[0:4]), int(date[5:7]), int(date[8:10])) >= datetime(int(start_date[0:4]),
458
+ int(start_date[5:7]), int(start_date[8:10])) and datetime(int(date[0:4]),
459
+ int(date[5:7]), int(date[8:10])) <= datetime(int(end_date[0:4]), int(end_date[5:7]), int(end_date[8:10])):
460
+ return True
461
+ return False
462
+
463
+ # used to convert summary "time" or "timestamp" dictionaries so values are "datetime"
464
+ new_dates = {}
465
+ def convert_to_datetime(dictionary):
466
+ logging.info(f"Starting to convert datetime")
467
+ for key, value in dictionary.items():
468
+ if key == "time" or key == "timestamp":
469
+ new_dates[key] = {}
470
+ for time, count in value.items():
471
+ if isinstance(time, int):
472
+ day = datetime.fromtimestamp(time).astimezone(timezone.utc)
473
+ new_dates[key][day] = count
474
+ elif time[-1:] == "Z":
475
+ day = datetime.fromisoformat(time[:-1]).astimezone(timezone.utc)
476
+ new_dates[key][day] = count
477
+ else:
478
+ day = datetime.fromisoformat(time).astimezone(timezone.utc)
479
+ new_dates[key][day] = count
480
+ else:
481
+ raise ValueError(f"Unexpected key: '{key}'")
482
+ return new_dates
483
+
484
+ def convert_to_epoch(date):
485
+ return datetime.strptime(date, "%Y-%m-%d")
486
+
487
+ def convert_to_date(timestamp):
488
+ return datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d')
489
+
490
+ # will be used when executing the script
491
+ if __name__ == "__main__":
492
+ filepath = sys.argv[1]
493
+ if os.path.exists(filepath):
494
+ # print_list(get_json_keys(sys.argv[1]))
495
+
496
+ # output dshield web honeypot data
497
+ # print_dict(get_summary_values(sys.argv[1], "url"))
498
+ print_dict(get_top_10(sys.argv[1], "url"))
499
+
500
+ # output dshield cowrie data
501
+ # print_dict(get_summary_values(sys.argv[1], "command"))
502
+ print_dict(get_top_10(sys.argv[1], "command"))
503
+
504
+ # output dshield firewall data
505
+ # print_dict(get_summary_values(sys.argv[1], "dport"))
506
+ print_dict(get_top_10(sys.argv[1], "dport"))
507
+ else:
508
+ print("Argument supplied is not a file that exists.")
@@ -0,0 +1,69 @@
1
+ import logging
2
+ import numpy as np
3
+ import pandas as pd
4
+ from sklearn.cluster import KMeans
5
+ from sklearn.decomposition import PCA
6
+ from sklearn.cluster import DBSCAN
7
+ from sklearn.decomposition import PCA
8
+ import matplotlib.pyplot as plt
9
+
10
+ def analyze_chunk(df, data, column_label, start, end, dbscan_minsamples=5, dbscan_eps=.5):
11
+ output = f'Start: {start}\n'
12
+ output += f'End: {end}\n'
13
+ output += f"Data limit: {end}\n"
14
+ output += f"Total data length: {len(df)}\n"
15
+ output += f"DBScan Min Samples: {dbscan_minsamples}\n"
16
+ output += f"DBScan EPS: {dbscan_eps}\n"
17
+ if (end > len(df)):
18
+ end = len(df) - 1
19
+ data = data[start:end]
20
+ logging.info("Starting PCA fit.")
21
+ pca = PCA(3)
22
+ pca.fit(data)
23
+ logging.info("Starting PCA transform.")
24
+ reduced = pca.transform(data)
25
+ dbscan = DBSCAN(eps=dbscan_eps, min_samples=dbscan_minsamples)
26
+ logging.info("Starting DBSCAN fit.")
27
+ dbscan.fit(reduced)
28
+ y_labels = dbscan.labels_
29
+ reduced_df = df[start:end]
30
+ reduced_df['cluster'] = y_labels
31
+ output += f'Number of Clusters: {len(np.unique(y_labels))}\n\n'
32
+ logging.info("creating text breakdown of output.")
33
+ for cluster in np.unique(y_labels):
34
+ output += str(f'Cluster {cluster}\n\n')
35
+ output += f"Number of items in cluster: {len(reduced_df.loc[reduced_df['cluster'] == cluster])}"
36
+ #output += str(df.iloc[reduced_df.loc[reduced_df['cluster'] == cluster].index[0]]) + "\n\n"
37
+ if cluster == -1:
38
+ pd.set_option('display.max_colwidth', None)
39
+ output += "\n" + reduced_df[column_label].loc[reduced_df['cluster'] == cluster].to_string() + "\n"
40
+ # for index in range(len(reduced_df.loc[reduced_df['cluster'] == cluster].index)):
41
+ # output += str(df.iloc[reduced_df.loc[reduced_df['cluster'] == cluster].index[index],1]) + "\n"
42
+ #else:
43
+ # output += str(df.iloc[reduced_df.loc[reduced_df['cluster'] == cluster].index[0],1]) + "\n"
44
+ #output += "\n" + str(reduced_df.loc[reduced_df['cluster', 0] == cluster])
45
+ else:
46
+ pd.set_option('display.max_colwidth', None)
47
+ output += "\n" + reduced_df[column_label].loc[reduced_df['cluster'] == cluster][:2].to_string() + "\n"
48
+ #pd.set_option('display.max_colwidth', 40)
49
+ pd.set_option('display.max_colwidth', None)
50
+ output += "\n" + str(reduced_df.loc[reduced_df['cluster'] == cluster])
51
+ output += "\n-----------------------------------------------------------------------\n\n"
52
+ #filehandle = open(f'{starttime}-{endtime}_clusters.txt', "w")
53
+ #filehandle.write(output)
54
+ #filehandle.close()
55
+ logging.info("Returning summary text output and dataframe with results.")
56
+ return(output, reduced_df)
57
+
58
+
59
+ def elbow_diagram(data, max_clusters=8, title=""):
60
+ wcss = []
61
+ for i in range(1, max_clusters):
62
+ kmeans = KMeans(n_clusters=i, init="k-means++", max_iter=50, n_init=10)
63
+ kmeans.fit(data)
64
+ wcss.append(kmeans.inertia_)
65
+ plt.plot(range(1, max_clusters), wcss)
66
+ plt.title(f'The Elbow Method Graph ({title})')
67
+ plt.xlabel('Number of clusters')
68
+ plt.ylabel('WCSS')
69
+ plt.show()